Enable network direct dial by default

master
Simon 3 weeks ago
parent 2fa05ae0a3
commit 923069deee

@ -61,7 +61,7 @@ SINGLE_MODE: True
VOICE_IDENT: True
DIAL_A_TG: True
DYNAMIC_TG_ROUTING: True
NETWORK_DIRECT_DIAL_SLOT: 0
NETWORK_DIRECT_DIAL_SLOT: 1
TS1_STATIC:
TS2_STATIC:
DEFAULT_DIAL_TS1: 0

@ -275,7 +275,7 @@ def activate_network_direct_dial(_dst_id, pkt_time):
if CONFIG['SYSTEMS'][system]['MODE'] != 'MASTER':
continue
slot = CONFIG['SYSTEMS'][system].get('NETWORK_DIRECT_DIAL_SLOT', 0)
slot = CONFIG['SYSTEMS'][system].get('NETWORK_DIRECT_DIAL_SLOT', 1)
if slot not in (1, 2):
continue
@ -1516,7 +1516,7 @@ def options_config():
if 'DYNAMIC_TG_ROUTING' not in _options:
_options['DYNAMIC_TG_ROUTING'] = int(CONFIG['SYSTEMS'][_system].get('DYNAMIC_TG_ROUTING', True))
if 'NETWORK_DIRECT_DIAL_SLOT' not in _options:
_options['NETWORK_DIRECT_DIAL_SLOT'] = CONFIG['SYSTEMS'][_system].get('NETWORK_DIRECT_DIAL_SLOT', 0)
_options['NETWORK_DIRECT_DIAL_SLOT'] = CONFIG['SYSTEMS'][_system].get('NETWORK_DIRECT_DIAL_SLOT', 1)
_default_dial_ts1 = parse_default_reflector_option(_options['DEFAULT_DIAL_TS1'], default=None)
if _default_dial_ts1 is None:
@ -1590,7 +1590,7 @@ def options_config():
_network_direct_dial_slot = parse_network_direct_dial_slot(_options['NETWORK_DIRECT_DIAL_SLOT'], default=None)
if _network_direct_dial_slot is None:
logger.debug('(OPTIONS) %s - NETWORK_DIRECT_DIAL_SLOT is not 0, 1, or 2, ignoring',_system)
_network_direct_dial_slot = CONFIG['SYSTEMS'][_system].get('NETWORK_DIRECT_DIAL_SLOT', 0)
_network_direct_dial_slot = CONFIG['SYSTEMS'][_system].get('NETWORK_DIRECT_DIAL_SLOT', 1)
if 'TS1_STATIC' not in _options or 'TS2_STATIC' not in _options or 'DEFAULT_DIAL_TS1' not in _options or 'DEFAULT_DIAL_TS2' not in _options or 'DEFAULT_UA_TIMER' not in _options:
logger.debug('(OPTIONS) %s - Required field missing, ignoring',_system)
@ -1653,7 +1653,7 @@ def options_config():
if _dynamic_tg_routing != CONFIG['SYSTEMS'][_system].get('DYNAMIC_TG_ROUTING', True):
logger.debug('(OPTIONS) %s DYNAMIC_TG_ROUTING changed to %s',_system,_dynamic_tg_routing)
if _network_direct_dial_slot != CONFIG['SYSTEMS'][_system].get('NETWORK_DIRECT_DIAL_SLOT', 0):
if _network_direct_dial_slot != CONFIG['SYSTEMS'][_system].get('NETWORK_DIRECT_DIAL_SLOT', 1):
logger.debug('(OPTIONS) %s NETWORK_DIRECT_DIAL_SLOT changed to %s',_system,_network_direct_dial_slot)
ts1 = []

@ -49,14 +49,14 @@ logger = logging.getLogger(__name__)
def config_network_direct_dial_slot(config, section):
try:
slot = config.getint(section, 'NETWORK_DIRECT_DIAL_SLOT', fallback=0)
slot = config.getint(section, 'NETWORK_DIRECT_DIAL_SLOT', fallback=1)
except ValueError:
logger.warning('(%s) NETWORK_DIRECT_DIAL_SLOT is not an integer, defaulting to 0', section)
return 0
logger.warning('(%s) NETWORK_DIRECT_DIAL_SLOT is not an integer, defaulting to 1', section)
return 1
if slot not in (0, 1, 2):
logger.warning('(%s) NETWORK_DIRECT_DIAL_SLOT is not 0, 1, or 2, defaulting to 0', section)
return 0
logger.warning('(%s) NETWORK_DIRECT_DIAL_SLOT is not 0, 1, or 2, defaulting to 1', section)
return 1
return slot
def master_timer_config(config, section):

@ -61,7 +61,7 @@ SINGLE_MODE: True
VOICE_IDENT: True
DIAL_A_TG: True
DYNAMIC_TG_ROUTING: True
NETWORK_DIRECT_DIAL_SLOT: 0
NETWORK_DIRECT_DIAL_SLOT: 1
TS1_STATIC:
TS2_STATIC:
DEFAULT_DIAL_TS1: 0

@ -16,12 +16,12 @@ Findings:
routing policy knob for dynamic/dial behavior.
Assumptions:
- `NETWORK_DIRECT_DIAL_SLOT=0` means disabled; `1` presents TG9 TS1; `2`
presents TG9 TS2 for DMO/simplex clients.
- `NETWORK_DIRECT_DIAL_SLOT=1` is the default and presents TG9 TS1; `0` means
disabled; `2` presents TG9 TS2 for DMO/simplex clients.
- Network direct dial is client/session-overridable through `OPTIONS`; the
compact alias is `NDD_SLOT`.
- RF-originated traffic must not activate network direct dial.
- Invalid `NETWORK_DIRECT_DIAL_SLOT` values in config should fall back to `0`
- Invalid `NETWORK_DIRECT_DIAL_SLOT` values in config should fall back to `1`
and log a warning; invalid OPTIONS values should leave the current session
value unchanged.

@ -100,7 +100,7 @@ NDD_SLOT=2
| `TS2_TG_TIMER` | Slot 2 conventional TG timeout in minutes. |
| `TS1_DIAL_TIMER` | Slot 1 dial-a-TG timeout in minutes. |
| `TS2_DIAL_TIMER` | Slot 2 dial-a-TG timeout in minutes. |
| `NETWORK_DIRECT_DIAL_SLOT` | Network direct dial slot. `0` disables it, `1` presents on TG9 TS1, `2` presents on TG9 TS2. |
| `NETWORK_DIRECT_DIAL_SLOT` | Network direct dial slot. Default is `1`. `0` disables it, `1` presents on TG9 TS1, `2` presents on TG9 TS2. |
| `DIALTG` | Enable/disable dial-a-TG private-call control. Use `1` or `0`. |
| `DYNAMIC` | Enable/disable automatic dynamic conventional TG routing. Use `1` or `0`. |
| `VOICE` | Enable/disable voice ident. Use `1` or `0`. |
@ -160,7 +160,7 @@ KEY=secret;TS1=91,92;TS2=235;DIAL1=4400;DIAL2=2350;TGTO1=10;TGTO2=10;DIALTO1=30;
configured slot. It does not enable general private voice routing.
- Network direct dial does not override an active or hangtime user-selected
dial-a-TG route.
- Invalid `NETWORK_DIRECT_DIAL_SLOT` values in server config fall back to `0`
- Invalid `NETWORK_DIRECT_DIAL_SLOT` values in server config fall back to `1`
and are logged. Invalid `NDD_SLOT`/`NETWORK_DIRECT_DIAL_SLOT` values in
client `OPTIONS` are ignored and the current session value remains in use.
- `DEFAULT_REFLECTOR`, `DIAL`, and `StartRef` only affect the slot-2 default.

@ -146,9 +146,10 @@ for rule timeout checks without sleeping.
- Network direct dial behaviour: when `NETWORK_DIRECT_DIAL_SLOT`/`NDD_SLOT` is
set to `1` or `2`, network-originated group traffic to the connected
repeater/hotspot ID may activate local TG9 on that slot. Tests must verify it
is disabled by default, does not affect RF-originated traffic, does not
override active/hangtime user-selected dial-a-TG state, and uses the existing
TG9 rewrite path rather than a new packet mutation path.
defaults to TS1, can be explicitly disabled with `0`, does not affect
RF-originated traffic, does not override active/hangtime user-selected
dial-a-TG state, and uses the existing TG9 rewrite path rather than a new
packet mutation path.
- Static TG configuration bugs: startup and live options reload should reject
prohibited local/control TGs consistently on both TS1 and TS2 after parsing the
configured TG strings to integers. Invalid IDs at or above `16777215` should

@ -91,12 +91,13 @@ Invalid specific timer fields are logged and ignored so valid broader timer
fields still apply. Voice ident override coverage verifies valid
override TGs are used, empty/false overrides use all-call, and malformed,
control, or all-call override values are logged and fall back to all-call.
Network direct dial coverage verifies the feature is disabled by default,
routes network-originated calls to a connected repeater/hotspot ID as RF-visible
TG9 on the configured slot, supports TS2 for DMO/simplex clients, does not
override a user-selected dial-a-TG route, can replace an idle configured default
dial route, and is not activated by RF-originated traffic. OPTIONS coverage also
verifies compact aliases and overlength `OPTIONS` rejection.
Network direct dial coverage verifies the feature defaults to TS1, can be
explicitly disabled, routes network-originated calls to a connected
repeater/hotspot ID as RF-visible TG9 on the configured slot, supports TS2 for
DMO/simplex clients, does not override a user-selected dial-a-TG route, can
replace an idle configured default dial route, and is not activated by
RF-originated traffic. OPTIONS coverage also verifies compact aliases and
overlength `OPTIONS` rejection.
Generated voice prompt helper coverage verifies prompt stream state is attached
to the router instance sending the prompt, even if a stale module-level
`system` variable names another master. This protects dial-a-TG prompts, idents,

@ -30,7 +30,7 @@
acknowledged but ignored so clients do not reconnect-loop and oversized
strings do not enter session state.
- Added startup validation for `NETWORK_DIRECT_DIAL_SLOT`; invalid values fall
back to disabled (`0`) and are logged.
back to the default TS1 setting (`1`) and are logged.
## Dial-a-TG
@ -47,10 +47,13 @@
- Preserved the current FreeDMR dial-a-TG policy cap of `999999`.
- Ensured FBP route targets created for dial-a-TG remain active across local
retunes/disconnects, in line with the mesh "everything everywhere" model.
- Added opt-in network direct dial: a network-originated group call to a
- Added network direct dial: a network-originated group call to a
connected repeater/hotspot's own ID can be presented locally as TG9 on the
configured slot. This is configured with `NETWORK_DIRECT_DIAL_SLOT` or
`NDD_SLOT` (`0` disabled, `1` TG9 TS1, `2` TG9 TS2).
- Changed network direct dial to default to enabled on TG9 TS1. Sysops can set
`NETWORK_DIRECT_DIAL_SLOT: 0`, or clients can send `NDD_SLOT=0`, to disable it
for a session.
- Network direct dial is a deliberate, narrow exception where network traffic
may activate the local RF TG9 presentation path. It does not override an
active or hangtime user-selected dial-a-TG route and does not apply to

@ -61,7 +61,7 @@ SINGLE_MODE: True
VOICE_IDENT: True
DIAL_A_TG: True
DYNAMIC_TG_ROUTING: True
NETWORK_DIRECT_DIAL_SLOT: 0
NETWORK_DIRECT_DIAL_SLOT: 1
TS1_STATIC:
TS2_STATIC:
DEFAULT_DIAL_TS1: 0

@ -315,7 +315,7 @@ def minimal_config(system_names: tuple[str, ...] = ("MASTER-A", "MASTER-B")) ->
"VOICE_IDENT": False,
"DIAL_A_TG": True,
"DYNAMIC_TG_ROUTING": True,
"NETWORK_DIRECT_DIAL_SLOT": 0,
"NETWORK_DIRECT_DIAL_SLOT": 1,
"TS1_STATIC": "",
"TS2_STATIC": "",
"DEFAULT_DIAL_TS1": 0,

@ -469,7 +469,7 @@ def write_bridge_master_config(
ts2_static: str = "91",
dial_a_tg: bool = True,
dynamic_tg_routing: bool = True,
network_direct_dial_slot: int = 0,
network_direct_dial_slot: int = 1,
master_extra_config: str = "",
) -> None:
global_use_acl_text = "True" if global_use_acl else "False"
@ -976,7 +976,7 @@ class UdpBlackBoxScenario:
ts2_static: str = "91",
dial_a_tg: bool = True,
dynamic_tg_routing: bool = True,
network_direct_dial_slot: int = 0,
network_direct_dial_slot: int = 1,
master_extra_config: str = "",
fbp_systems: dict[str, int] | None = None,
fbp_proto_versions: dict[str, int] | None = None,

@ -84,7 +84,7 @@ DEFAULT_REFLECTOR: 0
self.assertEqual(parsed["ALIASES"]["STALE_TIME"], 86400)
self.assertTrue(parsed["SYSTEMS"]["MASTER-A"]["DIAL_A_TG"])
self.assertTrue(parsed["SYSTEMS"]["MASTER-A"]["DYNAMIC_TG_ROUTING"])
self.assertEqual(parsed["SYSTEMS"]["MASTER-A"]["NETWORK_DIRECT_DIAL_SLOT"], 0)
self.assertEqual(parsed["SYSTEMS"]["MASTER-A"]["NETWORK_DIRECT_DIAL_SLOT"], 1)
self.assertEqual(parsed["SYSTEMS"]["MASTER-A"]["DEFAULT_DIAL_TS1"], 0)
self.assertEqual(parsed["SYSTEMS"]["MASTER-A"]["DEFAULT_DIAL_TS2"], 0)
@ -142,7 +142,7 @@ DEFAULT_DIAL_TS2: 92
self.assertEqual(parsed["SYSTEMS"]["MASTER-A"]["DEFAULT_DIAL_TS2"], 92)
self.assertEqual(parsed["SYSTEMS"]["MASTER-A"]["DEFAULT_REFLECTOR"], 92)
def test_config_invalid_network_direct_dial_slot_defaults_to_disabled(self):
def test_config_invalid_network_direct_dial_slot_defaults_to_ts1(self):
for value in ("3", "A"):
with self.subTest(value=value):
config_text = f"""
@ -168,7 +168,7 @@ NETWORK_DIRECT_DIAL_SLOT: {value}
finally:
os.unlink(path)
self.assertEqual(parsed["SYSTEMS"]["MASTER-A"]["NETWORK_DIRECT_DIAL_SLOT"], 0)
self.assertEqual(parsed["SYSTEMS"]["MASTER-A"]["NETWORK_DIRECT_DIAL_SLOT"], 1)
self.assertIn("NETWORK_DIRECT_DIAL_SLOT", "\n".join(logs.output))
def test_set_alias_updates_bridge_master_globals_and_shared_hblink_config(self):
@ -1342,8 +1342,9 @@ NETWORK_DIRECT_DIAL_SLOT: {value}
self.assertEqual(fbp_entry["TO_TYPE"], "NONE")
self.assertEqual(scenario.capture.packets, [])
def test_network_direct_dial_is_disabled_by_default(self):
def test_network_direct_dial_can_be_explicitly_disabled(self):
config = minimal_config(("MASTER-A",))
config["SYSTEMS"]["MASTER-A"]["NETWORK_DIRECT_DIAL_SLOT"] = 0
add_openbridge_system(config, "OBP-1", network_id=3001)
with DeterministicScenario(config=config) as scenario:
@ -1355,9 +1356,8 @@ NETWORK_DIRECT_DIAL_SLOT: {value}
self.assertNotIn("#1001", scenario.bridge_state)
self.assertEqual(scenario.capture.for_system("MASTER-A"), [])
def test_network_direct_dial_routes_repeater_id_to_tg9_on_configured_slot(self):
def test_network_direct_dial_routes_repeater_id_to_tg9_on_default_ts1(self):
config = minimal_config(("MASTER-A",))
config["SYSTEMS"]["MASTER-A"]["NETWORK_DIRECT_DIAL_SLOT"] = 1
add_openbridge_system(config, "OBP-1", network_id=3001)
with DeterministicScenario(config=config) as scenario:

Loading…
Cancel
Save

Powered by TurnKey Linux.