Linux Networking Commands: ip, ss, DNS and Troubleshooting
Learn Linux networking hands-on: ip, ss, ping, dig, curl and tcpdump in a practice network, then firewalls, static IPs and a troubleshooting checklist.
Almost every problem on a server eventually becomes a network question. Is the service listening? On which address? Can this machine reach the database? Does the name resolve? Is a firewall dropping the packets? Linux answers each of these with a small set of tools: ip for interfaces, addresses and routes, ss for sockets, ping, curl and nc for reachability, dig for DNS, tcpdump to see the packets, and nftables (or a front end such as ufw) for the firewall.
In this part you build a small practice network on your own machine, without changing its interfaces, routes or firewall, and use every one of those tools on it.
Prerequisites
- A Linux machine with
sudoand theiproute2tools (ip,ss), installed everywhere by default. - For the lab:
python3,curl,ping,nc,nftandtcpdump(on Ubuntu:sudo apt install curl iputils-ping netcat-openbsd nftables tcpdump). - Processes and ports from Part 7, and a fresh
~/labfrom Part 3: the lab web server serves~/lab/site.
The output comes from a lab on Ubuntu 24.04.5 LTS. The addresses 192.0.2.0/24 and 198.51.100.0/24 are reserved for documentation (RFC 5737), so they never clash with a real network. MAC addresses of virtual interfaces are random and will differ on your machine.
Network Basics in Two Minutes
- An interface connects the machine to a network:
eth0,enp1s0orens3for Ethernet (current systems use predictable names based on the hardware slot),wlp2s0for Wi-Fi,lofor the loopback that only reaches the machine itself, and virtual ones such asdocker0,veth...orwg0. - An IP address with a prefix length, such as
192.0.2.10/24. The/24says that the first 24 bits are the network, so192.0.2.1to192.0.2.254are on the same local network and can be reached directly. - The routing table decides where packets for other networks go. The default route (the gateway) catches everything without a more specific route.
- DNS turns names into addresses. Ports identify the service on a machine: 22 for SSH, 80 and 443 for HTTP and HTTPS, 5432 for PostgreSQL.
Build a Practice Network
Linux network namespaces give a process its own interfaces, addresses, routes and firewall, which is how containers get their network. Two namespaces connected by a virtual cable (a veth pair) make a small two-host network:
Create the first namespace and open a root shell inside it. Everything you run in that shell sees only the namespace's network:
sudo ip netns add client
sudo mkdir -p /etc/netns/client
sudo cp /etc/hosts /etc/netns/client/hosts # a private /etc/hosts for the lab
sudo ip netns exec client bash # a root shell in the empty "client" network
The prompt changes to # because you are root. A namespace only separates the network: the files on disk, including /etc, are shared with your machine. ip netns exec makes one exception and mounts the files in /etc/netns/client/ over their counterparts in /etc, which is why the lab gets its own copy of /etc/hosts for the DNS section below. A new namespace has only a loopback interface, and it starts switched off:
Now create the second namespace, connect the two with a veth pair, give each end an address and switch them on. ip -n web runs an ip command inside the web namespace:
Inspect Interfaces, Addresses and Routes
ip has subcommands for each layer: ip link for interfaces, ip addr (or ip a) for addresses, ip route (ip r) for routes and ip neigh for the neighbours it found on the local network. -br gives the short form; leave it out for details.
How to read ip addr: state UP and LOWER_UP mean the interface is on and has a link; mtu 1500 is the largest packet; link/ether is the MAC address; inet 192.0.2.1/24 is the IPv4 address with its prefix; scope global means it can be used to reach other machines; and valid_lft forever means it was set by hand, not leased from DHCP.
The route 192.0.2.0/24 dev veth-client proto kernel scope link was added automatically with the address: the whole /24 is directly reachable through veth-client. In ping, time= is the round-trip time, and the summary shows packet loss, which should be 0%.
Routes to Other Networks
There is no route to anything outside 192.0.2.0/24 yet:
ip route get is the fastest way to answer "which interface and gateway would this machine use to reach that address?". Compare the two failures. The first ping never left the machine: there was no route, so the kernel refused at once (connect: Network is unreachable). The last one left through the gateway, and the gateway sent back Destination Net Unreachable because it has no route onward. A third kind of failure is silence: packets leave and nothing comes back, which usually means a firewall drops them or the reply cannot find its way back. On a real machine, ip route shows a default route such as default via 192.168.1.1 dev enp1s0 proto dhcp.
Is the Service Listening? ss, curl and nc
Start a web server in the web namespace on port 80, then look at it from both sides. Use your own home directory instead of /home/dev:
ss replaces the old netstat. The options you will type most often:
| Command | Shows |
|---|---|
ss -tlnp |
TCP sockets that listen, with numeric ports and the process (needs sudo for other users' processes) |
ss -ulnp |
the same for UDP |
ss -tn |
established TCP connections |
ss -tn state established '( dport = :443 )' |
connections to port 443 only |
ss -s |
a summary with counts per state |
Look at the Local Address column. A service bound to 127.0.0.1 (or ::1) only accepts connections from the same machine, which is the most common reason a service works locally and not from outside. 0.0.0.0 (or * and [::]) means all addresses.
DNS: Names to Addresses
The resolver reads /etc/hosts first, then asks a DNS server. getent hosts resolves a name exactly the way programs do, so use it to test what an application will see:
To query DNS servers directly, use dig (from bind9-dnsutils on Ubuntu and Debian, bind-utils on Fedora and RHEL). It asks the server you name and shows exactly what came back, which ping and curl do not. The lab has no connection to the internet, so run these in a second terminal on your machine, outside the namespace:
The second column in the answer is the TTL: how many more seconds resolvers may cache the record. Without @server, dig asks the servers in /etc/resolv.conf.
On Ubuntu, and on Fedora since version 33, systemd-resolved manages DNS. /etc/resolv.conf points to its local stub resolver, so the file looks like this (example):
nameserver 127.0.0.53
options edns0 trust-ad
search .
Do not edit that file: it is generated. Use resolvectl status to see the real upstream servers per interface, resolvectl query example.com to resolve through the stub, and sudo resolvectl flush-caches to clear the cache. Set servers in the network configuration (netplan, NetworkManager or systemd-networkd). On Debian, which does not use systemd-resolved by default, /etc/resolv.conf usually lists the servers directly.
See the Packets: tcpdump
When the tools above disagree, look at the packets. tcpdump prints every packet that matches a filter (icmp, port 80, host 192.0.2.2):
Each line shows the time, source and destination, and what the packet is: here an ICMP echo request and the echo reply, twice. -n skips name lookups, -c 4 stops after four packets, and -w file.pcap saves the capture for Wireshark. On a real interface, add a filter such as port 443 and not port 22, so your own SSH session does not flood the output.
Firewalls: nftables, ufw and firewalld
The Linux kernel filters packets with netfilter. Current distributions program it through nftables (nft); the old iptables command still exists, but on Ubuntu 24.04, Debian 13, Fedora and RHEL it is a compatibility layer (iptables-nft) that writes nftables rules. Most people use a front end: ufw on Ubuntu and firewalld on Fedora and RHEL.
On a remote server, allow SSH before you enable a firewall with a default-deny policy, and keep your current session open until you have tested a new login. Otherwise you lock yourself out and need the cloud provider's console to get back in.
nftables
A ruleset for the web server: drop everything that is not explicitly allowed, accept replies to connections the server started, and allow ping, SSH and HTTP. Save it as /tmp/web.nft:
#!/usr/sbin/nft -f
flush ruleset
table inet filter {
chain input {
type filter hook input priority filter; policy drop;
ct state established,related accept
ct state invalid drop
iif "lo" accept
meta l4proto { icmp, ipv6-icmp } accept
tcp dport 22 accept
tcp dport 80 ip saddr 198.51.100.0/24 counter accept
counter comment "dropped by policy"
}
}
The rule for port 80 only accepts clients from 198.51.100.0/24, and our client is 192.0.2.1. Load the rules into the web namespace and test:
Always load this file with ip netns exec web. Its first line, flush ruleset, deletes every firewall rule in the network namespace it runs in; run outside the lab, that would be your machine's own firewall.
Order matters: nft add rule appends to the end of the chain, after the catch-all counter rule, and since that rule only counts and does not decide, the packet still reaches the new accept below it. Use nft insert rule to put a rule at the top instead.
On a real server, outside any lab, the rules live in /etc/nftables.conf on Debian and Ubuntu (RHEL 10 reads /etc/sysconfig/nftables.conf, which usually includes files from /etc/nftables/). Check the file with sudo nft -c -f /etc/nftables.conf, then start the service and enable it at boot with sudo systemctl enable --now nftables; after later changes, sudo systemctl reload nftables loads the file again. Do not copy the lab ruleset there as it is: write one for the services that server runs, and keep a second session open while you load it.
ufw on Ubuntu
ufw ("uncomplicated firewall") generates the rules for you. It stores its settings in /etc/ufw and turns itself on at every boot once enabled, so do not try it inside the lab namespace, which shares /etc with your machine. Use it on the server you want to protect. A typical setup for a web server, on a fresh Ubuntu 24.04 system:
Other useful forms: sudo ufw allow from 203.0.113.0/24 to any port 22 proto tcp (SSH only from your office network), sudo ufw limit 22/tcp (rate-limit new SSH connections), sudo ufw delete allow 80,443/tcp (remove a rule by repeating it, for IPv4 and IPv6 at once), and sudo ufw app list for profiles that packages such as OpenSSH and Nginx install. sudo ufw delete 3 removes rule 3 from the numbered list; check the number first, because in the list above rule 3 is SSH over IPv6.
Docker publishes container ports with its own rules, which bypass ufw. A port you published with docker run -p 8080:80 is reachable from outside even if ufw denies it. Publish on 127.0.0.1:8080:80 when only a local reverse proxy should reach it.
firewalld on Fedora and RHEL
sudo firewall-cmd --state
sudo firewall-cmd --get-active-zones # which zone each interface is in
sudo firewall-cmd --zone=public --list-all # services and ports of that zone
sudo firewall-cmd --permanent --zone=public --add-service=http # --permanent writes the config
sudo firewall-cmd --permanent --zone=public --add-port=8080/tcp
sudo firewall-cmd --reload # apply the permanent config
firewalld groups interfaces into zones, each with its own rules. Without --zone, commands use the default zone, which is not always the zone your interface is in, so check --get-active-zones and name the zone. Without --permanent, a change is active immediately but lost at the next reload or reboot, which is a handy way to test.
Cloud servers have a second firewall outside the machine: AWS security groups, DigitalOcean Cloud Firewalls, Azure network security groups. A packet must pass both, so check both when something does not connect.
Make Network Settings Permanent
Everything you set with ip is lost at reboot. Each distribution family stores its configuration differently. A static address of 192.0.2.10/24 with gateway 192.0.2.1 looks like this:
Changing the address of the interface you are connected through ends your SSH session. Use the real addresses of your network, not these examples, and keep a way back in: the VM console, your cloud provider's web console, or netplan try, which rolls the change back unless you confirm it.
Older netplan examples use gateway4: 192.0.2.1. That key is deprecated; use the routes list with to: default. Desktops and laptops usually leave all of this to NetworkManager: nmcli device wifi list and nmcli device wifi connect "MyNetwork" --ask connect to Wi-Fi from the terminal.
SSH: Working on Remote Machines
You connect to almost every Linux server with SSH. Beyond ssh user@host, a few features save a lot of time. Put per-host settings in ~/.ssh/config:
Host web
HostName 203.0.113.10
User alice
Port 22
IdentityFile ~/.ssh/id_ed25519
Host db
HostName 10.0.1.20
User alice
ProxyJump web
Now ssh web is enough, and ssh db hops through web to reach a database server on a private network. ssh -G prints the settings it would use, without connecting, which is how you debug a config file:
| Task | Command |
|---|---|
| Copy files | scp file web:/tmp/ (uses the SFTP protocol since OpenSSH 9.0), or rsync -av dir/ web:/srv/dir/ for directories |
| Reach a remote service locally | ssh -L 5433:localhost:5432 db then connect to localhost:5433 |
| Share a local service with the remote side | ssh -R 8080:localhost:3000 web |
| SOCKS proxy through the server | ssh -D 1080 web |
| Jump through a bastion host | ssh -J web alice@10.0.1.20 |
A Troubleshooting Checklist
When "it does not connect", go up the stack one layer at a time. Each step has a command that answers it:
- Link: is the interface
UPwithLOWER_UP? A missingLOWER_UPmeans no cable, no Wi-Fi association, or a VM network that is not attached. - Address: does it have the expected address and prefix?
169.254.x.xmeans DHCP failed. - Route: does
ip route get <target>pick the right interface and gateway? Can youpingthe gateway? - DNS: does
getent hosts namereturn the right address? Compare withdig @1.1.1.1 nameto tell a DNS problem from a network problem. - Port: does
ss -tlnpon the server show the service listening on the right address? Doesnc -zv host portfrom the client succeed? "Connection refused" means you reached the machine but nothing listens; a timeout means something dropped the packets. - Firewall: check the host firewall (
sudo nft list ruleset,sudo ufw status) and the cloud firewall.
For the path in between, tracepath host (no root needed) or mtr host show each router on the way and where the packets stop. Many routers do not answer these probes, so a few * * * lines in the middle are normal.
Old Commands and Their Replacements
The net-tools package (ifconfig, netstat, route, arp) is a legacy interface that current distributions no longer install by default, and it does not show everything the kernel can do (several addresses on one interface, policy routing, namespaces). Use the iproute2 equivalents:
| Old | New |
|---|---|
ifconfig |
ip addr, ip -br addr |
ifconfig eth0 up |
ip link set eth0 up |
route -n |
ip route |
arp -n |
ip neigh |
netstat -tulpn |
ss -tulpn |
netstat -i |
ip -s link |
brctl |
ip link add br0 type bridge, bridge link |
iwconfig, iwlist |
iw, nmcli device wifi |
iptables rules |
nft rules (or iptables-nft, which writes nftables rules) |
Clean Up the Lab
A namespace stays alive as long as a process runs in it, so stop the web server first. Once web is gone, its end of the veth pair goes with it, and that removes the other end too. Leave the client shell with exit, then run sudo ip netns del client and sudo rm -r /etc/netns/client.
Common Mistakes
- Enabling a default-deny firewall before allowing SSH on a remote server.
- A service listening on
127.0.0.1when it should accept outside connections, or the reverse for databases that should stay private. - Editing
/etc/resolv.confon systems where systemd-resolved or NetworkManager generates it. Your change disappears. - Applying a netplan change over SSH without
netplan try, which rolls back automatically if you cannot confirm. - Forgetting the cloud firewall or security group in front of the server.
- Trusting
pingalone. Many networks block ICMP; test the real port withnc -zvorcurl.
Summary
ip link,ip addr,ip routeandip neighshow and change interfaces, addresses, routes and neighbours;ip route getexplains one routing decision.ss -tlnpshows listening services and their processes; check the bind address.getent hostsresolves like an application does;dig @serverqueries DNS directly; on Ubuntu, systemd-resolved owns/etc/resolv.conf.tcpdumpshows the packets when other tools disagree.- nftables is the kernel firewall; ufw and firewalld are front ends. Allow SSH first.
- Make settings permanent with netplan, NetworkManager, ifupdown or systemd-networkd, depending on the distribution.
Next: Shell Scripting Basics, where you turn the commands from this guide into scripts.
Keep Learning
Tags
Found an issue?