This is the hands-on version of diary 00: two machines, one cable, and a question that sounds trivial until you try it: Can they just... talk? You run each step yourself and watch every answer show up on the wire.
This part is all procedure, not story. For the why, read the diary or watch the video version. For a frame-by-frame deep-dive into what the Address Resolution Protocol is, from cache states to actually poisoning it, read ARP from the ground up (coming soon!).
"Talk" sounds simple, but it's hiding three questions, and you'll see each one answer itself on the wire:
- Is there even a wire? (Layer 1: a dead port coming to life.)
- Are frames flowing? (Layer 2: the burst of chatter the instant the link comes up.)
- Can they reach each other by the address you'd type? (Layer 3: why a wire with no identity is invisible to the routing table, and how ARP fixes it.)
The scripts live on your machine where you clone this repo and drive two nodes.
For anyone who's built a hardware version of the little internet themselves (bless you), those nodes will be your two Pis. If you don't want or can't build the hardware version, there are two virtualized versions: a two-VM lab on macOS, and a lighter network-namespace lab on Linux.
You'll need two nodes flashed with the little internet image, an
Ethernet cable between their eth0 ports, and SSH reachability to each over
Wi-Fi. Walk the whole lesson with one command:
The scripts are in this lesson's scripts/ directory. Point them
at your nodes with A_HOST / B_HOST (they default to pi@pi-foo-01.local /
pi@pi-foo-02.local), then
./scripts/check.sh --hardware
./scripts/run.sh # walks every step, pausing between eachIf you changed your Pi's names from the default, you'll need to set them.
export A_HOST=pi@pi-foo-01.local B_HOST=pi@pi-foo-02.local
./scripts/run.sh # walks every step, pausing between eachEach step is also its own script, so you can run or re-run just one:
./scripts/00-link.sh # is there a wire? unplug, then seat, the cable
./scripts/01-listen.sh # the link-up burst, then a naive ping that flops
./scripts/02-no-address.sh # so where did that packet actually go?
./scripts/03-address.sh # give each node an identity
./scripts/04-arp.sh # the ping works now—watch the ARP that made it
./scripts/reset.sh # back to a blank wireNo Pis? The closest thing to the real bench is the VM lab in
scripts/virtual-vm/: two separate Debian VMs (two
real kernels) joined by one QEMU socket cable. It costs a QEMU install and a
few minutes to boot, but it delivers the beats no lighter setup can: a Layer 1
carrier you can seat and unseat by hand (./link.sh a off), so the "is there
even a wire?" question is live in software too; two genuinely independent
machines; and cable NICs that wear the Pi's b8:27:eb vendor prefix. It also
ships a live web dashboard (./dashboard.sh): both nodes' link state, address,
ARP cache, and serial console, plus every frame crossing the wire. It runs on
macOS today; see its README.md for the
two-terminal runbook.
Want something quicker, or you're on Linux (or CI)? ./scripts/run.sh --virtual recreates the lesson with network namespaces instead. A veth pair is
the closest thing to a single cable—two ends, nothing in between—so it stands
up two namespaces (pi-a and pi-b) joined by one veth, walks the same steps
(pausing for you between each, just like the hardware path), and tears it all
down when you're done.
sudo ./scripts/check.sh --virtual
sudo ./scripts/run.sh --virtualNetwork namespaces are a Linux feature, so this path needs a Linux machine—on macOS or Windows, a Linux VM (colima and lima both work).
You can inspect captures from the real Pis without running either lab. Each
exchange was recorded at both ends of the cable, so you can compare what
pi-foo-01 and pi-foo-02 observed:
Open a capture in Wireshark, or read it from the lesson directory with
tshark:
tshark -r captures/link-up_pi-foo-01.pcapng -n
tshark -r captures/arp_pi-foo-02.pcapng -nThese are also the canonical evidence for a read-only walkthrough with a coding agent: ask it to show you the decoded rows before explaining what they mean.
Agents should read the root AGENTS.md and this lesson's
manifest.json before running anything. The manifest lists
the lesson beats, which scripts drive them, what output to look for, and how to
recover from interrupted hardware or virtual runs.
Ask the agent to teach the lesson one beat at a time. It should show you the
command and the relevant raw output, help you read the evidence, and wait for
your prediction or questions before continuing. A collapsed tool message like
"Ran 4 shell commands" is not the experiment—you should see the ip,
ethtool, routing, capture, and ARP evidence that supports each conclusion.
For packet captures in particular, expect to see the actual tshark or
tcpdump rows in a code block before the agent explains them. Frame-by-frame
prose is useful only when you can look back at the corresponding timestamps,
source and destination addresses, protocols, and summaries yourself. If the
capture is too long, the agent should label any excerpt and tell you what it
left out—not silently replace the capture with its conclusions.
For an interactive walkthrough, the agent should use the individual step
scripts rather than batch-running scripts/run.sh. The full runner is handy for
an unattended demonstration or functional check, but a coding agent's job here
is to provide the pacing and instruction that a shell script cannot.
Even the VM lab (scripts/virtual-vm/) has no PHY, so
some physical details are gone in any virtual run:
- Speed/Duplex details on the
eth0device - mDNS or DHCP firing on link-up
- the 42-vs-60-byte tell on whether your device sent or received ARP frames
The namespace lab loses two more. There's no carrier to seat or unseat, and no
b8:27:eb<->Raspberry Pi vendor prefix on MACs, because virtual interfaces get
random ones. The VM lab keeps both: it drives a real carrier, so the link
up/down beat is live, and it assigns the b8:27:eb prefix to its cable NICs on
purpose.