> ## Content Index
> Fetch the complete content index at: https://www.pinglabz.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# Top Linux Networking Commands: The 2026 Field Reference
- URL: https://www.pinglabz.com/top-linux-networking-commands/
- Published: 2026-08-19T18:23:41.000Z
- Updated: 2026-08-19T18:38:26.000Z
- Description: Every Linux networking command that matters, grouped by the question it answers, with real captures from a four-hop lab and the triage order to run them in.
- Author: Jaime
- Tags: Linux, Tools, Troubleshooting, Networking, Labs

Every Linux box you inherit has the same twelve or so networking commands on it, and the ones you reach for first decide how long the outage lasts. This is the field reference for those commands: what each one is for, one real line of output from a live lab, and a link to the full article when you need the depth. No invented output, no command that only exists in a blog post.

It is the index page for the [complete guide to Linux networking commands](https://www.pinglabz.com/linux-networking-commands/), which is the cluster this article belongs to. Everything below was captured on a Debian 13 host at 10.77.0.100 bridged into a CML topology through `ens224`. Three IOS XE routers sit between it and the far side: R1 at 10.77.0.1, R2 at 10.77.12.2, R3 at 10.77.23.3, with an nginx server at 10.77.2.10 and a net-tools server at 10.77.3.10 running iperf3, a DNS server and not much else. Four hops of real routing, which is exactly enough to make traceroute interesting.

## How to read this reference

The commands are grouped the way you actually use them, by the question you are trying to answer: what address do I have, where does traffic go, who is listening, what resolves, can I reach that port, what is on the wire, how fast is it, and what is blocked. Within each group the cards give you the one-line purpose and a link to the deep article. The capture block after each group is the group's headline commands run back to back on the same host, so you can see what a real session looks like rather than a list of synopses.

If you are starting from nothing on a Debian or Ubuntu box, this single line installs everything used in this article that is not already present:

```
sudo apt-get install -y iproute2 net-tools ethtool network-manager \
  traceroute mtr-tiny iputils-tracepath dnsutils curl wget netcat-openbsd \
  socat tcpdump tshark ngrep nmap iperf3 iftop nload bmon vnstat nethogs \
  ifstat lsof whois
```

On RHEL, Rocky and Alma the package names differ a little (`bind-utils` rather than `dnsutils`, `nmap-ncat` rather than `netcat-openbsd`) but the tools are identical.

## Addressing and interfaces

This is where every investigation starts, and it is also where the biggest generational split lives. `ip` talks to the kernel over netlink and shows you everything; `ifconfig` reads a legacy interface that cannot represent multiple addresses per interface properly. Both are here because you will meet both.

ip addr / ip link

The one command that replaced six. Addresses, link state, MAC, MTU and per-interface counters, all from one tool. Add `-br` for a readable summary.

[The Linux ip command, in full](https://www.pinglabz.com/linux-ip-command/)

ifconfig / route / netstat

The net-tools generation. Deprecated for a decade, still installed on plenty of production hosts, and still the muscle memory of most senior engineers.

[Mapping net-tools to iproute2](https://www.pinglabz.com/ifconfig-vs-ip/)

ethtool

Below the IP layer: negotiated speed and duplex, link detection, offload settings, driver info and the NIC's own error counters.

[ethtool: speed, duplex, offloads](https://www.pinglabz.com/ethtool-linux/)

nmcli / nmtui

The config that survives a reboot. On any NetworkManager host, `ip` changes are temporary and nmcli changes are the real configuration.

[nmcli and nmtui](https://www.pinglabz.com/nmcli-linux/)

```
j@llmbits:~$ ip -br addr show
lo               UNKNOWN        127.0.0.1/8 ::1/128 
ens192           UP             192.168.88.156/24 fd64:f725:df42:4f01:20c:29ff:feb1:cc3d/64 fe80::20c:29ff:feb1:cc3d/64 
ens224           UP             10.77.0.100/24 

j@llmbits:~$ ip addr show ens224
3: ens224: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether 00:0c:29:b1:cc:47 brd ff:ff:ff:ff:ff:ff
    inet 10.77.0.100/24 scope global ens224
       valid_lft forever preferred_lft forever

j@llmbits:~$ ifconfig ens224
ens224: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 10.77.0.100  netmask 255.255.255.0  broadcast 0.0.0.0
        ether 00:0c:29:b1:cc:47  txqueuelen 1000  (Ethernet)
        RX packets 804447  bytes 75153559 (71.6 MiB)
        TX packets 528439  bytes 2185104277 (2.0 GiB)

j@llmbits:~$ ethtool ens224
Settings for ens224:
	Supported ports: [ TP ]
	Speed: 10000Mb/s
	Duplex: Full
	Auto-negotiation: off
	Link detected: yes

j@llmbits:~$ nmcli device status
DEVICE  TYPE      STATE                   CONNECTION         
ens192  ethernet  connected               Wired connection 1 
ens224  ethernet  connected (externally)  ens224             
```

Read `ens224` in that last block carefully. NetworkManager reports it as *connected (externally)*, which is its way of saying the address on it was configured by something other than NetworkManager and it is leaving it alone. On a host where the state says *connected* with a named profile, anything you set with `ip addr add` is gone at the next reboot or the next `nmcli con up`.

## Routing and neighbors

The routing table answers "where does this packet leave from," and the neighbor table answers "does the next hop actually exist." Nearly every "the network is down" call ends at one of those two tables.

ip route

Show, add, replace and delete routes. `ip route get` asks the kernel which route it would actually pick for one destination, which beats reading the table by eye.

[Managing the Linux routing table](https://www.pinglabz.com/ip-route-linux/)

ip rule

Policy routing. Linux has many routing tables, and rules decide which table a packet consults. This is how source-based routing and VPN split tunnels are built.

[Multiple tables and rules](https://www.pinglabz.com/ip-route-linux/)

ip neigh

The ARP and NDP cache with its state machine visible: REACHABLE, STALE, DELAY, PROBE, FAILED. The state tells you more than the MAC does.

[ARP on Linux and neighbor states](https://www.pinglabz.com/ip-neigh-arp-linux/)

route -n / netstat -rn

The same table in the old format, with Flags and Metric columns. Worth being able to read, because it is what you get on a minimal or ancient host.

[Reading the legacy output](https://www.pinglabz.com/ifconfig-vs-ip/)

```
j@llmbits:~$ ip route
default via 192.168.88.1 dev ens192 proto dhcp src 192.168.88.156 metric 101 
10.77.0.0/24 dev ens224 proto kernel scope link src 10.77.0.100 
10.77.0.0/16 via 10.77.0.1 dev ens224 
192.168.88.0/24 dev ens192 proto kernel scope link src 192.168.88.156 metric 101 

j@llmbits:~$ ip rule list
0:	from all lookup local
32766:	from all lookup main
32767:	from all lookup default

j@llmbits:~$ ip neigh show dev ens224
10.77.0.1 lladdr aa:bb:cc:00:04:00 STALE 

j@llmbits:~$ route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         192.168.88.1    0.0.0.0         UG    101    0        0 ens192
10.77.0.0       0.0.0.0         255.255.255.0   U     0      0        0 ens224
10.77.0.0       10.77.0.1       255.255.0.0     UG    0      0        0 ens224
192.168.88.0    0.0.0.0         255.255.255.0   U     101    0        0 ens192
```

Those two 10.77 entries are a longest-prefix-match lesson in four lines. The /24 is connected and covers the local segment; the /16 points at R1 and covers everything behind it. A packet to 10.77.3.10 matches only the /16 and goes to the router. A packet to 10.77.0.50 matches both and takes the /24, because the more specific prefix wins regardless of the order the lines appear in. This is the same rule your routers run, expressed in a different syntax.

And the neighbor entry says STALE, which is normal and not a fault. Linux keeps a cached entry after the reachability timer expires and revalidates it on next use rather than deleting it, so STALE means "we have an answer and we will confirm it when we need it." FAILED is the state that matters.

## Testing the path

Four tools, four different questions. `ping` asks whether the far end answers. `traceroute` asks which routers are in between. `mtr` asks which of them is losing packets, over time. `tracepath` asks how big a packet can be before somebody drops it.

ping

Reachability and round trip time. The TTL in the reply counts the hops back, and `-M do -s` turns it into an MTU test.

[Linux ping options worth knowing](https://www.pinglabz.com/linux-ping-options/)

traceroute

Hop by hop path discovery. Defaults to UDP, does ICMP with `-I` and TCP with `-T`, and the mode you pick changes which firewalls let you through.

[UDP, ICMP and TCP modes](https://www.pinglabz.com/traceroute-linux/)

mtr

traceroute and ping in one continuous view. The right tool for intermittent loss, and the one whose output people misread most often.

[Continuous path monitoring](https://www.pinglabz.com/mtr-linux/)

tracepath

Path MTU discovery without root. Reports the MTU at each step and tells you where it drops, which is the answer to most VPN and tunnel complaints.

[tracepath and PMTU](https://www.pinglabz.com/tracepath-pmtu/)

```
j@llmbits:~$ ping -c 4 10.77.3.10
PING 10.77.3.10 (10.77.3.10) 56(84) bytes of data.
64 bytes from 10.77.3.10: icmp_seq=1 ttl=61 time=4.98 ms
64 bytes from 10.77.3.10: icmp_seq=2 ttl=61 time=8.90 ms
64 bytes from 10.77.3.10: icmp_seq=3 ttl=61 time=5.57 ms
64 bytes from 10.77.3.10: icmp_seq=4 ttl=61 time=5.14 ms

--- 10.77.3.10 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3001ms
rtt min/avg/max/mdev = 4.980/6.145/8.896/1.602 ms

j@llmbits:~$ traceroute 10.77.3.10
traceroute to 10.77.3.10 (10.77.3.10), 30 hops max, 60 byte packets
 1  10.77.0.1 (10.77.0.1)  4.348 ms  4.385 ms  4.701 ms
 2  10.77.12.2 (10.77.12.2)  5.123 ms  5.096 ms  5.149 ms
 3  10.77.23.3 (10.77.23.3)  6.959 ms  6.847 ms  8.177 ms
 4  10.77.3.10 (10.77.3.10)  8.356 ms  8.554 ms  8.509 ms

j@llmbits:~$ mtr -rwc 5 10.77.3.10
Start: 2026-08-19T11:04:55-0700
HOST: llmbits    Loss%   Snt   Last   Avg  Best  Wrst StDev
  1.|-- 10.77.0.1   0.0%     5    3.1   3.1   3.1   3.3   0.1
  2.|-- 10.77.12.2  0.0%     5    3.8   4.1   3.8   4.3   0.2
  3.|-- 10.77.23.3  0.0%     5    5.2   5.1   4.8   5.5   0.3
  4.|-- 10.77.3.10  0.0%     5    5.3   5.2   5.1   5.3   0.1

j@llmbits:~$ tracepath 10.77.3.10
 1?: [LOCALHOST]                      pmtu 1500
 1:  10.77.0.1                                             3.454ms 
 2:  10.77.12.2                                            4.903ms 
 3:  10.77.23.3                                            5.439ms 
 4:  10.77.3.10                                            5.823ms reached
     Resume: pmtu 1500 hops 4 back 4 
```

The `ttl=61` in the ping replies is the quiet win here. The far end sent them at 64 and three routers decremented it, so the reply took three hops home, which matches the four lines traceroute printed (the fourth line is the destination itself, not a router). When a ping works but the TTL is not what you expect, traffic is not taking the path you think it is.

One caveat on `traceroute -T`: TCP mode needs raw sockets, so it needs root. Without it you get `You do not have enough privileges to use this traceroute method` and nothing else. TCP mode is the one that gets through firewalls which drop the default UDP high ports, so it is worth the sudo.

## Sockets, listeners and who owns them

"Is the service up" is not a network question until you have checked whether anything is bound to the port, and on which address. Half the tickets that arrive as connectivity problems are a daemon listening on 127.0.0.1.

ss -tulpn

The one socket command to memorize: TCP, UDP, listening, with process names and numeric ports. Run it with sudo or the process column is empty.

[ss: the modern socket tool](https://www.pinglabz.com/ss-command-linux/)

ss -ti / ss state

Filter by TCP state and read per-socket internals: congestion window, retransmits, RTT estimate. This is where you prove a slow app is a slow network.

[States, filters and socket internals](https://www.pinglabz.com/ss-command-linux/)

netstat -tulpn

Same flags, older tool, reads /proc line by line so it crawls on busy hosts. Still the command every runbook was written with.

[netstat against ss, flag by flag](https://www.pinglabz.com/netstat-vs-ss/)

lsof -i / fuser

Sockets as files. `lsof -i :22` answers "what is holding this port" including the user, which is the question you have when a bind fails.

[lsof and fuser for ports](https://www.pinglabz.com/netstat-vs-ss/)

```
j@llmbits:~$ sudo ss -tulpn
Netid State  Recv-Q Send-Q Local Address:Port  Peer Address:Port Process
udp   UNCONN 0      0            0.0.0.0:5353       0.0.0.0:*    users:(("avahi-daemon",pid=12165,fd=12))
tcp   LISTEN 0      128          0.0.0.0:22         0.0.0.0:*    users:(("sshd",pid=1272,fd=6))
tcp   LISTEN 0      4096       127.0.0.1:631        0.0.0.0:*    users:(("cupsd",pid=10805,fd=7))
tcp   LISTEN 0      128             [::]:22            [::]:*    users:(("sshd",pid=1272,fd=7))

j@llmbits:~$ ss -s
Total: 253
TCP:   8 (estab 1, closed 3, orphaned 0, timewait 3)

Transport Total     IP        IPv6
RAW	  1         0         1        
UDP	  5         3         2        
TCP	  5         3         2        
INET	  11        6         5        

j@llmbits:~$ sudo lsof -i :22
COMMAND     PID USER FD   TYPE DEVICE SIZE/OFF NODE NAME
sshd       1272 root 6u  IPv4   7039      0t0  TCP *:ssh (LISTEN)
sshd-sess 20562 root 7u  IPv4 562942      0t0  TCP llmbits:ssh->192.168.88.125:61016 (ESTABLISHED)
```

Compare lines two and three of the `ss` output. SSH is bound to 0.0.0.0, so it answers on every address the host owns. CUPS is bound to 127.0.0.1, so it answers on exactly one, and nothing arriving on the wire will reach it unless you deliberately relay or DNAT it there. That distinction, visible in one column, resolves an enormous share of "the port is not open" tickets before anyone opens a packet capture.

## DNS from the client side

Three tools that do the same lookup and one file that decides where the lookup goes. The reason to know all four is that they disagree in useful ways: `dig` shows you the protocol, `host` and `nslookup` show you the answer, and `/etc/resolv.conf` shows you why the answer might be wrong.

dig

The DNS tool for engineers. Query a specific server with `@`, see flags, TTLs, section counts and query time. `+short` when you only want the value.

[The complete dig guide](https://www.pinglabz.com/dig-command/)

nslookup

Present on every platform including Windows, which is exactly why it still matters. Interactive mode is genuinely useful for a run of related queries.

[nslookup, still worth knowing](https://www.pinglabz.com/nslookup-linux/)

host

The fastest way to turn a name into an address and back. One line in, one line out, ideal inside scripts and loops.

[host: the fastest lookup](https://www.pinglabz.com/host-command-linux/)

resolv.conf / resolvectl

Where the resolver actually looks, and in what order `nsswitch.conf` consults hosts and DNS. Query tools bypass this; your applications do not.

[How Linux resolves names](https://www.pinglabz.com/linux-dns-resolution/)

```
j@llmbits:~$ dig @10.77.3.10 web1.pinglabz.lab +noall +answer +stats
web1.pinglabz.lab.	3600	IN	A	10.77.2.10
;; Query time: 8 msec
;; SERVER: 10.77.3.10#53(10.77.3.10) (UDP)
;; WHEN: Wed Aug 19 11:05:26 PDT 2026
;; MSG SIZE  rcvd: 79

j@llmbits:~$ host web1.pinglabz.lab 10.77.3.10
Using domain server:
Name: 10.77.3.10
Address: 10.77.3.10#53

web1.pinglabz.lab has address 10.77.2.10
web1.pinglabz.lab has IPv6 address 2001:db8:77:2::10

j@llmbits:~$ cat /etc/resolv.conf
# Generated by NetworkManager
nameserver 45.90.28.181
nameserver 45.90.30.181
```

That last block is the trap in miniature. The `dig` query above resolved `web1.pinglabz.lab` perfectly, because `@10.77.3.10` sent it straight to the lab DNS server. But `/etc/resolv.conf` on this host points at a public resolver, so an application on the same box asking for the same name gets NXDOMAIN. Explicit server arguments are the reason "it resolves fine when I test it" and "the app cannot resolve it" are both true at once. When they disagree, test without the `@`.

For the offensive side of DNS (zone transfers, brute forcing, enumeration tooling) the [DNS enumeration cluster](https://www.pinglabz.com/dns-enumeration/) covers dnsrecon, dnsenum, massdns and the rest.

## Testing ports and moving data

Ping proves the host is alive. None of these commands care whether it is alive; they care whether a specific TCP port on it will complete a handshake and speak the protocol you expect. That is almost always the real question.

curl

Far more than a downloader. `-I` for headers, `-w` for a per-phase timing breakdown, `--resolve` to test a vhost before DNS moves.

[curl for network engineers](https://www.pinglabz.com/curl-network-engineers/)

wget

Recursive downloads, resumable transfers and `--spider` for a check that touches nothing. Better than curl when you want a file tree, not a request.

[wget: downloads and checks](https://www.pinglabz.com/wget-linux/)

nc (netcat)

The port tester of choice: `nc -zv host port`. Also a listener, a file transfer and a way to hand-type a protocol when nothing else is installed.

[netcat: checks and listeners](https://www.pinglabz.com/netcat-nc-linux/)

socat

netcat with both ends configurable. Port forwards, TLS wrappers, serial to TCP bridges, and it forks so it survives more than one client.

[socat relays and forwards](https://www.pinglabz.com/socat-linux/)

ssh -L / -R / -D, scp

Tunnels turn one reachable host into a path to everything behind it. `-D` plus a SOCKS-aware client is a whole network in one command.

[SSH beyond login](https://www.pinglabz.com/ssh-tunneling-scp/)

nmap

When you need many ports or many hosts at once, and the open, closed and filtered distinction that a single nc cannot give you.

[The full Nmap cluster](https://www.pinglabz.com/nmap/)

```
j@llmbits:~$ curl -I http://10.77.2.10/
HTTP/1.1 200 OK
Server: nginx/1.29.8
Content-Type: text/html
Content-Length: 896
Connection: keep-alive

j@llmbits:~$ curl -o /dev/null -sS -w 'dns=%{time_namelookup} connect=%{time_connect} ttfb=%{time_starttransfer} total=%{time_total} code=%{http_code}\n' http://10.77.2.10/
dns=0.000052 connect=0.004158 ttfb=0.009251 total=0.009722 code=200

j@llmbits:~$ nc -zv -w 3 10.77.3.10 5201
Connection to 10.77.3.10 5201 port [tcp/*] succeeded!

j@llmbits:~$ nc -zv -w 3 10.77.3.10 5202
nc: connect to 10.77.3.10 port 5202 (tcp) failed: Connection refused

j@llmbits:~$ socat TCP-LISTEN:8080,reuseaddr,fork TCP:10.77.2.10:80 &
j@llmbits:~$ curl -sS -I http://127.0.0.1:8080/
HTTP/1.1 200 OK
Server: nginx/1.29.8

j@llmbits:~$ nmap -Pn -p 53,80,5201 10.77.3.10
Nmap scan report for 10.77.3.10
Host is up (0.0059s latency).

PORT     STATE  SERVICE
53/tcp   open   domain
80/tcp   closed http
5201/tcp open   targus-getdata1
```

Two failures in that block and they mean different things. `Connection refused` from netcat and `closed` from nmap are the same event: the packet arrived, a host answered, and nothing was listening. That is a good failure, because it proves routing and firewalling worked all the way to the target. The bad failure is a timeout with no reply at all, which nmap calls `filtered`: something in the path swallowed the packet and nobody will tell you what.

The curl timing line is the other habit worth building. `dns`, `connect` and `ttfb` split one slow request into three measurable parts, and the gap between them tells you whether to blame the resolver, the network or the application. Here connect took 4 ms and the first byte arrived 5 ms later, which is a healthy server two routers away.

## Watching the wire

Everything above asks a host what it thinks. These three ask the wire what actually happened, and the wire does not have opinions.

tcpdump

Installed everywhere, small, and fluent in BPF. The right tool for capturing on a box you do not control and reading the file somewhere else.

[tcpdump: a practical reference](https://www.pinglabz.com/tcpdump-for-network-engineers/)

tshark

Wireshark's dissectors without the GUI. Display filters, field extraction with `-T fields` and protocol statistics from the terminal.

[tshark in the terminal](https://www.pinglabz.com/tshark-linux/)

ngrep

Pattern matching on payloads. When the question is "which request contained this string," it beats both of the above for speed of typing.

[ngrep: grep for traffic](https://www.pinglabz.com/ngrep-linux/)

/proc/net, sysctl

Where most of the interface and protocol counters above come from, and the knobs that change kernel behavior. Read the source of the numbers, not just the numbers.

[/proc/net and networking sysctls](https://www.pinglabz.com/proc-net-linux/)

```
j@llmbits:~$ sudo tcpdump -i ens224 -n -c 6 icmp
11:06:00.619681 IP 10.77.0.100 > 10.77.3.10: ICMP echo request, id 52, seq 1, length 64
11:06:00.625837 IP 10.77.3.10 > 10.77.0.100: ICMP echo reply, id 52, seq 1, length 64
11:06:01.621201 IP 10.77.0.100 > 10.77.3.10: ICMP echo request, id 52, seq 2, length 64
11:06:01.627580 IP 10.77.3.10 > 10.77.0.100: ICMP echo reply, id 52, seq 2, length 64
6 packets captured
6 packets received by filter
0 packets dropped by kernel

j@llmbits:~$ sudo tshark -i ens224 -n -c 6 -f 'tcp port 80'
    1 0.000000000  10.77.0.100 -> 10.77.2.10   TCP 74 43666 > 80 [SYN] Seq=0 Win=64240 Len=0 MSS=1460
    2 0.004204077   10.77.2.10 -> 10.77.0.100  TCP 74 80 > 43666 [SYN, ACK] Seq=0 Ack=1 Win=65160 Len=0
    3 0.004299575  10.77.0.100 -> 10.77.2.10   TCP 66 43666 > 80 [ACK] Seq=1 Ack=1 Win=64256 Len=0
    4 0.004511318  10.77.0.100 -> 10.77.2.10   HTTP 140 GET / HTTP/1.1 
    5 0.009044709   10.77.2.10 -> 10.77.0.100  TCP 66 80 > 43666 [ACK] Seq=1 Ack=75 Win=65536 Len=0
    6 0.009873965   10.77.2.10 -> 10.77.0.100  HTTP 304 HTTP/1.1 200 OK 

j@llmbits:~$ sudo ngrep -d ens224 -q -W byline 'GET' 'tcp port 80'
T 10.77.0.100:36384 -> 10.77.2.10:80 [AP] #4
GET / HTTP/1.1.
Host: 10.77.2.10.
User-Agent: curl/8.14.1.
Accept: */*.
```

(The ngrep block is from a separate run against the same server, which is why its source port differs from the tshark flow.) The tshark capture is a complete HTTP transaction in six frames and it is worth being able to read at a glance: SYN, SYN-ACK, ACK, request, ack of the request, response. When a connection is failing, the frame that is missing names the problem. No SYN-ACK means nothing is listening or something dropped it. SYN-ACK then RST means a firewall killed the established flow. Request sent and nothing back means the application is thinking, and the network is done being the suspect.

One warning that costs people hours: NIC offloads mean the frames your capture sees are not always the frames on the cable. With generic receive offload on, the kernel can hand tcpdump a single "packet" of up to 64 KB that never existed as one Ethernet frame. If MSS or fragmentation is what you are investigating, turn the relevant offloads off with [ethtool](https://www.pinglabz.com/ethtool-linux/) first and turn them back on when you are done.

Get the Linux Networking Field Reference - 10 pages, free

Every command in this cluster on ten printable pages: iproute2, sockets, DNS, capture, monitors and all three firewall front ends. Includes a ten-symptom troubleshooting decision tree and annotated real lab output. Free for PingLabz members, just sign up with your email.

[Get the Linux networking cheat sheet](https://www.pinglabz.com/linux-networking-cheatsheet/)

## Throughput and bandwidth

"The network is slow" needs a number before it needs an opinion. These tools produce numbers.

iperf3

The throughput test. Needs a server on the far end, gives you bitrate plus retransmits and congestion window, which is where the real story is.

[The full iPerf cluster](https://www.pinglabz.com/iperf/)

iftop / nethogs

Who is using the link right now. iftop breaks it down by conversation, nethogs by process, and between them you have your answer.

[Live bandwidth monitors compared](https://www.pinglabz.com/linux-bandwidth-monitoring/)

nload / bmon / ifstat

Interface-level rate in three flavors: a graph, a dashboard with per-driver counters, and plain columns you can pipe into something else.

[Which monitor to reach for](https://www.pinglabz.com/linux-bandwidth-monitoring/)

vnstat

The only one that answers "what did this interface do last Tuesday." A daemon logging counters to a database, near zero overhead.

[vnstat for history](https://www.pinglabz.com/linux-bandwidth-monitoring/)

```
j@llmbits:~$ iperf3 -c 10.77.3.10 -t 5
[ ID] Interval           Transfer     Bitrate         Retr
[  5]   0.00-5.00   sec  15.5 MBytes  26.0 Mbits/sec   45            sender
[  5]   0.00-5.01   sec  15.4 MBytes  25.8 Mbits/sec                  receiver

j@llmbits:~$ vnstat -i ens224 --oneline
1;ens224;2026-08-19;68.06 MiB;1.91 GiB;1.98 GiB;426.00 kbit/s;2026-08;4.07 GiB;6.04 GiB;10.11 GiB;362.10 kbit/s

j@llmbits:~$ ifstat -i ens224 1 3
      ens224      
 KB/s in  KB/s out
    0.00      0.00
    0.00      0.00
    0.00      0.00
```

Read the `Retr` column before the bitrate. Forty-five retransmits in five seconds is the interesting number in that test, not 26 Mbits/sec, and it says the path is dropping packets under load. (The absolute figure here is a property of software-forwarded lab routers, not a claim about anything real. Treat lab throughput as a comparison against itself, never as a benchmark.)

## Firewalling and what is blocked

Three layers of the same thing. ufw generates iptables syntax, iptables syntax is translated into nftables rules, and nftables is the engine in the kernel. Which one you use depends on the host; which one you can read determines whether you can debug it.

iptables -L -n -v

Rules with packet and byte counters. The counters are the diagnostic: a rule you suspect with zero hits is a rule that is not being reached.

[iptables fundamentals](https://www.pinglabz.com/iptables-linux/)

nft list ruleset

The whole policy in one readable document. On a modern distro your iptables commands are translated into rules that show up here too. Named sets and atomic loads live here.

[nftables: the modern firewall](https://www.pinglabz.com/nftables-linux/)

ufw status verbose

Policy in sentences. Fastest way to secure a single-homed host, and the default incoming deny will drop your SSH session if you enable it first.

[ufw on Debian and Ubuntu](https://www.pinglabz.com/ufw-linux/)

sysctl net.\*

Forwarding, reverse path filtering, congestion control, buffer sizes. A host that will not route is usually `ip_forward = 0` and nothing more.

[The sysctls that matter](https://www.pinglabz.com/proc-net-linux/)

```
j@llmbits:~$ sudo iptables -L INPUT -n -v
Chain INPUT (policy ACCEPT 909K packets, 80M bytes)
 pkts bytes target     prot opt in     out     source               destination         
    5   420 DROP       icmp --  ens224 *       10.77.3.10           0.0.0.0/0            icmptype 0

j@llmbits:~$ sudo nft list ruleset
table ip filter {
	chain INPUT {
		type filter hook input priority filter; policy accept;
	}
	chain FORWARD {
		type filter hook forward priority filter; policy accept;
	}
}

j@llmbits:~$ sysctl net.ipv4.ip_forward net.ipv4.tcp_congestion_control net.core.rmem_max
net.ipv4.ip_forward = 0
net.ipv4.tcp_congestion_control = cubic
net.core.rmem_max = 212992
```

Two of those blocks come from different sessions on the same host: the iptables rule was captured during the firewall work, and the `nft list ruleset` above it is the clean state afterwards, which is why the nft output carries no rules. That iptables rule drops *echo replies* arriving from 10.77.3.10, not echo requests, and the effect is a ping that reports 100% loss while the far end sees every request and answers every one. Five packets, 420 bytes, counted. When a symptom does not match a rule you are reading, read the direction and the icmptype again before you blame the network.

## The triage order

Commands are easy. Knowing which one to run second is the skill. This is the order that resolves the most incidents in the fewest steps, and every step narrows the problem instead of confirming what you already know.

01

**Do I have an address and a route?**

`ip -br addr` then `ip route get <dest>`. Answers "will this packet leave the box, and out of which interface."

02

**Is the next hop real?**

`ping <gateway>` then `ip neigh`. A FAILED neighbor entry is a layer 2 problem and nothing above it will work.

03

**Where does it stop?**

`traceroute` for the path, `mtr` if it works sometimes. Intermittent is a different bug from broken and needs the continuous view.

04

**Is it a name problem?**

`host <name>` with no server argument, then `dig @<server>`. If those two disagree, the resolver config is the bug.

05

**Is anything listening?**

On the server: `sudo ss -tulpn`. Check the bind address, not just the port. From the client: `nc -zv host port`.

06

**Is something dropping it?**

`sudo iptables -L -n -v` or `sudo nft list ruleset`, and read the counters. Refused is a good failure; silence is the suspicious one.

07

**What actually happened on the wire?**

`sudo tcpdump -i <iface> -n host <peer>` on both ends at once. Whichever side stops seeing packets is the side of the break.

Step seven is the one people skip and should not. Capturing on both ends simultaneously converts an argument into a fact in about thirty seconds, because packets either arrived or they did not.

## The other side of the same link

One habit separates engineers who fix Linux networking quickly from those who guess: checking whether the router agrees with the host. Every fact the Debian box reported above has a matching fact on R1, and they line up.

```
R1#show ip arp | include 10.77.0
Internet  10.77.0.1               -   aabb.cc00.0400  ARPA   Ethernet0/0
Internet  10.77.0.100             2   000c.29b1.cc47  ARPA   Ethernet0/0

R1#show ip route ospf
      10.0.0.0/8 is variably subnetted, 7 subnets, 2 masks
O        10.77.2.0/24 [110/20] via 10.77.12.2, 00:04:59, Ethernet0/1
O        10.77.3.0/24 [110/30] via 10.77.12.2, 00:04:48, Ethernet0/1
O        10.77.23.0/24 [110/20] via 10.77.12.2, 00:04:52, Ethernet0/1

R1#show ip ospf neighbor
Neighbor ID     Pri   State           Dead Time   Address         Interface
2.2.2.2           1   FULL/DR         00:00:35    10.77.12.2      Ethernet0/1
```

The MAC in R1's ARP table (`000c.29b1.cc47`) is the same MAC `ip addr show ens224` printed at the top of this article, in Cisco's dotted format rather than colons. The router learned the host, the host learned the router, and the routes to 10.77.2.0/24 and 10.77.3.0/24 are why the Linux side needed only one static route for the whole 10.77.0.0/16\. When a Linux host cannot reach something, checking the router's ARP table for the host's MAC tells you instantly whether the problem is before or after the first hop.

## FAQ

### Which Linux networking commands should I learn first?

Five, in this order: `ip addr`, `ip route`, `ss -tulpn`, `ping` and `tcpdump`. Those five answer "what am I," "where do I send," "who is listening," "can I reach it" and "what really happened." Everything else in this reference is a sharper version of one of those questions.

### Is ifconfig actually deprecated?

Yes. Debian and Red Hat deprecated net-tools in favor of iproute2 well over a decade ago, and it has been in maintenance-only mode since. It still works for a quick look at one interface, but it cannot correctly display multiple addresses on an interface, it truncates modern interface names, and it knows nothing about network namespaces or policy routing. Learn `ip`, and keep `ifconfig` for reading other people's runbooks. The full mapping is in [net-tools to iproute2](https://www.pinglabz.com/ifconfig-vs-ip/).

### Why do my ip commands disappear after a reboot?

Because `ip` writes to the running kernel, not to configuration. On a NetworkManager host use `nmcli`, on a systemd-networkd host edit the `.network` file, and on a Debian host still using ifupdown edit `/etc/network/interfaces`. `ip` is for testing a change; the config layer is for keeping it.

### Do I need root for all of this?

Less than you would think. Reading addresses, routes, neighbors, sockets and DNS all work as a normal user. Root is needed for packet capture, for TCP traceroute, for firewall commands, and to see the process column in `ss -tulpn`. If you are troubleshooting with sudo everywhere, you are borrowing risk you do not need.

### What is the Linux equivalent of show ip route?

`ip route`, and the closer analog is `ip route get 10.77.3.10`, which asks the kernel for the exact decision it would make rather than making you scan the table. The concepts port directly: connected routes, static routes, longest prefix match and metrics all behave the way they do on IOS. What Linux adds is multiple routing tables selected by `ip rule`, which is closer to VRF and policy based routing than to a plain routing table.

### Can I use these tools on containers and Kubernetes nodes?

Yes, and you should, though the interesting state lives in network namespaces. `nsenter -t <pid> -n ss -tulpn` runs any of these commands inside a container's namespace. Note that `ip netns list` usually comes back empty on a container host, because Docker and containerd do not bind-mount their namespaces into `/var/run/netns`. Use `lsns -t net` to find them instead. A pod that cannot reach a service is nearly always diagnosed with the same seven steps above, just executed one namespace to the left.

## Key takeaways

- Five commands cover most incidents: `ip addr`, `ip route`, `ss -tulpn`, `ping` and `tcpdump`. Learn those properly before collecting more tools.
- `ip` changes the running kernel; NetworkManager, systemd-networkd or ifupdown change the configuration. Only one of them survives a reboot.
- The bind address in `ss -tulpn` matters as much as the port. A service on 127.0.0.1 is unreachable no matter what the firewall says.
- Connection refused and `closed` are good failures, because they prove the packet reached a live host. Timeouts and `filtered` are the ones that hide a dropping device.
- Test names with and without an explicit server. `dig @server` bypasses `/etc/resolv.conf`, so it can succeed on a host where every application fails.
- Read `Retr` in iperf3 and the packet counters in `iptables -L -n -v` before you read anything else in those outputs.
- The TTL in a ping reply counts the return hops, and it is free path information most people never look at.
- NIC offloads mean a capture is not always the wire. Disable the relevant offloads with ethtool when MTU or segmentation is the question.
- Capture on both ends at the same time. The side that stops seeing packets locates the break with no argument required.
- Check the router's ARP table and routing table too. The host and the network telling you different stories is itself the diagnosis.

Every command on this page has a full article behind it, and all of them are indexed in reading order in the [complete guide to Linux networking commands](https://www.pinglabz.com/linux-networking-commands/). If you work across both worlds, the same troubleshooting logic in Cisco syntax lives in the [ping cluster](https://www.pinglabz.com/ping/), the [iPerf cluster](https://www.pinglabz.com/iperf/) and the [Nmap cluster](https://www.pinglabz.com/nmap/), and the scripted version of all of it is in [network automation](https://www.pinglabz.com/network-automation/).