Lablabfailure injectionlatencypacket lossdns failure

Network Failure Lab: Predict, Inject, Observe, Diagnose

Eight buttons — drop a packet, add 200 ms, add 5% loss, kill DNS, kill a router, expire the certificate, block the port, reset from the server — each produce a distinct symptom at the application, a distinct chain of events through the layers, and a distinct tool that confirms it; the skill is predicting all three before pressing the button.

SimulatedConceptualLinux
▶ InteractiveInterview question
Progress

The problem

A network can fail in a dozen ways, and most of them reach the application as "slow" or "error". Before you press a button that breaks one layer, can you say what the user will see, what happens at each layer underneath, and which single command would prove it?

The method: predict before you inject

Simulated

The lab is a simulated client → DNS → router → server path with a request in flight; every number it shows is simulated, chosen to be plausible, not measured. The value is not in watching it break but in committing to a prediction first. For each button, write down three things before pressing it: the symptom at the application (stall, slowness, an error — which error?, and what the client’s retry logic will do), the mechanism layer by layer (which packet is affected, what the transport does about it, what the application sees), and the confirming tool — the one command whose output would prove this cause and rule out the others.

Then inject, observe, and diagnose as if you did not know which button was pressed. The point is calibration: after a few rounds, "the request took 4 seconds and then succeeded" and "the request failed instantly with a certificate error" and "the request hung for 130 seconds" are three different diagnoses you can make from the symptom alone. Why Can’t I Connect? is the procedure; this lab is the practice.

  • Predict: symptom → mechanism → confirming tool. Write it down.
  • Inject: press one button.
  • Observe: what did the application see, and how long did it take?
  • Diagnose: which layer, and what would you check on a real system?

The eight failures

Simulated

The matrix is the lab’s answer key. Cover the right-hand columns and fill them from the left before you read them.

Button → what to predict → what happens layer by layer → how to confirm (simulated lab; real stacks differ in the numbers)
InjectSymptom to predictLayer by layerConfirm with
Drop one packetA brief stall (~one RTO or a fast-retransmit delay), then success; the user sees nothing or a hiccupTCP: a gap in seq; receiver sends dup ACKs; sender fast-retransmits after 3 dup ACKs, or waits for the RTO (~200 ms min on Linux) if it was the last segment; cwnd halvestcpdump / Wireshark tcp.analysis.retransmission; ss -ti retrans counter
Add 200 ms latencyEverything works, everything is slow; a fresh HTTPS request takes ~3–4 RTTs ≈ 0.8 s before the first byte; downloads crawl because cwnd growth is per-RTTNo loss, no error; TCP handshake 1 RTT, TLS 1 RTT, HTTP 1 RTT; bandwidth-delay product means small windows cannot fill the pipeping shows the RTT; curl -w shows time_connect ≈ 0.2, time_appconnect ≈ 0.4, time_starttransfer ≈ 0.6
Add 5% lossConnects, but throughput collapses and large transfers time out; small requests mostly workEvery 20th segment lost; repeated fast retransmits and RTOs; cwnd repeatedly halved; the Mathis bound ≈ MSS/(RTT·√p) — at 50 ms RTT and p=0.05, ~1.3 Mbit/s regardless of link speedping -c 100 loss %; mtr persistent loss to the destination; ss -ti retrans; Wireshark retransmission count
DNS failureFails fast with ENOTFOUND / "could not resolve host" (NXDOMAIN), or hangs ~5–10 s then fails (EAI_AGAIN, resolver unreachable); nothing was ever connected; retries fail identically until DNS recoversNo socket is created; the app never reaches TCP; cached answers keep working until their TTL expires, so the failure is staggered across clientsdig name status and SERVER line; dig @8.8.8.8 name to compare resolvers; resolvectl status
Router failureConnection attempts time out (silence); existing connections stall then die after retransmission limits; if BGP/IGP reroutes, a pause of seconds to minutes then recoveryPackets are black-holed or answered with ICMP unreachable by the previous hop; SYNs retransmit with backoff; established connections retransmit up to tcp_retries2 (~15 min) before failingtraceroute ends at the hop before; ping silent or "destination unreachable" from a router; ip route get shows the path still points there
TLS certificate expiredInstant, deterministic error: browser ERR_CERT_DATE_INVALID, curl (60) certificate has expired; retries fail identically; clients with -k or a wrong clock may still workDNS, TCP handshake succeed; TLS ServerHello and certificate arrive; the client rejects with alert certificate_expired and closes the connection; no HTTP is ever sentopenssl s_client → verify error 10; openssl x509 -noout -dates; date on the client
Port blocked (firewall DROP)Connect hangs, then ETIMEDOUT after the client’s connect timeout (or ~127 s at the kernel default); ping may still workSYN leaves the client, is dropped at the firewall; no SYN-ACK, no RST; SYN retransmitted at 1, 2, 4, 8… s; the server never sees itnc -vz host port hangs; tcpdump on the server shows no SYN; security group / nft list ruleset
Server resets the connectionConnect fails instantly with ECONNREFUSED (RST to SYN), or an established request dies with ECONNRESET / "connection reset by peer" / a gateway 502Host reachable; TCP RST answers the SYN (nothing listening) or arrives mid-stream (process crashed, LB idle timeout, firewall REJECT); the kernel tears down the socket at oncess -tlnp on the server; tcpdump shows Flags [R] and who sent it; process restart timestamps

Slow failures: latency and loss

Simulated

Two of the buttons produce no error at all, only time, and they are the hardest to diagnose in production because every tool reports "success". Latency taxes each round trip equally. A fresh HTTPS request needs a DNS lookup, a TCP handshake, a TLS handshake and the request itself — three to four RTTs before the first byte — so adding 200 ms adds ~0.8 s to time-to-first-byte, and a page of 40 assets on a cold HTTP/1.1 connection pool becomes unusable. The server’s CPU is idle throughout; this is the "slow request, idle server" interview question and the subject of Bandwidth vs Latency. Transfers suffer twice: cwnd grows once per RTT, so slow start takes four times longer to fill the pipe, and the bandwidth-delay product grows so a fixed receive window caps throughput at window / RTT.

Loss is worse than its percentage suggests. TCP interprets every loss as congestion and halves its sending rate, so throughput scales with 1/√p: 5% loss on a 50 ms path bounds a connection to roughly 1.3 Mbit/s no matter how fast the link is. Small requests that fit in a few segments often succeed after one retransmission; large downloads crawl and time out. Packet Loss: Duplicate ACKs, Fast Retransmit and the RTO and Congestion Control: Protecting the Network show the wire-level dance; the packet-loss challenge is the incident version. The confirming evidence is loss that persists to the destination in mtr, not loss at one intermediate hop (traceroute: Discovering the Path Hop by Hop explains why the distinction matters).

  • Latency: no errors; TTFB ≈ (3–4) × RTT for a cold connection; curl -w shows each rung paying one RTT.
  • Loss: throughput ∝ 1/√p; retransmissions visible in Wireshark; small requests survive, large ones crawl.
  • Both leave the server idle: a "slow API" with a quiet server is a network diagnosis until proven otherwise.

Hard failures: DNS, router, port, reset

The hard failures are distinguishable by when and how they fail. A DNS failure fails before any connection — the error text says "resolve" or "not found", and curl -v never prints "Trying <IP>". A blocked port and a dead router both fail by silence: the SYN goes out and nothing returns, so the client waits its full connect timeout. They are separated by traceroute (the router failure stops the trace one hop short; the firewall lets the trace reach the host but the port probe hangs) and by whether ping still works (a DROP rule on one port usually leaves ICMP alone). A server reset fails instantly with a RST: ECONNREFUSED at connect time means nothing is listening, ECONNRESET mid-request means the process died or a middlebox cut the flow.

Retry behaviour is the other tell. Retries against DNS failure fail identically until a cache expires or the resolver recovers, and different clients fail at different times as their cached answers age out. Retries against a blocked port cost the full timeout each time, which is how one blocked backend consumes a thread pool. Retries against a refused connection are cheap and immediate, which is how a crash-looping backend gets hammered with thousands of connections per second by a gateway that never backs off. The TCP Debugging: Reading the Handshake on the Wire and DNS Debugging: Who Answered, and With What? procedures name the check for each.

  • Fails before "Trying <IP>" → DNS. Fails by silence → drop (firewall or route). Fails instantly with RST → refused or reset.
  • Retry cost: DNS = fast fail; drop = full timeout each time; RST = instant, so back off deliberately.

Expired certificate, and the MTU black hole the lab does not have a button for

The expired certificate is the cleanest failure in the lab: DNS, TCP and the first half of TLS succeed, and then the client rejects the certificate with a deterministic error that no retry will fix. It is also the most preventable — certificates expire at a known second — and the one most often missed by monitoring that checks HTTP without TLS. The TLS Debugging: Why the Certificate Is "Invalid" lesson has the full set of reasons a certificate is "invalid"; the expired-certificate challenge is the incident version.

One real failure deserves a mention even though it is not a button: the MTU black hole. A link on the path has an MTU below 1500 (a VPN tunnel, PPPoE, an overlay network); large packets with the DF bit set are dropped there and the router sends ICMP Fragmentation Needed — which a firewall filters. Small packets (the handshake, a small request) pass, so the connection opens and then hangs the moment a full-size segment is sent: a login page loads, the dashboard behind it never does. tcpdump shows the same 1448-byte segment retransmitted with no ACK progress; ping -M do -s 1472 (Linux) fails where -s 1400 succeeds. The fix is MSS clamping on the tunnel or allowing ICMP type 3 code 4 through. The MTU black-hole challenge attached to this lesson is this incident.

  • Expired cert: succeeds up to the certificate, then fails deterministically; check date and openssl x509 -dates.
  • MTU black hole: connects, small things work, large things hang; test with DF-set pings at 1472 vs 1400 bytes.

Key points

  • Predict symptom, mechanism and confirming tool before injecting; the lab exists to calibrate those predictions.
  • Latency and loss produce no errors, only time: TTFB ≈ 3–4 RTTs for a cold HTTPS request; throughput ∝ 1/√loss.
  • DNS failure fails before any connection; drop (firewall/router) fails by silence after a full timeout; RST fails instantly.
  • An expired certificate fails after DNS and TCP succeed, deterministically, on every retry.
  • Retry behaviour distinguishes causes: identical fast failures (DNS), full-timeout retries (drop), instant cheap retries that hammer a crashing backend (RST).
  • The MTU black hole opens the connection and then hangs on the first large segment; DF-set pings find it.

Why does this exist?

Mechanisms are answers to constraints. Open each question before reading the answer.

Why predict before injecting?

Watching a failure teaches recognition; predicting it teaches the model. In production you never see the button that was pressed — you see the symptom and must infer the button.

Why do latency and loss not produce errors?

TCP is designed to hide them: it retransmits and slows down rather than failing. That reliability is why "the network is slow" reaches the application with no error to log.

Why is the confirming tool part of the prediction?

A diagnosis without a test is a guess. Naming the command that would prove the cause — and what its output would look like — is what makes the diagnosis actionable on a real system.

Network failure lab

Network failure lab
Pick a fault, predict what the user sees, inject it, then read the layer-by-layer trace. Most network failures are not errors — they are time.
Inject
predictions 0/0Educational model
Layers
  1. Application / HTTP
  2. DNS
  3. TLS
  4. Transport (TCP)
  5. IP / routing
  6. Link / NIC
Choose a fault. Before injecting it you must commit to a prediction — that is where the learning is.

How it fails

What the failure looks like from inside real software.

  • Blaming the server for a slow API when curl -w shows 600 ms spent before the request was even sent.
  • Treating 5% loss as "a bit of loss" and scaling servers while throughput is bounded by the loss rate, not capacity.
  • Retrying a blocked-port connection in a loop with no backoff and exhausting the thread pool on timeouts.
  • A gateway retrying refused connections to a crash-looping backend at thousands per second, preventing it from ever starting.
  • Missing an expired certificate because the health check spoke plain HTTP.
  • Debugging an MTU black hole as an application hang because the connection "obviously works" — the handshake succeeded.