MAC Addresses and ARP
Every host has two addresses that matter here, working at two different layers:
- A MAC address - a 48-bit hardware address baked into the network interface
(
aa:bb:cc:11:22:33). It is flat and permanent, and it only means anything on the local link. - An IP address - a logical, hierarchical address (
10.0.0.5) assigned by the network. It can change, and its structure is what lets routers forward packets across the whole Internet.
Why both exist
IP addresses make global routing possible: their hierarchy lets a router
decide "send anything for 10.0.0.0/24 out this interface" without knowing every
host. But the actual delivery over one physical link is done by the link layer,
which only understands MAC addresses. So a frame carries the destination
MAC of the next hop on this link, while the packet inside carries the
destination IP of the final host. To fill in that next-hop MAC, a host must
translate an IP it knows into a MAC it does not - that is ARP, the Address
Resolution Protocol.
Resolving an IP to a MAC
ARP works entirely within one subnet. When a host needs the MAC for a local IP:
- It checks its ARP cache first. A hit means no traffic at all.
- On a miss, it broadcasts an ARP request to every host on the link: "Who
has
10.0.0.2? Tell10.0.0.1." - Only the host that owns that IP unicasts a reply back with its MAC. Everyone else ignores the request.
- The requester caches the
IP -> MACmapping so the next lookup is a hit. Entries time out after a while so stale mappings do not linger.
Step through a resolution - watch the broadcast reach every host, the owner reply, and the ARP table fill in. Resolve the same IP twice to see the cache hit:
A worked example
Host 10.0.0.5 (MAC aa:bb:cc:11:22:33) needs to send to 10.0.0.9 on the
same /24 and has no cache entry yet:
- It broadcasts an ARP request in a frame with destination MAC
ff:ff:ff:ff:ff:ff, source MACaa:bb:cc:11:22:33, carrying the question "Who has 10.0.0.9? Tell 10.0.0.5 (aa:bb:cc:11:22:33)". Every host on the subnet receives and decodes this frame, but only10.0.0.9matches. 10.0.0.9(MACdd:ee:ff:44:55:66) unicasts a reply straight back toaa:bb:cc:11:22:33: "10.0.0.9 is at dd:ee:ff:44:55:66".10.0.0.5stores10.0.0.9 -> dd:ee:ff:44:55:66in its ARP cache. A typical default timeout is around 60 seconds of inactivity (Linux'sgc_stale_timeis tunable but defaults in this range) - short enough that a host that changes its NIC or gets a new IP is not stuck with a stale mapping for long, long enough that a chatty pair of hosts almost never re-ARPs mid-conversation.- Any packet to
10.0.0.9sent within that window reuses the cached MAC with zero ARP traffic; one broadcast and one reply amortize over potentially thousands of subsequent packets.
ARP has no authentication - any host can send an ARP reply (even an
unsolicited "gratuitous" one nobody asked for) claiming to own any IP, and
receivers just overwrite their cache with it. An attacker on the same LAN
exploits this by replying "10.0.0.1 (the gateway) is at <attacker's MAC>" to
the victim, and "10.0.0.5 (the victim) is at <attacker's MAC>" to the
gateway. Both sides now send the attacker their traffic, which it silently
forwards on after reading (or altering) it - a classic Layer-2
man-in-the-middle, invisible to anything watching only IP-layer traffic.
Mitigations include switch-level Dynamic ARP Inspection and static ARP entries
for critical hosts; see Common Network
Attacks for the fuller
picture.
ARP is local-only
ARP never crosses a router. It resolves addresses only within the same subnet, because a broadcast stays on the local link. So how does a host reach something off-subnet, like a server on the Internet?
It sends the frame to the default gateway instead. The host keeps the destination IP of the far-off server in the packet, but ARPs for the gateway's MAC and puts that in the frame. The router receives the frame, strips the link-layer header, looks up the destination IP, and forwards on - re-doing ARP for the next hop on the next link. IP addressing stays end-to-end; MAC addressing is rewritten hop by hop.
The MAC address in a frame only ever names something on the current link. If the destination is off-subnet, the destination MAC is the gateway's, not the final host's - even though the destination IP is the final host's. Confusing these two is a classic source of "it pings locally but not remotely" bugs.