The Tools, and Which Layer Each One Answers
ping, traceroute, dig, curl, ss, tcpdump, Wireshark, nc and openssl are not a list to memorise; each one asks a question at one layer and is blind to the others, so choosing the tool is choosing the layer you are testing.
The problem
Tools are questions, not commands
Every network tool sends something and interprets what comes back, and the something determines what the tool can see. ping sends ICMP echo, so it can tell you whether a host answers ICMP and how long that takes — and nothing about whether port 443 is open. nc opens a TCP connection, so it can tell you whether a listener exists — and nothing about whether the HTTP behind it works. curl sends an HTTP request, so it walks every layer and reports the first one that fails. tcpdump sends nothing at all; it watches, which is why it is the tool that settles arguments.
The map in this lesson answers, for each tool, three things: which layer it exercises, which question it answers cleanly, and — the part nobody memorises — what it cannot tell you. A tool used outside its layer produces confident, misleading output. A failed ping "proving" a service is down is the canonical example; ping: What an Echo Actually Proves is a whole lesson on why.
The interactive lets you pick a tool and see which rungs of the Why Can’t I Connect? ladder it lights up.
The matrix
Read the fourth column as carefully as the third. Most bad diagnoses come from a tool’s silence being read as evidence about a layer the tool never touched.
| Tool | Layer it exercises | Question it answers | What it cannot tell you |
|---|---|---|---|
ping | IP + ICMP | Does the host answer ICMP echo? RTT and loss to it? | Whether any TCP/UDP port is open; whether a process is running; anything if ICMP is filtered |
traceroute / tracert | IP (per-hop TTL) | Which routers does the forward path traverse, and where does it stop answering? | The return path; whether a silent hop dropped your packet or just refused to reply; application state |
dig / nslookup / Resolve-DnsName | DNS (application layer over UDP/TCP 53) | What does this resolver answer for this name, with what TTL and status? | What the browser’s DoH resolver or /etc/hosts says; whether the returned IP is reachable |
curl -v | All of them, top-down | Which is the first layer that fails on a real request; timings per layer with -w | Why a packet was dropped; anything about layers below a failure it reports |
ss / netstat | Transport, local kernel | What is listening, on which address; state of each connection; queue depths | Anything about the other host or the path; whether the listener is healthy |
tcpdump | Link → transport, on this host | Exactly which packets arrived and left, with flags, timing and sizes | What happened on any other host; encrypted payloads; why a packet never arrived |
| Wireshark | Same as tcpdump, with analysis | Retransmissions, RSTs, TLS alerts, HTTP/2 frames decoded; a full conversation timeline | Same blind spots as tcpdump — it sees only the capture point |
nc / Test-NetConnection | Transport | Does a TCP connect succeed, get refused, or time out? Can I send raw bytes? | Whether TLS or HTTP would work; who dropped a timed-out SYN |
openssl s_client | TLS | Which certificate chain is served for this SNI; does it verify; which protocol/cipher was negotiated? | Anything about HTTP behind it; whether a browser with different trust roots would agree |
curl is the whole ladder in one command
curl -v prints, in order: name resolution, the connect attempt with the IP it chose, the TLS handshake with the negotiated version and the certificate it verified, the request it sent, and the response it got. The first line that deviates from that script is the layer that failed. Two flags make it a precision instrument. --resolve host:port:ip skips DNS and pins the connection to an address while keeping the Host header and SNI correct — the only clean way to test "is the new server fine before I flip DNS". -w with a format string prints per-layer timings so you can see where 800 ms went.
On the timings: time_namelookup is DNS, time_connect minus that is the TCP handshake (one RTT), time_appconnect minus time_connect is TLS (one RTT on TLS 1.3, two on 1.2), time_starttransfer minus time_appconnect is the server thinking, and time_total minus time_starttransfer is the body. A server that is "slow" with time_connect at 150 ms and time_starttransfer 30 ms later is not slow; it is far away — see Where the Time Goes: The Request Timeline.
curl --resolve api.example.com:443:198.51.100.7 https://api.example.com/— test a specific backend with the right SNI and Host.curl -Isends HEAD;curl -sS -o /dev/null -w "%{http_code}"is the one-liner for monitoring scripts.curl --doh-url https://…makes curl resolve the way a DoH-enabled browser does — useful when browser and curl disagree (DNS Debugging: Who Answered, and With What?).
$ curl -sv https://api.example.com/health -o /dev/null \
-w 'dns=%{time_namelookup} tcp=%{time_connect} tls=%{time_appconnect} ttfb=%{time_starttransfer} total=%{time_total}\n'
* Host api.example.com:443 was resolved.
* IPv4: 203.0.113.10
* Trying 203.0.113.10:443...
* Connected to api.example.com (203.0.113.10) port 443
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
* Server certificate:
* subject: CN=api.example.com
* expire date: Nov 12 23:59:59 2026 GMT
* subjectAltName: host "api.example.com" matched cert's "api.example.com"
* SSL certificate verify ok.
> GET /health HTTP/1.1
> Host: api.example.com
< HTTP/1.1 200 OK
< server: nginx
< x-request-id: 5f1c9e2a
dns=0.012 tcp=0.031 tls=0.052 ttfb=0.078 total=0.079Seeing sockets: ss
ss (the modern replacement for netstat on Linux) reads socket state straight from the kernel. ss -tlnp — TCP, listening, numeric, with process — is the first command to run on any server that "refuses connections": it shows whether anything is listening, on which address and port, and which PID owns it. The address column is the most frequent surprise: 127.0.0.1:8080 is loopback-only, 0.0.0.0:8080 is every IPv4 interface, [::]:8080 is every interface for both families on a dual-stack host.
For listening sockets Recv-Q is the number of connections that completed the handshake and are waiting in the accept queue, and Send-Q is the backlog limit. A Recv-Q sitting at the limit means the application is not calling accept() fast enough — the server is the bottleneck, not the network. ss -tan lists every connection with its state; ss -tan state time-wait | wc -l and ss -s summarise. -i adds per-connection TCP internals: cwnd, rtt, retransmits.
- macOS:
netstat -an -p tcpandlsof -iTCP -sTCP:LISTEN -n -Pfor listeners with process names. - Windows:
netstat -ano(PID in the last column) orGet-NetTCPConnection -State Listen.
$ ss -tlnp
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 511 127.0.0.1:8080 0.0.0.0:* users:(("node",pid=4127,fd=19))
LISTEN 0 4096 0.0.0.0:22 0.0.0.0:* users:(("sshd",pid=812,fd=3))
LISTEN 0 4096 [::]:22 [::]:* users:(("sshd",pid=812,fd=4))
$ ss -tani state established '( dport = :443 )' | head -3
Recv-Q Send-Q Local Address:Port Peer Address:Port
0 0 10.0.2.15:51742 203.0.113.10:443
cubic wscale:7,7 rto:204 rtt:3.6/1.2 cwnd:10 bytes_sent:1247 bytes_received:5310 retrans:0/0Seeing packets: tcpdump, Wireshark, nc, openssl
tcpdump captures at the point where the kernel hands packets to and from the NIC, so it sees exactly what left and arrived on this host — the ground truth that resolves "the client says it sent it" versus "the server says it never came". -n avoids reverse DNS lookups that make it hang; -i any captures all interfaces; a BPF filter such as port 443 and host 203.0.113.10 keeps the noise out; -w out.pcap saves for Wireshark. Flags in the output are S (SYN), S. (SYN-ACK), . (ACK), P. (PSH-ACK, data), F. (FIN), R (RST). Three lines — S, S., . — are a healthy handshake.
Wireshark is the same capture with analysis on top: it flags retransmissions, out-of-order segments, zero windows and RSTs, reassembles TCP streams, and decodes TLS alerts and HTTP/2 frames. Display filters worth memorising: tcp.flags.reset==1, tcp.analysis.retransmission, tcp.analysis.zero_window, tls.alert_message, dns.flags.rcode != 0. Follow → TCP Stream turns a thousand packets into one readable conversation.
nc -vz host port is the fastest port check in existence: it reports "succeeded" (SYN-ACK came back), "Connection refused" (RST), or hangs until timeout (dropped). nc -l 9000 on a server plus nc host 9000 on a client tests a path with no application in the way — if bytes typed on one end appear on the other, the network is fine and the argument moves to the application. openssl s_client -connect host:443 -servername host performs a TLS handshake and prints the certificate chain, the verification result and the negotiated protocol; TLS Debugging: Why the Certificate Is "Invalid" reads its output line by line.
$ sudo tcpdump -ni any 'host 203.0.113.10 and (port 443 or port 8080)' 12:01:03.114 IP 10.0.2.15.51742 > 203.0.113.10.443: Flags [S], seq 3948210, win 64240, options [mss 1460,sackOK,wscale 7], length 0 12:01:03.118 IP 203.0.113.10.443 > 10.0.2.15.51742: Flags [S.], seq 1183702, ack 3948211, win 65160, options [mss 1460,sackOK,wscale 7], length 0 12:01:03.118 IP 10.0.2.15.51742 > 203.0.113.10.443: Flags [.], ack 1, win 502, length 0 12:01:07.201 IP 10.0.2.15.51750 > 203.0.113.10.8080: Flags [S], seq 88120441, win 64240, length 0 12:01:07.205 IP 203.0.113.10.8080 > 10.0.2.15.51750: Flags [R.], seq 0, ack 88120442, win 0, length 0
Windows equivalents
The questions are identical; the names differ. tracert is traceroute but probes with ICMP echo rather than UDP. Test-NetConnection -ComputerName host -Port 443 is nc -vz (and adds a ping and a route lookup; TcpTestSucceeded : True is the line that matters). Resolve-DnsName name -Server 8.8.8.8 is dig @8.8.8.8; ipconfig /displaydns and /flushdns inspect and clear the client cache. netstat -ano and Get-NetTCPConnection replace ss. pathping is a slow but useful mtr. Packet capture is pktmon or Wireshark with Npcap; curl.exe ships with Windows 10+ and behaves like the Unix one (the PowerShell alias curl is Invoke-WebRequest and does not).
tracert host·Test-NetConnection host -Port 443·Resolve-DnsName host·netstat -ano·Get-NetTCPConnection -State Listen.- PowerShell’s
curlis an alias forInvoke-WebRequest; usecurl.exefor the real thing.
Key points
- Choose the tool by the layer you want to test; each one is blind outside its layer.
curl -vwalks the whole ladder and reports the first failing rung;--resolvebypasses DNS,-wgives per-layer timings.ss -tlnpanswers "is anything listening, and on which address?" — the first command on a refusing server.tcpdumpis ground truth for what this host sent and received; capture on both ends to bisect a path.nc -vzdistinguishes refused from timed out in one second;openssl s_client -servernameshows the served chain.- Windows:
tracert,Test-NetConnection,Resolve-DnsName,netstat -ano; the same questions with different names.
Why does this exist?
Mechanisms are answers to constraints. Open each question before reading the answer.
▸Why not one tool that does everything?
Because each layer speaks its own protocol and a tool must speak it to test it. curl comes closest by driving a real request through every layer, but it cannot see why a packet was dropped — only a capture at the drop point can.
▸Why does tcpdump settle arguments?
Application logs describe intent ("sent request"). A capture describes reality at the NIC boundary: this packet with these flags left at this microsecond, and that one arrived. Two captures give you the segment where reality diverged.
▸Why learn what a tool cannot tell you?
Because the expensive mistakes are confident conclusions drawn from silence: "ping fails, host is down", "nc connects, service works". Knowing the blind spot is what stops the misdiagnosis.
Tools × layers
| tool | |||||||
|---|---|---|---|---|---|---|---|
$ curl -sv https://api.example.com/health -o /dev/null * Trying 203.0.113.10:443... * Connected to api.example.com (203.0.113.10) port 443 * SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 * ALPN: server accepted h2 > GET /health HTTP/2 < HTTP/2 200
macOS: ships. Windows 10+: curl.exe ships; Invoke-WebRequest is the PowerShell cousin.
How it fails
What the failure looks like from inside real software.
- Running
tcpdumpwithout-non a host with broken reverse DNS and waiting minutes for output that seems to hang. - Reading
ss -tlnpoutput that shows127.0.0.1and concluding the service is up because "it is listening". - Testing with PowerShell
curl(Invoke-WebRequest) and getting behaviour that does not match the Unixcurlin the runbook. - Using
pingas the health check and paging on ICMP filtering while HTTPS works perfectly. - Capturing on the wrong interface — the container’s
eth0versus the host bridge — and concluding no packets arrived.