MAC addresses, ARP, DHCP and NAT
40 min · Intermediate
Need help? Ask a tutor on WhatsApp (opens in a new tab)You join the hostel Wi-Fi and everything just works, which hides three small protocols. Interviewers ask about MAC, ARP, DHCP and NAT to check that you know how an address is given, found and shared.
MAC address vs IP address
A MAC address (Media Access Control) is a 48-bit hardware address of a network card, written as six hex pairs such as 3c:22:fb:1a:9e:40. It delivers a frame to the next device on the same local network and stays with the card.
The example below splits a MAC address into its maker part and device part, then shows the IP address changing when the laptop moves to another network while the MAC stays.
Example 1 · MAC address parts and IP changes
mac = "3C:22:FB:1A:9E:40"parts = mac.split(":")print(f"MAC {mac} has {len(parts) * 8} bits")print("Maker part (OUI):", ":".join(parts[:3]))print("Device part: ", ":".join(parts[3:]))
ip_at = {"Hostel Wi-Fi": "10.20.5.41", "Home Wi-Fi": "192.168.1.23"}for place, ip in ip_at.items(): print(f"{place}: IP {ip}, MAC {mac}")MAC 3C:22:FB:1A:9E:40 has 48 bits Maker part (OUI): 3C:22:FB Device part: 1A:9E:40 Hostel Wi-Fi: IP 10.20.5.41, MAC 3C:22:FB:1A:9E:40 Home Wi-Fi: IP 192.168.1.23, MAC 3C:22:FB:1A:9E:40
What this code does
- Six bytes of 8 bits give 48 bits. The first three bytes (the OUI) name the manufacturer; the last three are chosen by that maker.
- The same laptop gets a different IP on each network, but its hardware MAC stays the same. That is the core MAC vs IP difference.
The first three bytes, the OUI, identify the maker. An IP address is a logical layer 3 address that depends on the network you join: your laptop gets one IP in the hostel and a different one at home, while its MAC stays the same. A useful picture: the IP is like a postal address for the whole journey, and the MAC is like the name on the door for the last step.
MAC versus IP:
- MAC: 48 bits, hex, set by the maker, local delivery (one hop), layer 2.
- IP: 32 bits (IPv4) or 128 bits (IPv6), set by the network, end-to-end routing, layer 3.
- Phones now often show a random "private" MAC per Wi-Fi network for privacy, so "MAC never changes" is no longer always true.
ARP: finding a MAC from an IP
ARP (Address Resolution Protocol) finds the MAC address that belongs to an IP address on the same local network. A frame cannot be sent without a destination MAC, so ARP runs before the first packet.
The example keeps an ARP cache and broadcasts a question only on a miss, so you can see why the second lookup needs no broadcast.
Example 2 · An ARP cache with broadcast on a miss
lan = {"192.168.1.1": "aa:bb:cc:00:00:01", "192.168.1.7": "aa:bb:cc:00:00:07"}arp_cache = {}
def resolve(ip): if ip in arp_cache: print(f"cache hit {ip} -> {arp_cache[ip]}") return arp_cache[ip] print(f"broadcast Who has {ip}? Tell 192.168.1.5") mac = lan.get(ip) if mac is None: print(f"no reply {ip} is not on this network") return None print(f"reply {ip} is at {mac}") arp_cache[ip] = mac return mac
for ip in ["192.168.1.1", "192.168.1.1", "192.168.1.7", "192.168.1.99"]: resolve(ip)print("cache size", len(arp_cache))broadcast Who has 192.168.1.1? Tell 192.168.1.5 reply 192.168.1.1 is at aa:bb:cc:00:00:01 cache hit 192.168.1.1 -> aa:bb:cc:00:00:01 broadcast Who has 192.168.1.7? Tell 192.168.1.5 reply 192.168.1.7 is at aa:bb:cc:00:00:07 broadcast Who has 192.168.1.99? Tell 192.168.1.5 no reply 192.168.1.99 is not on this network cache size 2
What this code does
- The first lookup for the router broadcasts and stores the reply; the second is answered from the cache with no network traffic.
- An IP with no owner on the LAN gets no reply and nothing is cached. Real ARP entries also expire after some time (often minutes, depending on the OS).
The sender broadcasts "Who has 192.168.1.1? Tell 192.168.1.5". Only the owner replies with its MAC, directly to the sender. The answer is saved in an ARP cache for a while, so the next packet needs no broadcast.
Find the bug
Find the wrong ARP line
Each note has one wrong line. Tap it, then check the fix.
A candidate explains ARP.
Round 1 of 2DHCP: how a device gets its IP
DHCP (Dynamic Host Configuration Protocol) gives a device an IP address, subnet mask, default gateway and DNS server automatically when it joins a network. Without it, someone would type these by hand on every phone.
The example runs the four DHCP messages against a small address pool and prints who sends each one.
Example 3 · DHCP DORA with a small address pool
from collections import deque
pool = deque(["192.168.1.100", "192.168.1.101"])leases = {}
def join(mac): print(f"DISCOVER from {mac} (src 0.0.0.0, broadcast)") if mac in leases: print(f" ACK same lease {leases[mac]}") return if not pool: print(" no OFFER: pool exhausted") return ip = pool[0] print(f" OFFER {ip} /24, gw 192.168.1.1, dns 192.168.1.1, lease 24h") print(f" REQUEST {ip}") pool.popleft() leases[mac] = ip print(f" ACK {ip} leased")
for mac in ["aa:00:00:00:00:01", "aa:00:00:00:00:02", "aa:00:00:00:00:01", "aa:00:00:00:00:03"]: join(mac)print("Leases:", leases)DISCOVER from aa:00:00:00:00:01 (src 0.0.0.0, broadcast)
OFFER 192.168.1.100 /24, gw 192.168.1.1, dns 192.168.1.1, lease 24h
REQUEST 192.168.1.100
ACK 192.168.1.100 leased
DISCOVER from aa:00:00:00:00:02 (src 0.0.0.0, broadcast)
OFFER 192.168.1.101 /24, gw 192.168.1.1, dns 192.168.1.1, lease 24h
REQUEST 192.168.1.101
ACK 192.168.1.101 leased
DISCOVER from aa:00:00:00:00:01 (src 0.0.0.0, broadcast)
ACK same lease 192.168.1.100
DISCOVER from aa:00:00:00:00:03 (src 0.0.0.0, broadcast)
no OFFER: pool exhausted
Leases: {'aa:00:00:00:00:01': '192.168.1.100', 'aa:00:00:00:00:02': '192.168.1.101'}What this code does
- Each new device goes through Discover, Offer, Request, Acknowledge. A device that already holds a lease gets the same address back.
- With a pool of only two addresses, the third device gets no offer. A full pool is a real reason why "connected, no internet" appears on crowded college Wi-Fi.
The four messages are remembered as DORA: Discover (client broadcasts "any DHCP server?"), Offer (server offers an address), Request (client asks for that address) and Acknowledge (server confirms). The address is a lease for a fixed time and is renewed before it ends.
Order of execution
Put DORA in order
A phone joins the college Wi-Fi. Put the DHCP messages in the order they are sent.
NAT: many devices, one public IP
NAT (Network Address Translation) lets many devices with private IPs share one public IP. The router rewrites the source address of each outgoing packet and remembers the mapping in a NAT table.
The example builds a NAT table with port translation and uses it to send each reply back to the right private device.
Example 4 · A NAT table with port translation
PUBLIC_IP = "49.36.10.20"nat_table = {}next_port = 40001
def outgoing(private_ip, private_port, server): global next_port public_port = next_port next_port += 1 nat_table[public_port] = f"{private_ip}:{private_port}" return f"{PUBLIC_IP}:{public_port} -> {server}"
def incoming(public_port): inside = nat_table.get(public_port) return "dropped (no mapping)" if inside is None else f"forward to {inside}"
print(outgoing("192.168.1.23", 51000, "142.250.1.1:443"))print(outgoing("192.168.1.24", 51000, "142.250.1.1:443"))print(outgoing("192.168.1.30", 60123, "157.240.1.35:443"))print("reply to 40002:", incoming(40002))print("reply to 40009:", incoming(40009))print("NAT table:", nat_table)49.36.10.20:40001 -> 142.250.1.1:443
49.36.10.20:40002 -> 142.250.1.1:443
49.36.10.20:40003 -> 157.240.1.35:443
reply to 40002: forward to 192.168.1.24:51000
reply to 40009: dropped (no mapping)
NAT table: {40001: '192.168.1.23:51000', 40002: '192.168.1.24:51000', 40003: '192.168.1.30:60123'}What this code does
- Two phones use the same private port 51000, but the router gives them public ports 40001 and 40002, so replies never get mixed up.
- A packet for a public port with no mapping is dropped. That is why outside machines cannot open connections to devices behind NAT unless you set up port forwarding.
The common home form is PAT (Port Address Translation, also called NAT overload): the router also gives each connection its own public port, so replies can be sent back to the right device. This is how five phones in a hostel room share one broadband connection.
Trade-offs:
- Benefit: saves IPv4 addresses, and outside hosts cannot start connections to inside devices by default.
- Cost: breaks the end-to-end idea; hosting a server at home needs port forwarding.
- NAT is not a firewall, even though it blocks unrequested incoming traffic as a side effect.
Putting it together when you join hostel Wi-Fi
When your laptop joins the Wi-Fi, DHCP gives it an IP, mask, gateway and DNS server. To reach a website, it uses ARP to find the gateway's MAC and sends frames to the router.
Laptop joins Wi-Fi: DHCP -> IP 192.168.1.23/24, gateway 192.168.1.1, DNS 192.168.1.1Laptop opens a website at 142.250.1.1: ARP -> "Who has 192.168.1.1?" -> router MAC aa:bb:cc:00:00:01 Frame -> to router MAC, packet 192.168.1.23:51000 -> 142.250.1.1:443 NAT -> router rewrites source to 49.36.10.20:40001 and notes itReply 142.250.1.1:443 -> 49.36.10.20:40001 NAT -> table says 40001 = 192.168.1.23:51000, forward to laptopThe router uses NAT to swap your private source IP and port for its public ones and sends the packet out. When the reply comes back, the router looks up the NAT table and forwards it to your laptop's private IP and MAC.
Next step
Check your understanding
12 quick questions