Skip to content

Start typing to search

Network Segmentation Guide: VLANs, Layers, and Access

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.

Written by

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.

Advertisement

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 roomOn the network
The name you holdIP address
The face you’re looking forMAC address
Asking the room out loudARP request (broadcast)
The person raising a handARP reply
The walls of the roomBroadcast domain
Remembering a familiar faceARP 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.

Bash
sudo ip neigh flush dev ens33
ping -c 1 192.168.1.25

Watch 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 reply

The 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.”

  1. 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.
  2. 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.
  3. 563 microseconds separate lines one and two. The ping hasn’t even left yet. ARP is preparation that finishes faster than you could notice.
  4. 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:9a

This 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:

Bash
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 ens33
Bash
ip neigh show
192.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 STALE

Two 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:

StateMeaningIn the room
REACHABLERecently confirmed; the mapping is trustedYou just saw their face
STALEThe entry is kept but hasn’t been confirmed latelyYou remember them; not sure they’re still here
DELAYConfirmation pending, not yet askedAbout to double-check
INCOMPLETEAsked, no answer yet, still tryingYou called out; no hand went up
FAILEDResolution considered failedYou 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:

The five states of an ARP cache entry and the transitions between them. An INCOMPLETE entry becomes REACHABLE if an answer arrives, or FAILED if retries run out. REACHABLE drops to STALE after thirty seconds, moves to DELAY when a packet needs to be sent, and returns to REACHABLE once confirmed. answer arrived retries ran out 30 seconds passed packet to send confirmed INCOMPLETE REACHABLE STALE DELAY FAILED asked, no answer yet just confirmed kept, not refreshed awaiting confirmation stopped asking
The solid orange arrow is a normal day on the network: an entry ages, gets used, gets checked, gets refreshed. The dashed 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 STALE

The 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 loss
20: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.15
  1. Destination Host Unreachable comes from 192.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.
  2. The broadcast question goes unanswered and repeats every second. You call out, no hand goes up. In the cache: INCOMPLETE, then FAILED.

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 loss

But 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 request
192.168.1.115 dev ens33 lladdr ec:75:0c:9f:45:ea REACHABLE

The 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.

Check192.168.1.25192.168.1.115192.168.1.10
Pingreplies100% loss100% loss
ARP entryREACHABLEREACHABLEINCOMPLETE
ICMP on the wireto one destinationto one destinationnever leaves
What’s really going onhealthydevice present, filtereddevice 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:fb

One 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_ignore before 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:

TableWhere it livesMeasured lifetimeWhat it holds
ARP cacheworkstation and server30 secondsIP → MAC
MAC address tableswitch300 seconds (5 minutes)MAC → port
ARP cacheswitch management interface1,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: 3
console#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  0s

The 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  Reachable

The 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 15s

1A4A.ED68.B905 no longer exists anywhere. The switch kept it anyway:

TimePingMAC in the cacheEntry age
19:56100% loss1A4A.ED68.B905, dead6 min 15 s
20:06100% loss1A4A.ED68.B905, dead9 min 32 s
20:10100% loss1A4A.ED68.B905, dead19 min 44 s
20:120% loss4240.6730.A1A6, correct0 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.

DeviceRecoveryWhy
Workstation~5 secondschecks its entries regularly
Switch~21 minutesno 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:

Bash
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.1

None 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:

DeviceAddressWhere its cable goes
Router192.168.1.1center of the network, everything else connects here
Switch192.168.1.2only to the workstation’s Ethernet port
Workstation, Wi-Fi192.168.1.115to the router
Workstation, Ethernet192.168.1.224to the switch
ntp01192.168.1.15virtual machine bridged to the wireless side

On Windows, this command shows the neighbor table together with the interface each entry was resolved on:

PowerShell
Get-NetNeighbor -IPAddress 192.168.1.2, 192.168.1.1
InterfaceAlias 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  Incomplete

00-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:

Addressvia Wi-Fivia Ethernet
192.168.1.1, routeranswersno answer
192.168.1.2, switchno answeranswers

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.

Bash
sudo ip neigh flush dev ens33
ssh ilker@192.168.1.2
ssh: connect to host 192.168.1.2 port 22: No route to host

Then the neighbor table:

Bash
ip neigh show dev ens33
192.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                           INCOMPLETE

Four 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:

Bash
ping -c 2 192.168.1.115
2 packets transmitted, 0 received, 100% packet loss, time 1031ms

The 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:

  1. 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.
  2. One broadcast domain for everything. A who-has from 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.
  3. 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 askManaged switchUnmanaged switch
Which port is this MAC address on?show mac address-table answersno answer
Can I see traffic through this port?port mirroringno
When was a device plugged in or removed?in the logno record
Can I split the broadcast domain?with VLANsno
Can I connect to the device itself?management addressit 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/2

Three 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:

  1. 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.
  2. Cache refresh. Other devices hear the announcement and update their tables without asking.
  3. 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.99

Source 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:

  1. 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.
  2. It always answers. The flip side of “ping can stay silent, ARP can’t”: the machine whose firewall dropped ICMP still answered ARP.
  3. 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.
  4. 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:

  1. The attacker tells the victim: “the gateway’s MAC address is mine.”
  2. At the same time, tells the gateway: “the victim’s MAC address is mine.”
  3. 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

DefenseWhere it appliesLimit
Dynamic ARP InspectionManaged switchDrops forged replies as they enter a port; no equivalent on an unmanaged switch
Port securityManaged switchLimits how many MAC addresses a port may show; doesn’t fully stop an attacker
Static ARP entriesEnd deviceFeasible 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.

SymptomARP entryLikely causeFirst step
No ping reply, entry REACHABLEpresentFirewall drops ICMPDevice is up; test the service port
No ping reply, entry INCOMPLETEabsentDevice off, or in another broadcast domainCheck cable, VLAN, and port state
Entry present, MAC not the expected onepresentAddress changed hands, or a forged replyCheck the switch MAC table and port
One MAC appears for several IPspresentRedundancy, a multi-homed Linux host, or spoofingRule out the first two, then assume spoofing
Entry keeps showing STALE, traffic flowspresentNormal, not a faultLeave 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):

CMD
arp -d *
arp -d 192.168.1.25

Linux:

Bash
sudo ip neigh flush dev ens33
sudo ip neigh del 192.168.1.25 dev ens33

In 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.

Advertisement

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 neigh line tells you which it is.
  • The same address block is not the same broadcast domain. Two devices can sit in one /24 and 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

No. The machine forgets what it knew, asks again on the next packet, and the entry is back within milliseconds; in the measured run, the gap between deleting an entry and rebuilding it was 0.5 milliseconds. The risky case is not clearing but a wrong static entry that someone typed by hand: clearing does not fix it, because a static entry survives the flush.
Sharing an address block does not prove they share a broadcast domain. If they are in different VLANs, or one is on the cable side and the other on the wireless side of a device that does not bridge them, the ARP request never reaches the other side. Look at the ARP table before ping: if no entry ever forms, the problem is at layer 2 and searching higher layers wastes time.
It can, if it goes straight into enforcement. Dynamic ARP Inspection drops legitimate traffic when it is misconfigured, so the safe order is to mark trusted ports first, run the feature in monitor mode, read the logs for a few days, and enforce only when no false positives remain.
Usually it is not sending them, it is being asked. In a capture, the address after the word tell is the one asking; if that is your router rather than the device, the router is scanning the network and the device is only answering. A device's own announcement, a gratuitous ARP, has a different signature: the source and target addresses are the same.

This article is adapted from a guide the author first published in Turkish on sercebilisim.com: ARP Nedir? MAC Adresi, ARP Tablosu ve Spoofing

Advertisement

Written by

İlker Pehlivan

Network 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.