Extend forwarding filters and save moderation rules atomically
This commit is contained in:
1 parent
585393e36c
commit
953ad6defb
31 files changed
+3453
-342
No files matched your search
+112
-17
@@ -3333,7 +3333,7 @@ setting.
|
||||
|
||||
| Mode | Where the rule applies |
|
||||
| --- | --- |
|
||||
| `radio` | Ordinary LoRa flood forwarding. This is the default when `mode=` is omitted. |
|
||||
| `radio` | Ordinary LoRa forwarding; flood-only unless `route=direct` or `route=any` is selected. This is the default mode. |
|
||||
| `bridge` | Packets entering or leaving an RS232 or ESP-NOW bridge, and MeshCore RX/TX packet publication through MQTT. |
|
||||
| `cross` | Transmissions copied to the other radio profile, including a copy's retries. The same-profile transmission remains eligible. |
|
||||
| `bridge,cross` | Both transport paths using one rule. `cross,bridge` is equivalent. |
|
||||
@@ -3398,6 +3398,10 @@ removed. The fixed-size STM32WL FPF6 build cannot match authenticated channels.
|
||||
- `del flood.channel.scope.<n>`
|
||||
- `del flood.channel.scope all`
|
||||
|
||||
`fs` is the short family name for all these commands. Short options are
|
||||
`p=bl` (`path=blacklist`), `p=b1` through `p=b6` (`path=bucket:1` through
|
||||
`path=bucket:6`), and `tx=s`/`tx=f` (`tx=slow`/`tx=fast`).
|
||||
|
||||
**Parameters:**
|
||||
- `n`: Slot number within the table compiled for the target. Roomy ESP32 builds
|
||||
provide `1-255`; classic ESP32 repeaters and nRF52/other
|
||||
@@ -3606,6 +3610,9 @@ available for tables shared with `flood.filter path=blacklist` rules.
|
||||
- `del flood.channel.scope.require.<n>`
|
||||
- `del flood.channel.scope.require all`
|
||||
|
||||
`fsr` is the short family name, with the same arguments and region/scope-manager
|
||||
permissions.
|
||||
|
||||
**Default:** Empty; normal global region enforcement remains active.
|
||||
|
||||
Once this table contains a row, received flood `GRP_TXT` and `GRP_DATA`
|
||||
@@ -3671,6 +3678,10 @@ worked moderation examples, see [Flood Filtering and Moderation](flood_filtering
|
||||
- `del flood.filter.<n>`
|
||||
- `del flood.filter all`
|
||||
|
||||
Generalized rows also accept `sender=`, `text=`, `len=`, `route=`, `prob=`,
|
||||
`enabled=`, and anchored `path=` chains. These use the same table and ordering;
|
||||
see the parameters and short forms below.
|
||||
|
||||
The extended table is available on repeaters with the rule engine enabled and
|
||||
on FULL-profile ESP32 room servers. A FULL room server exposes
|
||||
`fr`, `flood.rule`, and `flood.filter`, has 31 slots, and requires an administrator
|
||||
@@ -3721,7 +3732,35 @@ compile this table.
|
||||
relayed login/admin traffic, but not direct packets or local delivery.
|
||||
- `prefix=<ID[,ID...]>`: Optional ordered source-path prefix of one to three
|
||||
pbyte IDs. IDs must all be 2, 4, or 6 hex characters, matching a packet's
|
||||
1-, 2-, or 3-byte pbyte width. `path=<prefix>` is an alias.
|
||||
1-, 2-, or 3-byte pbyte width. `p=` is the short form.
|
||||
- `path=[^]ID>ID...[$]` or `pa=...`: Ordered chain of up to four IDs, matched
|
||||
anywhere in a flood path. `^` anchors the earliest relay and `$` the most
|
||||
recent; both require exactly the chain. Commas may replace `>`.
|
||||
Each ID must use the packet's complete hash width. Direct paths and TRACE
|
||||
SNR bytes never match. `path=blacklist` remains the separate unordered table.
|
||||
- `sender=<pattern>` (`n=`), `text=<pattern>` (`x=`): Case-sensitive patterns
|
||||
on the decrypted sender name and message body. Require an explicit
|
||||
authenticated channel (`public`, `#name`, or key); `channel=*` and raw hashes
|
||||
are insufficient. These match only plain group text. Patterns search
|
||||
anywhere unless anchored with `^`/`$`, and support `.`, classes such as
|
||||
`[A-Z]` or `[^0-9]`, `*`, `+`, `?`, groups, `|`, and escaped literals.
|
||||
Use `[0-9]` for digits and quote values containing spaces. The maximum is
|
||||
63 bytes per pattern. Matching uses complete fields, removes AES zero
|
||||
padding, and shares a 131,072-operation work budget across one rule
|
||||
evaluation. Exhaustion rejects the packet; it cannot grant an allow,
|
||||
rewrite, or retry action.
|
||||
A message without `:` has no sender field but can match a text rule.
|
||||
- `len=N|N+|N-M` (`l=`): Inclusive range of payload bytes, excluding route/path
|
||||
metadata. Ciphertext padding counts toward payload length.
|
||||
- `route=flood|direct|any` (`rt=f|d|a`): Select route classes. Omitted radio
|
||||
rules retain flood-only behavior. Direct/any rows support drop, rate, and
|
||||
stop/allow; scope/region, retry, and TX timing require flood-only radio rules.
|
||||
- `prob=1-100` (`pr=`): Apply the row to approximately this percentage of
|
||||
otherwise-matching packets. Omitted means `100`; a failed roll leaves later
|
||||
rows eligible. It is a sampling rule, not a forwarding quota.
|
||||
- `enabled=on|off` (`e=1|0`): Keep a row while enabling or disabling it. To
|
||||
change only this state, use `set fr.<n> e=off` or `set fr.<n> e=on`; its
|
||||
conditions and actions stay saved.
|
||||
- `in=any|none|scoped|allowed|unknown|scope:<name>|region:<name>`: Optional
|
||||
condition on the original incoming scope, before any rule rewrites it.
|
||||
`none` is an unscoped flood. `scope:name` is the exact public
|
||||
@@ -3758,6 +3797,8 @@ compile this table.
|
||||
wildcard, or has no usable transport key, both the rewrite and its `stop`
|
||||
are inert so lower-order safety rows still run. A direct `scope=` target does
|
||||
not depend on region configuration.
|
||||
`allow`, `forward`, and `a` are aliases for `stop`; they do not override
|
||||
earlier drops, normal forwarding gates, or separate moderation rules.
|
||||
- `suspend=tempradio`: Optional. Skip this row while either `tempradio` or
|
||||
`tempradio2` is active, including scheduled temporary sessions.
|
||||
- `require=region`: Legacy alias for `in=allowed`. Apply the row only if the
|
||||
@@ -3799,11 +3840,11 @@ The payload names follow the [MeshCore packet-format allocation](https://docs.me
|
||||
| `0x0E` | `14` | reserved |
|
||||
| `0x0F` | `raw_custom` | `PAYLOAD_TYPE_RAW_CUSTOM` |
|
||||
|
||||
**Route scope:** `mode=radio` rules are evaluated only for the two flood route values:
|
||||
**Route scope:** `mode=radio` defaults to the two flood route values:
|
||||
`ROUTE_TYPE_TRANSPORT_FLOOD` (`0x00`, flood plus transport codes) and
|
||||
`ROUTE_TYPE_FLOOD` (`0x01`, unscoped flood). Direct routes `0x02` and `0x03`
|
||||
are unaffected by radio rules. Bridge/cross rules also cover direct routes at
|
||||
their respective transport boundaries.
|
||||
require an explicit `route=direct` or `route=any` on a radio row. Bridge/cross
|
||||
rules cover both route classes by default at their transport boundaries.
|
||||
|
||||
**Behavior:** Match fields within one row are ANDed. Radio rows are matched
|
||||
against the same immutable receive-time packet, before any rule changes its
|
||||
@@ -3853,7 +3894,10 @@ The packet is still received and can still be logged. Rules are persistent
|
||||
data and can be changed over serial or authenticated remote CLI without an OTA
|
||||
or reboot. `flood.rule` and `flood.filter` address the same table on extended
|
||||
builds; FPF6 files are migrated in memory and the next save writes FPF7.
|
||||
FPF1-FPF5 files are rejected and filtering fails open. FPF7 stores canonical
|
||||
New sender/text, length, route, probability, enabled-state, or extended-path
|
||||
fields use FPF8 instead. Old firmware without FPF8 support cannot load that
|
||||
table; remove the new fields before downgrading and keep a copy of the rules.
|
||||
FPF1-FPF5 files are rejected and filtering fails open. FPF7/FPF8 store canonical
|
||||
region names rather than transient numeric region
|
||||
IDs. Removing, reordering, or reusing a region ID therefore cannot silently
|
||||
retarget a rule. If a saved input or target region name is absent, that input
|
||||
@@ -3864,11 +3908,10 @@ mode; normal payload types can also use the temporary channel. Other rows
|
||||
remain in force, and suspended rows resume after both temporary sessions end.
|
||||
A malformed persisted table fails open (no general rules are applied).
|
||||
|
||||
Within one receive evaluation, rows that use the same channel key share one
|
||||
authentication result. The cache is discarded after that packet and stores
|
||||
neither plaintext nor passwords; different keys are authenticated separately.
|
||||
Raw `hash:XX` rows skip this authentication and compare only the one visible
|
||||
byte.
|
||||
Within one receive evaluation, rows using the same channel key share the
|
||||
initial authentication verdict; content patterns also decrypt into temporary
|
||||
scratch for matching. The cache and plaintext are discarded after the packet.
|
||||
Raw `hash:XX` rows skip authentication and compare only the visible byte.
|
||||
|
||||
**Per-channel retry examples:**
|
||||
|
||||
@@ -3962,24 +4005,56 @@ short setter; `get fr` lists the table. Unnumbered `set fr ...` reuses an
|
||||
identical row or adds one to the first empty slot. `del fr.N` and `del fr all`
|
||||
have the same behavior as their long forms.
|
||||
|
||||
Content, exceptions, and state changes use that same setter:
|
||||
|
||||
```text
|
||||
set fr grp_txt c=#local x="^BEACON" d
|
||||
set fr grp_txt c=#local n="^Alice$" pri=200 a
|
||||
set fr grp_txt c=#local d
|
||||
set fr.4 e=off
|
||||
set fr.4 e=on
|
||||
```
|
||||
|
||||
The Alice rule stops lower-order general drops; separate moderation and hard
|
||||
forwarding gates still apply. Sender names remain unverified display text.
|
||||
Saved older prefix rules keep their start-anchored semantics; when retyping an
|
||||
old `path=A1,B2`, use `prefix=A1,B2` or `pa=^A1>B2`.
|
||||
|
||||
| Full token | Short token |
|
||||
| --- | --- |
|
||||
| `type=...` | Positional type or `t=...`; `*` means `any` |
|
||||
| `hops=...` | Positional range or `h=...`; `*` means `all` |
|
||||
| `mode=radio/bridge/cross/bridge,cross` | `m=r/b/c/bc` (choose one value) |
|
||||
| `channel=...`, `prefix=...` | `c=...`, `p=...` |
|
||||
| `path=[^]ID>ID...[$]` | `pa=...` |
|
||||
| `sender=...`, `text=...` | `n=...`, `x=...` |
|
||||
| `len=N/N+/N-M` | `l=...` |
|
||||
| `route=flood/direct/any` | `rt=f/d/a` |
|
||||
| `prob=N`, `enabled=on/off` | `pr=N`, `e=1/0` |
|
||||
| `hashbytes=any/1/2/2+/3` | `pb=*/1/2/2+/3` (choose one value) |
|
||||
| `path=blacklist` | `p=bl` (repeater only) |
|
||||
| `in=any/none/scoped/allowed/unknown` | `i=*/n/s/a/u` (choose one value) |
|
||||
| `in=scope:name`, `in=region:name` | `i=s:name`, `i=r:name` |
|
||||
| `drop`, `stop`, `retry` | `d`, `s`, `r` |
|
||||
| `allow`, `forward` | `a` (same action as `stop`) |
|
||||
| `scope=name`, `region=name` | `s=name`, `r=name` |
|
||||
| `rate=N/min`, `priority=N` | `q=N`, `pri=N` |
|
||||
| `tx=slow`, `suspend=tempradio`, `retry` | `f=s`, `f=t`, `f=r`; combine as `f=str` |
|
||||
|
||||
All existing validation and permissions apply. `m=cb`, `m=b,c`, and `m=c,b`
|
||||
also mean bridge and crossover. Short aliases for `flood.channel.scope`,
|
||||
`flood.moderation`, and blacklist management are not added.
|
||||
also mean bridge and crossover.
|
||||
|
||||
| Full family | Short family | Commands |
|
||||
| --- | --- | --- |
|
||||
| `flood.moderation` | `fm` | `get`, `set`, `del` |
|
||||
| `flood.channel.scope` | `fs` | `get`, `set`, `del` |
|
||||
| `flood.channel.scope.require` | `fsr` | `get`, `set`, `del` |
|
||||
| `flood.filter.blacklist` | `fb` | `get`, `set`, `del` |
|
||||
| `flood.advert` | `fa` | `get`, `clear` |
|
||||
|
||||
Slot arguments, output, validation, and permissions are shared with the full
|
||||
family names. For example, `get fa key 1` inspects the full advert key and
|
||||
`clear fa all` clears advert history.
|
||||
|
||||
The numbered long getters use this complete short setter when descriptive
|
||||
fields would exceed a reply. Defaults are omitted and specific payload types
|
||||
@@ -3993,7 +4068,7 @@ matching rule. Unknown or ambiguous references are rejected. See the
|
||||
|
||||
On generalized repeaters, filter rows, scope-rewrite rows, the shared
|
||||
blacklist, and `flood.channel.data` compatibility state are committed in one
|
||||
atomic FPF7 image. Compact FPF6 profiles retain separate files. Replacing or deleting
|
||||
atomic FPF7/FPF8 image. Compact FPF6 profiles retain separate files. Replacing or deleting
|
||||
the blacklist does not delete rows containing `path=blacklist`; such rows
|
||||
remain dormant while the list is empty. Path hashes are truncated routing
|
||||
identifiers, not authenticated identities, so this is a forwarding signal
|
||||
@@ -4055,7 +4130,10 @@ The fixed 240 KB STM32WL profiles leave
|
||||
`MESH_ENABLE_FLOOD_RULE_ENGINE=0` and retain the compact, persistent FPF6
|
||||
`flood.filter` and blacklist commands. They still filter floods, but do not
|
||||
expose the `flood.rule` alias or its extended channel, prefix, input-scope,
|
||||
region-action, or rate fields. No partition size changes are required.
|
||||
region-action, or rate fields, nor sender/text patterns, length, route,
|
||||
probability, enabled-state, or anchored chains. Their compiled legacy
|
||||
families still accept the `fm`/`fs`/`fsr`/`fb`/`fa` aliases; `fm` requires
|
||||
moderation support in that build. No partition size changes are required.
|
||||
|
||||
---
|
||||
|
||||
@@ -4069,6 +4147,10 @@ region-action, or rate fields. No partition size changes are required.
|
||||
- `del flood.moderation.<n>`
|
||||
- `del flood.moderation all`
|
||||
|
||||
`fm` is the short family name. Options can use `d` (`drop`), `q=N`
|
||||
(`rate=N/min`), `h=N` (`hops=N`), and `p=...` (`path=...`). The short rate
|
||||
also accepts `q=N/min` or `q=N/m`; duplicate rate/drop options remain invalid.
|
||||
|
||||
**Parameters:**
|
||||
- `n`: Moderation slot from `1` to `16`.
|
||||
- `channel`:
|
||||
@@ -4090,6 +4172,12 @@ At least one of `drop`, `rate=X/min`, or `hops=N` is required. Rate and hop
|
||||
limits can be combined. Rate counters are local to this repeater and rule, use a
|
||||
60-second window beginning with the first matching message, and reset on reboot.
|
||||
|
||||
Configured exact names are limited to 31 bytes. A wildcard `*` with a hop
|
||||
restriction also matches longer valid sender names. The FGM1 storage format
|
||||
is unchanged; saves use staging, readback, and backup recovery. A save reported
|
||||
as unconfirmed may already have published, so rebooted settings can differ
|
||||
from the restored live table.
|
||||
|
||||
**Decode and identity behavior:** Moderation applies only to flood
|
||||
`PAYLOAD_TYPE_GRP_TXT`. The repeater first checks the packet's channel-hash byte,
|
||||
then validates and decrypts with the configured key. It extracts the text before
|
||||
@@ -4111,6 +4199,7 @@ retransmission is denied. There are no moderation rules by default.
|
||||
set flood.moderation public "Noisy User" rate=5/min
|
||||
set flood.moderation #local bot drop path=A1B2C3,D4E5F6
|
||||
set flood.moderation.3 00112233445566778899AABBCCDDEEFF alice rate=10/min hops=4 path=71CE82
|
||||
set fm public "Noisy User" q=5
|
||||
get flood.moderation
|
||||
get flood.moderation.3
|
||||
del flood.moderation.3
|
||||
@@ -4138,14 +4227,20 @@ del flood.moderation.3
|
||||
non-secret operational/filter status commands and can change the forwarding
|
||||
controls `repeat`, `loop.detect`, `flood.max*`, `flood.channel.data*`,
|
||||
`flood.filter*`, `flood.rule*`, `fr[.N]`, and
|
||||
`flood.moderation*`. It cannot read
|
||||
`flood.moderation*`, including the `fm`, `fb`, and `fa` aliases. It cannot read
|
||||
guest, WiFi, MQTT, bridge, or other credentials, and it cannot change regions,
|
||||
ACL entries, radio settings, or other admin configuration. Permission `4`
|
||||
is limited to region commands, `flood.channel.scope*`, and the same non-secret
|
||||
is limited to region commands, `flood.channel.scope*` (including `fs` and
|
||||
`fsr`), and the same non-secret
|
||||
status allowlist.
|
||||
Both delegated manager roles are protected from least-recently-active ACL
|
||||
eviction like administrators.
|
||||
|
||||
The same filter aliases work inside enrolled
|
||||
[signed fleet commands](fleet_management.md), including quoted sender/text
|
||||
patterns. Fleet signature, expiry, replay, and command-size checks still apply;
|
||||
ordinary channel text cannot authorize a change.
|
||||
|
||||
**Note:** Removes the entry when `permissions` is omitted
|
||||
|
||||
---
|
||||
|
||||
+149
-23
@@ -13,12 +13,15 @@ not compile the rule table. Filters decide whether the node retransmits a
|
||||
packet and can assign a transport scope before that decision. They do not stop
|
||||
local reception, packet logging, or MQTT observation.
|
||||
|
||||
Only flood routes are filtered:
|
||||
Radio rules default to the two flood routes:
|
||||
|
||||
- `0x00` / `ROUTE_TYPE_TRANSPORT_FLOOD` - flood routing with transport codes
|
||||
- `0x01` / `ROUTE_TYPE_FLOOD` - unscoped flood routing
|
||||
|
||||
Direct routes `0x02` and `0x03` are never affected by these rules.
|
||||
Generalized rules can opt into relayed direct routes (`0x02` and `0x03`) with
|
||||
`route=direct`, or both route classes with `route=any`. Scope rewriting,
|
||||
source-path matching, and the legacy moderation table remain flood-only.
|
||||
Local delivery and requests addressed to this node are not relay decisions.
|
||||
The route and payload values follow the upstream
|
||||
[packet-format reference](https://docs.meshcore.io/packet_format/) and
|
||||
[payload layouts](https://docs.meshcore.io/payloads/), with this fork's LoRa
|
||||
@@ -163,7 +166,7 @@ get flood.moderation
|
||||
```
|
||||
|
||||
`flood.rule` is an alias for `flood.filter`, not another table. Generalized
|
||||
repeater FPF7 has 63 forward-rule slots plus scope-rewrite and shared-blacklist
|
||||
repeaters have 63 forward-rule slots plus scope-rewrite and shared-blacklist
|
||||
sections in the same atomic policy file. FULL room servers have 31 forward
|
||||
slots and empty repeater-only sections. Compact target profiles retain their
|
||||
separate FPF6-era controls. `flood.moderation` has 16 slots. A new
|
||||
@@ -195,6 +198,10 @@ del flood.channel.scope.<slot>
|
||||
del flood.channel.scope all
|
||||
```
|
||||
|
||||
Use `fs` in place of `flood.channel.scope` for any of these commands. Its
|
||||
optional fields also accept `p=bl` for `path=blacklist`, `p=b1` through `p=b6`
|
||||
for `path=bucket:1` through `path=bucket:6`, and `tx=s`/`tx=f` for slow/fast.
|
||||
|
||||
The channel may be `public`, `#channel`, or a 128/256-bit hex key. A bare
|
||||
target names an existing region with a usable transport key. Use
|
||||
`scope=<name>` instead to derive a regionless public hashtag scope exactly as
|
||||
@@ -327,7 +334,7 @@ targets, except that very-tight STM32WL builds retain one reusable direct
|
||||
target. Both configured regions and regionless targets can be reused by any
|
||||
number of rules.
|
||||
|
||||
On generalized builds these records are the FPF7 rewrite phase, and the file
|
||||
On generalized builds these records are the shared rewrite phase, and the file
|
||||
stores only through the highest occupied slot. Compact FPF6 builds retain the
|
||||
standalone FCS5 file and the file sizes described by their build profile.
|
||||
|
||||
@@ -346,6 +353,9 @@ del flood.channel.scope.require.<slot>
|
||||
del flood.channel.scope.require all
|
||||
```
|
||||
|
||||
`fsr` is the short name for `flood.channel.scope.require`, with the same
|
||||
arguments, slots, and permissions.
|
||||
|
||||
An empty table preserves the normal global region behavior. Once at least one
|
||||
row exists, a group-channel packet that authenticates against a listed key must
|
||||
arrive as `ROUTE_TYPE_TRANSPORT_FLOOD` with a transport code matching a locally
|
||||
@@ -414,9 +424,12 @@ On repeaters with the rule engine enabled and on FULL-profile ESP32 room
|
||||
servers, `fr`, `flood.rule`, and `flood.filter` name the same persistent
|
||||
table. The evaluator is fixed firmware, but every row is data, so an
|
||||
authenticated operator can add, replace, inspect, or delete a row without an
|
||||
OTA or reboot. Existing `flood.filter` commands remain compatible. Only FPF6
|
||||
and FPF7 files are accepted; FPF1-FPF5 files are rejected and filtering fails
|
||||
open. A row saved by the extended engine uses FPF7.
|
||||
OTA or reboot. Existing `flood.filter` commands remain compatible. FPF6,
|
||||
FPF7, and FPF8 files are accepted; FPF1-FPF5 files are rejected and filtering
|
||||
fails open. New sender/text, payload-length, route, probability, enabled-state,
|
||||
or extended-path fields are stored in FPF8. Tables using only older fields
|
||||
retain FPF7. Firmware without FPF8 support cannot load an FPF8 table; remove
|
||||
the new fields before downgrading and keep a copy of the rules.
|
||||
Older firmware cannot preserve an FPF7 table containing the packed `retry`
|
||||
action, one-byte `hash:XX` matcher, or `mode=bridge`/`cross` flags; remove those
|
||||
rows before downgrading. Old firmware rejects such a table and fails open,
|
||||
@@ -439,7 +452,7 @@ of FPF7. Normal ordering applies, so a matching higher-priority `stop` row can
|
||||
exempt selected traffic. The compact rule list marks the managed row with
|
||||
`~data`.
|
||||
|
||||
FPF7 binds `in=region:<name>` and `region=<name>` to canonical region names,
|
||||
FPF7 and FPF8 bind `in=region:<name>` and `region=<name>` to canonical region names,
|
||||
not numeric region IDs. Removing, reordering, or reusing a region ID cannot
|
||||
silently redirect a rule. If the saved name is missing, an input-region match
|
||||
does not match and a target-region rewrite is skipped. Re-adding the same name
|
||||
@@ -450,7 +463,9 @@ The extended form is:
|
||||
```text
|
||||
set flood.rule[.<slot>] type=<type> [mode=radio|bridge|cross|bridge,cross]
|
||||
[hops=<range>] [channel=<channel>] [hashbytes=any|1|2|2+|3]
|
||||
[prefix=<ID[,ID...]>] [in=<input-scope>] <action> [rate=<N>/min]
|
||||
[prefix=<ID[,ID...]>|path=[^]ID>ID...[$]] [in=<input-scope>]
|
||||
[sender=<pattern>] [text=<pattern>] [len=<range>] [route=flood|direct|any]
|
||||
[prob=<1-100>] [enabled=on|off] <action> [rate=<N>/min]
|
||||
[priority=<0-255>] [stop] [tx=fast|slow] [suspend=tempradio]
|
||||
get flood.rule
|
||||
get flood.rule.<slot>
|
||||
@@ -510,8 +525,34 @@ Match fields:
|
||||
- `prefix=` is a source-path prefix containing one to three comma-separated
|
||||
pbyte IDs. Every ID must use the packet's pbyte width: 2, 4, or 6 hex
|
||||
characters for 1-, 2-, or 3-byte paths. Order matters and matching begins at
|
||||
the first received path entry. `path=<prefix>` is an alias;
|
||||
`path=blacklist` retains its separate unordered-list behavior.
|
||||
the first received path entry. Its short name is `p=`.
|
||||
- `path=` or `pa=` matches an ordered chain of up to four IDs anywhere in a
|
||||
flood path. Separate IDs with `>` or `,`; `^` anchors the first entry and
|
||||
`$` the last. Thus `pa=A1>B2` finds adjacent entries anywhere,
|
||||
`pa=A1$` selects the most recent relay, and `pa=^A1>B2$` requires exactly
|
||||
those two entries. Each ID must use the packet's full 1-, 2-, or 3-byte
|
||||
width. Direct paths and TRACE SNR bytes never match. `path=blacklist`
|
||||
retains its separate unordered-list behavior.
|
||||
|
||||
Saved older prefix rules keep their start-anchored meaning. When retyping an
|
||||
older `path=A1,B2` command, use `prefix=A1,B2` or `pa=^A1>B2` to retain it.
|
||||
- `sender=` (`n=`) and `text=` (`x=`) match decrypted group text. An explicit
|
||||
authenticated channel (`public`, `#name`, or a channel key) is required;
|
||||
a raw channel hash is insufficient. See [content patterns](#content-patterns).
|
||||
- `len=` (`l=`) matches payload bytes, using `N`, `N+`, or inclusive `N-M`.
|
||||
It excludes the route/path envelope; encrypted padding is part of the payload.
|
||||
- `route=` (`rt=`) selects `flood`, `direct`, or `any`; `f`, `d`, and `a` are
|
||||
short values. Omitted radio rules remain flood-only. Direct/any rows may
|
||||
drop, rate-limit, or stop; scope/region rewriting, retry, and TX timing
|
||||
require flood-only radio rows.
|
||||
- `prob=` (`pr=`) applies this rule to approximately that percentage of
|
||||
otherwise-matching packets. Values are `1-100`; omitted means `100`.
|
||||
A failed roll lets later rules run. This is sampling, not a delivery quota.
|
||||
The decision stays the same for the same packet and row during one boot,
|
||||
including repeated timing checks and scope rewrites.
|
||||
- `enabled=off` (`e=off`) retains a row without evaluating it. `on`/`1`
|
||||
enables and `off`/`0` disables. Change only this state with
|
||||
`set fr.<slot> e=off` and `set fr.<slot> e=on`; the rest of the row stays saved.
|
||||
- `in=any|none|scoped|allowed|unknown|scope:<name>|region:<name>` tests the
|
||||
original incoming route before any rewrite. `none` means an unscoped flood;
|
||||
`scoped` means any transport flood; `scope:name` compares the exact public
|
||||
@@ -549,12 +590,52 @@ Actions:
|
||||
raw hash, then wildcard) before slot order. `pri=` is the compact alias.
|
||||
- `stop` (or `action=stop`) applies this row and prevents lower-order FPF7 rows
|
||||
from acting. It can stand alone or accompany drop, rewrite, or rate.
|
||||
`allow`, `forward`, and `a` are aliases for the same stop action; they
|
||||
do not bypass higher-priority drops, hard forwarding gates, or moderation.
|
||||
|
||||
When several rows use the same channel key, authentication is performed once
|
||||
for that packet and reused by those rows. This cache lives only for the current
|
||||
receive evaluation; it is not persisted and never stores plaintext or a
|
||||
password. A `hash:XX` row deliberately skips authentication and uses only the
|
||||
one visible byte.
|
||||
### Content patterns
|
||||
|
||||
Sender/text patterns are case-sensitive byte matches. They search anywhere
|
||||
unless anchored with `^` and `$`. Supported forms include `.`, `[A-Z]`,
|
||||
`[^0-9]`, `*`, `+`, `?`, grouping, and `|` alternatives. Escape punctuation
|
||||
with `\` to match it literally; use `[0-9]` for digits. Each pattern is at
|
||||
most 63 bytes. Quote values containing spaces.
|
||||
|
||||
For `Alice: BEACON 123`, `n=^Alice$` matches the display name and
|
||||
`x=^BEACON` matches the body. They can be combined in one row. The parser
|
||||
uses the complete fields, including names longer than 31 bytes, and removes
|
||||
AES zero padding before matching. A message without `:` has no sender field;
|
||||
a text rule can still inspect its body. Wrong keys and non-text payloads do
|
||||
not match content rules. Pattern matching shares a 131,072-operation budget
|
||||
across the rules in one evaluation. Exhausting it rejects the packet rather
|
||||
than granting an unverified allow, rewrite, or retry action. Keep patterns
|
||||
short and specific.
|
||||
|
||||
```text
|
||||
# Block BEACON messages on this channel.
|
||||
set fr grp_txt c=#local x="^BEACON" d
|
||||
|
||||
# Exempt Alice from lower-order general rules, then block everyone else.
|
||||
set fr grp_txt c=#local n="^Alice$" pri=200 a
|
||||
set fr grp_txt c=#local d
|
||||
|
||||
# Temporarily disable an occupied row without deleting its settings.
|
||||
set fr.4 e=off
|
||||
set fr.4 e=on
|
||||
|
||||
# Block floods whose most recent relay has this 3-byte ID.
|
||||
set fr any pa=A1B2C3$ d
|
||||
```
|
||||
|
||||
The allow exception remains subject to separate moderation and normal
|
||||
forwarding gates. A sender name is freely chosen message text, not a verified
|
||||
public-key identity. Legacy `flood.moderation` keeps its existing exact,
|
||||
case-insensitive name matching.
|
||||
|
||||
Rows using the same channel key reuse the initial authentication verdict.
|
||||
Content patterns also decrypt into temporary scratch for matching. Neither
|
||||
the verdict cache nor plaintext survives the receive evaluation. A `hash:XX`
|
||||
row skips authentication and uses only the one visible byte.
|
||||
|
||||
The global retry controls remain hard gates. A `retry` row does not override a
|
||||
zero `flood.retry.count`, path/type attempt caps, disabled forwarding,
|
||||
@@ -696,6 +777,11 @@ set fr.3 any c=#wardriving m=bc d
|
||||
| `mode=radio`, `bridge`, `cross`, `bridge,cross` | `m=r`, `m=b`, `m=c`, `m=bc` | Select the admission path. Omitted means radio; `cb`, `b,c`, and `c,b` also mean both transport paths. |
|
||||
| `channel=...` | `c=...` | Same authenticated name/key or explicit `hash:XX` matcher. |
|
||||
| `prefix=...` | `p=...` | Ordered path prefix. |
|
||||
| `path=[^]ID>ID...[$]` | `pa=...` | Ordered chain anywhere, or anchored at either end. |
|
||||
| `sender=...`, `text=...` | `n=...`, `x=...` | Case-sensitive patterns on an authenticated group-text channel. |
|
||||
| `len=N`, `N+`, or `N-M` | `l=...` | Payload-byte range. |
|
||||
| `route=flood`, `direct`, `any` | `rt=f`, `rt=d`, `rt=a` | Route class. |
|
||||
| `prob=N`, `enabled=on/off` | `pr=N`, `e=1/0` | Sample matches or retain a disabled row. |
|
||||
| `hashbytes=any`, `1`, `2`, `2+`, or `3` | `pb=*`, `1`, `2`, `2+`, or `3` | Encoded path-hash width, even with zero hops; `2+` matches 2 and 3. |
|
||||
| `path=blacklist` | `p=bl` | Shared unordered blacklist; repeater only. |
|
||||
| `in=any`, `none`, `scoped`, `allowed`, `unknown` | `i=*`, `i=n`, `i=s`, `i=a`, `i=u` | Incoming scope condition. |
|
||||
@@ -705,6 +791,7 @@ set fr.3 any c=#wardriving m=bc d
|
||||
| `rate=N/min` | `q=N` | Per-row forwarding allowance. |
|
||||
| `priority=N` | `pri=N` | Higher values run first. |
|
||||
| `stop`, `retry` | `s`, `r` | Stop later rules or allow the configured flood retry. |
|
||||
| `allow`, `forward` | `a` | Same action as `stop`; other forwarding gates still apply. |
|
||||
| `tx=slow`, `suspend=tempradio`, `retry` | `f=s`, `f=t`, `f=r` | Flags can combine, for example `f=str`. |
|
||||
|
||||
The `=` distinguishes `s=name` (scope) from `s` (stop), and `r=name`
|
||||
@@ -752,9 +839,21 @@ the last rule holding that key, supply the original key instead. An unknown
|
||||
or ambiguous reference is rejected; it never becomes an unauthenticated
|
||||
channel-hash match.
|
||||
|
||||
The shorthand does not rename `flood.channel.scope`, `flood.moderation`, or
|
||||
the blacklist-management commands. Companions and compact FPF6 profiles do
|
||||
not expose `fr`.
|
||||
Other repeater families have short names too, with identical arguments,
|
||||
slot numbers, validation, persistence, and permissions:
|
||||
|
||||
| Full family | Short family | Commands |
|
||||
| --- | --- | --- |
|
||||
| `flood.moderation` | `fm` | `get`, `set`, `del` |
|
||||
| `flood.channel.scope` | `fs` | `get`, `set`, `del` |
|
||||
| `flood.channel.scope.require` | `fsr` | `get`, `set`, `del` |
|
||||
| `flood.filter.blacklist` | `fb` | `get`, `set`, `del` |
|
||||
| `flood.advert` | `fa` | `get`, `clear` |
|
||||
|
||||
For example, `get fa key 1` inspects a full advert key and `clear fa all`
|
||||
clears the advert limiter history. Companions do not expose these repeater
|
||||
families. Compact FPF6 profiles retain their compiled legacy commands and
|
||||
their family aliases, but do not expose `fr` or the new generalized fields.
|
||||
|
||||
### Scope actions and legacy rules
|
||||
|
||||
@@ -791,7 +890,7 @@ radio transmission, CAD, and airtime-budget limits are unchanged.
|
||||
|
||||
On generalized repeaters, forward rules, `flood.channel.scope` rewrite rows,
|
||||
the shared blacklist, and the `flood.channel.data` compatibility state are one
|
||||
atomic FPF7 policy image. Existing `/flood_ch_scope`, `/flood_filter_bl`, FPF6,
|
||||
atomic FPF7/FPF8 policy image. Existing `/flood_ch_scope`, `/flood_filter_bl`, FPF6,
|
||||
and FCB2 data is imported once; the old files are removed only after the new
|
||||
image verifies and commits. Compact FPF6 repeaters retain separate files.
|
||||
Deleting the blacklist leaves `path=blacklist` rows in place but dormant until
|
||||
@@ -975,6 +1074,11 @@ Available actions are:
|
||||
- `path=H1[,H2,H3]` - require the first one to three path hashes to match
|
||||
- `path=*` - match every path; this is the default
|
||||
|
||||
`fm` is the short family name. Its option aliases are `d` for `drop`,
|
||||
`q=N` for `rate=N/min`, `h=N` for `hops=N`, and `p=...` for `path=...`.
|
||||
`q=N/min` and `q=N/m` are also accepted; mixed duplicate rate/drop options
|
||||
are rejected just as in the full form.
|
||||
|
||||
At least one of `drop`, `rate=X/min`, or `hops=N` is required. Rate and hop
|
||||
limits can be combined. `rate=0/min` is equivalent to `drop`.
|
||||
|
||||
@@ -995,8 +1099,17 @@ set flood.moderation #local "Noisy User" rate=10/min
|
||||
|
||||
# Combine a rate limit with a maximum forwarding distance.
|
||||
set flood.moderation public alice rate=4/min hops=5
|
||||
|
||||
# Same rule using short names.
|
||||
set fm public alice q=4 h=5
|
||||
```
|
||||
|
||||
The wildcard sender `*` works with a hop restriction, including for names
|
||||
longer than 31 bytes. Exact configured names remain limited to 31 bytes.
|
||||
Moderation settings retain the FGM1 format and use staged writes, readback,
|
||||
and backup recovery. A failed save is reported as unconfirmed because a
|
||||
failure after publication can leave the new settings available on reboot.
|
||||
|
||||
### Match the start of a path
|
||||
|
||||
Path matching accepts one-, two-, or three-byte hashes. Every hash in one rule
|
||||
@@ -1056,7 +1169,8 @@ setperm <companion-public-key-hex> 5
|
||||
|
||||
A filter manager can read non-secret operational status and manage `repeat`,
|
||||
`loop.detect`, `flood.max*`, `flood.channel.data*`,
|
||||
`flood.filter*`, `flood.rule*`, `fr[.N]`, and `flood.moderation*`. Delegated `get` access uses an
|
||||
`flood.filter*`, `flood.rule*`, `fr[.N]`, and `flood.moderation*`, including
|
||||
the `fm`, `fb`, and `fa` aliases. Delegated `get` access uses an
|
||||
explicit allowlist: it cannot retrieve guest, WiFi, MQTT, bridge, or other
|
||||
credentials, and it cannot change regions, ACL entries, radio settings, or
|
||||
unrelated administrator settings. Because `flood.filter scope=` derives a
|
||||
@@ -1071,7 +1185,19 @@ ACL permission `4`, the region/scope-manager role, can read, add, replace, and
|
||||
delete `flood.channel.scope` and `flood.channel.scope.require` rows and manage
|
||||
regions. This lets the same delegate create target regions, assign forced
|
||||
scopes from regions or direct hashtag names, and select the channels that
|
||||
require valid incoming scopes.
|
||||
require valid incoming scopes. The `fs` and `fsr` aliases retain this role.
|
||||
|
||||
Admin CLI sessions and these delegated roles use the same setters and
|
||||
validation as the serial console. On enrolled builds, the aliases also work
|
||||
inside [signed fleet commands](fleet_management.md), for example:
|
||||
|
||||
```text
|
||||
fleet send 3 set fr.4 grp_txt c=public x="^BEACON" d
|
||||
fleet send 3 set fr.4 e=off
|
||||
```
|
||||
|
||||
Fleet enrollment, signature, expiry, replay, and command-size checks still
|
||||
apply. An ordinary channel message cannot authorize a filter change.
|
||||
|
||||
## Security limitations
|
||||
|
||||
@@ -1092,7 +1218,7 @@ The `mode=` matcher selects a separate admission phase within the same
|
||||
|
||||
| Mode | Where the rule applies |
|
||||
| --- | --- |
|
||||
| `radio` | Ordinary LoRa flood forwarding. This is the default when `mode=` is omitted. |
|
||||
| `radio` | Ordinary LoRa forwarding; flood-only by default, or opt into direct routes with `route=`. This is the default mode. |
|
||||
| `bridge` | Packets entering or leaving an RS232 or ESP-NOW bridge, and MeshCore RX/TX packet publication through MQTT. |
|
||||
| `cross` | Transmissions copied to the other radio profile, including a copy's retries. The same-profile transmission remains eligible. |
|
||||
| `bridge,cross` | Both transport paths using one rule. `cross,bridge` is equivalent. |
|
||||
|
||||
Reference in new issue
Block a user