fix: apply OPTIONS filter on all MASTER downlink paths

Remove inject-only bypass in _peer_should_receive_dmrd so peers with
empty OPTIONS (silent witness) never receive group DMRD. Align
peer_listen_slots for simplex with static TG lists.
pull/29/head
Rodrigo Pérez 3 months ago
parent b4ecf1244f
commit b914dda2f8

@ -90,9 +90,15 @@ def peer_listen_slots(peer: dict[str, Any], tgid: int) -> list[int]:
"""Voice slots where this peer listens for ``tgid`` (static OPTIONS or wire fallback)."""
from adn_server.application.report.payloads import parse_peer_options_static
if peer_is_simplex(peer):
return [SIMPLEX_VOICE_SLOT]
ts1, ts2 = parse_peer_options_static(peer.get("OPTIONS"))
if peer_is_simplex(peer):
tg = str(tgid)
if tg in ts1 or tg in ts2:
return [SIMPLEX_VOICE_SLOT]
static = peer_options_static_tg_slot(peer, tgid)
if static is not None:
return [static]
return []
tg = str(tgid)
in_ts1 = tg in ts1
in_ts2 = tg in ts2

@ -549,13 +549,13 @@ class HBPProtocol(DatagramProtocol):
return True
def _peer_should_receive_dmrd(self, peer_id: bytes, packet: bytes) -> bool:
if not self._inject_multi_peer_options_filter():
return True
if peer_id not in self._peers:
return False
parsed = parse_dmrd_route_fields(packet)
if parsed is None:
return self._cached_connected_peer_count() <= 1
if self._inject_multi_peer_options_filter():
return self._cached_connected_peer_count() <= 1
return True
slot, tgid, call_type = parsed
if call_type not in ("group", "vcsbk"):
return True

@ -73,6 +73,26 @@ def test_repeat_only_reaches_peers_with_matching_options() -> None:
assert other_pkts == []
def test_standalone_master_blocks_empty_options_witness() -> None:
"""Bridge MASTER without inject-only index still filters by OPTIONS (silent witness)."""
stack = build_hbp_repeat_stack(talker_alias=False, system_name="MASTER-A")
silent = bytes_4(730039263)
addr_silent = ("10.0.0.13", 62013)
stack.register_peer(_PEER_TX, _ADDR_TX, options=f"TS2={_TG};")
stack.register_peer(_PEER_RX_MATCH, _ADDR_MATCH, options=f"TS2={_TG};")
stack.register_peer(silent, addr_silent, options="SINGLE=0;")
stack.hbp._refresh_connected_peer_count()
assert not stack.hbp._inject_multi_peer_options_filter()
stack.transport.clear()
stack.hbp.send_peers(_voice_burst())
match_pkts = [p for p in stack.transport.for_addr(_ADDR_MATCH) if p[:4] == DMRD]
silent_pkts = [p for p in stack.transport.for_addr(addr_silent) if p[:4] == DMRD]
assert len(match_pkts) == 1
assert silent_pkts == []
def test_bridge_downlink_after_obp_tx_stamp_reaches_matching_peer() -> None:
"""OBP→MASTER send_peers must deliver when bridge leg stamped TX on STATUS."""
stack = _inject_proxy_stack()

@ -50,8 +50,10 @@ _EMB_SLICE = slice(116, 148)
def _proxy_fanin_stack() -> tuple[ProxyFanInProtocol, HbpRepeatStack]:
stack = build_hbp_repeat_stack(talker_alias=True, system_name="MASTER-A")
stack.register_peer(_PEER_TX, _ADDR_TX)
stack.register_peer(_PEER_RX, _ADDR_RX)
stack.config["PROXY"] = {"TARGET_SYSTEM": "MASTER-A"}
stack.hbp._CONFIG = stack.config
stack.register_peer(_PEER_TX, _ADDR_TX, options="TS2=7304;")
stack.register_peer(_PEER_RX, _ADDR_RX, options="TS2=7304;")
proxy = ProxyUseCases(
InMemoryProxySlotStore(),
@ -98,7 +100,7 @@ def test_proxy_attach_binds_sockaddr_used_for_master_ingress() -> None:
fanin, stack = _proxy_fanin_stack()
ensure_system_runtime_config(stack.config)
packet = DeterministicScenario.voice_head_spec(
PacketSpec(peer_id=730039210, stream_id=0x55667788)
PacketSpec(peer_id=730039210, dst_id=7304, stream_id=0x55667788)
).data()
fanin.datagramReceived(packet, _ADDR_TX)

Loading…
Cancel
Save

Powered by TurnKey Linux.