01 · The Linux Network Stack: Interfaces, IP & Routing¶
ping either works or it doesn't, and when it doesn't, "is it DNS, is it routing, is it a firewall, is it the
remote host" is the standard troubleshooting ladder — one this lesson and the next few build, starting from the
bottom: what an interface actually is, and how the kernel decides, for any destination address, which interface
and which next hop to send a packet through.
Interfaces and addresses¶
A network interface (eth0, lo, wlan0...) is the kernel's representation of something that can send and
receive packets — a real NIC, a virtual/paravirtualized device (as in this course's VM), or lo, the loopback
interface that exists purely so a machine can talk to itself without touching real hardware. An interface can
carry multiple IP addresses, each with a scope (global — routable beyond this machine; link — only valid
on this local network segment, like IPv6 link-local addresses) and, when address assignment comes from DHCP, a
lease duration you can see directly.
Worked example: a real interface, a real routing table, two real route decisions¶
$ ip addr show eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 52:55:55:78:ba:7d brd ff:ff:ff:ff:ff:ff
inet 192.168.5.15/24 metric 200 brd 192.168.5.255 scope global dynamic eth0
valid_lft 2021sec preferred_lft 2021sec
inet6 fe80::5055:55ff:fe78:ba7d/64 scope link proto kernel_ll
$ ip route show
default via 192.168.5.2 dev eth0 proto dhcp src 192.168.5.15 metric 200
192.168.5.0/24 dev eth0 proto kernel scope link src 192.168.5.15 metric 200
192.168.5.2 dev eth0 proto dhcp scope link src 192.168.5.15 metric 200
$ ip route get 8.8.8.8
8.8.8.8 via 192.168.5.2 dev eth0 src 192.168.5.15 uid 501
cache
$ ip route get 1.1.1.1
1.1.1.1 via 192.168.5.2 dev eth0 src 192.168.5.15 uid 501
cache
(Real output from this course's lab VM — a Lima-managed VM, which is why the address range and MAC prefix look
VM-typical rather than like real cloud infrastructure.) inet 192.168.5.15/24 ... dynamic: dynamic confirms this
address came from DHCP, not static configuration, and the valid_lft/preferred_lft lease timers are the real
reason a DHCP-assigned address can change later — nothing on this system "owns" this IP permanently. The routing
table has exactly three entries: a route to the local subnet (192.168.5.0/24, directly reachable, no gateway
needed), a host route to the gateway itself, and a default route (0.0.0.0/0, shown as default) via
192.168.5.2 for everything else. ip route get shows the kernel's actual routing decision for a specific
destination rather than making you read the whole table and reason it out by hand: both 8.8.8.8 (Google's public
DNS) and 1.1.1.1 (Cloudflare's) resolve to the exact same decision — out eth0, via the same gateway — because
neither matches the more specific 192.168.5.0/24 route, so both fall through to the default route, which is
exactly what a default route is for.
How It Actually Works¶
Routing table lookup is a longest-prefix match: the kernel doesn't scan routes in file order, it picks the
route whose destination prefix is the most specific match for the packet's destination address, and only falls
back to a less specific (eventually the default, /0) route when nothing more specific matches. This is why
192.168.5.0/24 (a /24, matching 256 addresses) correctly takes priority over the /0 default route for any
address starting 192.168.5., even though both are present in the same table — specificity, not order of
insertion, decides.
ip route get doesn't just print a table row back at you — it genuinely runs the kernel's route resolution logic
for that one destination (through the same code path an actual outgoing packet would use) and shows you the
result, including the source address the kernel would select (src 192.168.5.15 here) if you didn't specify
one yourself; on a machine with multiple addresses or interfaces, this source-selection step is itself a real
decision, not an arbitrary pick, and ip route get <dst> from <specific-src> lets you ask "what if I wanted to
send from this particular address instead." The cache line in the output is a reminder that Linux historically
cached routing decisions for reuse (a performance optimization removed and reworked more than once across kernel
versions); don't read too much into its exact presence or absence across different kernel releases — the
authoritative routing decision is always the via .../dev ... line above it.
Common mistakes¶
- Assuming
pingfailing means "the network is down."pingtests ICMP reachability specifically, which many networks and hosts deliberately block while TCP/UDP traffic flows fine — a failedpingwith workingcurl/sshto the same host usually means a firewall rule (Lesson 3), not a routing or connectivity problem. - Confusing
scope linkaddresses (like thefe80::...IPv6 address above) with globally routable ones. Link-local addresses are only meaningful on the directly-attached network segment and will never appear in a routing table as a path to anywhere beyond it. - Reading a routing table top-to-bottom and assuming the first matching line wins. As covered above, the
kernel selects by most-specific prefix match, not list order — a
/32host route listed after a/0default route inip route show's output still wins for its one specific address.
Exercise¶
Run ip addr show and ip route show on any Linux machine, and identify: your default gateway, whether your
main address is static or DHCP-assigned (look for the dynamic flag), and your subnet's prefix length. Then run
ip route get against three different destinations — one on your local subnet, one well-known public IP, and one
made-up address outside any configured range — and confirm which routing table entry each one actually uses.