From 62225c9f07650078f1338461f1bb9a2d063e3691 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rodrigo=20P=C3=A9rez?= Date: Wed, 19 Aug 2026 09:52:11 -0400 Subject: [PATCH] fix: stop unit calls to 4000 from being broadcast to every peer TG/ID 4000 is the dynamic-TG reset control code and can never appear in _SUB_MAP, so the unknown-destination fallback in _pvt_repeat_targets treated it as a private call to an unlocated radio and blasted it to every connected peer. That fallback runs before dmrd_received's own dst_id == 4000 guard, so the control code has to be filtered at the targeting step too. --- .../twisted_adapters/udp_hbp.py | 10 ++++++++++ .../test_hbp_private_call_targeting.py | 20 +++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/src/adn_server/infrastructure/twisted_adapters/udp_hbp.py b/src/adn_server/infrastructure/twisted_adapters/udp_hbp.py index ca181aa..1675633 100644 --- a/src/adn_server/infrastructure/twisted_adapters/udp_hbp.py +++ b/src/adn_server/infrastructure/twisted_adapters/udp_hbp.py @@ -436,12 +436,22 @@ class HBPProtocol(DatagramProtocol): to blast the call to peers it can't possibly be for. Returns a 1-tuple with the exact peer_id when it's still connected here. + TG/ID 4000 is the dynamic-TG reset control code (see + ``_handle_tg4000_packet``/``routing_use_cases.dmrd_received``), never + a real subscriber -- it can never appear in _SUB_MAP, so it would + otherwise always fall into the "unknown destination" broadcast + fallback above and get blasted to every peer. That fallback runs + before dmrd_received's own dst_id == 4000 guard ever sees the + packet, so it must be special-cased here too. + The report_slot / "SYSTEM-N" monitor display name is NOT used for this — it's cosmetic and can be reassigned to a different peer across refreshes (self-service peers without a stable report_slot fall back to a sorted-by-id allocation recomputed each time). The raw peer_id is what's actually stable. """ + if int_id(dst_id) == 4000: + return () sub_map = self._CONFIG.get("_SUB_MAP") if not sub_map: return None diff --git a/tests/infrastructure/test_hbp_private_call_targeting.py b/tests/infrastructure/test_hbp_private_call_targeting.py index 3f9f951..33d493e 100644 --- a/tests/infrastructure/test_hbp_private_call_targeting.py +++ b/tests/infrastructure/test_hbp_private_call_targeting.py @@ -31,6 +31,8 @@ parity) -- see test_hbp_repeat_private_call.py.""" from __future__ import annotations +import dataclasses + from tests.harness.deterministic import DeterministicScenario, PacketSpec from tests.support.hbp_repeat_stack import build_hbp_repeat_stack @@ -110,6 +112,24 @@ def test_private_call_not_delivered_locally_when_known_on_different_system() -> assert not stack.transport.for_addr(_ADDR_OTHER), "destination known elsewhere must not repeat locally" +def test_private_call_to_4000_not_broadcast() -> None: + """TG/ID 4000 is the dynamic-TG reset control code, never a real subscriber -- + it can never appear in _SUB_MAP, so without a special case it always fell into + the "unknown destination" broadcast fallback and reached every connected peer + instead of being stopped at the server (dmrd_received's own dst_id == 4000 + guard runs too late: _pvt_repeat_targets/the REPEAT loop already ran).""" + stack = build_hbp_repeat_stack(talker_alias=True) + stack.register_peer(_PEER_TX, _ADDR_TX, options="TS2=7304;") + stack.register_peer(_PEER_RX, _ADDR_RX, options="TS2=7304;") + stack.register_peer(_PEER_OTHER, _ADDR_OTHER, options="TS2=7304;") + + base = dataclasses.replace(_private_spec(), dst_id=4000) + _fire_private_call(stack, base) + + assert not stack.transport.for_addr(_ADDR_RX), "TG 4000 must not be broadcast to any peer" + assert not stack.transport.for_addr(_ADDR_OTHER), "TG 4000 must not be broadcast to any peer" + + def test_sub_map_entries_for_peer_purged_on_reconnect() -> None: """Once a hotspot finishes (re)logging in, any SUB_MAP entry pointing at it is stale -- the radio may have moved to a different hotspot while this one was