Address Resolution Protocol (ARP)
A host wants to send an IP packet to another machine on its own Ethernet segment. The IP layer knows the destination's 32-bit IP address. But Ethernet hardware only understands 48-bit MAC addresses. Something must bridge these two addressing worlds before a single bit can leave the network interface card. That bridge is the Address Resolution Protocol. It is a deceptively small piece of machinery. It quietly runs underneath almost every IP conversation on a local network. ARP matters because it sits at the exact seam between two layers designed with no knowledge of each other. Ethernet and the Frame Format moves frames using MAC addresses. The IP layer routes packets using IP addresses. Without a resolution mechanism, every host would need a hand-configured table mapping IP to MAC addresses. This approach collapses the moment machines join, leave, or change network cards. ARP replaces static configuration with a lightweight broadcast-query protocol. It lets hosts discover mappings dynamically and cache them for reuse. The big picture is this: ARP is a link-local protocol. It never crosses a router, never traverses the wider internet. It operates only within a single broadcast domain (a subnet or, later, a VLAN). It trades a small amount of broadcast traffic for enormous operational simplicity. It also uses caching aggressively, so the expensive broadcast step is rarely needed. Understanding ARP is a prerequisite for understanding switch behavior, VLAN segmentation, and a whole class of local-network security attacks such as ARP spoofing. This article gives a precise definition of ARP's data structures and protocol exchange. It includes a step-by-step algorithm with pseudocode, and a worked packet-level trace. It also offers a diagram of the cache and the request/reply exchange. There is an honest complexity and correctness analysis. Finally, it looks at where ARP shows up in real systems — setting up naturally for how switches and Virtual LANs (VLANs) shape the broadcast domains ARP depends on.
- ARP maps a known IP address to the unknown MAC address of a host on the same local network.
- It works by broadcasting an ARP request ("who has this IP?") and receiving a unicast ARP reply.
- Every host keeps an ARP cache (a table of IP→MAC mappings) with timed-out entries to avoid repeated broadcasts.
- ARP is stateless and trust-by-default, which makes it fast but vulnerable to spoofing.
- ARP operates strictly within a broadcast domain; routers do not forward ARP requests across subnets.
- Gratuitous ARP, proxy ARP, and ARP caching interact directly with how VLANs and switches partition broadcast domains.
ARP resolves "what MAC address corresponds to this IP address on my local network?" by broadcasting a request once and caching the answer for everyone's benefit thereafter.
The idea
The Address Resolution Protocol is a link-layer-adjacent protocol that maps a network-layer address (an IPv4 address) to a link-layer address (a 48-bit Ethernet MAC address), for hosts that share the same physical or virtual broadcast domain. ARP is defined for IPv4 by RFC 826; it operates below IP in the sense that IP relies on it, but it is encapsulated directly inside an Ethernet frame (EtherType `0x0806`), not inside an IP packet. This placement is important: ARP messages have no IP header, so they cannot be routed — they are inherently confined to one Ethernet segment. The central data structure ARP maintains on every host is the ARP cache (also called the ARP table): a set of entries, each a triple `(IP address, MAC address, timestamp/state)`. When a host's IP layer needs to send a packet to a destination on the same subnet, it consults this cache first. A cache hit costs nothing beyond a table lookup. A cache miss triggers the resolution protocol proper: a broadcast ARP request frame asking "who has IP address X? tell me at MAC address Y," followed (if some host owns X) by a unicast ARP reply frame carrying that host's MAC address. The querying host then inserts the new mapping into its cache before proceeding. Two refinements matter for a precise understanding. First, gratuitous ARP: a host can broadcast an ARP request for its own IP address, primarily to announce "I am here with this MAC," update other hosts' caches proactively (e.g., after a failover or IP reassignment), and detect IP address conflicts (if a reply comes back, someone else already claims that IP). Second, proxy ARP: a router can answer ARP requests on behalf of hosts on a different subnet, effectively pretending to be the destination at the link layer while actually forwarding packets onward — a technique now largely superseded by proper routing and VLAN configuration, but still useful for legacy address-hiding.
How it works
The protocol logic has two symmetric halves: what a host does when it needs to resolve an address (the requester), and what a host does when it receives a request that names its own IP (the responder). Every host runs both halves simultaneously.
ARP packet format (for Ethernet/IPv4, the common case):
| Field | Size | Meaning |
|---|---|---|
| Hardware type (HTYPE) | 2 bytes | 1 = Ethernet |
| Protocol type (PTYPE) | 2 bytes | 0x0800 = IPv4 |
| Hardware address length (HLEN) | 1 byte | 6 (MAC length) |
| Protocol address length (PLEN) | 1 byte | 4 (IPv4 length) |
| Operation (OPER) | 2 bytes | 1 = request, 2 = reply |
| Sender hardware address (SHA) | 6 bytes | sender's MAC |
| Sender protocol address (SPA) | 4 bytes | sender's IP |
| Target hardware address (THA) | 6 bytes | 0 in a request, filled in a reply |
| Target protocol address (TPA) | 4 bytes | the IP being resolved |
The whole ARP message is wrapped in an Ethernet frame whose destination MAC is `FF:FF:FF:FF:FF:FF` (broadcast) for a request, and the requester's unicast MAC for a reply.
Step-by-step resolution algorithm: 1. Host A's IP layer wants to send a packet to IP address T, determined (via the subnet mask) to be on the same LAN. 2. A checks its ARP cache for an entry keyed on T. 3. If found and not expired: use the cached MAC as the Ethernet destination address, hand the frame to the link layer, done — no network traffic generated. 4. If not found (cache miss): A constructs an ARP request with `SPA = A's IP`, `SHA = A's MAC`, `TPA = T`, `THA = 0`, and broadcasts it on the LAN. 5. Every host on the LAN receives the broadcast frame (this is the cost of resolution) and inspects TPA. 6. Any host that is not the owner of T silently discards the request (but see the cache-learning aside below). 7. The host that does own IP address T constructs an ARP reply with `SPA = T`, `SHA = its own MAC`, `TPA = A's IP`, `THA = A's MAC`, and sends it unicast back to A. 8. A receives the reply, inserts `(T, replier's MAC)` into its cache with a fresh timestamp, and now sends the originally queued packet(s). 9. Cache entries expire after a timeout (commonly on the order of minutes, e.g., 60–240 seconds in many OS defaults, sometimes up to 20 minutes) to accommodate NIC replacements, IP reassignment, or virtual machine migration. A useful optimization many implementations perform at step 5–6: even hosts that are not the target may opportunistically cache the sender's `(SPA, SHA)` mapping from the request, since it's provided for free and likely to be useful soon (A is about to become a communication partner for others too). ``` // Executed by every host; state = local ARP cache (map: IP -> (MAC, timestamp)) function SEND_IP_PACKET(destIP, payload): if destIP is on local subnet: targetIP = destIP else: targetIP = nextHopRouterIP // resolve router's MAC, not destIP's entry = ARP_CACHE.lookup(targetIP) if entry exists and not expired(entry): macDest = entry.mac send_ethernet_frame(macDest, payload) return // cache miss: must resolve queue_packet_pending(targetIP, payload) // hold packet until resolved if not already_waiting_for(targetIP): request = build_ARP_request( SHA = myMAC, SPA = myIP, THA = 0, TPA = targetIP) broadcast_ethernet_frame(request) start_retransmit_timer(targetIP) // resend if no reply in time function ON_RECEIVE_ARP_PACKET(pkt): // Opportunistic learning: cache the sender's mapping regardless of role ARP_CACHE.insert_or_refresh(pkt.SPA, pkt.SHA, now()) if pkt.OPER == REQUEST: if pkt.TPA == myIP: reply = build_ARP_reply( SHA = myMAC, SPA = myIP, THA = pkt.SHA, TPA = pkt.SPA) send_ethernet_frame(dest = pkt.SHA, reply) // unicast // else: not addressed to me, discard after opportunistic caching else if pkt.OPER == REPLY: if pkt.TPA == myIP: ARP_CACHE.insert_or_refresh(pkt.SPA, pkt.SHA, now()) for each queuedPacket in pending_for(pkt.SPA): send_ethernet_frame(pkt.SHA, queuedPacket) clear_pending(pkt.SPA) cancel_retransmit_timer(pkt.SPA) function CACHE_MAINTENANCE_TICK(): // runs periodically, e.g. every few seconds for each entry in ARP_CACHE: if now() - entry.timestamp > CACHE_TIMEOUT: ARP_CACHE.remove(entry) ``` Edge cases the pseudocode must handle correctly:
- Retransmission and packet queuing: while waiting for a reply, further packets to the same destination must not each trigger a new broadcast; they queue behind the single outstanding request.
- No answer (host T is down): after a bounded number of retries the IP layer gives up and reports "destination unreachable" upward — ARP itself has no notion of failure beyond a timeout.
- Duplicate replies / conflicting mappings: if two replies name different MACs for the same IP (a misconfiguration or an attack), most implementations overwrite with the most recent, which is exactly the property ARP-spoofing defenses must guard against.
- Gratuitous ARP: when `SPA == TPA` in a request, every receiving host should refresh (or insert) that mapping in its cache immediately, without needing a reply.
Complexity & correctness
Message and time complexity. In the worst case (cold cache), resolving one address costs exactly one broadcast frame plus one unicast reply — O(1) messages, independent of subnet size, because a single broadcast reaches all N hosts simultaneously in one physical transmission rather than N unicast probes. The per-host processing cost of a request is O(1): a single equality check `pkt.TPA == myIP`. Across a LAN of N hosts, the aggregate work triggered by one ARP request is O(N) (every host must at least inspect and discard/accept the frame), but this is inherent to broadcast media and is the same cost any broadcast-based discovery protocol pays.
Space complexity. The ARP cache holds at most one entry per distinct IP address ever contacted recently, so cache size is O(k) where k is the number of active local correspondents, bounded in practice by subnet size (at most ~254 hosts in a /24) and by the cache timeout, which evicts stale entries. This is far cheaper than a full static table requiring O(N) preconfigured entries maintained by hand on every host.
Amortized cost. Because resolved mappings are cached, the amortized number of broadcasts per packet sent tends toward zero: if a host exchanges M packets with the same neighbor before the cache entry expires, only 1 of those M packet-sends incurs a broadcast, giving amortized broadcast cost O(1/M) per packet. The timeout value is a tunable tradeoff: longer timeouts amortize better but risk staleness (e.g., after a NIC swap or DHCP lease change to a different device); shorter timeouts track topology changes faster but broadcast more often.
Correctness argument. The essential invariant ARP must maintain is: > If host A's cache contains `(T, m)`, then at some point in the (bounded) past, the host currently or then owning IP T identified its MAC address as m. This invariant is established as a base case by the request/reply exchange (step 7–8 above): a reply for T can only be generated by the process bound to IP address T on some host (assuming no misconfiguration or spoofing), so the mapping inserted at A is, at insertion time, accurate. The invariant can later be violated only by external changes — T's owner changes MAC (NIC replacement), T is reassigned to a different host, or a malicious host lies in a reply — which is precisely why the cache carries a timestamp and an eviction policy: correctness is only guaranteed to hold within the timeout window, not indefinitely. This is a deliberate soft-state design, the same pattern used in DNS TTLs and routing-protocol keepalives: rather than requiring active invalidation on every topology change (which needs a reliable, ordered notification channel), the protocol lets stale information decay on its own and re-verifies lazily on the next miss.
Honest tradeoff. ARP's request phase is a broadcast, so it does not scale to arbitrarily large flat networks — every request interrupts every host on the segment, which is one of the reasons real networks are partitioned into smaller broadcast domains using Virtual LANs (VLANs) rather than left as one giant flat LAN. ARP also performs no authentication whatsoever: any host can claim to own any IP address in a reply, and other hosts will accept and cache it, trading security for simplicity — the root cause of ARP spoofing/poisoning attacks discussed below.
Worked example
Consider a small LAN, subnet `10.0.0.0/24`, with three hosts already up:
| Host | IP | MAC |
|---|---|---|
| Alice | 10.0.0.5 | 02:AA:00:00:00:05 |
| Bob | 10.0.0.9 | 02:BB:00:00:00:09 |
| Carol | 10.0.0.15 | 02:CC:00:00:00:0F |
Alice's ARP cache starts empty. Alice's application calls `send("10.0.0.9", "hello")`.
| Step | Actor | Action | ARP cache state (Alice) |
|---|---|---|---|
| 1 | Alice's IP layer | destIP=10.0.0.9 is on-link (same /24); lookup cache → miss | `{}` |
| 2 | Alice | queue payload, build ARP request: SHA=02:AA:...05, SPA=10.0.0.5, THA=00:00:00:00:00:00, TPA=10.0.0.9 | `{}` |
| 3 | Alice | broadcast frame, dst MAC = FF:FF:FF:FF:FF:FF, EtherType 0x0806 | `{}` |
| 4 | Bob | receives frame; opportunistically caches (10.0.0.5 → 02:AA:...05); sees TPA==own IP | Bob's cache: `{10.0.0.5: 02:AA:...05}` |
| 5 | Carol | receives same broadcast frame; opportunistically caches Alice's mapping too; TPA≠own IP, discards | Carol's cache: `{10.0.0.5: 02:AA:...05}` |
| 6 | Bob | sends unicast ARP reply: SHA=02:BB:...09, SPA=10.0.0.9, THA=02:AA:...05, TPA=10.0.0.5 | — |
| 7 | Alice | receives reply, TPA==own IP, inserts mapping | `{10.0.0.9: 02:BB:...09}` |
| 8 | Alice | dequeues "hello", sends Ethernet frame dst=02:BB:...09 carrying the original IP packet | `{10.0.0.9: 02:BB:...09}` |
| 9 | (60s later) | Alice sends another packet to 10.0.0.9 | cache hit, no broadcast, direct unicast send |
| 10 | (past timeout, e.g. 4 min later) | cache entry for 10.0.0.9 expires and is evicted | `{}` — next send repeats steps 1–8 |
Notice two subtleties this trace surfaces: Carol never sent a reply (correct — she is not the target) yet still benefited by caching Alice's mapping for free, and Bob's reply is unicast (not broadcast) since Bob already knows exactly whom to answer, which is the asymmetry that keeps ARP's reply traffic light even though the request is heavy.
Common pitfalls & traps
| Pitfall | Why it happens | Consequence |
|---|---|---|
| Assuming ARP resolves addresses across subnets | Confusing IP routing (global) with ARP (link-local only) | Packets to remote hosts silently fail to resolve; the fix is resolving the default gateway's MAC, not the remote host's |
| Treating ARP replies as authenticated | ARP has no cryptographic signing or sender verification | ARP spoofing/poisoning: an attacker replies claiming to own the gateway's IP, redirecting traffic (man-in-the-middle) |
| Forgetting cache timeout entirely | Assuming a mapping is valid forever once learned | Stale entries persist after NIC swaps, DHCP reassignment, or VM migration, causing silent packet loss until manual cache flush |
| Sending a new broadcast for every packet while a resolution is pending | Not queuing packets behind one in-flight request | Needless broadcast storms under bursty traffic to a newly seen host |
| Ignoring gratuitous ARP on virtualization failover | Not re-announcing MAC after a VM/VIP moves to new hardware | Other hosts keep sending frames to the old, now-wrong MAC until their cache naturally expires |
| Overwriting cache entries on unsolicited replies without any check | ARP implementations trust any reply, even unrequested ones | Enables poisoning even without a matching outstanding request |
Pioneers & history
ARP was designed to solve a problem that appeared as soon as Ethernet LANs began running the fledgling Internet Protocol suite in the early 1980s: two independently designed addressing schemes needed a bridge, and that bridge had to be dynamic because Ethernet cards, unlike phone numbers, could be swapped or reassigned freely.
Origins · 1 minDavid C. Plummer and how this was discovered
Where this matters
Every IPv4 device on an Ethernet or Wi-Fi LAN — laptops, phones, IoT devices, servers, printers, routers — runs ARP continuously and invisibly; it is invoked before essentially any first packet on a new local connection. Operating systems expose the cache directly (`arp -a` on Windows and macOS, `ip neigh` on Linux), and network engineers inspect it routinely when diagnosing "can ping the gateway but not this other host" problems. Managed switches implement Dynamic ARP Inspection (DAI), which validates ARP replies against a trusted binding table (often built from DHCP snooping) to block spoofed replies — a direct security patch for ARP's lack of authentication. Load balancers and high-availability clusters use gratuitous ARP to redirect traffic to a standby node instantly after a failover, updating every peer's cache without waiting for natural expiry. Hypervisors send gratuitous ARP after live-migrating a virtual machine to a new physical host, so the rest of the LAN updates its cache to point at the new location. And because ARP broadcasts are confined to one broadcast domain, network designers deliberately shrink that domain with Virtual LANs (VLANs) to limit both ARP broadcast overhead and the blast radius of a spoofing attack — the natural next layer of local-network design built directly on top of what ARP does at the link layer.
Ask the tutor: How would ARP behavior change on a network segmented by VLANs versus one flat broadcast domain?
Does ARP work with IPv6?
No. IPv6 does not use ARP at all; it replaces address resolution with Neighbor Discovery Protocol (NDP), which is built on ICMPv6 messages (Neighbor Solicitation and Neighbor Advertisement) rather than a standalone EtherType broadcast protocol. NDP additionally folds in router discovery, duplicate address detection, and stateless address autoconfiguration, functions that IPv4 handled with separate mechanisms (DHCP, gratuitous ARP, manual router configuration). Conceptually the goal is identical — map an IP address to a link-layer address on the local segment — but NDP uses IPv6 multicast (to a solicited-node multicast address) instead of Ethernet-wide broadcast, reducing the number of hosts interrupted per resolution. If you see ARP traffic on a network, it's necessarily IPv4 traffic; IPv6-only hosts will show NDP exchanges instead in a packet capture.
Why does ARP use broadcast instead of asking a central directory server?
A central directory would require every host to know a server's address in advance, and that server would itself need bootstrapping (its own address resolved somehow), creating a chicken-and-egg problem at the lowest layer of the network stack. ARP deliberately avoids any dependency: broadcast requires nothing but working Ethernet hardware, which every host already has. The cost is that every host on the segment must inspect every request, which is O(N) work distributed across N hosts rather than concentrated on one server — acceptable because Ethernet segments are kept small precisely so this cost stays low, and because caching sharply reduces how often broadcasts occur in the first place.
What is ARP spoofing/poisoning, precisely?
ARP spoofing is an attack where a malicious host sends forged ARP replies (or unsolicited gratuitous ARP announcements) claiming to own an IP address it does not actually own — commonly the default gateway's IP. Because ARP has no authentication, victim hosts accept the forged mapping and update their cache, sending future traffic destined for the gateway to the attacker's MAC instead. The attacker can then silently relay the traffic onward (a transparent man-in-the-middle), read or modify it, or simply drop it (denial of service). Defenses include static ARP entries for critical hosts, switch-level Dynamic ARP Inspection tied to a trusted DHCP-snooping binding table, and cryptographic alternatives at higher layers (TLS) that make interception less useful even if traffic is redirected.
How large can an ARP cache get, and what happens when it's full?
In practice an ARP cache holds one entry per distinct local IP address a host has recently communicated with, so its size is bounded by the number of active neighbors on the subnet — rarely more than a few hundred entries even on a fully populated /24. Most operating systems impose a maximum cache size as a defensive measure (to bound memory and to blunt cache-exhaustion attacks that flood a host with bogus mappings); when full, the oldest or least-recently-used entries are typically evicted to make room for new ones, following the same soft-state philosophy as the timeout mechanism.
What is gratuitous ARP actually used for in production systems?
Three concrete uses recur constantly: (1) duplicate address detection — when a host configures a new IP, it gratuitously ARPs for that IP; if anyone replies, the address is already in use and a conflict is flagged before real traffic flows; (2) failover announcement — in high-availability clusters (e.g., VRRP, keepalived), the newly active node broadcasts a gratuitous ARP for the shared virtual IP the instant it takes over, forcing every peer to redirect traffic to it within milliseconds rather than waiting for stale cache entries to expire; (3) virtual machine migration — after a live migration to new physical hardware, the hypervisor gratuitously ARPs so the rest of the LAN immediately updates to the VM's new location without any application-visible interruption.
Is there a way to avoid the broadcast cost of ARP entirely?
Static ARP entries can be manually configured for specific, rarely-changing hosts (common for critical infrastructure like default gateways in security-sensitive environments), which removes both the broadcast and the spoofing risk for that entry but sacrifices the automatic adaptability that makes ARP useful in the first place. At larger scale, reducing broadcast domain size via Virtual LANs (VLANs) is the standard architectural answer: smaller segments mean fewer hosts interrupted per ARP broadcast, which is one of several reasons enterprise networks are partitioned into many small VLANs rather than one flat LAN.
How does ARP interact with a switched (not just a shared-bus) Ethernet network?
Modern Ethernet networks are almost universally switched rather than shared-bus, but ARP's logical model — one broadcast reaching every host in the domain — is preserved by the switch itself: a switch floods any frame addressed to the broadcast MAC out every port in that broadcast domain (except the one it arrived on), simulating the shared-bus behavior ARP was designed around. Switches also often build their own MAC-address forwarding tables by observing source addresses in passing frames (including ARP frames), which is a related but distinct mechanism from the IP-to-MAC mapping ARP itself performs — the switch table maps MAC address to physical port, while ARP maps IP address to MAC address.