What Is ARP? ARP Table States, Broadcast Domains, Spoofing
ARP maps an IP address to a MAC address on the local network. Cache states, where the broadcast domain ends, and ARP spoofing, shown with real packet captures.
- What Is ARP? From IP Address to MAC Address
- Why Two Addresses? How MAC and IP Split the Work
- How ARP Works: One Request, One Reply
- The ARP Cache and Its States: Who Does the Machine Remember?
- Lab: Three Machines, Three Different Silences
- One Question, Two Answers: Linux Answers for Addresses It Doesn’t Own Here
- Who Remembers How Long? Three Timers on One Path
- Lab: Same IP, New Device. Who Notices, and When?
- The Broadcast Domain: Where ARP’s Wall Ends
- Lab: Two Devices “on the Same Network,” Two Broadcast Domains
- Topology: ARP’s Hidden Bill
- Daisy-Chained Switches
- Why a “Dumb Switch” Blinds ARP Diagnosis
- Gratuitous ARP: Speaking Without Being Asked
- Announcements Are Sent, Not Necessarily Heard
- ARP Spoofing: The Price of Never Asking for ID
- Why Attackers Like ARP
- Defenses: Three Tools, All Partial
- Diagnosing with ARP: Which Output Tells You What
- Conclusion: What the Quietest Protocol Tells You
Imagine walking into a crowded conference room. You have a name, but you don’t know which face it belongs to. The only thing you can do is turn to the room and ask out loud: “Is Alex Morgan here?” Someone raises a hand, and you match that face to that name and start talking.
Two devices on a network do exactly this. An IP address is the name; the MAC address needed to put a frame on the wire is the face. The protocol that connects the two is ARP, and this article follows that question through real packet captures: what it looks like on the wire, how the answer is remembered, and what goes wrong when nobody checks who is answering.
ARP is the reason a VLAN boundary matters in practice. The previous article on VLANs kept pointing at the ARP table as the first thing to check; this one explains why. It is part of the network segmentation guide.
Every output below was captured on real lab machines with tcpdump, nothing reconstructed. The outputs come from the author’s lab network, and addresses are left exactly as measured. If you have a fault to fix right now, jump straight to which output tells you what.
What Is ARP? From IP Address to MAC Address
ARP (Address Resolution Protocol) finds which MAC address owns a given IP address on the same local network. A device asks the network “who has this IP?”, and the owner answers “I do, and this is my hardware address.” That is the whole job.
The conference room maps onto the network like this:
| In the room | On the network |
|---|---|
| The name you hold | IP address |
| The face you’re looking for | MAC address |
| Asking the room out loud | ARP request (broadcast) |
| The person raising a hand | ARP reply |
| The walls of the room | Broadcast domain |
| Remembering a familiar face | ARP cache |
What makes ARP interesting is how quietly it works. It has no settings screen, writes to no log, and shows up in no dashboard. A network can run for years without anyone noticing ARP exists, until the day two devices can’t find each other.
Why Two Addresses? How MAC and IP Split the Work
There are two addresses because they answer two different questions: the MAC address answers “who is on this wire,” the IP address answers “where in the world is this packet going.” One is local and burned into hardware; the other is logical and assigned.
The difference is clearest on a long route: as a packet travels from one continent to another, its IP address never changes, but its MAC address is rewritten at every router hop. A MAC address is only valid as far as the next stop.
Why not use just one?
With only MAC addresses, routing would collapse. MAC addresses carry no geography: two computers side by side have unrelated addresses, assigned by manufacturer and production order. They can’t be grouped. A router today can summarize millions of addresses in one line (“everything starting with this prefix goes that way”); with MAC addresses it would have to list every network card on Earth.
With only IP addresses, no device could get started. A freshly booted machine has no IP address; it asks the network for one. But asking means sending a packet, and sending a packet requires an address. The loop is broken by the MAC address: it is written at the factory, needs no configuration, and is there before the machine knows anything. DHCP is built on exactly this: the server recognizes an address-less machine by its MAC and ties the lease to it.
The evidence for that second point sits in the capture you’re about to see, and it’s easy to miss: the ARP line says ethertype ARP (0x0806), not IPv4 (0x0800). There is no IP header inside that frame at all. ARP runs beside IP, not on top of it; it couldn’t be otherwise, since its job is to find something IP doesn’t know yet.
How ARP Works: One Request, One Reply
ARP is one question and one answer: a who-has request goes out as a broadcast, an is-at reply comes back to the asker only. Those two words appear verbatim in a capture.
The setup: on ntp01 (192.168.1.15) I deleted the cached entry for ldap01 (192.168.1.25), sent a single ping, and recorded the traffic at the same time. The expectation was to see an ARP exchange before the ping.
sudo ip neigh flush dev ens33
ping -c 1 192.168.1.25Watch three things below: the destination MAC at the start of each line, the direction of question and answer, and the gaps between timestamps.
20:29:59.271996 00:0c:29:a0:71:9a > ff:ff:ff:ff:ff:ff, ethertype ARP (0x0806): Request who-has 192.168.1.25 tell 192.168.1.15
20:29:59.272559 00:0c:29:7c:79:af > 00:0c:29:a0:71:9a, ethertype ARP (0x0806): Reply 192.168.1.25 is-at 00:0c:29:7c:79:af
20:29:59.272572 00:0c:29:a0:71:9a > 00:0c:29:7c:79:af, ethertype IPv4 (0x0800): 192.168.1.15 > 192.168.1.25: ICMP echo request
20:29:59.273029 00:0c:29:7c:79:af > 00:0c:29:a0:71:9a, ethertype IPv4 (0x0800): 192.168.1.25 > 192.168.1.15: ICMP echo replyThe word most often misread is tell: it is not a command, it names the sender. The first line says exactly: “Who has 192.168.1.25? Asking: 192.168.1.15.” The second answers: “192.168.1.25 is at 00:0c:29:7c:79:af.”
- The first line’s destination is
ff:ff:ff:ff:ff:ff, meaning “everyone.” The question goes to every device in the broadcast domain. That is asking the whole room. - The second line’s destination is
00:0c:29:a0:71:9a. The answer is not broadcast; it goes straight back to the asker. Everyone hears the question, only the asker gets the answer. - 563 microseconds separate lines one and two. The ping hasn’t even left yet. ARP is preparation that finishes faster than you could notice.
- The ICMP packet in line three goes to the MAC address just learned. The order is: learn the face, then talk.
Five seconds later, the same capture showed one more exchange:
20:30:04.627661 00:0c:29:7c:79:af > 00:0c:29:a0:71:9a: Request who-has 192.168.1.15 tell 192.168.1.25
20:30:04.627682 00:0c:29:a0:71:9a > 00:0c:29:7c:79:af: Reply 192.168.1.15 is-at 00:0c:29:a0:71:9aThis time the other side is asking, but not the way it first looks. The request’s destination is not ff:ff:ff:ff:ff:ff but 00:0c:29:a0:71:9a directly. ldap01 already knew ntp01’s MAC address, because an ARP request doesn’t only ask; it also carries the asker’s own address pair. Any machine receiving the request can record it, and ldap01 did: it answered the ping in the first capture in 457 microseconds, without asking anything.
So what is the exchange five seconds later? Not learning, but verification. An operating system doesn’t fully trust an entry it overheard; the first time it really depends on it, it checks once that the other side is still there. On Linux the wait before that check is delay_first_probe_time, measured on ntp01 at 5 seconds; the gap between the two exchanges is 5.36 seconds. The check itself is fast: 21 microseconds between question and answer.
Two rules follow. First: ARP resolution is one-directional, but information flows both ways; the question goes one way, the address pair lands on both sides. Second, more useful in the field: when the other side doesn’t know you, the reason is usually not “it never asked” but “your question never reached it.” That is where to look in one-way faults.
The ARP Cache and Its States: Who Does the Machine Remember?
Resolution took half a millisecond. Cheap, until you imagine doing it for every packet: every question is a broadcast, and a broadcast interrupts everyone in the room. So the machine stores what it learns, in the ARP cache.
Linux has two commands for reading it: arp -a is the old one, ip neigh show is its modern replacement. They read the same table but don’t tell you the same thing. Here they are, back to back on the same machine:
arp -a? (192.168.1.25) at 00:0c:29:7c:79:af [ether] on ens33
? (192.168.1.1) at 60:d8:a4:a7:2e:99 [ether] on ens33
? (192.168.1.10) at <incomplete> on ens33
? (192.168.1.115) at ec:75:0c:9f:45:ea [ether] on ens33ip neigh show192.168.1.25 dev ens33 lladdr 00:0c:29:7c:79:af REACHABLE
192.168.1.1 dev ens33 lladdr 60:d8:a4:a7:2e:99 STALE
192.168.1.10 dev ens33 INCOMPLETE
192.168.1.115 dev ens33 lladdr ec:75:0c:9f:45:ea REACHABLE
fe80::1 dev ens33 lladdr 60:d8:a4:a7:2e:99 router STALETwo differences, and both matter in diagnosis. Every line now carries a state. And there is one extra line: fe80::1, an IPv6 neighbor the old command never shows. arp comes from the legacy net-tools package, gives no state, and knows only IPv4; use ip neigh. On Windows the command stays arp -a, which shows dynamic / static instead of a state, or Get-NetNeighbor, which does show states.
The states describe how much the machine trusts what it knows about that neighbor:
| State | Meaning | In the room |
|---|---|---|
REACHABLE | Recently confirmed; the mapping is trusted | You just saw their face |
STALE | The entry is kept but hasn’t been confirmed lately | You remember them; not sure they’re still here |
DELAY | Confirmation pending, not yet asked | About to double-check |
INCOMPLETE | Asked, no answer yet, still trying | You called out; no hand went up |
FAILED | Resolution considered failed | You stopped calling out |
What helps in diagnosis is not the meaning of each state but how entries move between them, because the state you see tells you the previous step too:
FAILED box is the only dead end, and the only way there is a question nobody answered.The most misread state is STALE. A STALE entry is not broken; the machine is only saying it hasn’t refreshed that fact lately. As soon as traffic flows, the entry is quietly confirmed and returns to REACHABLE. The router (192.168.1.1) above is in exactly that state, and nothing is wrong.
A second snapshot from the same machine, taken a few minutes earlier, shows the last state in the table as well:
192.168.1.1 dev ens33 lladdr 60:d8:a4:a7:2e:99 STALE
192.168.1.20 dev ens33 FAILED
192.168.1.10 dev ens33 FAILED
192.168.1.115 dev ens33 lladdr ec:75:0c:9f:45:ea REACHABLE
fe80::1 dev ens33 lladdr 60:d8:a4:a7:2e:99 router STALEThe difference between INCOMPLETE and FAILED saves time in the field: INCOMPLETE means the machine is still asking; FAILED means it has stopped. Both say the device isn’t answering, but one tells you the fault is live, the other that it happened a while ago. When you see FAILED, the attempt is long over, and the entry just sits there until new traffic arrives.
Lab: Three Machines, Three Different Silences
The cache earns its keep during a fault. One of the most common field assumptions is: “I ping it, no reply, so the device is down.” That conclusion is often wrong, and I measured why. I pinged three machines on the same network; all three went silent, but not for the same reason.
Machine one, 192.168.1.25: the healthy reference. Ping answers, the entry is REACHABLE. (The capture above belongs to this machine.)
Machine two, 192.168.1.10: a server that was switched off.
PING 192.168.1.10 (192.168.1.10) 56(84) bytes of data.
From 192.168.1.15 icmp_seq=1 Destination Host Unreachable
From 192.168.1.15 icmp_seq=2 Destination Host Unreachable
2 packets transmitted, 0 received, +2 errors, 100% packet loss20:27:11.143707 00:0c:29:a0:71:9a > ff:ff:ff:ff:ff:ff: Request who-has 192.168.1.10 tell 192.168.1.15
20:27:12.167705 00:0c:29:a0:71:9a > ff:ff:ff:ff:ff:ff: Request who-has 192.168.1.10 tell 192.168.1.15Destination Host Unreachablecomes from192.168.1.15, the pinging machine itself. No router is reporting anything; your own kernel is saying “I can’t put this packet on the wire, because I couldn’t find the destination’s MAC address.” Mistaking this line for a router message sends the search in the wrong direction from the start.- The broadcast question goes unanswered and repeats every second. You call out, no hand goes up. In the cache:
INCOMPLETE, thenFAILED.
Machine three, 192.168.1.115: and here the expectation breaks. The ping result looks almost the same:
PING 192.168.1.115 (192.168.1.115) 56(84) bytes of data.
3 packets transmitted, 0 received, 100% packet lossBut the capture shows something else entirely:
20:28:43.518120 00:0c:29:a0:71:9a > ec:75:0c:9f:45:ea, ethertype IPv4: 192.168.1.15 > 192.168.1.115: ICMP echo request
20:28:44.519826 00:0c:29:a0:71:9a > ec:75:0c:9f:45:ea, ethertype IPv4: 192.168.1.15 > 192.168.1.115: ICMP echo request192.168.1.115 dev ens33 lladdr ec:75:0c:9f:45:ea REACHABLEThe ICMP packets go to the right MAC address, to a single destination. ARP worked. The question was asked, a hand went up, the mapping is REACHABLE. What goes unanswered is not ARP but the ping itself: that machine’s firewall drops ICMP.
| Check | 192.168.1.25 | 192.168.1.115 | 192.168.1.10 |
|---|---|---|---|
| Ping | replies | 100% loss | 100% loss |
| ARP entry | REACHABLE | REACHABLE | INCOMPLETE |
| ICMP on the wire | to one destination | to one destination | never leaves |
| What’s really going on | healthy | device present, filtered | device not there |
The middle column is the thesis of this article: ping can stay silent; ARP can’t. Ping is subject to an application, a policy, and a firewall rule; ARP is mandatory for a frame to reach the wire at all. A device that is up and in the same broadcast domain has to answer ARP, or it receives no traffic. That same obligation, as you’ll see, also leaves attackers a channel that can’t be closed.
The practical consequence reverses the usual order: when “ping doesn’t work,” look at ip neigh first, not the upper layers. A REACHABLE entry means the device is there and the problem is filtering; no entry at all means the problem is at layer 2 and searching higher up wastes time.
One Question, Two Answers: Linux Answers for Addresses It Doesn’t Own Here
This finding is not in the Turkish original of this article, and it came from a measurement that went wrong first.
To test what happens when an IP address moves to a new MAC address, I gave ntp01 a second virtual interface with its own MAC address and put the test address 192.168.1.99 on it. The first run looked perfect: the address moved and the observer lost zero packets. Too perfect. A capture of a single question explained why:
ARP, Request who-has 192.168.1.99 tell 192.168.1.115
ARP, Reply 192.168.1.99 is-at 00:0c:29:a0:71:9a
ARP, Reply 192.168.1.99 is-at d6:76:8d:f7:d1:fbOne question, two answers, from two different MAC addresses on the same machine. The second reply came from the interface that actually held 192.168.1.99. The first came from ens33, the machine’s main interface, which did not hold that address at all.
This is Linux’s default, known as the weak host model: an IP address belongs to the machine, not to an interface, so the kernel answers ARP for any of its own addresses on any interface that hears the question. The setting behind it is arp_ignore, and its default is 0: “answer for any local address.” The zero-loss result was an artifact: the observer kept reaching the machine through whichever reply it believed, so no interruption could ever show.
Setting arp_ignore=1 (answer only if the address is configured on the interface that received the request) removed the duplicate reply, and only then did the measurement mean anything. That first run was discarded; the handover numbers in the next section come from the corrected setup.
Why this matters outside a lab:
- Duplicate replies are not automatically an attack. A Linux server with two interfaces on the same segment, a multi-homed host, or a virtualization host can answer the same question twice, and the tables in the spoofing section will flag it as suspicious. Check
arp_ignorebefore you escalate. - Traffic may arrive on the “wrong” interface. Whichever reply a neighbor believes last decides which cable its traffic uses. On a two-interface server, that can quietly route traffic through the interface you thought was only for management.
- Your test can lie. If you measure failover or an IP move on Linux with the default settings, a second interface can keep answering for the address and hide the very outage you’re trying to measure.
Who Remembers How Long? Three Timers on One Path
The number most repeated in the field is five minutes. The number is real, but it usually gets attached to the wrong table. On a single frame’s path there are three separate timers, on three different devices, with three different lifetimes:
| Table | Where it lives | Measured lifetime | What it holds |
|---|---|---|---|
| ARP cache | workstation and server | 30 seconds | IP → MAC |
| MAC address table | switch | 300 seconds (5 minutes) | MAC → port |
| ARP cache | switch management interface | 1,200 seconds (20 minutes) | IP → MAC |
The first row is base_reachable_time_ms on Linux and BaseReachableTime on Windows; I measured 30000 on both. The other two come from the lab switch itself:
console#show mac address-table
Aging time is 300 Sec
Vlan Mac Address Type Port
-------- --------------------- ----------- ---------------------
1 F48E.381B.184D Management Vl1
5 5811.22B1.0E54 Dynamic Gi1/0/1
5 F48E.381B.184D Management Vl5
Total MAC Addresses in use: 3console#show arp
Age Time (seconds)............................. 1200
Response Time (seconds)........................ 1
Retries........................................ 4
Cache Size..................................... 1000
Dynamic Renew Mode ............................ Enable
Total Entry Count Current / Peak .............. 2 / 2
IP Address MAC Address Interface Type Age
--------------- ----------------- -------------- -------- -----------
192.168.1.2 F48E.381B.184D Vl5 Local n/a
192.168.1.224 5811.22B1.0E54 Vl5 Dynamic 0h 0m 0sThe second table is what separates a switch from a hub: the switch learns which MAC address sits behind which port by reading the source address of passing frames, and sends each frame out only through that port. Nobody writes these entries; the Dynamic row above was learned from a cable. (The same table is also called the CAM table, after the content-addressable memory that holds it.)
These numbers are lower bounds, not deadlines. I measured the MAC table: after an address disappeared from the network completely, its entry was still there at second 373 and gone by second 439, despite the configured 300. Switches sweep the table periodically and drop an entry only after it has gone unused for a full sweep, so real removal lands somewhere between one and two times the configured value.
Lab: Same IP, New Device. Who Notices, and When?
The timers only hurt in one scenario, and it’s a common one: a device fails, you install a replacement and give it the same IP address. The address hasn’t changed; the hardware behind it has. Everyone who remembers the address is now holding a wrong entry, and nobody told them.
I rebuilt that exactly: kept 192.168.1.99 fixed, swapped the MAC address behind it, and removed the old one from the network completely. The new device made no announcement, because this round measured what happens without one. Then I watched two devices looking for that address: a workstation and a switch.
The workstation recovered in about five seconds:
17:24:13 OK D6-76-8D-F7-D1-FB Probe
17:24:18 KAYIP D6-76-8D-F7-D1-FB Unreachable
17:24:23 OK 52-57-6E-C6-D0-81 ReachableThe entry dropped to Probe, then Unreachable (KAYIP is Turkish for “lost”; the monitoring script was written in Turkish); the machine broadcast the question again, learned the right MAC, and moved on. Total loss: one packet.
The switch took twenty-one minutes. Six minutes after the swap:
console#ping 192.168.1.99
Pinging 192.168.1.99 with 0 bytes of data:
----192.168.1.99 PING statistics----
4 packets transmitted, 0 packets received, 100% packet loss
console#show arp
IP Address MAC Address Interface Type Age
--------------- ----------------- -------------- -------- -----------
192.168.1.99 1A4A.ED68.B905 Vl5 Dynamic 0h 6m 15s1A4A.ED68.B905 no longer exists anywhere. The switch kept it anyway:
| Time | Ping | MAC in the cache | Entry age |
|---|---|---|---|
| 19:56 | 100% loss | 1A4A.ED68.B905, dead | 6 min 15 s |
| 20:06 | 100% loss | 1A4A.ED68.B905, dead | 9 min 32 s |
| 20:10 | 100% loss | 1A4A.ED68.B905, dead | 19 min 44 s |
| 20:12 | 0% loss | 4240.6730.A1A6, correct | 0 min 14 s |
The entry dropped exactly when Age Time ran out, not a second earlier. Across four rounds of zero replies, the switch never once broadcast the question again; it trusted its entry and kept sending to a dead address. Dynamic Renew Mode was on and made no difference: it re-resolves when the timer expires, not when replies stop.
| Device | Recovery | Why |
|---|---|---|
| Workstation | ~5 seconds | checks its entries regularly |
| Switch | ~21 minutes | no checking; the entry only expires |
The field translation: after you move a camera or access point to a new address, your own laptop can reach it immediately while another device on the network can’t find it for twenty minutes. Both are working as designed; one asks again, the other doesn’t. Remember that the next time someone says “it works for me.”
The Broadcast Domain: Where ARP’s Wall Ends
Everything above depends on one condition: being in the same broadcast domain. If the ARP request can’t reach the target, no MAC address is learned, and no frame is ever built.
A broadcast domain is the widest area a broadcast packet can reach. Since an ARP request is a broadcast, that is exactly ARP’s range. The walls of the room: everyone inside hears you, nobody outside hears anything. In most business networks the wall is not a cable but a tag attached to frames, which is the subject of the VLAN article.
To see how real that is, I listened for ARP on ntp01 for 30 seconds while doing nothing:
sudo tcpdump -i ens33 -n "arp"20:25:30.133858 ARP, Request who-has 192.168.1.158 tell 192.168.1.115
20:25:38.766755 ARP, Request who-has 192.168.1.15 tell 192.168.1.1
20:25:38.869127 ARP, Request who-has 192.168.1.25 tell 192.168.1.1
20:25:38.971489 ARP, Request who-has 192.168.1.30 tell 192.168.1.1None of this is mine. In the first line a workstation looks for some other address; in the next three the router polls devices one by one. We hear all of it because we’re in the same room. A broadcast domain isn’t an abstraction; it’s the noise you hear when you listen.
If the target really is on another network, the machine doesn’t look for the target at all; it looks for the exit. In a capture, the who-has line names the gateway’s address instead of the destination.
Lab: Two Devices “on the Same Network,” Two Broadcast Domains
This part draws a fair objection in the field: if the switch’s address is 192.168.1.2/24, it’s on the same network, so how can it be unreachable? The answer isn’t where you’d expect.
During the measurement, five addresses were in play, all five inside 192.168.1.0/24:
| Device | Address | Where its cable goes |
|---|---|---|
| Router | 192.168.1.1 | center of the network, everything else connects here |
| Switch | 192.168.1.2 | only to the workstation’s Ethernet port |
| Workstation, Wi-Fi | 192.168.1.115 | to the router |
| Workstation, Ethernet | 192.168.1.224 | to the switch |
ntp01 | 192.168.1.15 | virtual machine bridged to the wireless side |
On Windows, this command shows the neighbor table together with the interface each entry was resolved on:
Get-NetNeighbor -IPAddress 192.168.1.2, 192.168.1.1InterfaceAlias IPAddress LinkLayerAddress State
-------------- --------- ---------------- -----
Wi-Fi 192.168.1.2 00-00-00-00-00-00 Unreachable
Wi-Fi 192.168.1.1 60-D8-A4-A7-2E-99 Reachable
Ethernet 192.168.1.2 F4-8E-38-1B-18-4D Reachable
Ethernet 192.168.1.1 00-00-00-00-00-00 Incomplete00-00-00-00-00-00 is not a MAC address; it’s an empty box. The machine asked, nobody answered, so there was nothing to write. The answers form a diagonal:
| Address | via Wi-Fi | via Ethernet |
|---|---|---|
192.168.1.1, router | answers | no answer |
192.168.1.2, switch | no answer | answers |
The router answers only on the wireless side, the switch only on the cable. Two addresses one digit apart, with the same mask, sitting on different wires. From the workstation, both pings succeed, because it has a foot in each domain; what shows the split is not ping but which interface the answer came from.
Now a machine with one interface: ntp01, bridged to the wireless side.
sudo ip neigh flush dev ens33
ssh ilker@192.168.1.2ssh: connect to host 192.168.1.2 port 22: No route to hostThen the neighbor table:
ip neigh show dev ens33192.168.1.1 lladdr 60:d8:a4:a7:2e:99 REACHABLE
192.168.1.115 lladdr ec:75:0c:9f:45:ea REACHABLE
192.168.1.2 FAILED
192.168.1.224 INCOMPLETEFour addresses, all four in the same /24, two found and two not. The router and the workstation’s wireless interface are REACHABLE; the switch is FAILED, the workstation’s Ethernet interface INCOMPLETE. The only thing the two missing addresses have in common is that they sit on the far side of the cable.
Here is the answer to the objection. ntp01 considers 192.168.1.2 part of its own network because of its mask. So it doesn’t hand the packet to the router; it ARPs directly and waits. No answer comes. Had the switch been in a different block, say 192.168.99.2, ntp01 would have given the packet to the gateway, and with routing in place it would have arrived. Being on “the same network” here is not a guarantee; it is the cause of the fault. The mask says “neighbor,” the cable says “no,” and ARP believes the cable.
The table has one more line, and on its own it carries this article’s thesis. From the same machine I pinged the workstation’s wireless address:
ping -c 2 192.168.1.1152 packets transmitted, 0 received, 100% packet loss, time 1031msThe ping is completely silent, yet the neighbor entry for the same address is REACHABLE with the correct MAC address. The device is there; Windows Firewall is dropping ICMP: the packet is discarded silently, and not even an error comes back. Ping can go quiet; ARP doesn’t. In this table the two sit side by side.
Topology: ARP’s Hidden Bill
The device that ends a broadcast domain is the router; a switch, by default, extends it. If you want the boundary on a switch, the tool is a VLAN. So ARP’s range is not set by the protocol; you set it, with how you run cables and what you buy, before a single packet flows.
Daisy-Chained Switches
One of the most troublesome topologies I’ve seen in my career looked like this: switches plugged into each other in a line, each new need patched into the previous switch’s free port. The whole site’s network performance had degraded. Topologies like that aren’t built by one bad decision; they grow from a series of small ones, each harmless on its own.
A switch chain accumulates three costs at once:
- Bandwidth. Traffic from every device on the far switch must pass through each switch’s single uplink, so that one cable carries more machines the longer the chain gets.
- One broadcast domain for everything. A
who-hasfrom one end crosses every switch and reaches every network card on site. Remove more walls, and more people hear every shout; past a point, everyone shouts at once and nobody hears anything. - Pressure inside the switches. Every switch in the chain has to learn every address in the domain. The MAC table is finite; when it fills, the switch can’t learn new addresses and floods frames for unknown destinations out of every port, behaving like a hub for those frames.
Why a “Dumb Switch” Blinds ARP Diagnosis
An unmanaged switch is called a “dumb switch” in the field, and the name misleads. It isn’t dumb: it has a MAC table, learns addresses, and forwards frames to the right port. What it lacks is the ability to answer you. There’s a doorman keeping a book; he just won’t show it to anyone.
| Question you ask | Managed switch | Unmanaged switch |
|---|---|---|
| Which port is this MAC address on? | show mac address-table answers | no answer |
| Can I see traffic through this port? | port mirroring | no |
| When was a device plugged in or removed? | in the log | no record |
| Can I split the broadcast domain? | with VLANs | no |
| Can I connect to the device itself? | management address | it has no address |
The ARP table tells you which MAC address answered; the switch’s table ties that address to a physical cable. Without the second, you have a MAC address and no idea where in the building it is.
Is there a way to find an unmanaged switch nobody documented? Yes, and you don’t need to ask it: ask the managed switch above it. A switch port normally sees one device. If a port shows several MAC addresses, something behind it is multiplying frames. From the lab:
Vlan Mac Address Type Port
-------- --------------------- ----------- ---------------------
5 000C.29A0.719A Dynamic Gi1/0/2
5 5811.22B1.0E54 Dynamic Gi1/0/2
5 EEA7.58A3.9726 Dynamic Gi1/0/2Three addresses on one port. In this case the cause was a workstation running virtual machines, but the signature is the same, and the candidates are few: an unmanaged switch under a desk, a virtualization host, an IP phone with a PC port, or an access point. Narrow it down by the manufacturer prefix of each MAC address and the port’s physical location. Count MACs per port; more than one means something is below. The ARP table can’t do this count, because it holds no port information.
Gratuitous ARP: Speaking Without Being Asked
Every ARP reply so far answered a question. The protocol also allows speaking without one: gratuitous ARP. A device announces its own address unprompted: standing up in the room and saying “I’m here, this is my face.”
The announcement does three jobs:
- Address conflict detection. A booting machine asks for its own address. If someone answers, the address is taken, and the conflict is caught in the first second.
- Cache refresh. Other devices hear the announcement and update their tables without asking.
- Handover. In redundant systems, when a virtual address moves from one machine to another, the new owner announces it so the network learns the new MAC address.
Announcements Are Sent, Not Necessarily Heard
The third job looks clean on paper. It didn’t measure that way.
I moved an address to a new MAC address and had the new owner announce it. The announcement really went out; it’s in the capture:
ARP, Request who-has 192.168.1.99 tell 192.168.1.99Source and target are the same address: that’s the signature of a gratuitous ARP. But the workstation on the same network ignored it. The old MAC address stayed in its cache for another 19 seconds, and changed only after the machine finished its own verification round and broadcast its own question.
That isn’t a bug; it’s a deliberate defense. Replacing a valid entry because of an unrequested announcement would hand ARP spoofing to anyone for free. Operating systems treat the announcement as a hint, not an order.
Two separate decisions hide here, and in the field they’re constantly confused. What happens when an announcement arrives for an address that has no entry at all is one question; on Linux, arp_accept decides it, and by default no new entry is created. Whether an existing entry gets overwritten by an announcement is a different question, and that’s what was measured above: the entry existed, the announcement arrived, the entry didn’t change. The workstation waited for its own verification round, not the announcement, before letting go of the old MAC address. The practical consequence: changing arp_accept won’t shorten those 19 seconds, because they’re set by the machine’s own verification timer, not by that setting.
There was a second surprise on the sending side, also absent from the Turkish original. Linux has a setting meant to send these announcements, arp_notify. With it turned on, I added a secondary address to a running interface and counted gratuitous ARPs on the wire: zero. The setting fires when an interface comes up, not when an address is added to an interface that’s already up. To get the announcement, the address had to be added first and the link brought up after. If your failover design relies on arp_notify to announce a floating address on a live interface, test it with a capture; the setting’s name promises more than it does.
In practice:
- For the sender: announcing is mandatory. Skip it, and nobody hears, and the handover time depends entirely on the other side’s timers. And verify on the wire that the announcement is actually sent.
- For the receivers: don’t trust the announcement to have worked. Who learned and who didn’t is only known by looking at their tables.
This is often why redundant firewall or load balancer failovers take minutes instead of seconds: the announcement wasn’t blocked, it wasn’t listened to.
ARP Spoofing: The Price of Never Asking for ID
Gratuitous ARP should have raised a question: a device can announce its own address unprompted and others believe it. How do they know it’s true? They don’t. ARP has no authentication.
Why Attackers Like ARP
Attackers don’t love ARP; they love that it can’t be turned off. Four reasons, all behaviors measured above:
- It can’t be disabled. Most vulnerabilities are fixed by turning off a service, closing a port, or upgrading. Not ARP: turn it off and the network stops.
- It always answers. The flip side of “ping can stay silent, ARP can’t”: the machine whose firewall dropped ICMP still answered ARP.
- It needn’t wait to be asked. The protocol accepts unrequested announcements, so an attacker speaks whenever they like, and a later announcement quietly overrides an earlier one once an entry is due to be replaced.
- It leaves no trace. “ARP writes to no log” was a curiosity for the defender; for the attacker it’s cover.
The other side of the coin is where the defense comes from: all of this only works if the attacker is already inside the broadcast domain. ARP doesn’t cross routers. The precondition is that someone got onto that network somehow: guest Wi-Fi, a compromised laptop, an empty meeting-room jack. That’s why segmentation is the first line of defense: you can’t stop the attack, but you can shrink the room it reaches.
Here the analogy breaks, and it’s worth saying plainly: in a real room you can’t raise your hand for someone else, because people know each other’s faces. ARP has no face recognition.
ARP spoofing is when an attacker answers for someone else’s IP address to pull traffic through their own machine. Three steps, none of them a protocol violation:
- The attacker tells the victim: “the gateway’s MAC address is mine.”
- At the same time, tells the gateway: “the victim’s MAC address is mine.”
- Both update their tables and traffic starts flowing through the attacker. Both ends see normal operation, because packets still arrive.
It’s the cheapest way to take a man-in-the-middle position, and it exposes everything sent unencrypted.
Defenses: Three Tools, All Partial
| Defense | Where it applies | Limit |
|---|---|---|
| Dynamic ARP Inspection | Managed switch | Drops forged replies as they enter a port; no equivalent on an unmanaged switch |
| Port security | Managed switch | Limits how many MAC addresses a port may show; doesn’t fully stop an attacker |
| Static ARP entries | End device | Feasible only for a few critical mappings; unmaintainable at scale |
Dynamic ARP Inspection comes first, because it puts the defense in the network rather than on each device. But don’t enable it straight into enforcement: misconfigured, it drops legitimate traffic too. Mark trusted ports, run it in monitor mode, read the logs for a few days, and enforce only once no false positives remain. And, as measured above, a Linux host answering for an address on two interfaces can look like exactly what DAI is meant to catch; know your multi-homed hosts before you enforce.
To be clear: none of this makes ARP secure; it only makes local abuse harder. The protocol has no authentication and won’t get any. The real defense is that intercepted traffic can’t be read: use encrypted protocols.
Diagnosing with ARP: Which Output Tells You What
Everything above comes down to one habit: in a connectivity problem, read ip neigh before you ping.
| Symptom | ARP entry | Likely cause | First step |
|---|---|---|---|
No ping reply, entry REACHABLE | present | Firewall drops ICMP | Device is up; test the service port |
No ping reply, entry INCOMPLETE | absent | Device off, or in another broadcast domain | Check cable, VLAN, and port state |
| Entry present, MAC not the expected one | present | Address changed hands, or a forged reply | Check the switch MAC table and port |
| One MAC appears for several IPs | present | Redundancy, a multi-homed Linux host, or spoofing | Rule out the first two, then assume spoofing |
Entry keeps showing STALE, traffic flows | present | Normal, not a fault | Leave it alone |
That last row is there on purpose. Plenty of people flush the table because they saw STALE and believe they fixed something. Flushing is harmless but fixes nothing: the entry would have refreshed on the next packet anyway.
Everything in this table looks at the ARP entry. There’s one more symptom, and it never shows up here: several MAC addresses on one switch port. ip neigh keeps no port information, so you can’t see it there; it only appears in the switch’s own table, and it tells you there’s a device behind that port multiplying frames. What it means and how to narrow it down is covered in the unmanaged switch section.
When you do need to clear it, it’s one line on each side.
Windows (both need an elevated shell; without one, the lab machine, which runs Turkish Windows, answered Erişim engellendi, its “Access is denied,” and nothing happened; the PowerShell equivalent is Remove-NetNeighbor, which needs the same rights):
arp -d *
arp -d 192.168.1.25Linux:
sudo ip neigh flush dev ens33
sudo ip neigh del 192.168.1.25 dev ens33In the measured run, a deleted entry was rebuilt in half a millisecond, so this is practically risk-free. The one risky case is a wrong static entry typed by hand: clearing doesn’t fix it, because static entries survive. That is also a static entry’s signature: it’s still there after you delete it. Windows arp -a shows static instead of dynamic, Get-NetNeighbor shows Permanent, and on Linux the ip neigh line ends in PERMANENT and survives flush. The commands that write one by hand are one line too: arp -s on Windows, ip neigh ... nud permanent on Linux. Use static entries to pin a mapping while you diagnose, never as a permanent fix; left in place, they become the fault nobody remembers the reason for six months later.
Conclusion: What the Quietest Protocol Tells You
ARP is the quietest protocol on your network. No settings screen, no log, nobody notices it working. But when it breaks, or when it’s lied to, it takes every layer above it down at once.
Four things measured here will pay off in the field:
- Ping can stay silent; ARP can’t. An unanswered ping doesn’t prove a device is down; one
ip neighline tells you which it is. - The same address block is not the same broadcast domain. Two devices can sit in one
/24and never hear each other. - ARP never asks for ID. It believes answers, and that’s design, not a bug. The defense lives in the switch and in encryption, not in the protocol.
- Linux answers for its addresses on every interface by default. One question can get two answers, and that can hide an outage in a test or look like an attack in production.
So why is something this basic discussed so little? You never configure it, it appears on no screen, it produces no error with its own name (you blame DHCP or DNS instead), and everyone’s troubleshooting starts at ping and traceroute. ARP isn’t unknown; it just isn’t looked at. But when the fault comes, a single word in ip neigh output can place a problem that would otherwise be searched for in the wrong layer for days.
Questions about ARP
This article is adapted from a guide the author first published in Turkish on sercebilisim.com: ARP Nedir? MAC Adresi, ARP Tablosu ve Spoofing
Written by
İlker PehlivanNetwork and systems engineer, founder of Serçe Bilişim
I run the networks and servers that other people's work depends on. Before founding my own consultancy I administered the backbone network, firewalls and core systems of a large multi-site organisation with thousands of users. I write about the things that actually broke.
Related articles
What Is a VLAN? 802.1Q Tags, Access vs. Trunk Ports
A VLAN splits one switch into separate networks using a 4-byte tag. How 802.1Q works, access vs. trunk ports, and native VLAN risk, with real packet captures.
What Is a Port? TCP vs UDP and Connection Faults
What a port number does, how TCP and UDP differ, how to check open ports, and, when a connection won't form, which side the fault is on and how to prove it.
What Is DNS and How Does It Work? Records and TTL
What DNS is and how it works: the root, TLD and authoritative chain; A, CNAME, MX, TXT records; nameserver delegation, TTL and caching, and diagnosing faults with nslookup.
What Is NAT? SNAT, DNAT, and the Translation Table
What NAT really does, how SNAT and DNAT differ, how PAT reuses one address, why double NAT and CGNAT break port forwarding, and a measured look at hairpin NAT.
On this page
Part of this guide
Network Segmentation Guide: VLANs, Layers, and Access