Networking
Every network operation is layers wrapped around layers. Debugging is just unwrapping them in order — so we learn to see each layer directly.
An interface has a hardware address (L2, the MAC) and an IP address (L3). Packets are addressed by IP but delivered on the wire by MAC.
List your interfaces and identify the MAC and the IP on one of them.
$ ip addr showlink/ether is the L2 MAC; inet is the L3 IP. Two addresses, two layers, one interface.
Reveal solution
$ ip addr show $ ip -br addr # compact view
A TCP connection opens with SYN → SYN-ACK → ACK. You can watch it happen on the wire.
Sniff TCP on port 443 in one terminal, then make an HTTPS request in another.
$ sudo tcpdump -i any -n 'tcp port 443' -c 6Run curl -s https://example.com >/dev/null in a second shell. You'll see flags [S], [S.], [.] — the handshake.
Reveal solution
$ sudo apt install -y tcpdump # if needed # terminal 1: $ sudo tcpdump -i any -n 'tcp port 443' -c 6 # terminal 2: $ curl -s https://example.com >/dev/null
A socket moves through states — LISTEN, ESTABLISHED, TIME_WAIT. ss is the modern way to see them (forget netstat).
Start a listener, then find it and its state.
$ ss -tlnp | grep 8000You see the socket in LISTEN on :8000. Connect to it and check ss -tan to watch ESTABLISHED appear.
Reveal solution
$ python3 -m http.server 8000 & $ ss -tlnp | grep 8000
Reach engineers who read the man page
Native, contextual, no tracking — this is how the curriculum stays free.
A name becomes an IP through a resolver chain (/etc/nsswitch.conf → /etc/hosts → DNS). Two tools show two views.
Resolve a name with the DNS-only path and with the full system path.
$ getent hosts example.comdig +short example.com asks DNS directly; getent follows the system resolver order — sometimes they differ, and that difference is a classic bug.
Reveal solution
$ dig +short example.com $ getent hosts example.com
Two failures that look similar but mean opposite things: connection refused (something said "no one's here") vs timeout (something ate the packet — usually a firewall).
Trigger a refused connection, then explain how you'd tell it apart from a timeout.
$ curl -sS --max-time 3 http://localhost:9999 ; echo "exit=$?"You get Connection refused immediately — nothing is listening. A firewall drop would instead hang until the timeout. Refused = fast + reset; timeout = slow + silent.
Reveal solution
# refused (fast): nothing listening $ curl -sS --max-time 3 http://localhost:9999 # start something and it connects: $ python3 -m http.server 9999 & sleep 1 $ curl -sS --max-time 3 http://localhost:9999 | head
Layers, the handshake, socket states, and resolution are the map you'll use to debug everything from a hung curl to a broken Kubernetes Service. In the Linux module you'll build the machinery that implements all of it.