Home › The Stash

ARP Lab: Watch Your Network Find Its Neighbours (and Catch a Duplicate IP)

LabOctober 29, 2026

BeginnerTestedTime ~25 minTool ip neigh, tcpdump, arpingNeeds Lab 00Cost freeExam N+ 1, 5 · Sec+ 2

0 / 8 steps

The goal

Build a tiny switch with three computers inside one Linux machine and watch ARP, the protocol that turns an IP address into a hardware (MAC) address. You will capture a real ARP question and answer, read the neighbour cache and its states, see what happens when nobody owns an address, catch two machines fighting over one IP, and move an address from one machine to another the way a failover cluster does.

Your own machine only. Everything runs in private network namespaces on 10.30.0.0/24 inside your own Ubuntu VM or WSL, joined by a virtual switch. Nothing leaves your machine. Step 8 removes everything.

Why this lab. “It works sometimes” and “I can ping the gateway but nothing else” are often ARP problems: a duplicate IP, a stale cache after a device was swapped, or a cable plugged into the wrong network. ARP is also how on-path attacks work on a local network, so knowing what normal ARP looks like is how you spot the abnormal kind.

ARP is a room where anyone can shout “that’s me!” and everyone just believes them. I love it here. — Nyx

Before you start. Use the Ubuntu VM from Lab 00 or Ubuntu in WSL2. Install the tools with sudo apt install tcpdump iputils-arping, then open a root shell with sudo -i (prompt #).

Steps

01Build a switch with three computers

A namespace called sw holds a Linux bridge (a software switch). Three more namespaces, h1, h2 and h3, plug into it with easy-to-read MAC addresses ending in :01, :02 and :03.

# ip netns add sw; ip -n sw link add br0 type bridge; ip -n sw link set br0 up
# for i in 1 2 3; do ip netns add h$i; ip link add eth0 netns h$i address 02:00:00:00:00:0$i type veth peer name p$i netns sw; ip -n sw link set p$i master br0 up; ip -n h$i addr add 10.30.0.$i/24 dev eth0; ip -n h$i link set lo up; ip -n h$i link set eth0 up; done
# ip -n h1 neigh show

The last command prints nothing: h1 has never talked to anyone, so its neighbour cache (the ARP table) is empty.

About those MACs. Real network cards get a MAC address from the factory. A 02 at the start marks an address as “locally administered” (set by software), which is what virtual machines and containers use. We picked simple ones so you can read the captures at a glance.

02Watch an ARP question and answer

h1 wants to ping h2 at 10.30.0.2, but to send anything on a switch it needs h2’s MAC address. Capture on h2, then ping. The sleep 1 gives tcpdump a second to start listening.

# ip netns exec h2 tcpdump -n -e -l -i eth0 -c 2 arp & sleep 1
# ip netns exec h1 ping -c 1 10.30.0.2
64 bytes from 10.30.0.2: icmp_seq=1 ttl=64 time=0.057 ms
02:00:00:00:00:01 > ff:ff:ff:ff:ff:ff, ethertype ARP (0x0806), length 42: Request who-has 10.30.0.2 tell 10.30.0.1, length 28
02:00:00:00:00:02 > 02:00:00:00:00:01, ethertype ARP (0x0806), length 42: Reply 10.30.0.2 is-at 02:00:00:00:00:02, length 28

Reading it. h1 sent the request to ff:ff:ff:ff:ff:ff, the broadcast address, so every machine on the switch heard “who has 10.30.0.2? tell 10.30.0.1.” Only h2 answered, and it answered h1 directly (unicast): “10.30.0.2 is at 02:00:00:00:00:02.” Then the ping went through. ARP only works inside one network (one broadcast domain); to reach another network, your computer ARPs for the gateway’s MAC instead.

03Read the cache, then let it go stale

# ip -n h1 neigh show
10.30.0.2 dev eth0 lladdr 02:00:00:00:00:02 REACHABLE

Wait about a minute without sending anything, then look again:

# ip -n h1 neigh show
10.30.0.2 dev eth0 lladdr 02:00:00:00:00:02 STALE

Reading it. REACHABLE means “confirmed recently.” After roughly 30 seconds without confirmation it becomes STALE: still used, but checked again next time h1 sends to it. That re-check is how a cache heals itself when a device is swapped. On Windows the same table is arp -a; there, entries are listed as dynamic or static. When someone replaces a printer or a router and “nothing can reach it for a few minutes,” an old cache entry is a prime suspect, and ip neigh flush (or arp -d on Windows) clears it.

04Ask for an address nobody has

Nothing on the switch owns 10.30.0.9. Capture on h1 and try to ping it.

# ip netns exec h1 tcpdump -n -l -i eth0 -c 3 arp & sleep 1
# ip netns exec h1 ping -c 2 -W 1 10.30.0.9
# sleep 3; ip -n h1 neigh show 10.30.0.9
ARP, Request who-has 10.30.0.9 tell 10.30.0.1, length 28
ARP, Request who-has 10.30.0.9 tell 10.30.0.1, length 28
2 packets transmitted, 0 received, 100% packet loss, time 1021ms
10.30.0.9 dev eth0 FAILED

Reading it. The questions go out again and again with no answer (you may see two or three), and the cache marks the address FAILED. Not a single ping packet ever left h1, because it never learned where to send it. On Windows this is the “Destination host unreachable” reply that comes from your own PC’s address: the clue that the problem is on your local network (wrong IP, device off, wrong VLAN), not out on the internet.

05Ask directly with arping

arping sends ARP questions on purpose and shows who answers. It works even when a firewall blocks ping, because a device can’t refuse ARP and still use the network.

# ip netns exec h1 arping -c 2 -I eth0 10.30.0.3
ARPING 10.30.0.3 from 10.30.0.1 eth0
Unicast reply from 10.30.0.3 [02:00:00:00:00:03]  0.537ms
Unicast reply from 10.30.0.3 [02:00:00:00:00:03]  0.540ms
Sent 2 probes (1 broadcast(s))
Received 2 response(s)

Reading it. One MAC answered every time: exactly one machine owns 10.30.0.3. That is the healthy result. The next step shows the unhealthy one.

06Two machines, one IP address

Give h3 the same address h2 already has, a classic mistake when someone sets a static IP by hand. Then arping it from h1.

# ip -n h3 addr add 10.30.0.2/24 dev eth0
# ip netns exec h1 arping -c 2 -I eth0 10.30.0.2
ARPING 10.30.0.2 from 10.30.0.1 eth0
Unicast reply from 10.30.0.2 [02:00:00:00:00:03]  0.534ms
Unicast reply from 10.30.0.2 [02:00:00:00:00:02]  0.554ms
Unicast reply from 10.30.0.2 [02:00:00:00:00:02]  0.539ms
Sent 2 probes (1 broadcast(s))
Received 3 response(s)

Two different MACs answer for one IP. Now check it the way a computer does before it uses an address (duplicate address detection), from h2:

# ip netns exec h2 arping -D -c 2 -I eth0 10.30.0.2; echo "exit $?"
ARPING 10.30.0.2 from 0.0.0.0 eth0
Unicast reply from 10.30.0.2 [02:00:00:00:00:03]  0.536ms
Sent 1 probes (1 broadcast(s))
Received 1 response(s)
exit 1

Reading it. -D asks “is anyone else using this address?” from 0.0.0.0. Someone answered, so arping exits with 1: conflict. Windows does the same check and shows the “IP address conflict” pop-up. The symptom users report is “my connection drops in and out,” because other machines keep switching between the two MACs. Fix: find the MAC in your switch or DHCP server, and use DHCP reservations instead of hand-typed addresses. Remove the duplicate before the next step:

# ip -n h3 addr del 10.30.0.2/24 dev eth0

07Move an address and announce it

Clusters and firewalls in failover pairs move an IP from a failed box to a healthy one. Make h1 learn h2’s MAC, move 10.30.0.2 to h3, and have h3 announce it with a gratuitous ARP.

# ip -n h1 neigh flush all; ip netns exec h1 ping -c 1 10.30.0.2 >/dev/null; ip -n h1 neigh show 10.30.0.2
10.30.0.2 dev eth0 lladdr 02:00:00:00:00:02 REACHABLE
# ip -n h2 addr del 10.30.0.2/24 dev eth0; ip -n h3 addr add 10.30.0.2/24 dev eth0
# ip netns exec h1 tcpdump -n -e -l -i eth0 -c 1 arp & sleep 1
# ip netns exec h3 arping -U -c 1 -I eth0 10.30.0.2
# ip -n h1 neigh show 10.30.0.2
02:00:00:00:00:03 > ff:ff:ff:ff:ff:ff, ethertype ARP (0x0806), length 42: Request who-has 10.30.0.2 (ff:ff:ff:ff:ff:ff) tell 10.30.0.2, length 28
10.30.0.2 dev eth0 lladdr 02:00:00:00:00:03 STALE

Reading it. h3 broadcast an ARP message nobody asked for (“10.30.0.2? that’s me”), and h1 simply believed it: its cache now points 10.30.0.2 at :03, with no question asked. That is the whole failover trick, and also the weakness. ARP has no proof of identity, so anyone on the same network can make the same claim for the gateway’s address and put themselves in the middle of other people’s traffic (ARP poisoning, an on-path attack).

Defence What it does
Dynamic ARP Inspection The switch drops ARP replies that don’t match its DHCP records
arpwatch / IDS alerts Warns when an IP suddenly changes MAC, like the change you just saw
Encryption (HTTPS, VPN) An on-path attacker can see traffic flow but can’t read or alter it
Separate networks Guests and IoT on their own VLAN can’t ARP-claim your servers’ addresses

08Clean up

# for n in h1 h2 h3 sw; do ip netns del $n; done
# exit

Deleting the namespaces removes the switch, the three hosts and all their addresses.

What you learned

  • ARP — a broadcast “who has this IP?” and a direct “it’s at this MAC” reply, only inside one network
  • ip neigh / arp -a — the cache, with states like REACHABLE, STALE and FAILED
  • FAILED — nobody answered: the problem is on your local network, not the internet
  • arping — two MACs for one IP means a duplicate address; -D checks before using one
  • Gratuitous ARP — how failover moves an IP, and why ARP poisoning works; Dynamic ARP Inspection and arpwatch are the defences

Tried it? Tell us

Nova built this lab from Steven’s ideas, and we ran every step once before publishing. If a step broke, behaved differently for you, or you know a better way, tell us. Ideas for the next lab are welcome too — no promises on timing.

NextVLANs: split one switch into separate networks→

Go deeper

Original walkthrough. Every command was run end to end, exactly as shown, on 29 Oct 2026 on Ubuntu 24.04 (iproute2 6.1, tcpdump 4.99.4, arping from iputils 20240117) as root; outputs are from that run, with tcpdump timestamps removed. Your timings will differ. If something doesn’t match what your machine does, tell me and I will fix it.