Add repeater flood filters and transport scopes
This commit is contained in:
1 parent
5e030ce77b
commit
09c8aaba9c
25 files changed
+1222
-111
No files matched your search
+172
-12
@@ -982,6 +982,14 @@ writes that slot.
|
||||
`set flood.channel.block #wardriving h=<all|1-7|default>` or removed with
|
||||
`del flood.channel.block #wardriving`. Once the block list has been saved, the
|
||||
firmware uses the saved list and does not recreate the default after deletion.
|
||||
Restore the exact seeded row with:
|
||||
|
||||
```text
|
||||
set flood.channel.block.1 #wardriving h=4
|
||||
```
|
||||
|
||||
This explicitly writes slot 1. Omit `.1` to update an existing wardriving row
|
||||
or use the first empty slot instead.
|
||||
|
||||
**Forwarding behavior:** Repeater firmware only. This only affects received
|
||||
flood `GRP_TXT` and `GRP_DATA` channel packets. The repeater still receives and
|
||||
@@ -1032,6 +1040,132 @@ del flood.channel.block.2
|
||||
|
||||
---
|
||||
|
||||
#### Force a transport scope onto unscoped floods
|
||||
**Usage:**
|
||||
- `get flood.channel.scope`
|
||||
- `get flood.channel.scope.<n>`
|
||||
- `set flood.channel.scope <channel|txt:*|login:*|other:*> <region>`
|
||||
- `set flood.channel.scope.<n> <channel|txt:*|login:*|other:*> <region>`
|
||||
- `del flood.channel.scope.<n>`
|
||||
- `del flood.channel.scope all`
|
||||
|
||||
**Parameters:**
|
||||
- `n`: Slot number within the table compiled for the target. Roomy ESP32 builds
|
||||
provide `1-255`; DRAM-tight classic ESP32 LoRa-OTA repeaters and nRF52/other
|
||||
normal constrained builds provide `1-31`; very-tight STM32WL repeaters provide
|
||||
`1-15`; the no-PSRAM LilyGo T-LoRa V2.1 repeater/observer provides `1-4`.
|
||||
- `channel`: `public`, a public `#channel`, or a 128/256-bit channel key in hex.
|
||||
- `txt:*`: Unauthenticated fallback for otherwise-unmatched `GRP_TXT` and
|
||||
`GRP_DATA`. Plain `*` is an alias for `txt:*`.
|
||||
- `login:*`: Type-based wildcard for the remote-login/admin family: `REQ`,
|
||||
`RESPONSE`, `TXT_MSG`, `ANON_REQ`, and `PATH` (`0x00`, `0x01`, `0x02`,
|
||||
`0x07`, and `0x08`). It classifies the outer type; a transit repeater cannot
|
||||
authenticate whether a packet is actually part of a login session.
|
||||
- `other:*`: Type-based wildcard for every remaining flood payload type except
|
||||
TRACE, including ACK, advert, multipart, control, OTA, reserved types, and
|
||||
raw custom. TRACE is deliberately exempt from forced-scope wildcards.
|
||||
- `region`: Existing named region with a usable transport key. A unique region
|
||||
name prefix is accepted; wildcard region `*` is not a scope target.
|
||||
|
||||
**Default:** No forced scopes.
|
||||
|
||||
Remote ACL permission `4` (region/scope manager) can use all `get`, `set`, and
|
||||
`del flood.channel.scope` forms. Filter managers and other non-admin roles
|
||||
cannot change this table.
|
||||
|
||||
Without `.n`, `set` updates the row for the same exact channel key or wildcard
|
||||
class, otherwise it uses the first empty slot. With `.n`, it replaces that
|
||||
slot. The three wildcard classes are independent and consume one slot each.
|
||||
`get flood.channel.scope` reports active/total slot counts; use the numbered
|
||||
form for row detail. Keyed rows are displayed by the first four bytes of their
|
||||
derived channel hash because channel secrets are never returned.
|
||||
|
||||
This acts on received, unscoped `ROUTE_TYPE_FLOOD` packets. For `GRP_TXT` and
|
||||
`GRP_DATA`, all exact channel-key rows are tried first and must validate the
|
||||
packet MAC/decryption. A row whose target region is missing or unusable is
|
||||
skipped; later exact rows and then `txt:*` are tried. Exact keyed rows with a
|
||||
usable target therefore beat `txt:*` regardless of slot number. `login:*` and
|
||||
`other:*` select their non-overlapping outer-type families without decrypting
|
||||
the payload. Duplicate rows within the same class are permitted with numbered
|
||||
slots; the lowest usable slot wins.
|
||||
|
||||
Standard traceroute is direct-routed and is therefore outside this flood-only
|
||||
table. A custom flood-form `TRACE` is also left unchanged: no wildcard adds a
|
||||
scope, an existing transport code is preserved, and region/unknown-code gates
|
||||
do not block it.
|
||||
|
||||
On a match, the repeater changes the route to
|
||||
`ROUTE_TYPE_TRANSPORT_FLOOD`, computes transport code 0 from the selected
|
||||
region key and packet payload, and leaves transport code 1 as zero. This occurs
|
||||
before region enforcement, forwarding filters, and the seen-packet lookup.
|
||||
Already-scoped packets and direct routes are never rewritten. The rewritten
|
||||
packet is no longer subject to `flood.max.unscoped`, but remains subject to
|
||||
normal payload handling, `flood.max`, region allow/deny, `flood.filter`,
|
||||
`flood.channel.block`, loop detection, and moderation. Assigning a scope does
|
||||
not make a packet type forwardable if the core would otherwise reject it.
|
||||
|
||||
If a row's target region has been removed or has no usable key, the repeater
|
||||
tries the next applicable row. For group packets this means later authenticated
|
||||
exact rows followed by `txt:*`; wildcard duplicates likewise fall through to
|
||||
the next usable slot. The packet remains unscoped only when no usable mapping
|
||||
exists.
|
||||
|
||||
LoRa OTA remains functional when `other:*` is configured. OTA packets are
|
||||
given that region's transport code, but the OTA handler still accepts and
|
||||
re-floods them during the temporary-radio window. The target region must allow
|
||||
flooding. The OTA core itself is dormant outside that window; no default flood
|
||||
filter row is needed for that behavior. Forced scope does not make OTA operate
|
||||
outside the window.
|
||||
|
||||
**Capacity cost:** Each slot uses 36 bytes of runtime RAM and persistent
|
||||
storage, plus a 5-byte file header. The four-slot minimum uses 144 bytes RAM and
|
||||
a 149-byte file; it has room for the three wildcard classes plus one exact
|
||||
channel mapping. Very-tight 15-slot builds use 540 bytes RAM and a 545-byte
|
||||
file. The 31-slot table uses 1,116 bytes RAM and a 1,121-byte file. Roomy ESP32
|
||||
builds use a 255-slot table: 9,180 bytes RAM and a 9,185-byte file. Classic
|
||||
ESP32 LoRa-OTA builds that cannot afford that RAM use 31 slots instead. The
|
||||
existing region table still permits 32 named regions, so a 255-slot channel
|
||||
table may reuse targets but cannot name more than 32 distinct configured region
|
||||
scopes.
|
||||
|
||||
**Duplicate behavior:** Mesh dedup hashes payload type and payload bytes; it
|
||||
does not hash route type, transport codes, or the ordinary flood path. Adding
|
||||
a transport scope therefore does not create a new duplicate identity. If the
|
||||
same payload later arrives scoped, unscoped, or through a different region, it
|
||||
is still the same seen packet. `TRACE` is the exception only in that its
|
||||
encoded `path_len` byte is also hashed.
|
||||
|
||||
While equivalent non-TRACE flood copies are waiting in `rxdelay`, the normal receive-quality
|
||||
timing still selects the packet to process, but that winner receives a scope
|
||||
from the queued scoped copies with the same dedupe identity. If the copies
|
||||
carry different locally allowed scopes, the scope from the shortest received
|
||||
path wins. Unknown and denied transport codes are not candidates and therefore
|
||||
cannot overwrite an unscoped winner. With equal path lengths, the deeper child
|
||||
region wins because it is narrower. A remaining tie keeps queue order. The
|
||||
winner's own path, SNR reading, and scheduled time are not changed, and an
|
||||
already-scoped winner may have its code replaced by the better queued scope.
|
||||
|
||||
The comparison is deferred until dequeue so each copy retains its original
|
||||
scope and path for arbitration. It can only use copies still present in
|
||||
`rxdelay`; it cannot replace a packet that already won the dedupe race. TRACE
|
||||
is excluded from scope arbitration entirely, so rxdelay never adds or replaces
|
||||
a trace transport code.
|
||||
|
||||
**Examples:**
|
||||
```text
|
||||
region put west
|
||||
region save
|
||||
set flood.channel.scope #local west
|
||||
set flood.channel.scope.2 txt:* west
|
||||
set flood.channel.scope.3 login:* west
|
||||
set flood.channel.scope.4 other:* west
|
||||
get flood.channel.scope
|
||||
get flood.channel.scope.1
|
||||
del flood.channel.scope.2
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### Filter flood packets by payload type and hop
|
||||
|
||||
For setup guidance, interactions with the existing forwarding controls, and
|
||||
@@ -1040,19 +1174,24 @@ worked moderation examples, see [Repeater Flood Filtering and Moderation](flood_
|
||||
**Usage:**
|
||||
- `get flood.filter`
|
||||
- `get flood.filter.<n>`
|
||||
- `set flood.filter <type> <hops>`
|
||||
- `set flood.filter.<n> <type> <hops>`
|
||||
- `set flood.filter <type> [hops] [suspend=tempradio]`
|
||||
- `set flood.filter.<n> <type> [hops] [suspend=tempradio]`
|
||||
- `del flood.filter.<n>`
|
||||
- `del flood.filter all`
|
||||
|
||||
**Parameters:**
|
||||
- `n`: Rule slot from `1` to `16`.
|
||||
- `type`: Payload type name, full `PAYLOAD_TYPE_*` name, numeric value `0-15`, or `any`.
|
||||
- `hops`:
|
||||
- `type`: Payload type name, full `PAYLOAD_TYPE_*` name, decimal value `0-15`,
|
||||
hexadecimal value `0x00-0x0F`, or `any`.
|
||||
- `hops`: Optional; omitted means `all`.
|
||||
- `N`: Block only at received hop count `N`.
|
||||
- `N+`: Block at received hop count `N` and higher.
|
||||
- `N-M`: Block the inclusive received-hop range.
|
||||
- `all`: Block at every received hop count (`0-63`).
|
||||
- `0+`, `all`, and an omitted hop expression are equivalent. The CLI displays
|
||||
the saved range as `all`.
|
||||
- `suspend=tempradio`: Optional. Skip this row only while the temporary radio
|
||||
is actually active.
|
||||
|
||||
The payload names follow the [MeshCore packet-format allocation](https://docs.meshcore.io/packet_format/):
|
||||
|
||||
@@ -1081,19 +1220,39 @@ The payload names follow the [MeshCore packet-format allocation](https://docs.me
|
||||
are never affected.
|
||||
|
||||
**Behavior:** A match prevents retransmission by this repeater. The packet is
|
||||
still received and can still be logged. Rules are persistent and default to an
|
||||
empty table, so upgrading does not change forwarding. A malformed persisted
|
||||
table fails open (no general rules are applied).
|
||||
still received and can still be logged. Rules are persistent and the table is
|
||||
empty by default. While the temporary radio is active, only rows explicitly
|
||||
marked `suspend=tempradio` are skipped. `tempradio` is a radio state, not an OTA
|
||||
mode; normal payload types can also use the temporary channel. Other rows
|
||||
remain in force. A malformed persisted table fails open (no general rules are
|
||||
applied).
|
||||
|
||||
Without `.n`, `set` returns an identical existing rule or uses the first empty
|
||||
slot. With `.n`, it replaces that slot. `get flood.filter` gives a compact list;
|
||||
use `get flood.filter.<n>` for full details.
|
||||
**Remote-admin protection:** `flood.filter` cannot block `anon_req`, `path`, or
|
||||
`response` at received hop counts `0-6`; those login-capable types become
|
||||
filterable at hop `7`. Flood `txt_msg` cannot be blocked at hops `0-4` and
|
||||
becomes filterable at hop `5`. `req`, `ack`, and multipart ACK have no special
|
||||
protection. Transit repeaters cannot decrypt these packets to distinguish an
|
||||
admin exchange from ordinary peer traffic, so each floor necessarily covers
|
||||
all flood packets of that outer type. These exceptions apply only to
|
||||
`flood.filter`; `repeat`, `flood.max*`, region, loop-detection, and other
|
||||
forwarding gates remain authoritative.
|
||||
|
||||
Without `.n`, `set` returns an identical existing rule, including its suspension
|
||||
setting, or uses the first empty slot. With `.n`, it replaces that slot.
|
||||
`get flood.filter` gives a compact list. Use `get flood.filter.<n>` for full
|
||||
details, including `suspend=tempradio`.
|
||||
|
||||
Standard traceroute is direct-routed and therefore outside `flood.filter`
|
||||
entirely. For a custom flood-form trace, catch-all `any` rows are deliberately
|
||||
ignored; only a deliberately explicit `set flood.filter trace ...` row can
|
||||
match it.
|
||||
|
||||
**Examples:**
|
||||
```text
|
||||
set flood.filter grp_data 4+
|
||||
set flood.filter.2 PAYLOAD_TYPE_ADVERT 6+
|
||||
set flood.filter ota 2-4
|
||||
set flood.filter grp_data all suspend=tempradio
|
||||
set flood.filter any 12+
|
||||
get flood.filter
|
||||
get flood.filter.2
|
||||
@@ -1174,7 +1333,7 @@ del flood.moderation.3
|
||||
- `1`: Read-only
|
||||
- `2`: Read-write
|
||||
- `3`: Admin
|
||||
- `4`: Region manager (repeater delegated region management)
|
||||
- `4`: Region/scope manager (repeater delegated region and forced-scope management)
|
||||
- `5`: Filter manager (repeater delegated forwarding-filter management)
|
||||
|
||||
**Filter manager scope:** Permission `5` can use an explicit allowlist of
|
||||
@@ -1183,7 +1342,8 @@ controls `repeat`, `loop.detect`, `flood.max*`, `flood.channel.data*`,
|
||||
`flood.channel.block*`, `flood.filter*`, and `flood.moderation*`. 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`
|
||||
remains limited to region commands and the same non-secret status allowlist.
|
||||
is limited to region commands, `flood.channel.scope*`, and the same non-secret
|
||||
status allowlist.
|
||||
Both delegated manager roles are protected from least-recently-active ACL
|
||||
eviction like administrators.
|
||||
|
||||
|
||||
+175
-17
@@ -27,13 +27,109 @@ get flood.max.advert
|
||||
get flood.channel.data
|
||||
get flood.channel.data.hops
|
||||
get flood.channel.block
|
||||
get flood.channel.scope
|
||||
get flood.filter
|
||||
get flood.moderation
|
||||
```
|
||||
|
||||
The `flood.filter` and `flood.moderation` tables each have 16 persistent slots.
|
||||
They are empty by default. A corrupt or truncated table fails open, so corrupt
|
||||
storage does not silently enable blocking.
|
||||
The `flood.filter` and `flood.moderation` tables each have 16 persistent slots
|
||||
and start empty. A row can opt into `suspend=tempradio`; temporary radio is not
|
||||
synonymous with OTA and can carry normal packet types too. A corrupt or
|
||||
truncated table fails open, so corrupt storage does not silently enable
|
||||
blocking.
|
||||
|
||||
## Force unscoped floods into a transport scope
|
||||
|
||||
`flood.channel.scope` can convert a received unscoped flood into a
|
||||
transport-scoped flood before this repeater forwards it:
|
||||
|
||||
```text
|
||||
set flood.channel.scope <channel|txt:*|login:*|other:*> <region>
|
||||
set flood.channel.scope.<slot> <channel|txt:*|login:*|other:*> <region>
|
||||
get flood.channel.scope
|
||||
get flood.channel.scope.<slot>
|
||||
del flood.channel.scope.<slot>
|
||||
del flood.channel.scope all
|
||||
```
|
||||
|
||||
The channel may be `public`, `#channel`, or a 128/256-bit hex key. The region
|
||||
must already exist and provide a usable transport key. Keyed rules first check
|
||||
the one-byte channel hash carried in the packet, then validate the MAC by
|
||||
decrypting with the configured channel key. A hash collision alone cannot
|
||||
force a scope.
|
||||
|
||||
There are three independent wildcard classes:
|
||||
|
||||
- `txt:*` handles otherwise-unmatched `GRP_TXT` and `GRP_DATA`; plain `*` is
|
||||
its alias.
|
||||
- `login:*` handles `REQ`, `RESPONSE`, `TXT_MSG`, `ANON_REQ`, and `PATH`.
|
||||
- `other:*` handles every remaining flood payload type except TRACE, including
|
||||
OTA. TRACE is deliberately exempt from forced-scope wildcards.
|
||||
|
||||
`login:*` and `other:*` classify only the visible outer payload type; they do
|
||||
not authenticate its contents. Exact channel rows with usable target regions
|
||||
always take precedence over `txt:*`, even if that wildcard has a lower slot
|
||||
number. A missing or unusable target is skipped, so later exact rows and then
|
||||
the applicable wildcard are tried. Within a wildcard class, the lowest usable
|
||||
duplicate row wins.
|
||||
|
||||
On a successful match, the route changes from `ROUTE_TYPE_FLOOD` to
|
||||
`ROUTE_TYPE_TRANSPORT_FLOOD`. Transport code 0 is calculated with the target
|
||||
region key over the payload type and payload; code 1 remains zero. The change
|
||||
happens before region enforcement, forwarding filters, and deduplication.
|
||||
Consequently `flood.max.unscoped` no longer applies to the rewritten packet,
|
||||
but `flood.max`, target-region permissions, `flood.filter`, channel blocking,
|
||||
loop detection, and moderation still do.
|
||||
|
||||
Already-scoped floods and direct routes are never rewritten. TRACE is never
|
||||
rewritten even in flood form; its existing code, if any, is preserved and it
|
||||
bypasses region/unknown-code enforcement. Scope assignment also does not
|
||||
override normal payload validation or make an otherwise non-forwardable packet
|
||||
type forwardable.
|
||||
|
||||
LoRa OTA (`0x0C`) falls under `other:*`. A matching row adds the selected
|
||||
transport code, but OTA still operates normally during the temporary-radio
|
||||
window because the OTA handler accepts both unscoped and transport-scoped flood
|
||||
routes. The target region must allow flooding. The OTA core is already dormant
|
||||
outside the temporary-radio window, so no default OTA filter row is installed.
|
||||
|
||||
Capacity is selected at build time:
|
||||
|
||||
- Roomy ESP32 builds: 255 slots, 9,180 bytes RAM, 9,185-byte file.
|
||||
- DRAM-tight classic ESP32 LoRa-OTA repeaters, nRF52, and other normal
|
||||
constrained builds: 31 slots, 1,116 bytes RAM, 1,121-byte file.
|
||||
- Very-tight STM32WL builds: 15 slots, 540 bytes RAM, 545-byte file.
|
||||
- The no-PSRAM LilyGo T-LoRa V2.1 repeater/observer: 4 slots, 144 bytes RAM,
|
||||
149-byte file. This minimum holds the three wildcard classes and one exact
|
||||
channel mapping.
|
||||
|
||||
The region map still has 32 named-region entries. Large ESP32 tables can map
|
||||
many channels to the same targets, but cannot reference more than 32 distinct
|
||||
configured region names.
|
||||
|
||||
### Interaction with duplicate detection
|
||||
|
||||
The seen-packet hash contains the payload type and exact payload bytes. It does
|
||||
not contain the route type, either transport code, or the ordinary flood path.
|
||||
For `TRACE` only, the encoded `path_len` byte is also included. Therefore an
|
||||
unscoped packet and the same packet after this repeater adds a transport code
|
||||
are the same duplicate. A later copy with a different scope is also the same
|
||||
duplicate; changing or adding scope cannot evade the seen table.
|
||||
|
||||
When equivalent non-TRACE flood copies overlap in `rxdelay`, the normal receive-quality timing
|
||||
still chooses the packet to process, but that winner takes a scope from the
|
||||
queued copies whose transport code matches an allowed region in this repeater.
|
||||
Unknown and denied scopes are ignored. If eligible copies have different
|
||||
scopes, the shortest received path supplies the scope. Equal path lengths
|
||||
prefer the deepest matching child region (the narrowest configured scope). A
|
||||
remaining tie keeps queue order. The winner keeps its own path, SNR, and delay
|
||||
schedule; only its route and transport codes can change, including replacement
|
||||
of a less-preferred scope it already carried.
|
||||
|
||||
Scope selection happens at dequeue so the original scopes remain available for
|
||||
comparison. It applies only while copies are queued and cannot alter a copy
|
||||
already processed into the seen table. TRACE is excluded from scope arbitration
|
||||
entirely, so rxdelay never adds or replaces a trace transport code.
|
||||
|
||||
## Filter by payload type and received hop count
|
||||
|
||||
@@ -41,16 +137,57 @@ Use `flood.filter` when the packet type and its current path length are enough
|
||||
to make the decision:
|
||||
|
||||
```text
|
||||
set flood.filter <type> <hops>
|
||||
set flood.filter.<slot> <type> <hops>
|
||||
set flood.filter <type> [hops] [suspend=tempradio]
|
||||
set flood.filter.<slot> <type> [hops] [suspend=tempradio]
|
||||
get flood.filter
|
||||
get flood.filter.<slot>
|
||||
del flood.filter.<slot>
|
||||
del flood.filter all
|
||||
```
|
||||
|
||||
Without a slot number, `set` reuses an identical rule or selects the first empty
|
||||
slot. With a slot number, it replaces that slot.
|
||||
Without a slot number, `set` reuses an identical rule, including its suspension
|
||||
setting, or selects the first empty slot. With a slot number, it replaces that
|
||||
slot. Omitting the hop expression means `all` (`0-63`).
|
||||
|
||||
No flood-filter row is created automatically. Operators may add
|
||||
`suspend=tempradio` to any row that should be skipped while the radio is on a
|
||||
temporary channel.
|
||||
|
||||
Suspension does not approve a packet or bypass the rest of the filter table. It
|
||||
skips that row, then evaluation continues with the next row and the remaining
|
||||
forwarding gates. An ordinary `any` row therefore still applies during the
|
||||
temporary-radio window, subject to the short-path remote-admin protection
|
||||
below. `repeat`, `flood.max*`, region, loop-detection, and the OTA subsystem's
|
||||
own hop limit also remain in force.
|
||||
|
||||
Standard traceroute uses direct routing and never enters `flood.filter`. For a
|
||||
custom flood-form trace, catch-all `any` rows are deliberately ignored; only an
|
||||
explicit `trace` row can match it. The stock core does not normally
|
||||
flood-forward TRACE packets.
|
||||
|
||||
### Remote administration cannot be type-filtered on short paths
|
||||
|
||||
`flood.filter` uses two minimum filterable hop counts:
|
||||
|
||||
- `anon_req`, `path`, and `response` cannot be blocked at received hops `0-6`;
|
||||
configured rules begin applying at hop `7`.
|
||||
- Flood `txt_msg` cannot be blocked at received hops `0-4`; configured rules
|
||||
begin applying at hop `5`.
|
||||
|
||||
`req`, `ack`, and multipart ACK have no special floor and remain filterable from
|
||||
hop `0`.
|
||||
|
||||
A flooded login starts as `ANON_REQ`; its reply is commonly a `PATH` packet
|
||||
carrying an encrypted `RESPONSE`. Before a direct return path is established,
|
||||
administrative replies and CLI text can also be flooded. Transit repeaters do
|
||||
not have the session key and cannot distinguish those encrypted admin exchanges
|
||||
from ordinary peer packets with the same outer type. Each hop floor therefore
|
||||
covers the complete outer packet class, not only packets that ultimately
|
||||
authenticate as administrators.
|
||||
|
||||
This protects only against the configurable `flood.filter` table. It does not
|
||||
override `repeat`, `flood.max*`, region enforcement, loop detection, or other
|
||||
forwarding gates.
|
||||
|
||||
Hop expressions are based on the path count when this repeater receives the
|
||||
packet:
|
||||
@@ -59,6 +196,8 @@ packet:
|
||||
- `N+` matches `N` or more received hops.
|
||||
- `N-M` matches the inclusive range.
|
||||
- `all` matches `0` through `63` hops.
|
||||
- `0+`, `all`, and omitting the hop expression are equivalent. `get` reports
|
||||
the stored range using the canonical spelling `all`.
|
||||
|
||||
Examples:
|
||||
|
||||
@@ -72,7 +211,7 @@ set flood.filter.2 advert 6+
|
||||
# Keep LoRa OTA floods from crossing this repeater at path counts 2 through 4.
|
||||
set flood.filter.3 ota 2-4
|
||||
|
||||
# Apply a hard ceiling to every flood payload type.
|
||||
# Apply a hard ceiling to flood payload types at 12 or more received hops.
|
||||
set flood.filter any 12+
|
||||
```
|
||||
|
||||
@@ -97,9 +236,10 @@ Accepted payload names are:
|
||||
| `0x0E` | `14` | reserved |
|
||||
| `0x0F` | `raw_custom` | `PAYLOAD_TYPE_RAW_CUSTOM` |
|
||||
|
||||
The numeric values `0` through `15`, the full `PAYLOAD_TYPE_*` names, and
|
||||
`any` are also accepted. Upstream currently reserves `0x0C`; this fork assigns
|
||||
it to LoRa OTA.
|
||||
Decimal values `0` through `15`, hexadecimal values `0x00` through `0x0F`, the
|
||||
full `PAYLOAD_TYPE_*` names, and `any` are also accepted. Upstream currently
|
||||
reserves `0x0C`; this fork assigns it to LoRa OTA. Rows are suspended during
|
||||
temporary-radio operation only when explicitly configured that way.
|
||||
|
||||
## Moderate group text by channel and username
|
||||
|
||||
@@ -174,11 +314,13 @@ the packet contains all path entries listed by the rule.
|
||||
A flood packet is retransmitted only if it passes every applicable control. In
|
||||
other words, the controls combine as deny rules:
|
||||
|
||||
1. `repeat`, `flood.max*`, and the channel-data gate are checked.
|
||||
2. `flood.filter` checks payload type and hop range.
|
||||
3. `flood.channel.block` checks keyed channels.
|
||||
4. Region and loop-detection rules are checked.
|
||||
5. `flood.moderation` checks decrypted group text, username, rate, hops, and path.
|
||||
1. `flood.channel.scope` first rewrites a matching unscoped flood packet.
|
||||
2. `repeat`, `flood.max*`, and the channel-data gate are checked.
|
||||
3. `flood.filter` checks payload type and hop range, subject to the login floor
|
||||
of `7` and flood-text floor of `5` described above.
|
||||
4. `flood.channel.block` checks keyed channels.
|
||||
5. Region and loop-detection rules are checked.
|
||||
6. `flood.moderation` checks decrypted group text, username, rate, hops, and path.
|
||||
|
||||
The first denial is enough to prevent retransmission. A packet that is denied
|
||||
can still appear in local logs or MQTT output. Moderation runs last because its
|
||||
@@ -200,6 +342,10 @@ 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.
|
||||
|
||||
ACL permission `4`, the region/scope-manager role, can read, add, replace, and
|
||||
delete `flood.channel.scope` rows and manage regions. This lets the same
|
||||
delegate create target regions and assign them to forced-scope rows.
|
||||
|
||||
## Security limitations
|
||||
|
||||
Public and hashtag channels use shared, well-known keys. A valid channel MAC
|
||||
@@ -212,9 +358,21 @@ Use username and path rules as traffic moderation, not as an authorization
|
||||
boundary. For a strict network boundary, combine these tools with region ACLs,
|
||||
private transport/channel keys, and controlled device access.
|
||||
|
||||
## Restore the factory-seeded channel block
|
||||
|
||||
The repeater's factory-seeded forwarding row can be restored through the CLI:
|
||||
|
||||
```text
|
||||
set flood.channel.block.1 #wardriving h=4
|
||||
```
|
||||
|
||||
This explicitly replaces slot 1. Inspect the slot first if it may now contain
|
||||
another rule. To preserve existing slot assignments, omit `.1`; the command
|
||||
then updates an identical channel row or uses the first empty slot.
|
||||
|
||||
## Remove the custom rules
|
||||
|
||||
To return the two new tables to their default empty state:
|
||||
To save both tables in an empty state:
|
||||
|
||||
```text
|
||||
del flood.filter all
|
||||
|
||||
@@ -83,7 +83,8 @@ set flood.retry.ignore none
|
||||
| `flood.channel.data.hops` | Separate hop gate used only when `flood.channel.data` is `off`; `all` blocks `GRP_DATA` at any hop count, `1`-`7` repeats at that hop count or lower and blocks longer paths. | `get flood.channel.data.hops`, `set flood.channel.data.hops <all|1-7>` | `set flood.channel.data.hops 7` |
|
||||
| `flood.channel.block` | Blocks selected flood `GRP_TXT`/`GRP_DATA` channels when the key validates the packet. New repeater block lists start with editable/deletable `#wardriving h=4`. Add `h=<all|1-7|default>` for a per-channel hop override. | `get flood.channel.block`, `set flood.channel.block[.n] <key|#channel> [name] [h=...]`, `del flood.channel.block[.n]` | `set flood.channel.block #wardriving h=4` |
|
||||
| `flood.channel.block.hops` | Limits keyed channel-block matches to short flood paths. `all` blocks matching packets at any hop count; `1`-`7` repeats packets at that hop count or lower and blocks longer matches. This does not restrict unkeyed `GRP_DATA`; use `flood.channel.data.hops` for that. | `get flood.channel.block.hops`, `set flood.channel.block.hops <all|1-7>` | `set flood.channel.block.hops 3` |
|
||||
| `flood.filter` | Persistent repeater-only forwarding rules for flood routes `0x00`/`0x01`, selected by payload type and received hop count/range. Direct routing and local receive/logging are unchanged. | `get flood.filter[.n]`, `set flood.filter[.n] <type> <N|N+|N-M|all>`, `del flood.filter.<n>|all` | `set flood.filter grp_data 4+` |
|
||||
| `flood.channel.scope` | Assigns a transport-region scope to received unscoped floods. Exact channel keys beat `txt:*`; `login:*` covers the remote-login family, and `other:*` covers every remaining flood type except TRACE, including OTA. TRACE remains unchanged across scope boundaries. ACL permission `4` can manage the table. | `get flood.channel.scope[.n]`, `set flood.channel.scope[.n] <channel|txt:*|login:*|other:*> <region>`, `del flood.channel.scope.<n>|all` | `set flood.channel.scope login:* west` |
|
||||
| `flood.filter` | Persistent repeater-only forwarding rules for flood routes `0x00`/`0x01`, selected by payload type and optional received hop count/range (omitted means `all`). The table starts empty; only rows marked `suspend=tempradio` are skipped during temporary-radio operation. Login-capable `anon_req`/`path`/`response` types become filterable at hop `7`; flood `txt_msg` becomes filterable at hop `5`. Standard direct traceroute, other direct routing, and local receive/logging are unchanged. | `get flood.filter[.n]`, `set flood.filter[.n] <type> [N|N+|N-M|all] [suspend=tempradio]`, `del flood.filter.<n>|all` | `set flood.filter grp_data 4+` |
|
||||
| `flood.moderation` | Decrypts keyed `GRP_TXT` channels and applies drop, per-username messages/minute, and maximum-hop controls, optionally matched against the first 1-3 path hashes. Supports `public`, `#channel`, and 128/256-bit channel keys. Sender names and truncated path hashes are moderation hints, not authenticated identities. | `get flood.moderation[.n]`, `set flood.moderation[.n] <channel> <sender> <drop|rate=X/min|hops=N> [path=...]`, `del flood.moderation.<n>|all` | `set flood.moderation public "Noisy User" rate=5/min hops=4` |
|
||||
| `clock.sync.mesh` | After 30 minutes of uptime, estimates UTC from a configurable consensus of fresh signed-advert or valid Public-channel sources. Only timestamps from firmware build time through build time plus ten years are recorded. Successful CLI, GPS, or WiFi/NTP clock updates suppress LoRa time collection until reboot; after reboot LoRa is the fallback if NTP cannot sync. | `get clock.sync.mesh`, `set clock.sync.mesh <on|off>`, `get clock.sync.status` | `set clock.sync.mesh on` |
|
||||
| `clock.sync.internet` | Adds a read-only internet/NTP estimate at the same delayed check on WiFi MQTT repeater-observer builds. Other builds retain the setting but report internet unavailable. | `get clock.sync.internet`, `set clock.sync.internet <on|off>` | `set clock.sync.internet on` |
|
||||
|
||||
Reference in new issue
Block a user