Files
Cort BuffingtonandClaude Opus 4.8 011f2ce501 ACLS.md: drop code snippets and internals for a config-focused read
Remove the Python source excerpts, return-value structure, and the
'how matching works' section that would confuse most operators; keep the
config grammar, examples, performance guidance, and warnings.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 07:08:09 -05:00

8.1 KiB
Raw Permalink Blame History

DMRlink3 Access Control Lists (ACLs)

DMRlink3 shares its ACL engine with HBlink3, but because of what DMRlink3 is — a Motorola MOTOTRBO IPSC bridge/monitor — it applies ACLs in a narrower, more focused way. In DMRlink3 the ACL system is primarily about peer registration: deciding which repeaters/peers are allowed to join an IPSC network where DMRlink3 is the master. The bridge application adds one global subscriber filter on top. This document explains the grammar, the exact matching rules (verified against config.py, dmrlink.py, and bridge.py), worked examples, and honest performance guidance.

ACLs here are cheap and safe. Read the Performance section — for an IPSC master, a registration ACL is close to free and is your first line of defense.


1. The two-part grammar

Every ACL is a single string of the form:

ACTION:entry,entry,entry,...
  • ACTION is either PERMIT or DENY.
  • entries are comma-separated, and each entry is one of:
    • a single ID — e.g. 12345
    • a range — two IDs joined by a hyphen, low first — e.g. 1000-2000
    • the literal keyword ALL — matches every possible ID.

Examples of valid ACLs:

PERMIT:ALL
DENY:1
PERMIT:311111,311222,312000-312099
DENY:3120101,3120124

An empty value is treated as PERMIT:ALL (permit everything).

There is exactly one ACTION per ACL. You cannot mix PERMIT and DENY entries in the same list.


2. The single most important rule: what happens to IDs you didn't list

This is the part that trips everyone up, so read it twice.

An ACL does two things at once. It defines an ACTION for the IDs you list, and it implies the opposite ACTION for every ID you don't list.

You write A listed ID is… An unlisted ID is… Mental model
PERMIT:… permitted denied whitelist — only these get in
DENY:… denied permitted blacklist — everyone except these

So for a registration ACL:

  • PERMIT:311111,311222 means "only peers 311111 and 311222 may register, reject every other login."
  • DENY:311999 means "reject peer 311999, accept every other peer."

A listed ID gets the ACTION; an unlisted ID gets the opposite.

Rule of thumb: use PERMIT: when you run a closed IPSC network and want only a known list of repeaters to join. Use DENY: when you want an otherwise-open network minus a few blocked IDs.


3. The two ACLs DMRlink3 actually uses

Unlike HBlink3, DMRlink3 does not apply per-timeslot talkgroup ACLs or per-system subscriber ACLs. Its process_acls() wires up exactly two:

Key Where Gates Value domain Enforced when
REG_ACL per-system ([SAMPLE-PEER] etc.) Peer registration (an IPSC peer requesting to join) peer IDs, 1 … 4294967295 Only when MASTER_PEER: True
SUB_ACL [GLOBAL] only Subscriber source ID of a group/private call passing through the bridge subscriber IDs, 1 … 16776415 When the bridge (bridge.py) is running

3a. REG_ACL — the heart of DMRlink3 ACLs

A registration ACL only makes sense on a system where DMRlink3 is the IPSC master (MASTER_PEER: True). When a peer sends a registration request, DMRlink3 checks the peer's ID against REG_ACL and, if it is denied, refuses the registration and logs Peer Registration ***REJECTED BY ACL***.

If DMRlink3 is a peer on someone else's master (MASTER_PEER: False), the REG_ACL is ignored — a peer does not decide who else joins the master's network. The sample config says so directly:

# REG_ACL is ignored when MASTER_PEER: False — peers don't control registration
REG_ACL: PERMIT:ALL

Registration IDs are peer IDs and are validated against the full 32-bit range (1 … 4294967295, const.PEER_MAX).

3b. SUB_ACL — global subscriber gate on the bridge

When the bridge is running, it checks the global SUB_ACL against the source subscriber before propagating a call, and drops the call (logging ***REJECTED BY ACL***) if the subscriber is denied.

There is only one SUB_ACL, in [GLOBAL]. It is checked against subscriber IDs (1 … 16776415, const.ID_MAX). Set it to PERMIT:ALL if you don't want subscriber filtering.

USE_ACL in [GLOBAL] is the master enable switch for subscriber filtering.


4. Worked examples

Closed IPSC network (recommended for a private master). Only these repeaters may register; everything else is rejected:

[MY-IPSC-MASTER]
MASTER_PEER: True
REG_ACL: PERMIT:311111,311222,311333

Open network minus a few bad actors. Accept any peer except two known-bad IDs:

[MY-IPSC-MASTER]
MASTER_PEER: True
REG_ACL: DENY:311998,311999

Registering a block of site repeaters by range. Allow an allocated peer-ID block plus one out-of-block gateway:

[MY-IPSC-MASTER]
MASTER_PEER: True
REG_ACL: PERMIT:311000-311099,312500

Global subscriber blacklist on the bridge. Let all subscribers through except a few, while leaving registration wide open:

[GLOBAL]
USE_ACL: True
SUB_ACL: DENY:1,2606234,3141592

[MY-IPSC-MASTER]
MASTER_PEER: True
REG_ACL: PERMIT:ALL

Members-only subscriber policy on the bridge. Only your membership block may be bridged:

[GLOBAL]
USE_ACL: True
SUB_ACL: PERMIT:3120000-3120999

Large but efficient range list. Adjacent and contiguous ranges are merged at load time, so this…

REG_ACL: PERMIT:311000-311099,311100-311199,311200-311299,312500

…collapses internally to a single span 311000–311299 plus the single ID 312500. Write ranges however is clearest to you; the engine tidies them up.


Performance

Short version: on an IPSC master, use a REG_ACL. It costs almost nothing.

  • All of the work of reading and organizing your ACLs happens once, when DMRlink3 starts — not while traffic is flowing.
  • Registration is a rare event (a peer registers, then periodically re-registers on the ping interval). A REG_ACL check runs a handful of times per peer per interval — utterly negligible, and it protects your network from unwanted peers.
  • The global SUB_ACL is checked once per call on the bridge, and it stays fast no matter how long your list gets — a thousand entries is checked essentially as quickly as ten. You won't measure it.
  • Runs of consecutive IDs are combined internally, so a large-looking config often takes very little memory.

Practical guidance:

  • Prefer ranges (311000-311099) over listing many consecutive IDs — less typing, smaller memory, same speed.
  • A registration ACL is the single highest-value, lowest-cost ACL you can set on a master. There is no performance reason to leave it at PERMIT:ALL if you actually want to restrict who joins.
  • If you truly want no subscriber filtering, USE_ACL: False (or SUB_ACL: PERMIT:ALL) skips it.

Warnings

  • PERMIT is a whitelist — it rejects every peer/ID you didn't list. Writing REG_ACL: PERMIT:311111 intending "also allow 311111" will lock out every other repeater on the network. Re-read Section 2.
  • REG_ACL only applies when MASTER_PEER: True. On a peer system it is ignored — you cannot filter registrations on a network you don't master.
  • Out-of-range entries stop DMRlink3 at startup. A subscriber entry above 16776415, or a peer entry above 4294967295, triggers ACL CREATION ERROR, VALUE OUT OF RANGE and the program exits. Recheck your numbers if DMRlink3 won't start after an ACL edit.
  • Write ranges low-to-high (311000-311099, not 311099-311000). A reversed range will silently never match.
  • There is only one SUB_ACL, and it lives in [GLOBAL]. DMRlink3 does not read per-system subscriber or talkgroup ACLs — don't expect a SUB_ACL under a peer stanza to do anything.
  • USE_ACL gates the subscriber filter, not registration. REG_ACL on a master is governed by MASTER_PEER, not by USE_ACL.