mirror of
https://github.com/n0mjs710/HBlink4.git
synced 2026-11-08 04:19:26 +00:00
A repeater's registration was displaced by an unauthenticated RPTL: any packet carrying a known radio ID, from any source address, tore down the live registration before anything was proven. Radio IDs are public, so this was enough to knock any repeater off the network with an 8-byte packet, repeatable at will. A login claiming an ID that is already registered from a different sockaddr is now held in _pending_logins and challenged. The incumbent is removed only after the claimant proves the passphrase; on any failure the claimant is discarded and the registration is untouched. Recovery for a genuine NAT remap is unchanged in speed — login, auth, config, one round trip later. Liveness is now gated on the address matching too. _check_repeater_timeouts decides purely on last_ping, which was refreshed by any packet bearing a known ID before validation, so a stale or spoofed source could keep a dead entry from ever aging out. Report reconnects instead of diagnosing them. When a radio ID logs in again, log how long the previous session lasted as D:HH:MM:SS. Hours means a NAT lease expired or the device was power cycled; seconds, repeating, means it cannot stay online — and two addresses alternating is a duplicate radio ID. The operator reads that in a glance; no thresholds are guessed at in code. reconnect_report_threshold (default 3600, 0 disables) filters out routine long sessions. Only reported once a session actually existed, since connect_time is meaningless before the connected state. Fix dashboard uptime resetting for repeaters that never dropped. The dashboard stamped its own event-arrival time as connected_at, but repeater_connected is also emitted on missed pings, on recovery, and on state replay when the dashboard reconnects. The server's connect_time now rides in the payload, with a fallback for older servers. Logging: ping and pong lines now both carry radio ID and sockaddr, as do all inbound dispatch lines, so either field can be grepped for a whole exchange. Fixes an f-string whose conditional sat inside the string literal, raising AttributeError and surfacing as a generic datagram error for exactly the wrong-address pings this change is about. Adds tests/test_id_takeover.py: 31 tests over the auth gate, the login races, threshold filtering and connect_time. Verified by mutation — each reverted behavior fails a specific test. Also documents three unreachable/misleading branches in the dispatch chain found along the way, in code and in docs/TODO.md. Behavior unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
220 lines
9.0 KiB
JSON
220 lines
9.0 KiB
JSON
{
|
|
"_comment": "REFERENCE FILE -- shows every configuration section HBlink4 understands, with example values. Do NOT copy this to config/config.json wholesale: the blacklist and repeater patterns below match invented ID ranges that may overlap your own repeaters, and the link/trunk examples point at hosts that do not exist. Start from config/config_sample.json and paste in only the sections you need. Full reference: docs/configuration.md.",
|
|
|
|
"global": {
|
|
"max_missed": 3,
|
|
"timeout_duration": 30,
|
|
"reconnect_report_threshold": 3600,
|
|
"disable_ipv6": false,
|
|
"bind_ipv4": "0.0.0.0",
|
|
"bind_ipv6": "::",
|
|
"port_ipv4": 62031,
|
|
"port_ipv6": 62031,
|
|
"logging": {
|
|
"file": "logs/hblink.log",
|
|
"console_level": "INFO",
|
|
"file_level": "DEBUG",
|
|
"retention_days": 30
|
|
},
|
|
"stats_interval": 60,
|
|
"report_stats": true,
|
|
"stream_timeout": 2.0,
|
|
"stream_hang_time": 10.0,
|
|
"user_cache": {
|
|
"timeout": 600
|
|
}
|
|
},
|
|
"connection_type_detection": {
|
|
"_comment": "Categorize connections for dashboard display (📶 Repeaters, 📱 Hotspots, 🔗 Network Inbound, ❓ Other). Matching is case-insensitive and uses substring matching against package_id (primary) or software_id (fallback). Omit this section entirely to use these same values as built-in defaults.",
|
|
"hotspot_packages": [
|
|
"mmdvm_hs", "dvmega", "zumspot", "jumbospot", "nanodv",
|
|
"openspot", "dmo", "simplex"
|
|
],
|
|
"network_packages": [
|
|
"hblink", "freedmr", "brandmeister", "xlx", "dmr+", "tgif", "ipsc"
|
|
],
|
|
"repeater_packages": [
|
|
"repeater", "duplex", "stm32", "unknown"
|
|
],
|
|
"hotspot_software": [
|
|
"pi-star", "pistar", "ps4", "wpsd"
|
|
],
|
|
"network_software": [
|
|
"hblink", "freedmr", "brandmeister", "xlx"
|
|
]
|
|
},
|
|
"dashboard": {
|
|
"_comment": "OUTBOUND connection to the dashboard. HBlink4 is the client here: it dials the dashboard and pushes events to it. Naming convention: host_* = an address we connect out to, bind_* = an address we listen on (see bind_ipv4/bind_ipv6 under 'global'). host_ipv4/host_ipv6 are the dashboard's reachable address - NOT bind addresses, so never 0.0.0.0 or ::. The dashboard's matching listening socket is dashboard/config.json -> event_receiver.",
|
|
"enabled": true,
|
|
"disable_ipv6": false,
|
|
"transport": "unix",
|
|
"host_ipv4": "127.0.0.1",
|
|
"host_ipv6": "::1",
|
|
"port": 8765,
|
|
"unix_socket": "/tmp/hblink4.sock",
|
|
"buffer_size": 65536
|
|
},
|
|
"blacklist": {
|
|
"_comment": "EXAMPLE RANGES -- these IDs are invented for illustration. Replace them with IDs you actually want to refuse, or omit the section entirely.",
|
|
"patterns": [
|
|
{
|
|
"name": "Blocked IDs",
|
|
"description": "Should never be used",
|
|
"match": {
|
|
"ids": [1, 2]
|
|
},
|
|
"reason": "Repeated abuse of network"
|
|
},
|
|
{
|
|
"name": "Blocked Range",
|
|
"description": "Unauthorized network range",
|
|
"match": {
|
|
"id_ranges": [[315000, 315999]]
|
|
},
|
|
"reason": "Unauthorized DMR-MARC range"
|
|
},
|
|
{
|
|
"name": "Blocked Multiple Ranges",
|
|
"description": "Example of multiple ranges in one pattern",
|
|
"match": {
|
|
"id_ranges": [[100000, 109999], [200000, 209999], [900000, 909999]]
|
|
},
|
|
"reason": "Multiple unauthorized network ranges"
|
|
},
|
|
{
|
|
"name": "Blocked Callsigns",
|
|
"description": "Banned operators",
|
|
"match": {
|
|
"callsigns": ["BADACTOR*", "SPAM*"]
|
|
},
|
|
"reason": "Network abuse"
|
|
}
|
|
]
|
|
},
|
|
"repeater_configurations": {
|
|
"_comment": "Patterns are tried in order and the FIRST match wins -- a repeater matched by a pattern never falls through to 'default'. The example ranges below are invented; if you copy them unchanged and one of your own repeater IDs falls inside a range, that repeater will be required to use the example passphrase and will fail to connect. Delete the patterns you do not need.",
|
|
"patterns": [
|
|
{
|
|
"name": "Core Network Repeaters (Trusted)",
|
|
"description": "Trusted repeaters managed by core team - can use any TG",
|
|
"match": {
|
|
"ids": [312000, 312001]
|
|
},
|
|
"config": {
|
|
"passphrase": "core-network-key",
|
|
"trust": true,
|
|
"slot1_talkgroups": [8, 9],
|
|
"slot2_talkgroups": [3120, 3121, 3122],
|
|
"default_unit_calls": true
|
|
}
|
|
},
|
|
{
|
|
"name": "Regional Network Repeaters",
|
|
"description": "Example of multiple ranges in one pattern - regions 310, 311, 312",
|
|
"match": {
|
|
"id_ranges": [[310000, 310999], [311000, 311999], [312000, 312999]]
|
|
},
|
|
"config": {
|
|
"passphrase": "regional-network-key",
|
|
"trust": false,
|
|
"slot1_talkgroups": [1, 2, 3, 8],
|
|
"slot2_talkgroups": [3100, 3110, 3120]
|
|
}
|
|
},
|
|
{
|
|
"name": "Club Network",
|
|
"description": "Multiple specific repeaters for a club network",
|
|
"match": {
|
|
"ids": [312100, 312101, 312102]
|
|
},
|
|
"config": {
|
|
"passphrase": "club-network-key",
|
|
"slot1_talkgroups": [8],
|
|
"slot2_talkgroups": [3100, 3101, 3102]
|
|
}
|
|
}
|
|
],
|
|
"default": {
|
|
"passphrase": "passw0rd",
|
|
"slot1_talkgroups": [8],
|
|
"slot2_talkgroups": [8]
|
|
}
|
|
},
|
|
"outbound_connections": [
|
|
{
|
|
"_comment": "Connect out to another HomeBrew master as if we were a repeater. Shipped disabled -- set enabled=true and fill in a real address once you have somewhere to link to.",
|
|
"enabled": false,
|
|
"name": "Regional-Master-Server",
|
|
"address": "master.example.com",
|
|
"port": 62031,
|
|
"passphrase": "remote-server-password",
|
|
"radio_id": 312999,
|
|
"callsign": "K0USY-L",
|
|
"rx_frequency": 449000000,
|
|
"tx_frequency": 444000000,
|
|
"power": 50,
|
|
"colorcode": 1,
|
|
"latitude": 38.0,
|
|
"longitude": -97.0,
|
|
"height": 100,
|
|
"location": "Network Link",
|
|
"description": "Link to regional master",
|
|
"url": "https://hblink.example.com",
|
|
"software_id": "HBlink4",
|
|
"package_id": "HBlink4 v2.0",
|
|
"options": "TS1=1,2,3,8,9;TS2=3100,3120,3121,9998",
|
|
"unit_calls_enabled": true
|
|
},
|
|
{
|
|
"enabled": false,
|
|
"name": "Backup-Server",
|
|
"address": "backup.example.com",
|
|
"port": 62031,
|
|
"passphrase": "backup-password",
|
|
"radio_id": 312998,
|
|
"callsign": "K0USY-B",
|
|
"options": "TS1=*;TS2=*",
|
|
"description": "Backup connection (disabled)"
|
|
}
|
|
],
|
|
"openbridge_connections": [
|
|
{
|
|
"_comment": "Stream-multiplexed trunk to another server core. Shipped disabled -- see docs/openbridge.md before enabling.",
|
|
"enabled": false,
|
|
"name": "KS-Core-Trunk",
|
|
"network_id": 3129900,
|
|
"local_address": "0.0.0.0",
|
|
"local_port": 62035,
|
|
"target_address": "core.ks-dmr.example",
|
|
"target_port": 62035,
|
|
"passphrase": "shared-obp-secret",
|
|
"preserve_source_peer": true,
|
|
"talkgroup_slots": {
|
|
"31": "1",
|
|
"8": "1",
|
|
"3120": "2",
|
|
"3100": "2"
|
|
},
|
|
"description": "OBP trunk to the HBlink3 core; carries these canonical TGIDs (TGID -> local TS)"
|
|
},
|
|
{
|
|
"enabled": false,
|
|
"name": "KS-Core-Trunk-Backup",
|
|
"network_id": 3129901,
|
|
"local_address": "0.0.0.0",
|
|
"local_port": 62036,
|
|
"target_address": "core-backup.ks-dmr.example",
|
|
"target_port": 62035,
|
|
"passphrase": "shared-obp-secret",
|
|
"preserve_source_peer": true,
|
|
"talkgroup_slots": {
|
|
"31": "1",
|
|
"8": "1",
|
|
"3120": "2",
|
|
"3100": "2"
|
|
},
|
|
"description": "Standby trunk (disabled) — mirrors the active trunk's TGIDs for manual failover"
|
|
}
|
|
]
|
|
}
|