mirror of
https://github.com/n0mjs710/dmrlink3
synced 2026-11-08 04:19:24 +00:00
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>
226 lines
8.1 KiB
Markdown
226 lines
8.1 KiB
Markdown
# 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`.
|