# 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](#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: ```ini # 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: ```ini [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: ```ini [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: ```ini [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: ```ini [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: ```ini [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… ```ini 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](#2-the-single-most-important-rule-what-happens-to-ids-you-didnt-list). * **`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`.