diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index e05f8b8f0a..b2804910cc 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -3082,14 +3082,12 @@ HopStartStatus classifyHopStart(const meshtastic_MeshPacket &p) return HopStartStatus::INVALID; if (p.hop_start == 0) { - // hop_start == hop_limit == 0: intentional zero-hop broadcast (e.g. beacon). Valid by definition - - // the packet was never meant to travel any hops, so no hop_start ambiguity applies. - if (p.hop_limit == 0) - return HopStartStatus::VALID; - // Firmware prior to 2.3.0 (585805c) lacked a hop_start field. Firmware version 2.5.0 (bf34329) introduced a - // bitfield that is always present. Use the presence of the bitfield to determine if the origin's firmware - // version is guaranteed to have hop_start populated. Note that this can only be done for decoded packets as - // the bitfield is encrypted under the channel encryption key. + // hop_start == 0 is either a modern zero-hop broadcast (e.g. beacon) or pre-2.3.0 firmware (585805c) + // that never populated hop_start. Firmware 2.5.0 (bf34329) introduced a bitfield that is always + // present; use it to tell the two apart. The bitfield is encrypted under the channel key, so this can + // only be resolved for decoded packets - until then the status stays MISSING_OR_UNKNOWN. Callers + // acting before decode must therefore not treat UNKNOWN as a drop (the bitfield isn't readable yet); + // the verdict is re-checked post-decode in Router::handleReceived. if (p.which_payload_variant == meshtastic_MeshPacket_decoded_tag && p.decoded.has_bitfield) return HopStartStatus::VALID; return HopStartStatus::MISSING_OR_UNKNOWN; diff --git a/src/mesh/NodeDB.h b/src/mesh/NodeDB.h index 8aa32dcc75..f7494811f6 100644 --- a/src/mesh/NodeDB.h +++ b/src/mesh/NodeDB.h @@ -156,7 +156,10 @@ inline bool shouldDropPacketForPreHop(const meshtastic_MeshPacket &p) if (isFromUs(&p)) { return false; // local-originated packets should never be dropped by pre-hop drop policy } - return classifyHopStart(p) != HopStartStatus::VALID; + // Pre-decode: the channel-encrypted bitfield isn't readable yet, so a missing/unknown hop_start can't be + // distinguished from a modern packet. Only drop the provably-corrupt case here; the bitfield-dependent + // verdict is re-checked post-decode in Router::handleReceived. + return classifyHopStart(p) == HopStartStatus::INVALID; #endif } diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index 67d724433f..0641cbc3cd 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -879,6 +879,18 @@ void Router::handleReceived(meshtastic_MeshPacket *p, RxSource src) else printPacket("handleReceived(REMOTE)", p); +#if MESHTASTIC_PREHOP_DROP + // Pre-hop firmware drop, post-decode half: the bitfield that proves the origin populated hop_start is + // encrypted under the channel key, so it can only be evaluated now that the packet is decoded. A packet + // whose hop_start is still missing/unknown comes from pre-hop firmware - keep it out of module + // processing, admin handling, phone delivery, MQTT and rebroadcast. Local-origin packets are exempt. + if (!isFromUs(p) && classifyHopStart(*p) != HopStartStatus::VALID) { + logHopStartDrop(*p, "post-decode pre-hop drop"); + cancelSending(p->from, p->id); + skipHandle = true; + } +#endif + // Neighbor info module is disabled, ignore expensive neighbor info packets if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag && p->decoded.portnum == meshtastic_PortNum_NEIGHBORINFO_APP &&