The Address Resolution Protocol (ARP) finds the media access control (MAC) address that belongs to a known IPv4 address on the local link. Ethernet delivers a frame by MAC address; IP addresses a packet by IP address; ARP is the step that turns the second into the first.
A device that wants to reach 192.0.2.53 does not know a MAC address to put in the frame it is about to build.
It has an IP address, and IP addresses mean nothing to a switch.
So before the frame can exist, the sender asks the only question that matters on a local link: who has this address?
Asking, and being answered
The question goes out as a broadcast, because the sender has no unicast address to send it to yet.
who has 192.0.2.53? tell 192.0.2.10Every device on the link receives it, since a broadcast is flooded to all of them.
Only the one that owns 192.0.2.53 answers, and it answers directly rather than broadcasting the reply:
192.0.2.53 is at 00:00:5e:00:53:0bThe requester stores that pairing in its neighbour cache (the ARP cache), so the next packet to the same address skips the question entirely. On Linux, the cache is read with:
ip neigh show
That reply is the entire protocol. No handshake, and no field that says which request this is answering - a reply arrives, and whatever it claims goes into the cache.
Why the reply is trusted
ARP was built to resolve addresses on a link where everyone was assumed to belong there, not to authenticate who is allowed to answer for an address.
Nothing in the reply above proves that 00:00:5e:00:53:0b is really the interface with 192.0.2.53; it proves that something on the link said so, and the receiver has no way to check further than that.
That is the whole opening a machine-in-the-middle (MITM) attack needs. If any device on the link can send a reply, any device on the link can claim any address - not just its own.
The forged reply
The lab for this puts three hosts on one bridge: host A wants 192.0.2.53, which really belongs to host B, and host C sits on the same segment doing nothing it was asked to do.
./arpspoof.sh
Host C's reply is built with scapy, because it has to claim an identity the sender's own interface does not have:
frame = Ether(src=claimed_mac, dst=dest_mac) / ARP(
op=2, # reply
hwsrc=claimed_mac, psrc=claimed_ip,
hwdst=dest_mac, pdst=dest_ip,
)psrc is 192.0.2.53, host B's real address. hwsrc is host C's own MAC address. Nothing else in the packet says this is a lie.

Run this against a cache that resolved seconds ago and it does nothing: Linux locks a fresh entry against a MAC change for close to a second, net.ipv4.neigh.default.locktime (measured in hundredths of a second, 99 by default).
A reply sent 0.74 seconds after the baseline resolution was silently ignored; the same reply sent 1.18 seconds after it went straight into the cache.
That lock is short, resets on every legitimate re-resolution, and stops nothing but a reply that arrives too eagerly. An attacker only has to wait it out.
Where the next packet goes
The cache now says 192.0.2.53 is at host C's address. Host A has no reason to doubt it, and builds its next frame accordingly.

The IP layer still says 192.0.2.53, because host A's poisoned cache never touched that field, and neither this frame nor host A itself knows anything is wrong.
The Ethernet layer is what a switch forwards on, and it now says host C.
The packet was captured arriving there, not at host B:
00:00:5e:00:53:0a 00:00:5e:00:53:0c 192.0.2.53 8Source host A, destination host C, still carrying an IP packet addressed to host B, still an echo request. Host B never saw it. It was never sent there.
Redirection is the goal: making the cache point somewhere the attacker controls. An attacker whose own interface is configured to forward traffic can relay each packet on to the real host after reading it, so the sender never learns anything changed - this lab stops at the redirection itself and does not build that relay. Poisoning the other side too, so the reply on the way back is intercepted as well, is the same technique run twice, once against each half of the conversation.
A widely used tool for exactly that, bettercap, does it with a command instead of a script, and by default runs the two-way version: spoofing the target and the gateway at once.
The banner at the top of this page is its own log, net.probe finding host A and arp.spoof starting against it, on the same lab link and the same addresses as the rest of this post - marked in red is the exact line where it finds host A and reads back the same MAC address host A introduced itself with all the way up in the first figure.
No traffic exists on this synthetic link for it to intercept, so its own "could not find spoof targets" line is left in rather than cut.
Detecting and limiting it
The tell is the same neighbour cache, read from the other direction: an address that should belong to one device suddenly belongs to another, or one MAC address turns up against IP addresses it has no reason to answer for.
tcpdump -ni eth0 arpwatching the link directly shows a reply exactly like the forged one above arriving with no matching request in front of it.
None of this needs encryption to matter less. HTTPS and the Secure Shell (SSH) protocol protect what the redirected traffic contains; they do not stop the redirection. What narrows the opening:
| Defence | What it does |
|---|---|
| Dynamic ARP Inspection | A managed switch validates ARP replies against a known-good table before forwarding them |
| Dynamic Host Configuration Protocol (DHCP) snooping | Builds that known-good table from DHCP activity rather than trusting ARP itself |
| Static ARP entries | Fixes a mapping so no reply, forged or real, can change it - for the handful of hosts worth the maintenance |
None of them are things ARP does for itself. All of them exist because the protocol never checks who is allowed to answer.



