Support 2+ path-hash width filters

This commit is contained in:
mikecarper committed 2026-10-02 10:21:17 -07:00
1 parent d084f5b837
commit 7324bf06ef
12 files changed
+202 -37

No files matched your search

+13 -5
View File
@@ -257,7 +257,8 @@
channel: normalizeChannel(input.channel, true),
incoming: normalizeIncoming(input.incoming),
hashBytes: ["", "0", "any", "*"].includes(clean(input.hashBytes).toLowerCase())
? 0 : requiredInteger(input.hashBytes, 1, 3, "Path hash bytes"),
? 0 : clean(input.hashBytes) === "2+" ? "2+"
: requiredInteger(input.hashBytes, 1, 3, "Path hash bytes"),
pathKind: enumValue(input.pathKind, PATH_KINDS, "Path matcher", "none"),
pathPrefix: "",
sender: clean(input.sender),
@@ -280,7 +281,8 @@
if (rule.pathKind === "prefix") rule.pathPrefix = normalizePathPrefix(input.pathPrefix, 3);
if (rule.pathKind === "prefix" && !rule.pathPrefix) throw new FilterToolError("Prefix path matcher requires at least one pbyte ID.");
if (rule.pathKind === "prefix" && rule.hashBytes
&& rule.pathPrefix.split(",")[0].length / 2 !== rule.hashBytes) {
&& (rule.hashBytes === "2+" ? rule.pathPrefix.split(",")[0].length / 2 < 2
: rule.pathPrefix.split(",")[0].length / 2 !== rule.hashBytes)) {
throw new FilterToolError("Path hash bytes conflicts with prefix width.");
}
if (rule.sender) {
@@ -577,7 +579,9 @@
else if (rule.pathKind === "blacklist") conditions.push("a path matching the passive blacklist");
else if (rule.pathKind.startsWith("bucket:")) conditions.push(`a path matching ${rule.pathKind}`);
else if (rule.pathKind.startsWith("loop:")) conditions.push(`the ${rule.pathKind.slice(5)} own-ID loop threshold`);
if (rule.hashBytes) conditions.push(`${rule.hashBytes}-byte encoded path hashes, including zero hops`);
if (rule.hashBytes) conditions.push(rule.hashBytes === "2+"
? "2- or 3-byte encoded path hashes, including zero hops"
: `${rule.hashBytes}-byte encoded path hashes, including zero hops`);
if (rule.sender) conditions.push(`decrypted sender "${rule.sender}"`);
conditions.push(`traffic path ${rule.via}`);
if (rule.tempRadio !== "any") conditions.push(`temporary radio ${rule.tempRadio}`);
@@ -607,7 +611,7 @@
&& rule.hops === "all" && !rule.channel && rule.incoming === "any"
&& rule.pathKind === "none" && rule.tempRadio === "any") {
warnings.push(rule.hashBytes
? `This drops all ${rule.hashBytes}-byte path-hash packets across both flood route types.`
? `This drops all ${rule.hashBytes === "2+" ? "2- or 3" : rule.hashBytes}-byte path-hash packets across both flood route types.`
: "This is a global drop rule across both flood route types.");
}
if (limitsRemoteManagement(rule)) {
@@ -1038,7 +1042,8 @@
if (packet.hops < minimum || packet.hops > maximum) misses.push(`hop ${packet.hops} is outside ${rule.hops}`);
if (rule.channel && rule.channel !== packet.channel) misses.push("channel differs or is unavailable");
if (!incomingMatches(rule.incoming, packet)) misses.push(`original scope does not satisfy ${rule.incoming}`);
if (rule.hashBytes && rule.hashBytes !== packet.hashBytes) misses.push(`path hash width is ${packet.hashBytes} bytes`);
if (rule.hashBytes && (rule.hashBytes === "2+" ? packet.hashBytes < 2
: rule.hashBytes !== packet.hashBytes)) misses.push(`path hash width is ${packet.hashBytes} bytes`);
if (rule.pathKind === "prefix") {
const wanted = rule.pathPrefix.split(",");
const actual = packet.path ? packet.path.split(",") : [];
@@ -1164,6 +1169,9 @@
hash_width: Object.freeze([
{ ...documentedDropRule("drop-one-byte-paths", "any", "all"), hashBytes: 1 },
]),
hash_width_two_plus: Object.freeze([
{ ...documentedDropRule("drop-two-plus-byte-paths", "any", "all"), hashBytes: "2+" },
]),
channel_scope: Object.freeze([
{
...defaultRule("rgdata-scope"),
+7 -5
View File
@@ -2910,9 +2910,9 @@ worked moderation examples, see [Flood Filtering and Moderation](flood_filtering
- `get flood.rule`
- `get flood.rule.<n>`
- `get fr[.<n>]` (numbered form returns a complete short setter for copying)
- `set fr[.<n>] <type> [h=<range>] [m=r|b|c|bc] [c=<channel>] [pb=any|1|2|3] <d|s=<scope>|r=<region>|q=<N>|r|s> [...]`
- `set fr[.<n>] <type> [h=<range>] [m=r|b|c|bc] [c=<channel>] [pb=any|1|2|2+|3] <d|s=<scope>|r=<region>|q=<N>|r|s> [...]`
- `del fr.<n>` or `del fr all`
- `set flood.rule[.<n>] type=<type> [mode=radio|bridge|cross|bridge,cross] [hops=<range>] [channel=<channel>] [hashbytes=any|1|2|3] [prefix=<path-prefix>] [in=<input-scope>] <drop|scope=<name>|region=<name>|rate=<N>/min|retry|stop> [priority=<0-255>] [tx=slow] [suspend=tempradio]`
- `set flood.rule[.<n>] type=<type> [mode=radio|bridge|cross|bridge,cross] [hops=<range>] [channel=<channel>] [hashbytes=any|1|2|2+|3] [prefix=<path-prefix>] [in=<input-scope>] <drop|scope=<name>|region=<name>|rate=<N>/min|retry|stop> [priority=<0-255>] [tx=slow] [suspend=tempradio]`
- `del flood.rule.<n>`
- `del flood.rule all`
- `get flood.filter`
@@ -2968,10 +2968,12 @@ compile this table.
MAC/decrypt check with the Public channel key. That distinguishes an ordinary
`0x11` collision, but it is channel authentication rather than sender
authentication: the group MAC is two bytes and the Public key is shared.
- `hashbytes=any|1|2|3`: Optional encoded path-hash width, independent of hop
- `hashbytes=any|1|2|2+|3`: Optional encoded path-hash width, independent of hop
count and hash value. Omitted, `any`, or `*` matches every supported width.
Width-specific rules also match zero-hop packets. Four-byte hashes are
reserved and unsupported. A simultaneous `prefix=` must use the same width.
reserved and unsupported. `2+` selects both 2- and 3-byte paths in one row,
sharing any rate limit across both. A simultaneous `prefix=` must satisfy the
selector; a 2- or 3-byte prefix narrows `2+` to its own exact width.
Example: `set fr any pb=1 d` drops all 1-byte floods from forwarding, including
relayed login/admin traffic, but not direct packets or local delivery.
- `prefix=<ID[,ID...]>`: Optional ordered source-path prefix of one to three
@@ -3223,7 +3225,7 @@ have the same behavior as their long forms.
| `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=...` |
| `hashbytes=any/1/2/3` | `pb=*/1/2/3` (choose one value) |
| `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` |
+11 -1
View File
@@ -33,6 +33,11 @@ and local delivery remain unaffected. Width-only rules require updated
firmware. The simulator's policy definitions remain design-preview text, not
installable commands.
Select **2+ bytes (2 or 3)** to match both larger widths in one rule. The
firmware command is `set fr any pb=2+ d`. A rate-limit action on this rule uses
one allowance shared by both widths, rather than a separate limit for each.
Four-byte paths remain unsupported.
<div class="filter-tool" data-filter-tool>
<div class="filter-management-safety" role="note">
<strong>Remote login and direct routes</strong>
@@ -77,7 +82,7 @@ installable commands.
<tr><td><code>channel=</code></td><td><code>*</code> means no channel condition; a name or key authenticates one group channel; <code>hash:XX</code> is an unauthenticated one-byte fallback</td></tr>
<tr><td><code>rx.scope=</code></td><td>Original incoming transport scope</td></tr>
<tr><td><code>path=</code></td><td>Path prefix, blacklist, bucket, or loop match</td></tr>
<tr><td><code>hashbytes=</code></td><td>Encoded path-hash width: 1, 2, or 3 bytes, including zero-hop packets</td></tr>
<tr><td><code>hashbytes=</code></td><td>Encoded path-hash width: 1, 2, 3, or 2+ (2 and 3 bytes), including zero-hop packets</td></tr>
<tr><td><code>tempradio=</code></td><td>Temporary-radio state</td></tr>
<tr><td><code>do drop</code></td><td>Do not retransmit</td></tr>
<tr><td><code>do scope=</code></td><td>Set the outgoing transport scope</td></tr>
@@ -96,6 +101,10 @@ installable commands.
<span>Drop all floods with 1-byte path hashes (including zero hops)</span>
<code>when type=any hops=all hashbytes=1 do drop</code>
</button>
<button type="button" data-example="hash_width_two_plus">
<span>Drop all floods with 2- or 3-byte path hashes (including zero hops)</span>
<code>when type=any hops=all hashbytes=2+ do drop</code>
</button>
<button type="button" data-example="channel_scope">
<span>Set #BlackHole86 scope on all #rgdata group traffic</span>
<code>when type=class:group hops=all channel=#rgdata do scope=#BlackHole86 timing=fast</code>
@@ -205,6 +214,7 @@ installable commands.
<option value="0">Any width</option>
<option value="1">1 byte</option>
<option value="2">2 bytes</option>
<option value="2+">2+ bytes (2 or 3)</option>
<option value="3">3 bytes</option>
</select>
</label>
+10 -7
View File
@@ -445,7 +445,7 @@ 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|3]
[hops=<range>] [channel=<channel>] [hashbytes=any|1|2|2+|3]
[prefix=<ID[,ID...]>] [in=<input-scope>] <action> [rate=<N>/min]
[priority=<0-255>] [stop] [tx=fast|slow] [suspend=tempradio]
get flood.rule
@@ -495,12 +495,14 @@ Match fields:
collision, while `public` verifies and decrypts with the Public key. This is
channel authentication, not sender authentication: the group MAC is two
bytes and the Public key is shared.
- `hashbytes=any|1|2|3` matches the encoded width of each path hash, independent
- `hashbytes=any|1|2|2+|3` matches the encoded width of each path hash, independent
of its value or the hop count. Omitted or `any` means any width; `*` is an
alias for `any`. It includes zero-hop packets carrying the selected width.
Four-byte path hashes are reserved and unsupported. Combine this with other
match fields as needed; a `prefix=` must have the same width when both are
specified. The compact alias is `pb=`.
`2+` matches the supported 2- and 3-byte widths in one rule; a rate limit is
shared across both widths. Four-byte path hashes are reserved and unsupported.
Combine this with other match fields as needed; a `prefix=` must satisfy the
width selector. With `2+`, a 2- or 3-byte prefix narrows the rule to that exact
width, and saved output shows the prefix. The compact alias is `pb=`.
- `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
@@ -690,7 +692,7 @@ 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. |
| `hashbytes=any`, `1`, `2`, or `3` | `pb=*`, `1`, `2`, or `3` | Encoded path-hash width, even with zero hops. |
| `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. |
| `in=scope:name`, `in=region:name` | `i=s:name`, `i=r:name` | Match a specific incoming scope or region. |
@@ -716,7 +718,8 @@ slot without replacing the default rules. It stops this node forwarding all
1-byte flood packets, including advertisements and relayed login/admin traffic;
direct routes and local delivery are unchanged. Use a narrower `type=` or
`channel=` if those floods must remain reachable. Use `hashbytes=2` or `3` for
the other supported widths, and `get fr.<slot>` to copy the saved command.
an exact width, or `set fr any pb=2+ d` to drop both larger supported widths with
one rule. Use `get fr.<slot>` to copy the saved command.
Width-only rules require updated firmware. Existing rules remain unchanged and
the table uses no additional RAM or storage. Before downgrading to firmware