The Network Layer
NAT, DHCP and ICMP
NAT lets many private addresses share one
JrCodex·9 min read
Jr Codex Computer Networks Notes
Level: Intermediate Prerequisites: Chapter 4: Routing Algorithms Time to complete: ~25 minutes
Table of Contents
1. Three Protocols You Meet Daily
Their Roles
─────────────────────────────────────────
NAT lets many private addresses share one
public address — the workaround that
postponed IPv4 exhaustion (Chapter 2)
DHCP hands out addresses automatically, so
nothing is configured by hand
ICMP the network layer's diagnostic and error
channel — how a network reports problems
about itself
─────────────────────────────────────────
2. NAT
The Idea
─────────────────────────────────────────
A home network:
laptop 192.168.1.10 ┐
phone 192.168.1.11 ├─► router ─► ONE public
TV 192.168.1.12 ┘ address
203.0.113.5
All three use private addresses (Chapter 1) that
are not routable on the internet. The router
REWRITES the source address on the way out and
reverses it on the way back.
─────────────────────────────────────────
PAT — how one address serves many
─────────────────────────────────────────
Rewriting the address alone is not enough — the
replies would be indistinguishable. So NAT
rewrites the PORT too. This is PAT, or NAT
overload, and it is what "NAT" means in practice.
THE TRANSLATION TABLE
Inside Outside Proto
─────────────────────────────────────────
192.168.1.10:51234 ──► 203.0.113.5:40001 TCP
192.168.1.11:51234 ──► 203.0.113.5:40002 TCP
192.168.1.12:33445 ──► 203.0.113.5:40003 TCP
Both the laptop and the phone happened to use
source port 51234. The router assigns each a
DIFFERENT outside port, so the reply's
destination port identifies which device it
belongs to.
─────────────────────────────────────────
class NAT:
def __init__(self, public_ip, port_start=40000):
self.public_ip, self.next_port = public_ip, port_start
self.out, self.back = {}, {} # inside->outside, outside->inside
def outbound(self, src_ip, src_port, proto):
key = (src_ip, src_port, proto)
if key not in self.out:
port = self.next_port; self.next_port += 1
self.out[key] = port
self.back[(port, proto)] = (src_ip, src_port) # the reverse mapping
return self.public_ip, self.out[key]
def inbound(self, dst_port, proto):
"""Only a mapping created by an OUTBOUND packet can be reversed."""
return self.back.get((dst_port, proto)) # None ──► DROPTHE ASYMMETRY THAT DEFINES NAT
─────────────────────────────────────────
An inbound packet is translated ONLY IF a mapping
already exists — and mappings are created only by
OUTBOUND traffic.
So unsolicited inbound connections are dropped,
because there is nothing to translate them to.
This is why NAT is often described as a firewall.
It is a SIDE EFFECT, not a security design — but
it is a real and useful one, and it is why home
devices are not directly reachable from the
internet.
─────────────────────────────────────────
3. What NAT Breaks
The Costs
─────────────────────────────────────────
NO INBOUND CONNECTIONS
You cannot run a server behind NAT without
explicit PORT FORWARDING configured on the
router.
PEER-TO-PEER IS HARD
Two devices both behind NAT cannot connect
directly — neither can initiate. This needs
STUN, TURN and ICE, which is why video calling
has so much connection machinery
(Module 7, Chapter 4).
BREAKS THE END-TO-END PRINCIPLE
Chapter 3 said IP addresses never change in
transit. NAT deliberately violates that. A
fundamental architectural assumption is now
conditionally false.
BREAKS EMBEDDED ADDRESSES
A protocol that puts an IP address in its
PAYLOAD — FTP's active mode, SIP — sends a
private address the far end cannot reach. NAT
devices need protocol-specific helpers to
rewrite the payload too.
COMPLICATES LOGGING
Thousands of users share one public address, so
an IP in a log identifies a household or a
carrier, not a device.
CARRIER-GRADE NAT
Some ISPs NAT their entire customer base. Now
there are two layers, and inbound connectivity
is impossible even with port forwarding.
─────────────────────────────────────────
The Honest Summary
─────────────────────────────────────────
NAT saved IPv4 and complicated everything built
since.
Almost every awkward piece of modern networking —
NAT traversal, connection brokers, relay servers,
keepalives to hold mappings open — exists because
of it.
IPv6's abundance (Chapter 2) removes the need
entirely, which is the strongest practical
argument for it.
─────────────────────────────────────────
4. DHCP
The Four-Step Exchange — DORA
─────────────────────────────────────────
1. DISCOVER client broadcasts
"is there a DHCP server?"
src 0.0.0.0, dst 255.255.255.255
── it has no address yet, so it must
broadcast
2. OFFER server proposes an address, mask,
gateway, DNS servers, lease time
3. REQUEST client broadcasts its acceptance
── broadcast so any OTHER servers
that offered know to withdraw
4. ACK server confirms; the lease begins
─────────────────────────────────────────
What a Lease Actually Delivers
─────────────────────────────────────────
IP address and subnet mask (Chapter 1)
default gateway (Chapter 3)
DNS servers (Module 5, Ch.1)
lease duration
optionally: NTP servers, domain name, MTU, PXE
boot information
Notice this is the ENTIRE network configuration.
Everything a host needs to participate arrives in
one exchange, which is why plugging in a cable
simply works.
─────────────────────────────────────────
Leases and Renewal
─────────────────────────────────────────
A lease has a duration, and the client renews at
50% elapsed (T1), directly to the server.
If that fails, it broadcasts at 87.5% (T2) to any
server. If the lease expires, the address is
released and the client starts over.
This is why addresses are reclaimed when devices
leave, and why a short lease suits guest wifi
while a long one suits an office.
─────────────────────────────────────────
DHCP RELAY
─────────────────────────────────────────
DHCP uses broadcasts, and routers do not forward
broadcasts (Module 2, Chapter 4).
So a network with many subnets would need a DHCP
server on each. Instead, routers run a DHCP RELAY
AGENT: it receives the broadcast, forwards it as
a UNICAST to a central server, and relays the
reply back.
One server serves the whole organisation.
─────────────────────────────────────────
The Diagnostic, Again
─────────────────────────────────────────
No DHCP reply ──► the client self-assigns
169.254.x.x (Chapter 1).
That address is a specific message: the DHCP
exchange failed. Check the cable, the VLAN, the
relay configuration, or whether the server's pool
is exhausted.
─────────────────────────────────────────
5. ICMP
What It Is For
─────────────────────────────────────────
ICMP is how the network reports problems ABOUT
ITSELF. It carries no user data.
It is not a transport protocol — it rides
directly on IP, with Protocol = 1.
─────────────────────────────────────────
The Messages That Matter
─────────────────────────────────────────
TYPE 0 / 8 Echo Reply / Echo Request
── this is ping
TYPE 3 Destination Unreachable, with codes:
0 network unreachable
1 host unreachable
3 PORT unreachable ── how UDP
reports a closed port
(Module 4, Chapter 2)
4 fragmentation needed but DF set
── PATH MTU DISCOVERY
(Module 1, Chapter 3)
TYPE 11 Time Exceeded — TTL hit zero
── this is what makes traceroute
work
TYPE 5 Redirect — "there is a better router
for that destination"
─────────────────────────────────────────
WHY BLOCKING ICMP IS A MISTAKE
─────────────────────────────────────────
Firewall rules that drop all ICMP are common, and
they break things in ways that are very hard to
diagnose.
BLOCK TYPE 3 CODE 4 ──► path MTU discovery fails.
Connections establish, small requests work, large
transfers hang forever (Module 1, Chapter 3).
BLOCK TYPE 11 ──► traceroute stops working, so
nobody can locate a routing problem.
BLOCK TYPE 3 CODE 3 ──► a closed UDP port gives
no error, so clients wait for a timeout instead
of failing immediately.
THE RIGHT POLICY: rate-limit ICMP; do not drop
it. Blocking echo requests to hide from scans is
defensible. Blocking the error messages breaks
the protocol.
─────────────────────────────────────────
6. Traceroute
A tool built entirely out of TTL and ICMP — and the clearest demonstration of both.
The Trick
─────────────────────────────────────────
1. Send a packet toward the destination with
TTL = 1
2. The FIRST router decrements it to 0, discards
it, and returns ICMP Time Exceeded
── revealing router 1's address
3. Send TTL = 2. The SECOND router responds.
4. Continue until the destination itself replies
with something other than Time Exceeded
Each TTL value exposes one more hop. The whole
path is mapped using a field designed only to
prevent infinite loops.
─────────────────────────────────────────
import socket, struct, time
def traceroute(dest, max_hops=30, timeout=2):
dest_ip = socket.gethostbyname(dest)
for ttl in range(1, max_hops + 1):
rx = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP)
tx = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
tx.setsockopt(socket.IPPROTO_IP, socket.IP_TTL, ttl) # ← the whole trick
rx.settimeout(timeout)
rx.bind(("", 33434 + ttl))
start = time.time()
tx.sendto(b"", (dest_ip, 33434 + ttl))
try:
_, addr = rx.recvfrom(512)
print(f"{ttl:2} {addr[0]:16} {(time.time()-start)*1000:.1f} ms")
if addr[0] == dest_ip:
break # arrived
except socket.timeout:
print(f"{ttl:2} * * *") # no reply — see below
finally:
rx.close(); tx.close()Reading traceroute Output Correctly
─────────────────────────────────────────
* * * DOES NOT MEAN A BROKEN HOP.
Many routers rate-limit or disable ICMP
generation. Traffic passes through them
perfectly; they simply do not answer.
If later hops respond, that hop is fine.
LATENCY THAT RISES THEN FALLS
Not an error. ICMP replies are generated by the
router's CPU at LOW PRIORITY, so a busy router
may answer slowly while forwarding at full
speed.
ASYMMETRIC PATHS
The return path may differ from the forward
path, so the times mix both directions.
WHAT TO ACTUALLY LOOK FOR
A large, SUSTAINED jump at one hop that
persists in every subsequent hop. That is a
real bottleneck. A single slow hop followed by
fast ones is noise.
─────────────────────────────────────────
7. Summary & Next Steps
Key Takeaways
- NAT rewrites both address and port so many devices share one public address, and inbound packets are dropped unless an outbound packet created the mapping first.
- NAT postponed IPv4 exhaustion and broke the end-to-end principle, which is the root cause of nearly all NAT-traversal machinery in modern applications.
- DHCP's four-step exchange delivers a host's entire network configuration, and a 169.254 address is a specific signal that it failed.
- Blocking ICMP breaks path MTU discovery and traceroute in ways that are extremely hard to diagnose; rate-limit it rather than dropping it.
Module 3 Complete — What's Next
Packets can now reach any machine on the internet. But IP promises nothing: packets may be lost, duplicated, reordered or corrupted. Module 4 adds the layer that turns that into a reliable conversation between two programs.
Concept Check
- Why can two devices behind NAT both use source port 51234 without confusion?
- Why must a DHCP client broadcast its initial Discover message rather than unicasting it?
- A firewall drops all ICMP. Name two specific failures this causes and explain each.
Next Module
→ Module 4: The Transport Layer
Jr Codex — 1-on-1 Personalized Coaching | Back to Module Index | Back to Computer Networks Index