---
title: "Linux Networking"
description: "The commands you reach for to diagnose and manage Linux networks. ip for interfaces and routes, ss for sockets, dig for DNS, traceroute for paths, and tcpdump for packets."
author: "Mohammad Abu Mattar"
canonical: https://mkabumattar.com/cheatsheets/linux-networking
---

# Linux Networking

Every Linux box speaks the network through a small set of tools, and knowing them turns "the network is broken" into a specific, fixable answer. This cheatsheet covers the modern stack: `ip` for interfaces and routes, `ss` for sockets, `dig` for DNS, `traceroute` for the path, `tcpdump` for the raw packets, and `curl` for the application layer.

When something won't connect, work up the layers. Confirm the interface has an address and a route (`ip`), check the port is listening (`ss`), make sure the name resolves (`dig`), trace where packets die (`traceroute`), and only then capture the wire (`tcpdump`) or test the app (`curl`).

## Key Features

- **One tool per layer**: `ip` for L2/L3, `ss` for sockets, `dig` for DNS, `tcpdump` for packets, `curl` for HTTP.
- **Modern replacements**: `ip` supersedes `ifconfig`/`route`, and `ss` supersedes `netstat`.
- **Ground truth on demand**: `tcpdump` shows the actual bytes when higher-level tools disagree.
- **Scriptable diagnostics**: `dig +short` and `curl -w` give clean, parseable output for automation.

## Interfaces and Routing

The modern ip command replaces ifconfig and route. It's one tool for addresses, links, and the routing table.

### Addresses and Links

Show and change what IPs live on which interface, and bring links up or down.

**Keywords:** ip addr, ip link, interface, ifconfig

#### Show addresses and interface state

```bash
# Every interface, its addresses, and whether it's UP
ip addr           # 'ip a' for short

# Just one interface
ip addr show eth0

# Only the link layer (MAC, MTU, state), no IPs
ip link show
```

Look for UP in the flags and a scope global inet line. A link that's DOWN or missing an address is usually the whole problem.

- ifconfig comes from the old net-tools package and may not be installed. ip is the modern default everywhere.

#### Add, remove, and toggle an address

```bash
# Add a second IP to eth0 (needs root)
sudo ip addr add 192.168.1.50/24 dev eth0

# Remove it again
sudo ip addr del 192.168.1.50/24 dev eth0

# Bounce the interface without a reboot
sudo ip link set eth0 down
sudo ip link set eth0 up
```

Changes made with ip are live but not persistent. They vanish on reboot unless you write them into your distro's network config.

- Persist addresses via netplan, NetworkManager, or systemd-networkd depending on the distro. ip alone is for the running system.

**Best practices:**

- Add 'ip -br addr' (brief) when you just want a one-line-per-interface summary. It's far easier to scan.
- Reach for 'ip -c' to colorize output when you're eyeballing many interfaces.

**Common errors:**

- **RTNETLINK answers: Operation not permitted**: You're not root. Prefix the command with sudo. Reading state is fine as a user, but changing it needs privileges.

### Routing Table

Which gateway handles which destination. Most connectivity bugs show up here as a missing or wrong default route.

**Keywords:** ip route, default gateway, routing table

#### Inspect and test routes

```bash
# The full routing table
ip route          # 'ip r' for short

# Which route would the kernel actually use for this IP?
ip route get 1.1.1.1
```

'ip route get' is the honest answer. It shows the exact route, source IP, and interface the kernel picks, so you don't have to guess from the table.

#### Add and delete routes

```bash
# Set the default gateway
sudo ip route add default via 192.168.1.1

# Route one subnet through a specific gateway
sudo ip route add 10.0.0.0/24 via 192.168.1.254 dev eth0

# Remove a route
sudo ip route del 10.0.0.0/24
```

A host with addresses but no default route can talk to its local subnet and nothing else. That's the classic 'DNS works, internet doesn't' setup.

**Common errors:**

- **Network is unreachable**: There's no route to that destination. Check 'ip route' for a default gateway, and confirm the gateway is on a subnet you actually have an address in.

## Sockets and Ports

ss is the modern replacement for netstat. It answers 'what's listening' and 'who's connected' fast, even on busy hosts.

### What's Listening

Find which process owns a port before you fight over it.

**Keywords:** ss, netstat, listening ports, LISTEN

#### List listening TCP and UDP ports with the owning process

```bash
# t=TCP, u=UDP, l=listening, n=numeric, p=process (p needs root for other users)
sudo ss -tulpn

# Only TCP listeners
ss -tln
```

_exec_
```bash
sudo ss -tulpn
```

_output_
```text
Netid State  Local Address:Port  Peer Address:Port Process
tcp   LISTEN 0.0.0.0:22          0.0.0.0:*         users:(("sshd",pid=812,fd=3))
tcp   LISTEN 127.0.0.1:5432       0.0.0.0:*         users:(("postgres",pid=1140,fd=5))
```

The -p flag ties each port to a PID and program name, which is how you find the process squatting on a port you need.

- 0.0.0.0 means listening on all interfaces. 127.0.0.1 means localhost only, so it's unreachable from other machines by design.

#### Filter by state and port

```bash
# Only established connections
ss -t state established

# Everything talking to or from port 443
ss -t '( dport = :443 or sport = :443 )'

# Count connections per state (quick health check)
ss -s
```

ss has a real filter language, so you can slice by state, port, or address instead of piping netstat through grep.

**Best practices:**

- Prefer ss over netstat. netstat ships in the deprecated net-tools package and is slower on hosts with many connections.
- When you only need the PID on a port, 'sudo ss -tlpn sport = :8080' beats scrolling the full list.

**Common errors:**

- **A port shows no process even with -p**: You need root to see processes owned by other users. Re-run with sudo, otherwise ss hides the owning PID.

## DNS Lookups

When a name won't resolve, dig is the tool that tells you exactly what the DNS servers are (or aren't) returning.

### Querying with dig

dig is scriptable and precise. It shows the actual answer, the authority, and the server that replied.

**Keywords:** dig, DNS, A record, MX, nameserver

#### Basic and short lookups

```bash
# Full answer for the A record
dig example.com

# Just the resolved IPs, nothing else
dig +short example.com

# Ask a specific record type
dig example.com MX
dig example.com TXT
```

+short strips everything down to the answer. It's the form you want inside scripts and quick checks.

#### Query a specific server and trace the delegation

```bash
# Ask a particular resolver instead of the system default
dig @1.1.1.1 example.com

# Walk the delegation from the root servers down
dig +trace example.com

# Reverse lookup: IP back to a name
dig -x 93.184.216.34
```

@server bypasses your local resolver, so you can tell whether a stale local cache or the real DNS is the problem. +trace shows where a broken delegation breaks.

- If 'dig @8.8.8.8' works but a bare 'dig' doesn't, your system resolver or /etc/resolv.conf is the culprit, not DNS itself.

**Best practices:**

- Compare 'dig +short name' against 'dig @1.1.1.1 +short name'. If they differ, you're looking at a caching or split-horizon issue.
- Install dig via the bind-utils (RHEL) or dnsutils (Debian) package if it's missing on a minimal image.

### nslookup vs dig

nslookup is everywhere and fine for a quick check, but dig is the tool you want when the answer matters.

**Keywords:** nslookup, getent, resolver

#### Quick checks with nslookup and getent

```bash
# Simple forward lookup
nslookup example.com

# getent resolves the way the OS actually does it,
# honoring /etc/hosts and nsswitch.conf (dig ignores both)
getent hosts example.com
```

Use getent when you want the name to resolve exactly how an application will resolve it, including /etc/hosts overrides that dig never reads.

- dig talks straight to DNS and skips /etc/hosts. If a host entry is overriding a name, only getent (or the app) will show it.

## Path and Reachability

Is the host up, and where does traffic die on the way there? ping proves reachability, traceroute finds the failing hop.

### ping and Reachability

The first thing you run, with the caveat that plenty of networks drop ICMP on purpose.

**Keywords:** ping, ICMP, reachability

#### Ping with a count and timeout

```bash
# Send 4 packets and stop, instead of pinging forever
ping -c 4 example.com

# Give up on each packet after 2 seconds
ping -c 4 -W 2 example.com
```

Always use -c in scripts so ping actually exits. A bare ping runs until you Ctrl-C it.

- No reply doesn't always mean down. Cloud security groups and firewalls routinely block ICMP while TCP still works fine.

**Best practices:**

- When ping fails but you need to prove reachability, test the actual port with 'nc -vz host 443' or curl instead. ICMP being blocked says nothing about your app.

**Common errors:**

- **ping: example.com: Name or service not known**: This is DNS, not connectivity. The name didn't resolve. Debug it with dig before blaming the network.

### traceroute and tracepath

Map the hops between you and a destination to see where latency spikes or packets stop.

**Keywords:** traceroute, tracepath, mtr, hops

#### Trace the path, TCP when ICMP is blocked

```bash
# Classic hop-by-hop trace
traceroute example.com

# Use TCP SYN to port 443 (gets through firewalls that drop UDP/ICMP)
sudo traceroute -T -p 443 example.com

# tracepath needs no root and also discovers the path MTU
tracepath example.com
```

Rows of '* * *' mean a hop isn't answering, which is often just a router ignoring probes rather than a real break. Switch to -T to get past filters.

- mtr combines ping and traceroute into a live, continuously updating view. It's the better tool when you suspect intermittent loss on one hop.

## Packet Capture

When you need to see the actual bytes on the wire, tcpdump is the ground truth. Nothing lies at this layer.

### Capturing with tcpdump

Capture selectively. An unfiltered capture on a busy host is a firehose you can't read.

**Keywords:** tcpdump, packet capture, pcap, BPF filter

#### Capture by interface, host, and port

```bash
# Watch traffic on eth0, don't resolve names/ports (-nn keeps it fast)
sudo tcpdump -i eth0 -nn

# Only packets to or from one host on port 443
sudo tcpdump -i eth0 -nn host 10.0.0.5 and port 443

# Only inbound HTTP to this box
sudo tcpdump -i eth0 -nn 'tcp dst port 80'
```

-nn turns off DNS and port-name lookups, so tcpdump stays fast and doesn't generate its own traffic while you watch.

- Use '-i any' to capture on all interfaces at once when you're not sure which one the traffic uses.

#### Write to a file for Wireshark

```bash
# Save raw packets to a pcap (-w), grab 200 then stop (-c)
sudo tcpdump -i eth0 -w capture.pcap -c 200 port 443

# Read a saved capture back, with verbose decoding
tcpdump -r capture.pcap -nn -v
```

_exec_
```bash
sudo tcpdump -i eth0 -w capture.pcap -c 200 port 443
```

Capture on the server with -w, then open capture.pcap in Wireshark on your laptop. That's the standard workflow for anything past a quick glance.

- Add '-s 0' on very old tcpdump versions to capture full packets. Modern versions already default to the whole packet.

**Best practices:**

- Always narrow with a filter (host, port, or protocol). An unfiltered capture on a production host fills the terminal and can drop packets.
- Bound the capture with -c (packet count) or run it briefly. It's easy to fill a disk with an open-ended -w capture.

**Common errors:**

- **tcpdump: eth0: You don't have permission to capture on that device**: Packet capture needs root (or the CAP_NET_RAW capability). Run tcpdump with sudo.

## HTTP Testing

curl and wget test the network at the application layer, where a working TCP path still hides TLS or HTTP problems.

### Testing with curl

curl is the swiss-army knife for HTTP. Headers, timing, and TLS details are all one flag away.

**Keywords:** curl, wget, HTTP, headers, TLS

#### Inspect status, headers, and timing

```bash
# Headers only (HEAD request), follow redirects (-L)
curl -sIL https://example.com

# Print just the HTTP status code
curl -s -o /dev/null -w '%{http_code}\n' https://example.com

# Where does the time go? DNS vs connect vs TLS vs transfer
curl -s -o /dev/null -w 'dns=%{time_namelookup} connect=%{time_connect} tls=%{time_appconnect} total=%{time_total}\n' https://example.com
```

The -w timing breakdown is how you tell a slow DNS lookup apart from a slow TLS handshake apart from a slow server. It turns 'the site is slow' into a real answer.

- -s silences the progress bar, '-o /dev/null' throws away the body so you only see what -w prints. That trio is the standard scripting form.

#### Force a resolution or skip cert checks while debugging

```bash
# Test a specific backend by faking DNS just for this request
curl -sI --resolve example.com:443:10.0.0.7 https://example.com

# Ignore an invalid/self-signed cert (debugging ONLY, never in prod)
curl -skI https://localhost:8443
```

--resolve hits one specific server without touching DNS, which is perfect for testing a single backend behind a load balancer.

- Reach for wget instead when you just want to download a file. 'wget -c URL' resumes a partial download where curl needs -C -.

**Best practices:**

- Keep -f (--fail) in scripts so curl returns a non-zero exit code on HTTP 4xx/5xx instead of silently saving an error page.
