Harden receiver ingest against unknown senders
This commit is contained in:
@@ -15,7 +15,8 @@ enum class RxRejectReason : uint8_t {
|
||||
InvalidMsgKind = 2,
|
||||
LengthMismatch = 3,
|
||||
DeviceIdMismatch = 4,
|
||||
BatchIdMismatch = 5
|
||||
BatchIdMismatch = 5,
|
||||
UnknownSender = 6
|
||||
};
|
||||
|
||||
struct FaultCounters {
|
||||
|
||||
@@ -21,6 +21,8 @@ const char *rx_reject_reason_text(RxRejectReason reason) {
|
||||
return "device_id_mismatch";
|
||||
case RxRejectReason::BatchIdMismatch:
|
||||
return "batch_id_mismatch";
|
||||
case RxRejectReason::UnknownSender:
|
||||
return "unknown_sender";
|
||||
default:
|
||||
return "none";
|
||||
}
|
||||
|
||||
47
src/main.cpp
47
src/main.cpp
@@ -96,6 +96,8 @@ static uint32_t g_sender_sleep_ms = 0;
|
||||
static uint32_t g_sender_power_log_ms = 0;
|
||||
static RxRejectReason g_sender_rx_reject_reason = RxRejectReason::None;
|
||||
static uint32_t g_sender_rx_reject_log_ms = 0;
|
||||
static RxRejectReason g_receiver_rx_reject_reason = RxRejectReason::None;
|
||||
static uint32_t g_receiver_rx_reject_log_ms = 0;
|
||||
static MeterData g_last_meter_data = {};
|
||||
static bool g_last_meter_valid = false;
|
||||
static uint32_t g_last_meter_rx_ms = 0;
|
||||
@@ -421,6 +423,18 @@ static void sender_note_rx_reject(RxRejectReason reason, const char *context) {
|
||||
}
|
||||
}
|
||||
|
||||
static void receiver_note_rx_reject(RxRejectReason reason, const char *context) {
|
||||
if (reason == RxRejectReason::None) {
|
||||
return;
|
||||
}
|
||||
g_receiver_rx_reject_reason = reason;
|
||||
uint32_t now_ms = millis();
|
||||
if (SERIAL_DEBUG_MODE && now_ms - g_receiver_rx_reject_log_ms >= 1000) {
|
||||
g_receiver_rx_reject_log_ms = now_ms;
|
||||
serial_debug_printf("rx_reject: %s reason=%s", context, rx_reject_reason_text(reason));
|
||||
}
|
||||
}
|
||||
|
||||
static BatchBuffer *batch_queue_peek() {
|
||||
if (g_batch_count == 0) {
|
||||
return nullptr;
|
||||
@@ -1419,8 +1433,31 @@ static void receiver_loop() {
|
||||
}
|
||||
}
|
||||
|
||||
bool duplicate = sender_idx >= 0 && g_last_batch_id_rx[sender_idx] == batch_id;
|
||||
if (sender_idx >= 0) {
|
||||
if (sender_idx < 0) {
|
||||
receiver_note_rx_reject(RxRejectReason::UnknownSender, "batch");
|
||||
note_fault(g_receiver_faults, g_receiver_last_error, g_receiver_last_error_utc, g_receiver_last_error_ms, FaultType::Decode);
|
||||
display_set_last_error(g_receiver_last_error, g_receiver_last_error_utc, g_receiver_last_error_ms);
|
||||
serial_debug_printf("batch: reject unknown_sender short_id=%04X sender_id=%u batch_id=%u",
|
||||
pkt.device_id_short,
|
||||
static_cast<unsigned>(batch.sender_id),
|
||||
static_cast<unsigned>(batch_id));
|
||||
goto receiver_loop_done;
|
||||
}
|
||||
|
||||
uint16_t expected_sender_id = static_cast<uint16_t>(sender_idx + 1);
|
||||
if (batch.sender_id != expected_sender_id) {
|
||||
receiver_note_rx_reject(RxRejectReason::DeviceIdMismatch, "batch");
|
||||
note_fault(g_receiver_faults, g_receiver_last_error, g_receiver_last_error_utc, g_receiver_last_error_ms, FaultType::Decode);
|
||||
display_set_last_error(g_receiver_last_error, g_receiver_last_error_utc, g_receiver_last_error_ms);
|
||||
serial_debug_printf("batch: reject device_id_mismatch short_id=%04X sender_id=%u expected=%u batch_id=%u",
|
||||
pkt.device_id_short,
|
||||
static_cast<unsigned>(batch.sender_id),
|
||||
static_cast<unsigned>(expected_sender_id),
|
||||
static_cast<unsigned>(batch_id));
|
||||
goto receiver_loop_done;
|
||||
}
|
||||
|
||||
bool duplicate = g_last_batch_id_rx[sender_idx] == batch_id;
|
||||
SenderStatus &status = g_sender_statuses[sender_idx];
|
||||
if (status.rx_batches_total < UINT32_MAX) {
|
||||
status.rx_batches_total++;
|
||||
@@ -1435,14 +1472,12 @@ static void receiver_loop() {
|
||||
}
|
||||
status.rx_last_duplicate_ts_utc = duplicate_ts;
|
||||
}
|
||||
}
|
||||
|
||||
send_batch_ack(batch_id, batch.n);
|
||||
if (duplicate) {
|
||||
goto receiver_loop_done;
|
||||
}
|
||||
if (sender_idx >= 0) {
|
||||
g_last_batch_id_rx[sender_idx] = batch_id;
|
||||
}
|
||||
if (batch.n == 0) {
|
||||
goto receiver_loop_done;
|
||||
}
|
||||
@@ -1520,7 +1555,6 @@ static void receiver_loop() {
|
||||
goto receiver_loop_done;
|
||||
}
|
||||
|
||||
if (sender_idx >= 0) {
|
||||
web_server_set_last_batch(static_cast<uint8_t>(sender_idx), samples, count);
|
||||
for (size_t s = 0; s < count; ++s) {
|
||||
mqtt_publish_state(samples[s]);
|
||||
@@ -1539,7 +1573,6 @@ static void receiver_loop() {
|
||||
publish_faults_if_needed(samples[count - 1].device_id, g_sender_faults_remote[sender_idx], g_sender_faults_remote_published[sender_idx],
|
||||
g_sender_last_error_remote[sender_idx], g_sender_last_error_remote_published[sender_idx],
|
||||
g_sender_last_error_remote_utc[sender_idx], g_sender_last_error_remote_ms[sender_idx]);
|
||||
}
|
||||
} else if (decode_error) {
|
||||
note_fault(g_receiver_faults, g_receiver_last_error, g_receiver_last_error_utc, g_receiver_last_error_ms, FaultType::Decode);
|
||||
display_set_last_error(g_receiver_last_error, g_receiver_last_error_utc, g_receiver_last_error_ms);
|
||||
|
||||
@@ -698,7 +698,7 @@ static void handle_manual() {
|
||||
html += "<li>RSSI/SNR: LoRa link quality from last packet.</li>";
|
||||
html += "<li>err_tx: sender-side LoRa TX error counter.</li>";
|
||||
html += "<li>err_last: last error code (0=None, 1=MeterRead, 2=Decode, 3=LoraTx).</li>";
|
||||
html += "<li>rx_reject: last RX reject reason (0=None, 1=crc_fail, 2=invalid_msg_kind, 3=length_mismatch, 4=device_id_mismatch, 5=batch_id_mismatch).</li>";
|
||||
html += "<li>rx_reject: last RX reject reason (0=None, 1=crc_fail, 2=invalid_msg_kind, 3=length_mismatch, 4=device_id_mismatch, 5=batch_id_mismatch, 6=unknown_sender).</li>";
|
||||
html += "<li>faults m/d/tx: receiver-side counters (meter read fails, decode fails, LoRa TX fails).</li>";
|
||||
html += "<li>faults last: last receiver-side error code (same mapping as err_last).</li>";
|
||||
html += "</ul>";
|
||||
|
||||
Reference in New Issue
Block a user