Network Fault DiagnosisLesson 1 of 3
Diagnosing a network fault layer by layer
Test link, address, route, name, port and application in order, and read refused, timed out and unreachable correctly.
ip linkip -br addrip routeip neighpingtracepathss -tlnpnccurlcurl -vdiggetentnmcli device status
After this lesson you can
- Apply a fixed order of checks that moves from the cable to the application and stops at the first failure.
- Interpret connection refused, timeout, no route to host, and name resolution failures.
- Choose the tool that tests each layer: ip, ping, tracepath, getent, nc, curl, ss.
The idea
“The network is broken” is almost never one problem. A TCP connection from an application to a server depends on a stack of things, each of which can fail on its own. Testing them in order, bottom to top, means the first failure you find is the real one, and everything above it is a consequence.
1 Link is the interface up, does it have carrier? ip -br link
2 Address does it have the right IP and prefix? ip -br addr
3 Route is there a route to the destination (or a default)? ip route get ADDR
4 Neighbour can the next hop's MAC address be found? ip neigh
5 Reachability does the destination answer ICMP? ping -c2 ADDR (may be blocked)
6 Path where do packets stop? tracepath ADDR
7 Name does the name resolve to the right address? getent hosts NAME
8 Port does TCP connect to the port? nc -zv HOST PORT
9 Service does the application answer correctly? curl -v URL
10 Server side is anything listening, and is the firewall open? ss -tlnp ; firewall-cmd --list-all
Two rules:
- Test by address before you test by name. If
ping 192.168.50.12works andping node2does not, the network is fine and the problem is naming. - Run the same check from both ends when you can. A listener bound to
127.0.0.1accepts connections from the server itself and refuses everyone else.
What each failure means
The wording of an error is information. curl, nc, ssh and ping report the same few situations in slightly different words.
| What you see | What happened | Where to look |
|---|---|---|
Network is unreachable | The local routing table has no route for the destination | ip route, the default gateway |
Name or service not known, Could not resolve host | Name lookup failed | /etc/hosts, /etc/nsswitch.conf, /etc/resolv.conf, DNS server |
Connection refused | Packet arrived, host replied “no” (TCP RST or ICMP port unreachable): nothing is listening | ss -tlnp on the server; wrong port; service down; bound to localhost |
Connection timed out | No answer at all: packets dropped (firewall DROP), lost on the path, or the host is down | Firewall on the server and on the path; routing back; tcpdump |
No route to host | The kernel or a router sent back “host unreachable” (ICMP) or ARP failed | Neighbour table, gateway, VLAN, the target’s own link |
Connection reset by peer | Connection was made then torn down | Application crashed, TLS/protocol mismatch, firewall reset |
| Ping works, TCP does not | The host is up, the port or a firewall rule is the problem | nc -zv, firewall rules |
| TCP works, application fails | Protocol-level error | curl -v, application logs |
| Works for small data, hangs on large | MTU mismatch (packets dropped when large) | ping -M do -s 1472 ADDR, tracepath |
The most useful distinction is the first one above all others: refused means you reached something; timed out means you reached nothing (or something silently dropped you).
Try it
All of the following are real, from a test machine.
Refused versus timed out
Connect to a port where nothing listens:
nc -zv 127.0.0.1 9
curl -sS -m 3 http://127.0.0.1:9/
nc: connect to 127.0.0.1 port 9 (tcp) failed: Connection refused
curl: (7) Failed to connect to 127.0.0.1 port 9 after 0 ms: Couldn't connect to server
curl exit code 7 is “failed to connect”. It happened in 0 ms: the answer came back immediately, because the kernel replied with a reset.
A name that does not exist:
curl -sS -m 3 http://no-such-host.invalid/
getent hosts nosuch.invalid; echo "rc=$?"
curl: (6) Could not resolve host: no-such-host.invalid
rc=2
curl exit code 6 is name resolution. getent hosts returns status 2 when the name is not found; it uses the same resolver order as applications (/etc/nsswitch.conf), which dig does not.
Reading the connection from the outside
curl -v shows each step: name resolution, the address tried, the TCP connection, and the HTTP exchange:
curl -v --max-time 5 http://node2.lab.example:8080/ 2>&1 | head -8
* Trying 192.168.50.12:8080...
* Connected to node2.lab.example (192.168.50.12) port 8080
> GET / HTTP/1.1
> Host: node2.lab.example:8080
> User-Agent: curl/8.9.1
> Accept: */*
>
< HTTP/1.0 200 OK
(Representative output.) If it had stopped after Trying ... the problem is the TCP connect (route, firewall, nothing listening). If it stops at the very first line with an error about the host, it is naming.
The server side
On the server, the question “is it listening, on which address?” has a one-line answer:
sudo ss -tlnp 'sport = :8080'
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 5 0.0.0.0:8080 0.0.0.0:* users:(("python3",pid=5120,fd=3))
0.0.0.0:8080 means all IPv4 addresses. 127.0.0.1:8080 would mean local only: remote clients get refused, even though the service is running and the firewall is open. [::]:8080 is all IPv6 addresses (and usually IPv4 too).
Going further
Symptoms that only appear sometimes. Intermittent failures usually come from DNS round-robin pointing at a bad host, an IPv6 address that does not work while IPv4 does (try curl -4 and curl -6), connection tracking tables filling up, or an MTU problem. The tcpdump lesson shows how to prove which. Beyond the exam: mtr combines ping and traceroute over time, ethtool -S shows interface error counters, and nstat/ss -s summarise TCP statistics (retransmissions are the first sign of a lossy path).
Practice
0 of 3 done
Do these on your own system. They get harder. Check each result, then tick it off.
- Warm-up
Match symptom to layer
For each message, name the layer that failed and the next command: (1)
Network is unreachable; (2)Could not resolve host: app.lab.example; (3)Connection refused; (4)Connection timed outafter a long wait; (5)No route to host; (6) a browser shows502 Bad Gateway.Check your result
- (1) no route in the table,
ip route; (2) name resolution,getent hosts/dig; (3) the host is reachable, nothing listens on that port,ss -tlnpon the server; (4) packets dropped by a firewall or lost on the path,tcpdumpand the firewall rules; (5) the local host knows a route but ARP or an ICMP reply says unreachable, check the neighbour and the gateway; (6) the application layer: the proxy cannot reach its backend, read the proxy log.
Hint
Refused is an answer (an RST or ICMP came back). Timed out is the absence of an answer. They need different next steps.
Solution
Network is unreachable -> routing table (no matching route) ip route Could not resolve host -> DNS / hosts / nsswitch getent hosts NAME Connection refused -> reached, port closed ss -tlnp (server) Connection timed out -> filtered or lost tcpdump, firewall No route to host -> ARP failed or ICMP unreachable ip neigh, gateway 502 Bad Gateway -> application layer proxy and backend logs - (1) no route in the table,
- Practice
Walk a connection down the stack
From
node1, the service athttp://node2.lab.example:8080/is unreachable. Without assuming the cause, run the layered checks in order from node1 and record the result of each: link state, own address, default route, the gateway's neighbour entry, name resolution ofnode2.lab.example, ICMP to its address, TCP to port 8080, and an HTTP request. Stop and report at the first failure with the evidence.Check your result
- Your list has these commands in this order:
ip -br link,ip -br addr,ip route,ip neigh,getent hosts node2.lab.example,ping -c2,nc -zv node2.lab.example 8080,curl -v http://node2.lab.example:8080/. - You stopped at the first failure and named it with one line of evidence.
Hint
Each check depends on the one before it: a failing ping to a name and a working ping to its address means DNS, not the network. Test by address first, then by name.
Solution
ip -br link ip -br addr ip route ip neigh show getent hosts node2.lab.example ping -c2 192.168.50.12 nc -zv node2.lab.example 8080 curl -v --max-time 5 http://node2.lab.example:8080/ - Your list has these commands in this order:
- Challenge
Three faults in one path
Setup (lab VMs
node1client andnode2server; run on node1 unless stated):# node2: serve something on 8080 python3 -m http.server 8080 --bind 0.0.0.0 & # node1: break it (CON is the name of the active profile) CON=$(nmcli -g NAME connection show --active | head -1) sudo nmcli connection modify "$CON" ipv4.gateway 192.168.50.99 sudo nmcli connection up "$CON" echo "192.168.50.13 node2.lab.example" | sudo tee -a /etc/hostsand on node2
sudo firewall-cmd --remove-port=8080/tcp(if open). From node1curl http://node2.lab.example:8080/fails. There are three independent faults. Find them one at a time, in layer order, with evidence for each, fix them persistently, and finish with a successful request.Check your result
curl -s http://node2.lab.example:8080/ | head -3returns HTML.ip route | grep defaultshows the real gateway;getent hosts node2.lab.examplereturns node2's real address;sudo firewall-cmd --permanent --list-portson node2 contains8080/tcp.- The wrong
/etc/hostsline is gone.
Hint
Layer order: route (gateway), name (a stale
/etc/hostsentry overrides DNS becausehosts: files dnsinnsswitch.confreads the file first), then the firewall on the server. Usegetent hosts, notdig, to see what applications see.Solution
ip route; ping -c2 192.168.50.99 || echo "gateway unreachable" CON=$(nmcli -g NAME connection show --active | head -1) sudo nmcli connection modify "$CON" ipv4.gateway 192.168.50.1 sudo nmcli connection up "$CON" getent hosts node2.lab.example; dig +short node2.lab.example sudo sed -i '/node2.lab.example/d' /etc/hosts nc -zv node2.lab.example 8080 # node2: sudo ss -tlnp | grep 8080 sudo firewall-cmd --permanent --add-port=8080/tcp && sudo firewall-cmd --reload # node1 curl -s http://node2.lab.example:8080/ | head -3