Repository navigation
os/v3/puppy: the firewall drops every mDNS query, so 1bit-os.local is announced but never answers - #1016
Conversation
… announced but never answers The c22 board (4 Nov 12:5x AST) logged `avahi-daemon up: 1bit-os.local is announced on this network` and a raw mDNS query from ryzen got no answer. #952/#953 put the responder in the image; what was never opened is the path for a *query*, and the cause is the same `rc.firewall` that `zz-onboard-ports` already works around for tcp. Read out of the packed c22 puppy SFS, `etc/init.d/rc.firewall`: -P INPUT DROP -A INPUT -p ALL -d 224.0.0.1 -j DROP -A INPUT -p UDP -j udp_inbound (drops 137/138, accepts DHCP 67->68, then -j RETURN) -A INPUT -m pkttype --pkt-type broadcast -j DROP -A OUTPUT -p ALL -j ACCEPT so every NEW inbound UDP packet falls through to the DROP policy, and a query on 5353 is exactly that; the appliance's own announcements leave fine, which is why avahi reported success while nothing answered. This file already opens tcp/13305 and tcp/22 for the same reason - mDNS is the UDP half. LAN-only, the same ranges hosts.allow already names below plus link-local for avahi's no-lease fallback, ahead of udp_inbound and the broadcast drop, idempotent via -C, and in `zz-onboard-ports` rather than `18onboard-mdns`, whose name sorts before `rc.firewall` and would be wiped by it. Measured in a booted c22 image (`~/wt/os3i/c23-mdns-probe6.sh`, one controlled announcement burst per arm, the rules' own packet counters): arm policy DROP the fix's INPUT rule shipped firewall 5 -> 12 (+7/2095) absent + one `-I INPUT 1 ... dport 5353` 12 (unchanged) 0 -> 7 packets / 2095 bytes rule removed again 12 -> 19 (+7/4963) absent the same seven packets that were dropped are the ones the rule accepts, byte for byte. Two caveats stated rather than implied: those packets are avahi's own multicast announcements looped back (IP_MULTICAST_LOOP), not a query from a peer - same packet class, same chain, and a peer's query is what the board row must still prove; and QEMU user networking cannot carry inbound UDP at all (a `hostfwd=udp` delivers nothing even with the firewall flushed), which is why this is measured on the appliance's own interface. Gate: a `U3 mdns rule` row in vm-check.sh that fails an image without the rule. closes #1011
PR Reviewer Guide 🔍(Review updated until commit b27ba43)Here are some key observations to aid the review process:
|
bong-water-water-bong
left a comment
There was a problem hiding this comment.
VERDICT: READY
The diagnosis is right and I reproduced both halves of it in a private network namespace — no box firewall touched,
unshare -rn with lo up, rc.firewall's UDP shape rebuilt from the diff's own listing:
before the rule: listener TIMED OUT (no query arrived) <- the c22 board symptom, reproduced
after iptables -I INPUT 1 -p udp --dport 5353 -s <src> -j ACCEPT:
listener RECEIVED 10 bytes from 127.0.0.1
chain afterwards: -P INPUT DROP
-A INPUT -s <src>/… -p udp -m udp --dport 5353 -j ACCEPT
-A INPUT -d 224.0.0.1/32 -j DROP
-A INPUT -p udp -j udp_inbound
So "announced but never answers" is exactly what -P INPUT DROP + udp_inbound's -j RETURN does to a query, and
-I INPUT 1 is the position that fixes it (ahead of the 224.0.0.1 drop and of udp_inbound). The substitute I made
is the source: 127.0.0.1/32 for one of the four LAN ranges, since a loopback source is what a single netns can produce;
the match semantics are the same and the ranges themselves are declared and -C-tested by the new row.
Idempotency, which is the other thing a rule-inserting init script can get wrong — ran the function three times in
the same namespace:
run1 rc=0 rules=4 run2 rc=0 rules=4 run3 rc=0 rules=4
one rule per range, stable, because -C short-circuits before -I.
bash -n clean on both files and shellcheck reports 0 findings on zz-onboard-ports (8 lines of open_mdns included).
Three notes, none a gate:
- The broadcast drop is not what was biting, and the test says so. My query was unicast, so the path proven is
udp_inbound-> DROP policy;-m pkttype --pkt-type broadcast -j DROPnever matched (mDNS is multicast, not
broadcast). Worth knowing if someone later "simplifies" the ordering — the-I INPUT 1is doing the work for the
common case, and the224.0.0.1DROP would only matter for a query sent to the all-hosts address, which mDNS does not. open_mdns's return value is ignored at the call site, exactly like the twoopen_portcalls above it — so a failed
insert prints to stderr and the script still finishes 0. Consistent with the file, but it means a boot with no
iptableswould be quiet in the exit status. The newU3 mdns rulerow (assertingudp-5353-open) is what actually
catches it, which is the right place for it.- IPv4-only is declared as a
shortcut:with the condition for upgrading (a global v6 address), which is the form I
want to see. No mDNS over v6 on this image today, so nothing is lost.
Not run by me: the guest rows themselves (U3 mdns rule, and the responder's own log line) — they need the VM or the
c22 stick, and I mount nothing on this box.
…nnot regress silently
`mdns-ports-selftest.sh` runs the shipped `zz-onboard-ports` against a stub `iptables` (a rule table in a file, `-C`
answered from it): no root, no ruleset, no device. It asserts four `udp/5353` inserts with the four intended
sources, that a second run inserts nothing, and that the two TCP ports this file already opened are still opened.
Measured both ways before committing:
shipped script run 1: 6 inserts, 4 of them udp/5353 run 2: 0 inserts, 4 -C -> RESULT: PASS
origin/main's run 1: 2 inserts, 0 of them udp/5353 -> RESULT: FAIL
`sh -n`, `shellcheck -S warning` and `tools/copyright.py --check` are clean.
|
Persistent review updated to latest commit 9c0b734 |
bong-water-water-bong
left a comment
There was a problem hiding this comment.
VERDICT: READY (re-verdicted at 9c0b73487)
The push adds os/v3/puppy/mdns-ports-selftest.sh (+78), which is the right answer to "this is the kind of code that
fails silently". I ran it, and then ran it against the pre-fix script as a control:
$ sh os/v3/puppy/mdns-ports-selftest.sh os/v3/puppy/embed/etc/init.d/zz-onboard-ports
run 1: 6 insert(s), 4 of them udp/5353, 8 udp/5353 command(s)
run 2 (same ruleset): 0 insert(s), 4 udp/5353 command(s) - all -C, none -I
RESULT: PASS (four udp/5353 rules inserted once, the re-run inserts nothing, sources as intended)
$ sh os/v3/puppy/mdns-ports-selftest.sh <the same file at origin/main>
RESULT: FAIL - first-run-inserts=2(expected 6: two TCP ports, four udp/5353) udp5353-inserts=0(expected 4)
re-run-inserted-udp5353=0(not idempotent) … missing-rule-10.0.0.0/8 missing-rule-172.16.0.0/12
missing-rule-192.168.0.0/16 missing-rule-169.254.0.0/16
So the check discriminates in both directions and it also pins that the two pre-existing TCP ports are still opened —
which is the regression a wrapper like this most plausibly causes. sh -n clean on both files.
This stands alongside the netns result from my earlier review (query dropped before the rule, delivered after it, rule at
the top of INPUT), and it does not need root, a device or a real ruleset.
bong-water-water-bong
left a comment
There was a problem hiding this comment.
VERDICT: READY at d0b5f7194 — merge-only update; the delta is #1007 arriving from main.
git diff 9c0b7348 d0b5f7194 -- os/ is os/v3/puppy/g3-probe.sh | 5 +++++-, i.e. main's -1bit release-spelling fix
and nothing of this PR's own three files. So the evidence carries: the netns result (query dropped before the rule,
delivered after, rule at the top of INPUT), the rule's idempotency (4 rules after three runs), and the new
mdns-ports-selftest.sh (PASS at the head, RESULT: FAIL against the pre-fix script).
Note for the merge sweep: #1020 is stacked on this PR (merge-base --is-ancestor on this head -> yes), so #1016 goes
first and #1020 second, or #1020 needs a rebase.
|
Persistent review updated to latest commit b9991d8 |
|
Persistent review updated to latest commit b27ba43 |
…t - rc.firewall drops inbound mDNS Cycle 23 (#1016) opened udp/5353 in zz-onboard-ports for the LAN ranges and added the `U3 mdns rule` gate row. Section 1 said only the responder and the LAN could leave you reading an address, which reads as "works on a c22 stick" when the measured c22 board shows the opposite. Stated what was measured, with the fix's citations, and kept the one hop nobody has proved: the packets the rule accepts were avahi's own loopback announcements, not a peer's query. Also re-points the blank-mode U3 citation (:740-754 -> :749-762) after #1016.
Board, c22 stick (
rc-2026-11-04-c22), 4 Nov 12:5x AST —TOS3-C23-NOTES.mditem 1: "mDNS is announced butunreachable: avahi says '1bit-os.local is announced', yet a raw mDNS query from ryzen gets no answer."
Root cause
#952/#953put the responder in the image and it runs — the c22 image logs1bit-os.local is announced on this network. What was never opened is the path for a query, and the cause is the samerc.firewallthatzz-onboard-portsalready works around for TCP. Read out of the packed c22 puppy SFS,etc/init.d/rc.firewall:Every NEW inbound UDP packet falls through to DROP, and an mDNS query is exactly that packet. The appliance's own
announcements are outbound and always left, which is why avahi reported success while nothing answered.
Fix (one file, one function)
os/v3/puppy/embed/etc/init.d/zz-onboard-ports— anINPUTACCEPT forudp/5353:hosts.allow(10/8,172.16/12,192.168/16)plus
169.254/16for avahi's no-lease fallback: announcing to a phone on the LAN is the point, being a publicresponder is not;
udp_inboundand of the broadcast drop, which is the whole reason a portrule for UDP has to live in this file;
-Cfirst, so a re-run is a no-op, same shape as the two port rules already here;zz-onboard-portsand not18onboard-mdns: the name is the mechanism,rc.firewallsorts under "r" andwould wipe a rule set by a
18-service.shortcut:comment in the file): this image's IPv6 is unconfigured and the boardreaches it at
192.168.50.110, so a v6 rule would be untested code.vm-check.shgainsU3 mdns rule, which fails an image without the rule (the same reasoning as the existingU3 port rulerow) and prints the responder's own last log line.Measured (booted c22 image,
~/wt/os3i/c23-mdns-probe6.sh, one controlled announcement burst per arm)The counters are the rules' own, so this is the packet path talking, not a grep:
-I INPUT 1 -p udp --dport 5353 -s 10.0.0.0/8 -j ACCEPTThe seven packets dropped in arm 1 are the seven the rule accepts in arm 2, byte for byte, and arm 3 puts them back
on the drop path.
udp_inboundcounted 16 packets in arm 1, so the burst really did traverse the chain.Two limits, stated rather than implied:
IP_MULTICAST_LOOP), not a query from apeer. Same packet class (
udp→dport 5353, inbound, real interface), same chain, and the rule is blind to whosent it — but "a peer on the LAN resolves
1bit-os.localand gets:13305200" is the board row and staysSKIP U4 boardin the harness.hostfwd=udp:…-:5353delivers nothing to a guestlistener even with
iptables -F; -P INPUT ACCEPT(policy counter 0 packets). A veth pair does not help either —both ends are one host's addresses, so the route is
localvialo— and this image'sipis BusyBox, which hasno
netnssubcommand. All three dead ends are recorded in the probe scripts so the next lane does not pay forthem again.
Closes #1011.