---
title: ARP
source: 'https://academia.sh/en/courses/network-models/arp'
course: 'Network Models and Protocols'
language: en
updated: '2026-08-17T18:07:07+00:00'
license: 'CC BY-SA 4.0'
---

# ARP

Resolving an IP address to a hardware address, the request–reply cycle, cache behavior, proxy and gratuitous usage, and address poisoning.

The previous lesson showed that a hardware address is written into a frame's
destination field. Yet applications specify the destination by IP address: the client on
the example network wants to reach `198.51.100.20`, knows its gateway is
`192.168.10.193`, but knows the hardware equivalent of neither address.

A bridge is needed between the two address spaces. This lesson's question is how that
bridge is built, and what assumptions are made while building it.

## Two Address Spaces

There is **no** computable relationship between an IP address and a MAC address. One is
a logical name assigned by a network administrator or a leasing service; the other is a
physical address tied to the interface's hardware. When the same machine moves from
network to network, its IP address changes; its MAC address does not.

Since the mapping cannot be computed, it **must be asked**. **ARP (Address Resolution
Protocol)** is the protocol that asks this question. The method is direct: a broadcast is
sent to the entire local network asking "who has this IP address?" and the owner of the
address replies.

ARP is not carried inside IP; it is carried directly in the Ethernet frame with type
value `0x0806`. This is a concrete example of the observation from the previous topic
that "some protocols concern two layers at once": an ARP message carries a network-layer
address, but does not use a network-layer header.

## The Fields of the Message

An ARP message is 28 bytes and consists of nine fields.

| Field | Size | Example value |
|---|---|---|
| Hardware type | 2 bytes | `1` (Ethernet) |
| Protocol type | 2 bytes | `0x0800` (IPv4) |
| Hardware address length | 1 byte | `6` |
| Protocol address length | 1 byte | `4` |
| Operation | 2 bytes | `1` request, `2` reply |
| Sender hardware address | 6 bytes | MAC address of the one asking |
| Sender protocol address | 4 bytes | IP address of the one asking |
| Target hardware address | 6 bytes | Zero in the request, filled in the reply |
| Target protocol address | 4 bytes | The IP address being asked about |

The presence of the length fields shows the protocol is not limited to Ethernet and
IPv4: the format is designed for technologies with different address widths.

The program below builds the request the client on the example network sends asking for
its gateway, and the reply that comes back.

```python
import ipaddress
import struct

FORMAT = "!HHBBH6s4s6s4s"


def pack_mac(mac: str) -> bytes:
    return bytes(int(x, 16) for x in mac.split(":"))


def format_mac(raw: bytes) -> str:
    return ":".join(f"{b:02x}" for b in raw)


def arp_message(operation: int, sender_mac: str, sender_ip: str,
               target_mac: str, target_ip: str) -> bytes:
    return struct.pack(
        FORMAT,
        1,          # hardware type: Ethernet
        0x0800,     # protocol type: IPv4
        6,          # hardware address length
        4,          # protocol address length
        operation,      # 1 = request, 2 = reply
        pack_mac(sender_mac), ipaddress.ip_address(sender_ip).packed,
        pack_mac(target_mac), ipaddress.ip_address(target_ip).packed,
    )


def print_arp(raw: bytes) -> None:
    fields = struct.unpack(FORMAT, raw)
    operation = {1: "request", 2: "reply"}[fields[4]]
    print(f"op={operation:7s} sender={format_mac(fields[5])} / {ipaddress.ip_address(fields[6])}"
          f"  target={format_mac(fields[7])} / {ipaddress.ip_address(fields[8])}")


request = arp_message(1, "00:00:5e:00:53:01", "192.168.10.196",
                   "00:00:00:00:00:00", "192.168.10.193")
reply = arp_message(2, "00:00:5e:00:53:00", "192.168.10.193",
                   "00:00:5e:00:53:01", "192.168.10.196")

print("message size:", len(request), "bytes")
print(request.hex())
print_arp(request)
print(reply.hex())
print_arp(reply)
```

Output:

```
message size: 28 bytes
000108000604000100005e005301c0a80ac4000000000000c0a80ac1
op=request sender=00:00:5e:00:53:01 / 192.168.10.196  target=00:00:00:00:00:00 / 192.168.10.193
000108000604000200005e005300c0a80ac100005e005301c0a80ac4
op=reply   sender=00:00:5e:00:53:00 / 192.168.10.193  target=00:00:5e:00:53:01 / 192.168.10.196
```

The target hardware address field in the request message is zero — that is the question
itself. In the reply this field is filled in, and the sender and target roles swap.

## How the Request and Reply Are Carried

As much as the message itself, the addresses of the frame carrying it matter:

| | ARP request | ARP reply |
|---|---|---|
| Frame source address | `00:00:5e:00:53:01` | `00:00:5e:00:53:00` |
| Frame destination address | `ff:ff:ff:ff:ff:ff` | `00:00:5e:00:53:01` |
| Devices it reaches | The entire broadcast domain | Only the one who asked |

The request goes by broadcast because it is not known which device holds the address
being asked about. The reply, by contrast, goes to a unicast address: the responder has
learned both of the asker's addresses from the sender fields inside the request.

This asymmetry has a side effect: every device in the broadcast domain sees the request
and, if it wants, writes the sender's mapping into its own cache. This behavior is the
basis of the security problem covered in a later section.

## Local or Remote

The first decision made before a packet is sent is whether the destination is on the
same network. The decision is made by comparing the destination address against the
local netmask; netmask arithmetic is built up in the Subnetting lesson. The result of
this decision determines **which address gets resolved**:

| Destination | Same network? | Address asked via ARP | Frame's destination MAC |
|---|---|---|---|
| `192.168.10.200` | Yes | `192.168.10.200` | The destination's own MAC address |
| `198.51.100.20` | No | `192.168.10.193` (gateway) | The gateway's MAC address |

The second row is the most frequently misunderstood point in network mechanics. In a
packet headed to a remote destination, **the IP destination address is the remote
server, the MAC destination address is the gateway.** The two addresses point to
different devices, and this is not an inconsistency: the IP address specifies the
end-to-end destination, the MAC address specifies the next hop.

The remote server's MAC address is never asked for. It cannot be, either: an ARP request
travels by broadcast, and a broadcast does not cross a router.

## The ARP Cache

Because broadcasting for every resolution would be expensive, results are kept in a
**cache**. The client on the example network's cache, after talking to its gateway and
reaching a printer in the same section, holds this content:

| IP address | MAC address | State |
|---|---|---|
| `192.168.10.193` | `00:00:5e:00:53:00` | Reachable |
| `192.168.10.210` | `00:00:5e:00:53:04` | Reachable |
| `192.168.10.205` | — | Incomplete (awaiting reply) |

Entries are not permanent. An entry unused for a while is removed; removal lets the
mapping correct itself when a device's network card changes or an address moves to
another device. The first send after an entry is removed triggers a fresh resolution,
and that send is delayed slightly.

The commands used to read cache state and their output formats vary by operating system.
What stays constant is the three pieces of information shown in the table above:
protocol address, hardware address, and the entry's state.

## Gratuitous and Proxy Usage

**Gratuitous ARP** is a device announcing its own address without anyone asking: a
request is broadcast whose target protocol address is the device's own address. It
serves three purposes. It detects an address conflict — if a reply comes back, the same
address is on another device. A standby device announces it has taken over an address
during failover and refreshes switches' tables. It updates neighbors' stale mappings
when an interface comes up.

**Proxy ARP** is a router replying for an address that is not its own. If the address
asked about is remote, the router gives its own MAC address and takes on the traffic. It
keeps clients with a misconfigured netmask working; but because it hides the problem
itself and produces many identical-looking entries in caches, it makes diagnosis harder.

## ARP Poisoning

ARP's design has no authentication. Who sent a reply is not checked; even an unsolicited
reply gets written to the cache in many implementations. The result is the attack called
**ARP poisoning**.

If a device in the same broadcast domain sends replies claiming that `192.168.10.193` is
at its own MAC address, its neighbors' caches are corrupted and traffic meant for the
gateway gets redirected to the attacker. The attacker can keep the connection alive by
forwarding the traffic on to the gateway; in that case the user sees no interruption at
all.

The source of the vulnerability is not an implementation bug but the protocol's design
assumption: devices on the same local network are assumed trustworthy. Countermeasures
are therefore outside ARP itself:

- Shrinking broadcast domains and putting untrusted devices on separate networks.
- ARP inspection on the switch: allowing a port to announce only the addresses assigned
  to that device.
- Pinning critical mappings.
- Using end-to-end encryption and authentication: an intermediary listening to the
  traffic cannot read or modify the content.

The last item is an application of the end-to-end principle: when a layer inside the
network cannot make a security promise, the promise has to be made at the endpoints.

## Its Counterpart in IPv6

IPv6 does not use ARP. The equivalent function has moved into ICMPv6 under the name
**neighbor discovery**, and uses multicast instead of broadcast: the question is
delivered only to the multicast group associated with the address being asked about.
This keeps every device in the broadcast domain from having to process every question.
Neighbor discovery also takes on functions like duplicate address detection and router
advertisement; these are covered in the IPv6 lesson.

## Summary

- There is no computable relationship between an IP address and a MAC address; the
  mapping is asked for with ARP.
- An ARP request travels by broadcast, the reply returns to a unicast address; the
  message is 28 bytes and uses no IP header.
- If the destination is on the same network, its own address is resolved; if not, the
  gateway's address is; in a packet headed to a remote destination, the IP target is the
  server, the MAC target is the gateway.
- Resolution results are kept in a time-limited cache; when the time expires, the
  mapping corrects itself.
- Gratuitous ARP is used for address announcement and conflict detection, proxy ARP for
  taking on another network's address.
- ARP has no authentication; countermeasures against poisoning are shrinking the
  broadcast domain, switch-level inspection, and end-to-end encryption.

## Next Step

Up to this point, IP addresses were used like labels: `192.168.10.196` was written, its
meaning was not questioned. Yet there is a structure inside this number — one part
points to the network, one part to the host within that network. The next lesson covers
the bit structure of the IPv4 address, the split between network and host parts, and the
blocks set aside for special purposes.
