Harden firmware recovery and retries

Improve companion BLE delivery and idempotent remote CLI retries, make FPF7 persistence atomic with FPF6 migration, verify staged OTA data on resume, and clarify filter policy wildcard and BlackHole semantics.
This commit is contained in:
mikecarper committed 2026-08-07 16:24:53 -07:00
1 parent 7f56da7a34
commit 11b692abfa
37 files changed
+1666 -307

No files matched your search

+41 -6
View File
@@ -740,7 +740,7 @@ void MyMesh::onPeerDataRecv(mesh::Packet *packet, uint8_t type, int sender_idx,
const char* text = (const char*)&data[5];
const size_t text_len = strlen(text);
uint8_t temp[166];
uint8_t temp[5 + mesh::RemoteCliReplyCache::MAX_REPLY_TEXT + 1];
temp[5] = 0;
bool send_ack = false;
@@ -776,17 +776,52 @@ void MyMesh::onPeerDataRecv(mesh::Packet *packet, uint8_t type, int sender_idx,
send_ack = true;
}
} else { // TXT_TYPE_CLI_DATA
if (sender_timestamp < client->last_timestamp) {
uint32_t request_id = sender_timestamp;
mesh::RemoteCliRequest::parse(data, len, 5, request_id);
const uint32_t command_fingerprint =
mesh::RemoteCliReplyCache::fingerprint(text, text_len);
const char* cached_response = NULL;
const bool cached_retry = remote_cli_reply_cache.lookup(
client->id.pub_key, request_id, command_fingerprint,
&cached_response);
if (sender_timestamp < client->last_timestamp && !cached_retry) {
MESH_DEBUG_PRINTLN("onPeerDataRecv: possible replay attack detected");
return;
}
const bool is_retry = sender_timestamp == client->last_timestamp;
client->last_timestamp = sender_timestamp;
if (client->isAdmin() && !is_retry) {
const bool repeated_timestamp = sender_timestamp == client->last_timestamp;
if (sender_timestamp > client->last_timestamp) {
client->last_timestamp = sender_timestamp;
}
if (cached_retry) {
MESH_DEBUG_PRINTLN("onPeerDataRecv: replaying cached remote CLI reply");
size_t cached_len = strlen(cached_response);
if (cached_len > mesh::RemoteCliReplyCache::MAX_REPLY_TEXT) {
cached_len = mesh::RemoteCliReplyCache::MAX_REPLY_TEXT;
}
memcpy(&temp[5], cached_response, cached_len);
temp[5 + cached_len] = 0;
temp[4] = (TXT_TYPE_CLI_DATA << 2);
} else if (repeated_timestamp) {
MESH_DEBUG_PRINTLN("onPeerDataRecv: duplicate remote CLI request has no cached reply");
return;
} else if (client->isAdmin()) {
handleCommand(sender_timestamp, (char*)text, (char*)&temp[5],
i, packet->getPathHashSize());
temp[4] = (TXT_TYPE_CLI_DATA << 2); // attempt and flags, (NOTE: legacy was: TXT_TYPE_PLAIN)
temp[5 + mesh::RemoteCliReplyCache::MAX_REPLY_TEXT] = 0;
if (temp[5] == 0) strcpy((char*)&temp[5], "OK");
remote_cli_reply_cache.remember(client->id.pub_key, request_id,
command_fingerprint,
(char*)&temp[5]);
temp[4] = (TXT_TYPE_CLI_DATA << 2);
} else {
const char* error = "Err - admin permission required";
strcpy((char*)&temp[5], error);
remote_cli_reply_cache.remember(client->id.pub_key, request_id,
command_fingerprint, error);
temp[4] = (TXT_TYPE_CLI_DATA << 2);
}
// CLI_DATA replies are the result signal; no separate ACK is expected.
}