Routing & NAT
In lesson 1 you saw that a packet for another network goes to a router. But how does the router know where to send it next? And how can millions of homes all use 192.168.1.x at once without crashing into each other? This lesson follows a packet out of your server, across the internet and back.
You will learn
- What a routing table is, and how Linux picks a route (longest prefix match)
- The default gateway, and why "no route to host" happens
- Following a packet hop by hop with
tracepath - Adding a route to another network, and keeping it on Rocky and Ubuntu
- How NAT lets a whole house share one public address
Every machine has a routing table
Before sending a packet, a computer looks up the destination in its routing table: a short list of "to reach these addresses, send the packet here". On Linux, ip route shows it:
default via 192.168.1.1 dev enp0s3 proto static metric 100 # everything else: hand it to the router
10.20.0.0/24 via 192.168.1.60 dev enp0s3 proto static metric 100 # this network: through the lab router
192.168.1.0/24 dev enp0s3 proto kernel scope link src 192.168.1.50 # my own network: deliver directly
- A route with
viasends the packet to that router, which must be on your own network. - A route without
viameans "it's right here on this cable": Linux adds one for each address you have. - The default route (
default=0.0.0.0/0) catches everything else. That router is your default gateway.
The most specific route wins
An address can match several routes: 10.20.0.15 is inside 10.20.0.0/24 and inside 0.0.0.0/0 (everything is). Linux picks the route with the longest prefix, the most specific match. /24 beats /0, so packets for the lab go to the lab router and everything else goes to the default gateway. Every router on the internet makes the same decision for every packet. To see which route a packet will take, ask: ip route get ADDRESS.
"Network is unreachable": no route matches at all, often because there's no default route. Silence or timeouts: a route matched, but a router further on didn't know what to do with the packet and dropped it.
Following a packet: tracepath and traceroute
Every packet carries a TTL (time to live, here a hop counter), and each router takes one off. At zero, the router throws the packet away and sends back a "time exceeded" note. tracepath and traceroute use that trick: they send packets with TTL 1, 2, 3… and list who answers at each step.
| Tool | Rocky / RHEL | Ubuntu / Debian |
|---|---|---|
| Follow the path | tracepath -n 8.8.8.8 same on both, preinstalled | |
| traceroute (more options) | sudo dnf install traceroute | sudo apt install traceroute |
| On Windows | tracert 8.8.8.8 a laptop, not the server | |
* * * or "no reply" means nobody answered at that step. Sometimes that's just a router that doesn't reply to these packets, but if the trace never gets any further, that's where your packets are dying.
Adding a route
When a network sits behind a different router than your default gateway, add a route to it. Try it with ip route, which lasts until reboot, and then save it:
| Task | Rocky / RHEL | Ubuntu / Debian |
|---|---|---|
| Add it now (until reboot) | sudo ip route add 10.20.0.0/24 via 192.168.1.60 same on both | |
| Save it | sudo nmcli con mod enp0s3 +ipv4.routes "10.20.0.0/24 192.168.1.60" | a routes: entry in a netplan file under /etc/netplan/ |
| Apply what's saved | sudo nmcli con up enp0s3 | sudo netplan apply |
| See what's saved | nmcli con show enp0s3 | grep routes | sudo netplan get or cat /etc/netplan/*.yaml |
On Ubuntu, netplan reads every .yaml file in /etc/netplan/ and merges them, so you can add a route in a small file of its own:
network:
version: 2
ethernets:
enp0s3:
routes:
- to: 10.20.0.0/24
via: 192.168.1.60
Changing routes over SSH can cut off your own connection: delete the wrong route, and the server can't send its replies back to you. Add routes rather than replacing the default, and keep a console handy. Ubuntu's sudo netplan try undoes the change by itself if you don't confirm within two minutes.
NAT: one public address for a whole house
Private addresses like 192.168.1.50 aren't allowed on the internet. So how does your server download updates? Your home router does NAT (network address translation). When a packet leaves for the internet, the router swaps the private source address for its own public address and remembers the swap. When the reply comes back, it swaps it back and passes it to the right machine.
- That's why
ip addrshows192.168.1.50, but a website sees a different, public address.curl ifconfig.meshows what the world sees. - It's also why nobody on the internet can start a connection to your server: the router has no remembered swap for it. To run a public service at home, you'd add a port forward on the router (outside connections to port 443 →
192.168.1.50:443). - Many internet providers add a second layer of NAT (carrier-grade NAT, using
100.64.0.0/10). You'll spot it in a trace as a100.64.x.xor10.xhop right after your router. Behind CGNAT, even a port forward won't work.
A Linux machine can be a router and do NAT itself. That's what cloud NAT gateways and many home routers are inside. Routing between networks is switched on with sysctl net.ipv4.ip_forward=1. The NAT part is done by the firewall: firewall-cmd --add-masquerade on Rocky, or nft/ufw rules on Ubuntu.
Try it: reach the club's lab 🛣️
The club built a lab network, 10.20.0.0/24, behind its own small router, labrouter at 192.168.1.60. The lab server is 10.20.0.15. From the club server you can't reach it. Find out why, fix it, and make the fix stick.
Quick check
1. Your table has default via 192.168.1.1 and 10.20.0.0/24 via 192.168.1.60. Where does a packet for 10.20.0.15 go?
✓ Longest prefix match: the most specific route wins.
2. ip addr says 192.168.1.50, but curl ifconfig.me says 203.0.113.77. Why?
✓ Private addresses stay inside. The router swaps them for its public address on the way out.
3. You added a route with sudo ip route add … and it works. What happens after a reboot?
✓ ip changes the running system only. The saved config is what comes back after a reboot.