Swiss got a DHCP lease on hardware (10.0.0.64/24), but the script flagged
every ARP from the GC as "frame did NOT parse as DHCP ... this is what a
corrupted TX frame looks like". That was wrong and actively misleading: the
frames are valid lwIP duplicate-address probes (sender 0.0.0.0, target =
the offered address), sent to check the lease is free before accepting it.
The parser conflated "not DHCP" with "corrupted". A diagnostic that cries
wolf on normal traffic is worse than none, because 'malformed' then means
nothing. Now:
'info' from the client but not DHCP -- ARP decoded properly (probe vs
request vs reply, with addresses), other ethertypes named
'malformed' CLAIMED IPv4/UDP->67 and then failed to parse, i.e. what a
genuinely damaged TX frame would look like
So the corruption diagnostic still works -- that was the point of using a raw
socket -- but it only fires when it means something.
scripts/test-mini-dhcpd.py keeps the self-test, now including the exact
60-byte frame from the console (T6), a damaged DHCP packet that must still be
flagged (T7), and a raw ethertype (T8).
One note for anyone extending that test: the real frame goes in as ONE string
literal. Splitting the hex across two adjacent literals is how the first
version of T6 silently lost a byte and blamed the parser for decoding the
target as 0.10.0.0 -- the parser was correct.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
sudo ./scripts/mini-dhcpd.py enp0s31f6
Serves one address, logs every exchange, and does not touch the rest of the
system. Needed because the Swiss DHCP timeout proves nothing while there is no
server on the segment to answer.
Deliberately AF_PACKET rather than a UDP socket on port 67. A UDP socket only
sees frames the kernel accepts as well-formed IP/UDP, and one of the open
questions is whether the GC's frames arrive INTACT: libogc streams TX payload
with EXI_Dma and the GC's DMA-write engine is documented to have a 1-bit-shift
bug. A corrupted DISCOVER would never reach a UDP socket and the server would
look "fine, just no client". On a raw socket every frame from the adapter is
parsed by hand and anything that fails is reported as MALFORMED with the reason
and a hexdump -- which is exactly the evidence that would confirm or kill the
corruption hypothesis.
Replies go out as raw ethernet UNICAST to the client's MAC, so a successful
lease also demonstrates the new MACRAW MF filter accepts unicast to SHAR and
not just broadcast.
Self-tested without a network: a well-formed DISCOVER parses with the right
chaddr/xid, a corrupted magic cookie is reported as malformed rather than
ignored, frames from other MACs are skipped, and the generated OFFER is a valid
342-byte frame -- unicast to the client, correct IP checksum and total length,
ports 67->68, BOOTP padded to the 300-byte minimum, yiaddr as configured.
On-wire behaviour is NOT tested here; the devcontainer has only a veth.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>