An XLX system is a MODE: OUTBOUND system with an XLX_MODULE letter set. XLX's
DMR interface is the homebrew protocol unmodified -- DMRGateway uses the same
CDMRNetwork class for XLX and BrandMeister, and there are no XLX conditionals in
its network layer -- so this adds no new mode, subclass, or data path. The only
protocol difference is one in-band private call at connect that selects the
module, plus pinning the connection to TS2/TG9.
Module binding is static by design. Runtime or end-user module selection would
break the nailed-up systems/bridges model; DMRGateway is the right tool for
user-selectable modules at a repeater or hotspot.
TS2/TG9 is injected by expand_xlx_bridges rather than read from rules.py, so
XLX_BRIDGES is a flat {system: bridge} map with no TS or TGID column. This is
not a default -- it makes a wrong value inexpressible. That matters more here
than elsewhere because every XLX failure is silent: the reflector never
acknowledges a link, and no frame on the wire carries a module identity, so a
mistyped talkgroup would leave HBlink looking healthy while traffic vanished.
The existing conference-bridge retarget path already rewrites the header dst,
flips the slot bit, and regenerates the full and embedded LC, so no XLX-specific
rewrite code is needed -- an XLX system is just a bridge member with a fixed
TS/TGID.
An XLX system in UNIT is a startup error. Reflector module selection *is* a
private call to 4001-4026, so forwarding a unit call to one could silently move
the reflector into a different room for every user connected to it.
Behaviour verified against xlxd source (src/cdmrmmdvmprotocol.cpp):
* Five acceptance gates at :636-655, each of which silently drops the packet:
exactly 55 bytes, DATA_SYNC frame type, TS2, slot type VOICE_LC_HEADER, and
a valid BS/MS data sync. The 55-byte requirement is why the packet carries
two trailing pad bytes; a bare 53-byte DMRD is rejected.
* Only the three header frames are parsed for the link command; the two
terminators have slot type 2 and are ignored, but are sent to frame the call.
* Unlink-then-link is required, not defensive. At :297-325 a link naming a
different module while already linked is discarded and rewritten to the
module already held, so a reconnect that skipped the unlink would silently
stay in the previous room.
* xlxd reads the destination from DMRD header bytes 8-10 and never decodes the
LC (its BPTC decode is commented out at :657-660). The LC is still built
honestly from the same src/dst: a packet whose payload contradicts its header
is a debugging trap for anything that does decode it.
Note that a stock xlxd is built with NB_OF_MODULES = 10, so only modules A-J
exist unless the operator rebuilt it. A-Z is accepted here since the protocol
allows it, but an absent module produces no link and no error.
tests/test_xlx_link.py adds 35 tests: the acceptance gates transcribed from
xlxd's parser, packet structure, XLX_BRIDGES expansion and its guards, and
XLX_MODULE validation. One test reproduces the canned bursts from the 2019
reference implementation byte for byte, from their own decoded src/dst and
colour code, which pins the burst builder against six years of field-proven
behaviour.
No new dependencies: bitarray and dmr_utils3 were already required.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
193 lines
6.6 KiB
INI
Executable File
193 lines
6.6 KiB
INI
Executable File
# HBlink3 main configuration. Full field reference: CONFIGURING.md ACL guide: ACLS.md
|
|
#
|
|
# NOTE: every [SECTION] name below is referenced by rules.py. If a name in rules.py has
|
|
# no matching system here, bridge.py will NOT start. Keep the two files in sync.
|
|
#
|
|
# [GLOBAL] — program-wide timers plus the global ACLs (run before per-system ACLs;
|
|
# both must pass). ACL format: ACTION:list, e.g. PERMIT:ALL, DENY:1, DENY:1-2000,17.
|
|
# Leave PATH alone unless you must change it.
|
|
[GLOBAL]
|
|
PATH: ./
|
|
PING_TIME: 5
|
|
MAX_MISSED: 3
|
|
# Chronic connectivity monitoring (optional; these defaults apply if omitted).
|
|
# The dashboard reports each repeater's ping loss -- the percentage of its
|
|
# expected keepalive pings that went missing -- over the last PING_LOSS_WINDOW
|
|
# minutes (1-60; larger = smoother/longer memory), and turns a repeater's
|
|
# callsign gold at or above PING_LOSS_WARN percent. This catches a repeater that
|
|
# stays connected but has a lossy link (choppy/dropped audio). 0 disables the
|
|
# gold flag. A repeater that simply pings less often is not counted as lossy.
|
|
PING_LOSS_WINDOW: 5
|
|
PING_LOSS_WARN: 5
|
|
USE_ACL: True
|
|
REG_ACL: PERMIT:ALL
|
|
SUB_ACL: DENY:1
|
|
TGID_TS1_ACL: PERMIT:ALL
|
|
TGID_TS2_ACL: PERMIT:ALL
|
|
|
|
|
|
# [REPORTS] — newline-delimited JSON event/stat feed for the dashboard (dashboard/).
|
|
# Leave REPORT True; set REPORT_PORT to match the dashboard's HBLINK_PORT and point
|
|
# REPORT_CLIENTS at the dashboard host (comma-separated IPs, or a single * for all).
|
|
# Full reference: CONFIGURING.md.
|
|
[REPORTS]
|
|
REPORT: True
|
|
REPORT_INTERVAL: 10
|
|
REPORT_PORT: 4321
|
|
REPORT_CLIENTS: 127.0.0.1
|
|
|
|
|
|
# [LOGGER] — LOG_HANDLERS: null|console|console-timed|file|file-timed|syslog
|
|
# (comma-join, no spaces). LOG_LEVEL: DEBUG|INFO|WARNING|CRITICAL. Under systemd,
|
|
# set LOG_FILE: /dev/null with a console handler and just rely on the journal.
|
|
# Full reference: CONFIGURING.md.
|
|
[LOGGER]
|
|
LOG_FILE: /tmp/hblink.log
|
|
LOG_HANDLERS: console-timed
|
|
LOG_LEVEL: DEBUG
|
|
LOG_NAME: HBlink
|
|
|
|
# [ALIASES] — DMR-ID -> callsign lookups. The DASHBOARD owns these files: set
|
|
# TRY_DOWNLOAD: False here and point PATH at the dashboard's directory. Loading the full
|
|
# subscriber DB here costs ~500 MB RAM per process (>1 GB if the dashboard loads it too),
|
|
# so keep USE_ALIASES: False unless the DB has been filtered small (dashboard
|
|
# FILTER_COUNTRIES). Full reference incl. the memory warning: CONFIGURING.md.
|
|
[ALIASES]
|
|
USE_ALIASES: False
|
|
TRY_DOWNLOAD: False
|
|
PATH: dashboard/
|
|
PEER_FILE: peer_ids.json
|
|
SUBSCRIBER_FILE: subscriber_ids.json
|
|
TGID_FILE: talkgroup_ids.json
|
|
PEER_URL: https://www.radioid.net/static/rptrs.json
|
|
SUBSCRIBER_URL: https://www.radioid.net/static/users.json
|
|
STALE_DAYS: 7
|
|
|
|
# [OPENBRIDGE] — Server-to-Server link (e.g. BrandMeister, IPSC2). Duplicate for more.
|
|
# TARGET_IP/PORT = far server; PASSPHRASE = shared HMAC key (must match both ends);
|
|
# NETWORK_ID identifies this server. All traffic is TS1 (no registration).
|
|
# BOTH_SLOTS: True is HBlink-to-HBlink ONLY -- no other server is known to accept it.
|
|
# PRESERVE_SOURCE_PEER: True forwards the originating peer ID instead of NETWORK_ID
|
|
# (unvalidated field; improves call provenance; best when both ends enable it).
|
|
# Full reference: CONFIGURING.md.
|
|
[OBP-1]
|
|
MODE: OPENBRIDGE
|
|
ENABLED: True
|
|
IP: 0.0.0.0
|
|
PORT: 62035
|
|
NETWORK_ID: 3129100
|
|
PASSPHRASE: password
|
|
TARGET_IP: 1.2.3.4
|
|
TARGET_PORT: 62035
|
|
BOTH_SLOTS: True
|
|
PRESERVE_SOURCE_PEER: False
|
|
USE_ACL: True
|
|
SUB_ACL: DENY:1
|
|
TGID_ACL: PERMIT:ALL
|
|
|
|
# [SERVER] — you are the Server; Clients (repeaters/hotspots) connect in. Duplicate for
|
|
# more. IP may NOT be blank (0.0.0.0 or :: = all interfaces); PORT must be unique/unused.
|
|
# REPEAT: True repeats between connected Clients; MAX_REPEATERS caps connections.
|
|
# Full reference: CONFIGURING.md.
|
|
[SERVER-1]
|
|
MODE: SERVER
|
|
ENABLED: True
|
|
REPEAT: True
|
|
MAX_REPEATERS: 10
|
|
IP: 0.0.0.0
|
|
PORT: 54000
|
|
PASSPHRASE: s3cr37w0rd
|
|
GROUP_HANGTIME: 5
|
|
USE_ACL: True
|
|
REG_ACL: DENY:1
|
|
SUB_ACL: DENY:1
|
|
TGID_TS1_ACL: PERMIT:ALL
|
|
TGID_TS2_ACL: PERMIT:ALL
|
|
|
|
# [OUTBOUND] — you are a Client: HBlink masquerades as a repeater/hotspot and dials out
|
|
# to a Server. Duplicate for more. RX/TX_FREQ in Hz; LAT/LONG in decimal degrees; HEIGHT
|
|
# in meters. LOOSE: True relaxes validation for non-compliant Servers (XLXD, DMR+).
|
|
# NOTE: BrandMeister does NOT accept OUTBOUND/Client connections -- it requires OpenBridge.
|
|
# Full reference: CONFIGURING.md.
|
|
[OUTBOUND-1]
|
|
MODE: OUTBOUND
|
|
ENABLED: True
|
|
LOOSE: False
|
|
IP: 0.0.0.0
|
|
PORT: 54001
|
|
SERVER_IP: 172.16.1.1
|
|
SERVER_PORT: 54000
|
|
PASSPHRASE: homebrew
|
|
CALLSIGN: W1ABC
|
|
RADIO_ID: 312000
|
|
RX_FREQ: 449000000
|
|
TX_FREQ: 444000000
|
|
TX_POWER: 25
|
|
COLORCODE: 1
|
|
SLOTS: 1
|
|
LATITUDE: 38.0000
|
|
LONGITUDE: -095.0000
|
|
HEIGHT: 75
|
|
LOCATION: Anywhere, USA
|
|
DESCRIPTION: This is a cool repeater
|
|
URL: www.w1abc.org
|
|
SOFTWARE_ID: 20170620
|
|
PACKAGE_ID: MMDVM_HBlink
|
|
GROUP_HANGTIME: 5
|
|
OPTIONS:
|
|
USE_ACL: True
|
|
SUB_ACL: DENY:1
|
|
TGID_TS1_ACL: PERMIT:ALL
|
|
TGID_TS2_ACL: PERMIT:ALL
|
|
|
|
# [OUTBOUND to an XLX reflector] — identical to the Client stanza above plus one
|
|
# field. XLX's DMR interface *is* the homebrew protocol, so no special mode exists:
|
|
# adding XLX_MODULE is what marks the system as a reflector connection.
|
|
#
|
|
# XLX_MODULE is the module (room) letter A-Z. HBlink sends the in-band private call
|
|
# that selects it immediately after login, and again after every reconnect, because
|
|
# the binding lives on the reflector for the life of the session.
|
|
#
|
|
# * The module is fixed. There is no runtime or end-user module selection -- that
|
|
# would break the nailed-up systems/bridges model. Use DMRGateway at a repeater
|
|
# or hotspot if users need to pick modules.
|
|
# * One module per connection. To bridge two modules, define two systems.
|
|
# * SERVER_PORT is 62030 and PASSPHRASE is usually passw0rd. LOOSE: True, since
|
|
# xlxd is not strict about the login handshake.
|
|
# * The reflector never acknowledges the link, and no frame on the wire says which
|
|
# module you are in. If traffic does not flow, check the reflector's dashboard --
|
|
# HBlink cannot tell you. Note a stock xlxd is built with 10 modules (A-J).
|
|
# * Bridge membership goes in XLX_BRIDGES in rules.py, not in BRIDGES.
|
|
#
|
|
# [XLX-950-D]
|
|
# MODE: OUTBOUND
|
|
# ENABLED: True
|
|
# LOOSE: True
|
|
# IP: 0.0.0.0
|
|
# PORT: 54002
|
|
# SERVER_IP: 172.16.1.1
|
|
# SERVER_PORT: 62030
|
|
# PASSPHRASE: passw0rd
|
|
# CALLSIGN: W1ABC
|
|
# RADIO_ID: 312000
|
|
# RX_FREQ: 449000000
|
|
# TX_FREQ: 444000000
|
|
# TX_POWER: 25
|
|
# COLORCODE: 1
|
|
# SLOTS: 1
|
|
# LATITUDE: 38.0000
|
|
# LONGITUDE: -095.0000
|
|
# HEIGHT: 75
|
|
# LOCATION: Anywhere, USA
|
|
# DESCRIPTION: XLX950 module D
|
|
# URL: www.w1abc.org
|
|
# SOFTWARE_ID: 20170620
|
|
# PACKAGE_ID: MMDVM_HBlink
|
|
# GROUP_HANGTIME: 5
|
|
# OPTIONS:
|
|
# XLX_MODULE: D
|
|
# USE_ACL: True
|
|
# SUB_ACL: DENY:1
|
|
# TGID_TS1_ACL: PERMIT:ALL
|
|
# TGID_TS2_ACL: PERMIT:ALL
|