Same Job, Different Shell Part 7: ARP and Neighbor Tables

This is the shortest table in the whole stack and one of the most useful when something on the local subnet isn’t behaving: the neighbor cache, the local mapping of IP address to MAC address for every host your box has actually talked to on-link recently.

Linux: ip neigh, and legacy arp

$ ip neigh show
192.0.2.1 dev eth0 lladdr 02:fc:00:00:00:05 REACHABLE

One entry per neighbor: IP, the interface it’s reachable through, the resolved MAC (lladdr), and a state. REACHABLE means the kernel has confirmed this MAC answers for this IP recently; other states you’ll see in the wild are STALE (was reachable, hasn’t been re-verified, still used optimistically), INCOMPLETE (ARP request sent, no reply yet), FAILED, and PERMANENT (statically configured, never expires).

The legacy tool, same information, older column layout:

$ arp -a
? (192.0.2.1) at 02:fc:00:00:00:05 [ether] on eth0

ip neigh is the one worth learning going forward, it also handles IPv6 neighbor discovery (ip -6 neigh show) in the same command, where arp is IPv4-only by name and by design.

Windows: Get-NetNeighbor, and legacy arp -a

C:\> arp -a

works the same way it always has, same terse IP-to-MAC listing. Get-NetNeighbor is the structured, PowerShell-native version:

PS> Get-NetNeighbor -AddressFamily IPv4 |
    Format-Table IPAddress, LinkLayerAddress, State, InterfaceAlias -AutoSize
# just the confirmed-reachable ones
PS> Get-NetNeighbor -State Reachable

Where this one is genuinely worth a direct table: state names

Windows’ neighbor states map almost one-to-one onto Linux’s, close enough that if you already read ip neigh output, Get-NetNeighbor’s State column needs no new mental model, just a name change:

Windows Get-NetNeighbor stateLinux ip neigh stateWhat it means
ReachableREACHABLEConfirmed responsive within the last minute
StaleSTALEPreviously reachable, not re-verified, still usable
Delay(transient, no separate Linux label)No longer confirmed reachable, delaying a probe to allow passive confirmation first
ProbePROBEActively sending unicast probes to re-verify
IncompleteINCOMPLETEResolution request sent, no reply yet
Unreachable(shows as the entry aging out / FAILED)Resolution failed
PermanentPERMANENTStatically configured, never expires

The one genuine difference: Windows names Delay as its own explicit state (RFC 4861’s neighbor unreachability detection state machine, spelled out); Linux’s ip neigh output doesn’t surface that exact transient state as its own label the same way, it moves through it internally but you’re less likely to catch it mid-command.

Why this table matters more than it looks like it should

An empty or single-entry neighbor table, like the one this sandbox produced above, one entry, the gateway, is completely normal for a host that’s only ever talked to its own gateway. It gets interesting the moment two hosts on the same subnet can’t reach each other: ip neigh / Get-NetNeighbor tells you immediately whether the problem is Layer 2 (no MAC resolved at all, stuck INCOMPLETE) or something above it (MAC resolved fine, REACHABLE, so the failure is a firewall, a routing issue, or the application itself). That’s a five-second check that rules out an entire class of problem before you go looking anywhere more complicated, and it’s identical in value on either OS.

Clearing the cache

Both platforms let you drop a stale entry rather than wait for it to age out on its own.

# Linux: flush the whole table
$ sudo ip neigh flush all
# Windows: no single "flush all" switch, pipe every current entry through Remove-NetNeighbor instead
PS> Get-NetNeighbor | Remove-NetNeighbor -Confirm:$false

Remove-NetNeighbor takes filters (-IPAddress, -State, and so on) if you only want to drop specific entries rather than everything, the pipe-from-Get-NetNeighbor pattern above is just the “clear it all” case, mirroring ip neigh flush all without a single dedicated flag for it. The classic arp -d * still works too, and is the more familiar one-liner if you’re not in a PowerShell session already.

Quick reference

What you wantLinuxWindows
Full neighbor/ARP tableip neigh showGet-NetNeighbor
Legacy toolarp -aarp -a
IPv6 neighborsip -6 neigh showGet-NetNeighbor -AddressFamily IPv6
Only confirmed-reachable entriesip neigh show nud reachableGet-NetNeighbor -State Reachable
Clear/flush the cacheip neigh flush allarp -d * (or Get-NetNeighbor | Remove-NetNeighbor -Confirm:$false)

What’s next

Part 8 moves from “who’s on this subnet” to “is this specific service actually listening”: nc/curl//dev/tcp against Test-NetConnection -Port, and the real difference between a closed port and a filtered one.