Start by symptom
No address
ip -br aNo route out
ip rName does not resolve
dig host +shortPort refuses the connection
ss -tulpnPackets disappear
tcpdump -i any -nn host XWorks locally, blocked from outside
firewalld or ufw
ifconfig, netstat and route are gone. They belong to the unmaintained net-tools package and are not installed by default on RHEL 8 and later, on recent Ubuntu, or on most appliances. The replacements are
ip and ss, and they report more.
Addresses, links and routes
| Task | Command |
|---|---|
| Everything, one line per interface | ip -br a |
| Full detail | ip a |
| One interface | ip a show eth0 |
| Link state, speed and MAC | ip -br link |
| Interface counters, errors and drops | ip -s link show eth0 |
| Routing table | ip r |
| Which route will be used for a target | ip route get 8.8.8.8 |
| Neighbours, the ARP cache | ip neigh |
| Clear one neighbour entry | ip neigh del 10.0.0.1 dev eth0 |
| Bring an interface up or down | ip link set eth0 up |
| Add an address, until reboot | ip addr add 10.0.0.5/24 dev eth0 |
| Add a route, until reboot | ip route add 10.20.0.0/16 via 10.0.0.1 |
| Negotiated speed and duplex | ethtool eth0 |
| Driver level error counters | ethtool -S eth0 |
ip changes are not persistent. Anything set with
ip addr or ip route is gone at the next reboot or the next time the network service restarts. Use it to test, then write the change into the distribution’s own configuration, which is the next section.
Sockets and ports
| Task | Command |
|---|---|
| What is listening, with the process | ss -tulpn |
| Established connections | ss -tnp state established |
| One port | ss -tulpn sport = :443 |
| Connections to one host | ss -tn dst 10.0.0.50 |
| Summary counts by state | ss -s |
| Is the port reachable from here | timeout 3 bash -c "</dev/tcp/10.0.0.50/443" && echo open |
| Same, if netcat is installed | nc -zv 10.0.0.50 443 |
| Which process holds a file or socket | lsof -i :443 |
The ss flags read as words.
-t TCP, -u UDP, -l listening, -p process, -n numeric. -tulpn is simply all of them, and it is the one form worth memorising.
DNS
| Task | Command |
|---|---|
| Just the answer | dig example.com +short |
| One record type | dig MX example.com |
| Ask a specific server | dig @10.0.0.10 host.corp.local |
| Reverse lookup | dig -x 10.0.0.25 +short |
| Follow the delegation from the root | dig +trace example.com |
| Quick answer without dig | host example.com |
| What the resolver is configured with | cat /etc/resolv.conf |
| With systemd-resolved, the real picture | resolvectl status |
| Resolve through systemd-resolved | resolvectl query host.corp.local |
| Flush the resolver cache | resolvectl flush-caches |
| Order of hosts file versus DNS | /etc/nsswitch.conf |
/etc/resolv.conf is often a lie. On Ubuntu and anything with systemd-resolved it is a symlink to a stub listing 127.0.0.53, and the real upstream servers only show in
resolvectl status. Editing the file by hand is overwritten on the next network change. dig is installed by the bind-utils package on the RHEL family and dnsutils on Debian and Ubuntu.
Making it persistent, by distribution
| Distribution | What manages the network |
|---|---|
| RHEL, Rocky, AlmaLinux, Fedora | NetworkManager, driven by nmcli |
| Ubuntu Server 18.04 and later | netplan, YAML in /etc/netplan/ |
| Ubuntu Desktop | netplan on top of NetworkManager, so nmcli works too |
| Debian | /etc/network/interfaces, or netplan on newer images |
| SLES and openSUSE | wicked, or YaST |
| Photon OS, including the vCenter appliance | systemd-networkd, files in /etc/systemd/network/ |
| NetworkManager task, RHEL family | Command |
|---|---|
| Devices and their state | nmcli device status |
| Connection profiles | nmcli connection show |
| Details of one profile | nmcli connection show "ens192" |
| Set a static address | nmcli con mod "ens192" ipv4.method manual ipv4.addresses 10.0.0.5/24 ipv4.gateway 10.0.0.1 ipv4.dns "10.0.0.10" |
| Back to DHCP | nmcli con mod "ens192" ipv4.method auto |
| Apply the change | nmcli con down "ens192" then nmcli con up "ens192" |
| Add a static route | nmcli con mod "ens192" +ipv4.routes "10.20.0.0/16 10.0.0.1" |
| Set the hostname | hostnamectl set-hostname srv01.corp.local |
Ubuntu Server, /etc/netplan/01-netcfg.yaml. Indentation is two spaces and it matters.
network:
version: 2
ethernets:
ens192:
addresses: [10.0.0.5/24]
routes:
- to: default
via: 10.0.0.1
nameservers:
addresses: [10.0.0.10, 10.0.0.11]
search: [corp.local]
| netplan task | Command |
|---|---|
| Apply with an automatic rollback if you lose the session | netplan try |
| Apply for real | netplan apply |
| Show the merged configuration | netplan get |
Use netplan try over a remote session. It reverts after 120 seconds unless you confirm, which is the difference between a typo you undo and a trip to the console. The
gateway4 key you may see in older guides is deprecated: use the routes form above.
Firewall, by distribution
| Task | firewalld, RHEL family | ufw, Ubuntu |
|---|---|---|
| Is it running | firewall-cmd --state | ufw status |
| Full current rules | firewall-cmd --list-all | ufw status verbose |
| Open a port | firewall-cmd --add-port=8443/tcp --permanent | ufw allow 8443/tcp |
| Open a named service | firewall-cmd --add-service=https --permanent | ufw allow https |
| Limit to a source network | firewall-cmd --zone=internal --add-source=10.0.0.0/24 --permanent then --zone=internal --add-port=22/tcp --permanent | ufw allow from 10.0.0.0/24 to any port 22 |
| Apply | firewall-cmd --reload | immediate |
| Remove a rule | firewall-cmd --remove-port=8443/tcp --permanent | ufw delete allow 8443/tcp |
| Which zone is this interface in | firewall-cmd --get-active-zones | no zones, ufw is flat |
| Underneath both | Command |
|---|---|
| The actual ruleset on modern systems | nft list ruleset |
| Legacy view, where iptables still exists | iptables -L -n -v |
| Is anything dropping, with counters | nft list ruleset | grep -i drop |
Without –permanent the rule dies at reload. firewalld keeps a runtime set and a permanent set. Add a rule permanently and reload, or add it at runtime to test and then promote it with
firewall-cmd --runtime-to-permanent.
Path and capture
| Task | Command |
|---|---|
| Trace the path | traceroute -n 8.8.8.8 |
| Same, without extra packages | tracepath 8.8.8.8 |
| Continuous path and loss | mtr -n 8.8.8.8 |
| Watch traffic on a port | tcpdump -i any -nn port 443 |
| Watch one host, stop after 100 packets | tcpdump -i any -nn host 10.0.0.50 -c 100 |
| Write a capture for Wireshark | tcpdump -i any -nn -w /tmp/cap.pcap host 10.0.0.50 |
| Read it back | tcpdump -nn -r /tmp/cap.pcap |
| See the payload as text | tcpdump -i any -nnA port 80 |
| Test an HTTP endpoint | curl -I https://example.com |
| Test TLS and see the certificate | openssl s_client -connect host:443 -servername host |
-nn twice is deliberate. One
n stops host name lookups, the second stops port name lookups. On a box with broken DNS, a plain tcpdump appears to hang while it tries to resolve every address it sees.
Symptom to command
| Symptom | Run this, in order |
|---|---|
| No address at all | ip -br link for carrier, then journalctl -u NetworkManager or -u systemd-networkd |
| Address but nothing routes | ip r for a default route, then ip route get for the target |
| Ping by IP works, names fail | dig @server name, resolvectl status, /etc/nsswitch.conf |
| Connection refused | ss -tulpn on the server: nothing is listening, or it listens on 127.0.0.1 only |
| Connection times out | Firewall. firewall-cmd --list-all or ufw status, then nft list ruleset |
| Works locally, not from another subnet | Routing or the service bound to the wrong address. Check both ends with tcpdump. |
| Slow or lossy | ip -s link for drops, ethtool eth0 for a duplex mismatch, then mtr |
| Config survives a test but not a reboot | You used ip addr add. Write it into nmcli, netplan or networkd. |
| Two interfaces, traffic leaves the wrong one | ip route get target, then compare route metrics |
FAQ
The service is listening but nobody can connect.
Read the local address in
ss -tulpn. If it says 127.0.0.1:8080 the service is bound to loopback only and no firewall change will help; the fix is in the application configuration. 0.0.0.0:8080 or *:8080 means it listens everywhere, and then it is the firewall or routing.
Which file do I edit to change the IP?
It depends on the distribution, which is the whole problem. RHEL family:
nmcli. Ubuntu Server: the YAML in /etc/netplan/. Debian: /etc/network/interfaces. Photon OS and the vCenter appliance: /etc/systemd/network/. Editing the wrong one changes nothing and looks like the system ignoring you.
I changed resolv.conf and it reverted.
Something owns that file: systemd-resolved, NetworkManager or the DHCP client. Set DNS where the network is configured instead, with
nmcli con mod ... ipv4.dns or the nameservers block in netplan.
Is there an equivalent of Test-NetConnection?
Not as one command.
nc -zv host port comes closest when netcat is installed, and bash can do it with no packages at all: timeout 3 bash -c "</dev/tcp/host/port" returns success if the port accepts. For the full picture combine ip route get, dig and ss.
Can I capture traffic on a VM without installing anything?
tcpdump is present on most server images and is the right tool inside the guest. If the traffic never reaches the guest, capture on the ESXi host instead with pktcap-uw, because the problem is then the portgroup, the vSwitch or the physical uplink.