Networking Fundamentals · beginner · ~10 min
- Describe the four-step DORA exchange a device uses to obtain an IP address automatically. - Explain what a DHCP lease contains (IP, mask, gateway, DNS) and why leases expire. - Explain how NAT/PAT lets many private hosts share one public IP by rewriting source addresses and ports. - Read a NAT translation table and trace how a reply packet finds its way back to the correct internal host. - Explain why an internal `192.168.x.x` host is reachable from inside but not directly from the internet, and what a port-forward changes. - Recognise where DHCP and NAT fit into a real network map, including for defensive inventory work.
Every time a laptop joins Wi-Fi or a phone connects to a hotspot, two invisible services do the work that used to require a human typing numbers into a settings screen. This lesson is about those two services: DHCP and NAT.
In the prerequisite lesson "IP addresses: IPv4, IPv6, public vs private" you learned that every device on a network needs a unique IP address, and that addresses come in two flavours: private ranges (like 192.168.0.0/16 or 10.0.0.0/8) that are only meaningful inside a local network, and public addresses that are routable across the internet. That raises two very practical questions, and DHCP and NAT are the answers.
The first question: where does a device get its private address from? Nobody hand-assigns an address to your phone. DHCP (Dynamic Host Configuration Protocol) is a service, usually running on your router, that hands out addresses automatically the moment a device asks. It also hands out the extra settings a device needs to actually use the network — the subnet mask, the default gateway, and the DNS servers.
The second question: if my laptop has a private address that the internet cannot route to, how does it reach a website? NAT (Network Address Translation) is the answer. Your home has one public IP address (rented from your ISP), but a dozen devices behind it. NAT is the trick the router uses to let all of them share that single public address — it rewrites the addresses on packets as they pass through and keeps a table so replies come back to the right device.
In plain language: DHCP is the front desk that checks you in and gives you a room key; NAT is the mailroom that forwards your outgoing letters under the building's return address and routes the replies back to your specific room.
DHCP and NAT are not exotic — they run on essentially every home and office network on Earth, which is exactly why understanding them pays off.
169.254.x.x self-assigned address and cannot talk to anything. Recognising that address instantly tells you the DHCP handshake failed rather than, say, DNS being broken.192.168.1.50 has no globally routable address, so an attacker on the internet cannot simply connect to it — there is no path unless someone deliberately creates one (a port-forward) or the internal host reaches out first. This shapes how external testing differs from internal testing.Definition. DHCP (Dynamic Host Configuration Protocol) is a client/server protocol that automatically gives a newly connected device its IP configuration.
Plain-language explanation. When your laptop joins a network it knows nothing — not its own address, not who the router is. It cannot send a normal targeted message because it does not know any addresses yet. So it shouts to everyone on the local network, and a DHCP server answers. This four-message conversation is nicknamed DORA.
How it works internally. DHCP runs over UDP: the client sends from port 68, the server listens on port 67. Because the client has no address yet, the first message is a broadcast (destination 255.255.255.255) — every device on the segment sees it, but only DHCP servers respond.
CLIENT (no IP yet) DHCP SERVER (router)
| |
| --- 1. DISCOVER (broadcast) ------------> | "Anyone? I need an address."
| |
| <-- 2. OFFER ---------------------------- | "You can have 192.168.1.50."
| |
| --- 3. REQUEST (broadcast) -------------> | "Yes please, I'll take 192.168.1.50."
| |
| <-- 4. ACK ------------------------------ | "Confirmed. Lease is yours for 24h."
| |
[ client now configured: IP + mask + gateway + DNS ]
The REQUEST is broadcast (not unicast) on purpose: if several servers made offers, the broadcast tells the un-chosen servers to withdraw their offers so they can reuse those addresses.
When to use / when not. DHCP is ideal for laptops, phones, and any device that moves around or where manual setup is impractical. You deliberately do not use plain DHCP for things whose address must never change — a server, a printer everyone prints to, the router itself. Those use a static address or a DHCP reservation (the server always hands the same device the same address, keyed to its MAC).
Common pitfall. Assuming a device keeps its address forever. Leases expire. A device tries to renew at roughly half the lease time; if the DHCP server is unreachable when the lease finally runs out, the device loses its address.
Knowledge check: A device on the network suddenly shows the address
169.254.10.7. No DHCP server ever handed that out. What most likely happened, and which of the four DORA steps never completed?
Definition. A DHCP lease is the temporary grant of an IP address plus supporting configuration, valid until an expiry time.
Plain-language explanation. DHCP does not give you an address; it lends it to you for a while, like a library book with a due date. This lets a network with, say, 200 possible addresses serve far more than 200 devices over a day, because devices that leave give their addresses back (or let them expire) for others to reuse.
What a lease contains.
| Field | What it is | Why the device needs it |
|---|---|---|
| IP address | The device's own address, e.g. 192.168.1.50 |
To be addressed by others |
| Subnet mask | e.g. 255.255.255.0 (/24) |
To know which hosts are "local" vs remote |
| Default gateway | e.g. 192.168.1.1 |
Where to send traffic leaving the local network |
| DNS server(s) | e.g. 192.168.1.1 or 1.1.1.1 |
To turn names into addresses |
| Lease time | e.g. 86400 seconds (24h) | When the grant expires |
When to use / when not. Short lease times suit networks with lots of churn (a coffee-shop hotspot) so addresses recycle quickly. Long leases suit stable offices to reduce renewal chatter.
Common pitfall. Forgetting that gateway and DNS come from the lease. If the DHCP server is misconfigured to hand out a wrong gateway, the device gets an address successfully but still cannot reach the internet — the failure looks like "connected but no internet."
Knowledge check: In your own words, why is it useful that a network with 254 usable addresses can serve a hotel with 400 guest devices over a day?
Definition. NAT (Network Address Translation) rewrites the IP addresses (and, in the common form, ports) in packets as they cross the boundary between a private network and the public internet.
Plain-language explanation. Your ISP gives your home one public IP. You have many devices, each with a private IP that the internet cannot route to. When a device sends a packet out, the router swaps the private source address for the router's single public address, and remembers the swap. When the reply comes back, the router looks up its notes and swaps the address back, delivering the reply to the correct device.
PAT (the everyday form). Because many devices share one public IP, addresses alone are not enough to tell replies apart — two devices might both be talking to the same website. So the router also rewrites the source port and uses that port as the key. This is PAT (Port Address Translation), also called NAT overload. "NAT" in home-router speak almost always means PAT.
How it works internally — the translation table:
PRIVATE SIDE ROUTER (public IP 203.0.113.7) INTERNET
192.168.1.50:52001 --> [ src rewritten to 203.0.113.7:40001 ] --> 93.184.216.34:443
192.168.1.51:52001 --> [ src rewritten to 203.0.113.7:40002 ] --> 93.184.216.34:443
NAT TRANSLATION TABLE
+------------------------+---------------------------+----------------------+
| Inside (private) | Outside (what web sees) | Destination |
+------------------------+---------------------------+----------------------+
| 192.168.1.50:52001 | 203.0.113.7:40001 | 93.184.216.34:443 |
| 192.168.1.51:52001 | 203.0.113.7:40002 | 93.184.216.34:443 |
+------------------------+---------------------------+----------------------+
Reply arrives for 203.0.113.7:40002 -> table says that's 192.168.1.51:52001
Router rewrites destination and forwards it to 192.168.1.51. Correct host, every time.
Note how both hosts happened to use source port 52001; the router gives them different outside ports (40001, 40002) so replies never get confused.
When to use / when not. NAT is what makes the modern IPv4 internet survive address exhaustion — billions of devices behind a limited number of public addresses. Where you generally do not want NAT: IPv6, which has so many addresses that hosts can have real public addresses and NAT's translation overhead and complications are unnecessary.
Common pitfall. Believing NAT is a firewall. NAT incidentally hides internal hosts because there is no inbound mapping until an internal host starts a conversation — but that is a side effect, not a security control. Malware on an internal host still reaches out freely, and NAT does nothing to inspect or block traffic.
Definition. Because NAT only creates a translation entry when an inside host initiates traffic, unsolicited inbound connections from the internet have no entry to match and are dropped.
Plain-language explanation. From the internet's point of view, your home is a single address with no obvious way in. Replies to conversations you started come back fine, but a stranger cannot start a conversation with your laptop, because the router has no table entry telling it which internal host that traffic belongs to.
Port forwarding is the deliberate exception: an administrator adds a static rule such as "anything arriving on public port 8080 goes to 192.168.1.50:80." Now that one service is intentionally reachable from outside.
| Situation | Reachable from the internet? | Why |
|---|---|---|
| Internal host, no rule | No | No NAT entry for unsolicited inbound |
| Internal host after it connects out | Reply traffic only | Entry exists just for that conversation |
| Internal host with a port-forward | Yes, on the forwarded port | Admin created a static inbound mapping |
Common pitfall. Forgetting that a port-forward is a permanent open door. Every forwarded port is an exposed service that must be patched and secured, because the NAT shielding no longer applies to it.
Knowledge check (find-the-bug): An admin says "our internal database on
10.0.0.20:5432is safe because we're behind NAT." Then they add a port-forward from public5432to10.0.0.20:5432so a remote branch can connect. Why is the original safety assumption no longer true, and what should protect the database now?
There is no programming syntax here — DHCP and NAT are protocols and router behaviours, not language features. What you do work with are the command outputs and config lines that describe them. A few you will meet constantly:
# Look at the address a device received (Linux/macOS)
ip addr show # look for 'inet 192.168.x.x/24'
ipconfig /all # Windows: shows IP, mask, gateway, DNS, lease times
# Ask for a fresh DHCP lease
sudo dhclient -r && sudo dhclient # release, then request (Linux)
ipconfig /release && ipconfig /renew # Windows
# A NAT rule, expressed in Linux iptables (conceptual)
# -t nat operate on the NAT table
# POSTROUTING rewrite as packets leave
# MASQUERADE use the router's own (dynamic) public IP as the new source
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
Read these as descriptions of behaviour: the MASQUERADE line literally says "as packets leave interface eth0, rewrite their source to my public address" — that one line is PAT.
Two services make everyday networking work without manual configuration.
When a device joins a network, it does not yet know its IP address. DHCP (Dynamic Host Configuration Protocol) hands one out automatically.
It works through a four-step exchange called DORA:
The lease includes the IP address, the subnet mask, the default gateway, and the DNS servers.
NAT (Network Address Translation) lets a whole private network share one public IP address.
When a packet leaves the network, the router rewrites its source address (and port) to the router's public IP. The router remembers this mapping, then reverses it for the reply.
This port-based form is called PAT, or "NAT overload." It is the standard behaviour of a home router.
NAT is the reason an internal host with a 192.168.x.x address cannot be reached directly from outside. To get in, you need a foothold or a port-forward.
DHCP logs and lease tables are useful during internal engagements. They help you map what is on the network.
Since this is a concept lesson, here is a small, complete Python program that simulates a NAT/PAT translation table so you can watch address rewriting happen. It is runnable with plain Python 3 (no external packages) and models the core idea: outbound packets get their source rewritten and recorded; replies are matched back to the correct internal host.
#!/usr/bin/env python3
"""A tiny simulation of PAT (NAT overload): many private hosts, one public IP."""
class NatRouter:
def __init__(self, public_ip):
self.public_ip = public_ip
self.next_port = 40001 # pool of outside ports to hand out
self.table = {} # outside (ip, port) -> inside (ip, port)
self.reverse = {} # inside (ip, port, dst) -> outside port (reuse)
def outbound(self, src_ip, src_port, dst_ip, dst_port):
"""A private host sends a packet out. Return the rewritten source."""
key = (src_ip, src_port, dst_ip, dst_port)
if key in self.reverse: # same flow already mapped
out_port = self.reverse[key]
else:
out_port = self.next_port
self.next_port += 1
self.table[(self.public_ip, out_port)] = (src_ip, src_port)
self.reverse[key] = out_port
print(f"OUT {src_ip}:{src_port} -> {dst_ip}:{dst_port}"
f" rewritten src -> {self.public_ip}:{out_port}")
return self.public_ip, out_port
def inbound(self, out_ip, out_port):
"""A reply comes back to the public IP. Find the real internal host."""
inside = self.table.get((out_ip, out_port))
if inside is None:
print(f"IN reply to {out_ip}:{out_port} -> DROPPED (no NAT entry)")
return None
print(f"IN reply to {out_ip}:{out_port} -> delivered to {inside[0]}:{inside[1]}")
return inside
def main():
router = NatRouter("203.0.113.7")
# Two different hosts, both using source port 52001, both hitting the same server.
_, p1 = router.outbound("192.168.1.50", 52001, "93.184.216.34", 443)
_, p2 = router.outbound("192.168.1.51", 52001, "93.184.216.34", 443)
# The web server replies to each outside port; NAT untangles them.
router.inbound("203.0.113.7", p2)
router.inbound("203.0.113.7", p1)
# An unsolicited inbound packet with no matching entry is dropped (the NAT shield).
router.inbound("203.0.113.7", 55555)
if __name__ == "__main__":
main()
What it does. It creates a virtual router with one public IP, sends two outbound "packets" from two different internal hosts that both happen to use source port 52001, and shows the router assigning each a distinct outside port. Then it feeds the replies back in and shows each being routed to the correct internal host. Finally it sends an unsolicited packet to a port that was never handed out — modelling an attacker's probe — and shows it dropped.
Expected output:
OUT 192.168.1.50:52001 -> 93.184.216.34:443 rewritten src -> 203.0.113.7:40001
OUT 192.168.1.51:52001 -> 93.184.216.34:443 rewritten src -> 203.0.113.7:40002
IN reply to 203.0.113.7:40002 -> delivered to 192.168.1.51:52001
IN reply to 203.0.113.7:40001 -> delivered to 192.168.1.50:52001
IN reply to 203.0.113.7:55555 -> DROPPED (no NAT entry)
Edge cases to notice. The reverse map means the same flow reuses its outside port instead of consuming a new one each packet. The dropped final packet is exactly why an internal host is not directly reachable from outside — a real router keeps this table too, and no table entry means no delivery.
Walking through the key parts of the simulation:
NatRouter.__init__ sets public_ip (the single address the world sees), a next_port counter starting at 40001 (the pool of outside ports the router will hand out), and two dictionaries. table maps outside (ip, port) back to the real inside host — this is the lookup a reply needs. reverse remembers a full flow so repeated packets of the same conversation reuse one outside port.
First outbound call: 192.168.1.50:52001 -> 93.184.216.34:443. The flow key is not in reverse, so a new outside port 40001 is allocated. table[('203.0.113.7', 40001)] = ('192.168.1.50', 52001) records the mapping. It prints the rewrite and returns ('203.0.113.7', 40001).
Second outbound call: 192.168.1.51:52001 — same source port as host 1, which on the real internet would be ambiguous. Because it is a new flow, it gets outside port 40002. This is the crucial moment: the two hosts are now distinguishable purely by their assigned outside port.
| Step | Inside | Assigned outside port | Table entry created |
|---|---|---|---|
| out #1 | 192.168.1.50:52001 | 40001 | (203.0.113.7,40001) → (192.168.1.50,52001) |
| out #2 | 192.168.1.51:52001 | 40002 | (203.0.113.7,40002) → (192.168.1.51,52001) |
inbound('203.0.113.7', 40002): the router looks up ('203.0.113.7', 40002) in table, finds ('192.168.1.51', 52001), and reports delivery to host 51. Then inbound(..., 40001) resolves to host 50. Order does not matter — the port key is unambiguous.
inbound('203.0.113.7', 55555): port 55555 was never handed out, so table.get(...) returns None. The packet is dropped. In memory, there is simply no key to match, so the router has nowhere to send it — this is the NAT reachability shield in one line of logic.
The result — two same-port hosts cleanly separated, and an unsolicited packet dropped — is produced entirely by the outside-port bookkeeping, which is precisely what PAT does in hardware.
Mistake 1 — Treating NAT as a firewall.
Wrong belief: "We're behind NAT, so nothing internal can be attacked."
Why it's wrong: NAT hides hosts only from unsolicited inbound traffic, and only as a side effect of its table logic. It inspects nothing. Any internal host can still reach out to a malicious server, download malware, and have that malware call home — NAT happily builds a table entry for the outbound connection and lets the replies flow.
Corrected approach: Use NAT for what it is (address sharing) and deploy an actual firewall with explicit inbound/outbound rules for security. Recognise the mistake whenever someone justifies leaving a service unpatched "because it's internal."
Mistake 2 — Assuming a device's IP never changes.
Wrong approach: Hard-coding 192.168.1.50 as "the printer" in scripts or configs.
Why it's wrong: That address came from a DHCP lease and can change when the lease expires or the device reconnects, breaking everything pointed at it.
Corrected approach: Use a DHCP reservation (server always gives that MAC the same IP) or a static address outside the DHCP pool. Recognise the mistake when things "randomly" break after a reboot or a weekend.
Mistake 3 — Confusing "got an address" with "has internet."
Wrong reasoning: "ipconfig shows an IP, so DHCP is fine and the problem is elsewhere."
Why it's wrong: A lease can succeed while carrying a wrong gateway or DNS. The device is addressed but cannot route out or resolve names.
Corrected approach: Check the whole lease — IP, mask, gateway, DNS — not just that an IP exists. Ping the gateway to test local routing, then a public IP, then a hostname, to isolate the layer.
Mistake 4 — Misreading a self-assigned address.
Wrong reading: Seeing 169.254.x.x (or Auto-configuration IPv4) and assuming DHCP worked.
Why it's wrong: 169.254.0.0/16 is APIPA — the address a device gives itself when DHCP fails. It is a symptom, not a success.
Corrected approach: Treat any 169.254.x.x as "no DHCP server answered." Check that the DHCP server is up, reachable on the segment, and has free addresses in its pool.
Because these are protocols rather than code you compile, "debugging" means diagnosing a misbehaving network. Work layer by layer.
When a device has no/wrong address:
ip addr / ipconfig /all. A 169.254.x.x address means DHCP failed — no server answered.sudo dhclient -r && sudo dhclient (Linux) or ipconfig /release && ipconfig /renew (Windows). Watch whether an address is granted.When a device has an address but no internet:
1.1.1.1. Fails → routing/NAT problem upstream. Succeeds → routing works.ping example.com. Fails only here → DNS problem, not addressing (revisit the DNS lesson).When a port-forward "doesn't work":
ss -tlnp / netstat) on the expected port and address.Questions to ask when it doesn't work: Did the device get a full, correct lease? Can it reach its gateway? Is there exactly one DHCP server? For inbound access, is there a matching NAT/forward entry, and is the target service listening?
This is a concept lesson with no C memory management, but robustness and safety still matter — DHCP and NAT touch real security boundaries.
Robustness of the simulation. The example router hands out ever-increasing outside ports and never frees them; a real router ages entries out of the table after inactivity, or it would eventually run out of ports. If you extend the simulation, add an expiry so old flows are reclaimed — mirroring how real NAT tables and DHCP leases both time out.
Security framing (defensive, general).
Keep all of this lab-only: practise on your own network or a virtual lab, never probe or reconfigure networks you do not own or have written permission to test.
Concrete real-world uses.
Best-practice habits.
Beginners should:
169.254.x.x = DHCP failed; 192.168.x.x / 10.x.x.x = private, behind NAT.Advanced practitioners should:
Beginner 1 — Read your own lease.
Objective: Identify the four core lease values on a machine you own.
Requirements: Run ipconfig /all (Windows) or ip addr plus cat /etc/resolv.conf / resolvectl status (Linux/macOS). Write down your IP address, subnet mask (in both dotted and /nn form), default gateway, and DNS server(s). State whether your address is private or public and how you can tell.
Concepts: lease contents, private vs public addressing.
Hint: A 192.168.x.x, 10.x.x.x, or 172.16–31.x.x address is private.
Beginner 2 — DORA in your own words. Objective: Explain the DHCP handshake precisely. Requirements: Write four short sentences, one per DORA step, each stating who sends the message (client or server) and whether it is a broadcast or unicast. Then explain in one sentence why the very first message must be a broadcast. Concepts: DORA, broadcast vs unicast, why an addressless host must shout. Hint: The client has no IP and no knowledge of the server's address when it starts.
Intermediate 1 — Trace a NAT table by hand.
Objective: Show that you can untangle shared-IP traffic.
Requirements: Given public IP 198.51.100.9, two internal hosts 10.0.0.5:60000 and 10.0.0.6:60000 both connecting to 172.217.0.0:443, build the NAT translation table the router would create (assign outside ports starting at 50001). Then state which internal host a reply to 198.51.100.9:50002 goes to, and explain why the router is not confused even though both hosts used port 60000.
Input/output example: Input: the two flows above. Output: a two-row table plus the reply's destination host.
Concepts: PAT, translation table, outside-port disambiguation.
Hint: The outside port, not the inside port, is the key for replies.
Intermediate 2 — Diagnose a broken connection.
Objective: Localise a fault using layered tests.
Requirements: A laptop shows address 169.254.7.20. Write the ordered diagnostic steps you would take and, for each possible outcome, what it tells you. Include what you would check on the DHCP server. Conclude with the single most likely root cause of a 169.254.x.x address.
Concepts: APIPA, DHCP failure, gateway/DNS isolation.
Hint: 169.254.x.x is self-assigned — it means no server answered.
Challenge — Extend the NAT simulator.
Objective: Make the provided Python simulator behave more like real hardware.
Requirements: Add (a) entry expiry — each table entry stores a creation time and is removed after an idle timeout you choose; (b) a port_forward(public_port, inside_ip, inside_port) method that pre-installs a static inbound mapping, so an unsolicited inbound to that public port is delivered instead of dropped; and (c) a check that refuses to allocate a new outside port once the pool (say 40001–40010) is exhausted, printing a clear "NAT table full" message. Demonstrate all three with a short main().
Constraints: Standard library only; keep the outbound/inbound printing behaviour intact.
Concepts: NAT table aging, port forwarding as a static mapping, port-pool exhaustion.
Hint: Store (inside, created_at) in the table and sweep expired entries at the start of each inbound/outbound call using time.monotonic().
DHCP and NAT are the two services that make everyday networks work without manual setup.
169.254.x.x address means DHCP failed.