Linux
iproute2
ip, ss, tc and bridge commands for addresses, routes, policy routing, namespaces, tunnels and link diagnostics.
Cheatsheet #
| Task | Command |
|---|---|
| Interfaces and state | ip -br link |
| Addresses, brief | ip -br addr |
| Routing table | ip route |
| Which route wins for a destination | ip route get 10.0.5.7 |
| Neighbours (ARP/NDP) | ip neigh |
| Add an address | ip addr add 10.0.0.5/24 dev eth0 |
| Default route | ip route add default via 10.0.0.1 |
| Bring a link up | ip link set eth0 up |
| Change MTU | ip link set eth0 mtu 1450 |
| Listening sockets | ss -ltnp |
| Established, with timers | ss -tno |
| Socket internals (rtt, cwnd) | ss -ti |
| Interface counters | ip -s link show eth0 |
| Policy rules | ip rule |
| Run in a namespace | ip netns exec ns1 ip addr |
| Watch changes live | ip monitor |
| Queue discipline | tc qdisc show dev eth0 |
-br (brief) and -j (JSON) apply to most ip objects: ip -j -br addr | jq is the scriptable form.
A connectivity problem #
Work up the stack; each step tells you whether to keep going.
ip -br link # is the link up and does it have carrier
ip -br addr # does it have the address you expect
ip route get 10.0.5.7 # which route, which source address, which device
ip neigh show 10.0.0.1 # is the gateway resolving to a MAC
ping -c3 10.0.0.1 # layer 3 to the gateway
ss -ltnp # is anything actually listening
tracepath 10.0.5.7 # where it stops, and the MTU along the way| Symptom | Next check |
|---|---|
state DOWN | ip link set dev up; if it stays down, cable or driver — dmesg |
NO-CARRIER | Physical link or the far-end switch port |
| Address missing after reboot | Configured imperatively, not persisted (netplan, NetworkManager, systemd-networkd) |
ip route get picks the wrong device | Policy rules or a more specific route — check ip rule |
Neighbour FAILED | ARP not answered: wrong VLAN, wrong subnet, or filtering |
| Works small, fails large | MTU/PMTU — test with ping -M do -s 1472 |
| Connects then stalls | Asymmetric routing or a stateful firewall dropping the return path |
Links #
ip -br link # name, state, MAC
ip -s link show eth0 # RX/TX packets, errors, drops
ip link set eth0 up | down
ip link set eth0 mtu 9000
ip link set eth0 address 02:42:ac:11:00:02
ip link add link eth0 name eth0.100 type vlan id 100
ip link add br0 type bridge && ip link set eth0 master br0
ip link add bond0 type bond mode 802.3ad
ip link add veth0 type veth peer name veth1 # a pair: one end per namespace
ip link set veth1 netns ns1
ip link del eth0.100Interface errors in ip -s link distinguish problems: RX dropped is usually the host not keeping up (ring buffers, CPU), while RX errors points at the cable, the optic or the switch port.
Addresses #
ip -br addr
ip addr add 10.0.0.5/24 dev eth0
ip addr add 10.0.0.6/24 dev eth0 label eth0:1 # secondary
ip addr del 10.0.0.5/24 dev eth0
ip addr flush dev eth0
ip -6 addr add 2001:db8::5/64 dev eth0
ip addr add 10.0.0.9/32 dev lo # service address on loopback (DSR, anycast)The prefix length matters: /32 on an interface means no on-link subnet and therefore no automatic route to neighbours. That is deliberate for loopback service addresses and a mistake on a physical NIC.
Routes #
ip route # main table
ip route show table all # every table
ip route get 8.8.8.8 # the actual decision, including source address
ip route add 10.1.0.0/16 via 10.0.0.1 dev eth0
ip route add default via 10.0.0.1 metric 100
ip route add 10.2.0.0/16 dev wg0 # on-link, no gateway
ip route replace default via 10.0.0.254 # atomic swap
ip route del 10.1.0.0/16
ip route add blackhole 192.0.2.0/24 # drop silently
ip route add 10.3.0.0/16 nexthop via 10.0.0.1 weight 1 nexthop via 10.0.0.2 weight 1 # ECMPSelection is longest prefix first, then metric within equal prefixes. ip route get answers the only question that matters, because it applies rules, tables and source selection together.
Policy routing #
Rules choose a routing table per packet, which is how a host with two uplinks answers on the interface a request arrived on.
ip rule # ordered by priority, lowest first
echo '100 uplink2' >> /etc/iproute2/rt_tables
ip route add default via 192.0.2.1 dev eth1 table uplink2
ip rule add from 192.0.2.10 table uplink2 priority 100
ip rule add fwmark 0x1 table uplink2 # paired with an iptables/nftables mark
ip rule add to 10.9.0.0/16 table uplink2
ip rule del priority 100Without a from rule, replies from the second interface leave via the default route with the wrong source address and are dropped by reverse-path filtering.
sysctl net.ipv4.conf.all.rp_filter # 1 = strict, 2 = loose, 0 = offNeighbours #
ip neigh
ip neigh show dev eth0 nud reachable
ip neigh add 10.0.0.50 lladdr 02:42:ac:11:00:32 dev eth0 # static entry
ip neigh flush dev eth0
ip -s neigh show 10.0.0.1 # with statisticsStates: REACHABLE (confirmed), STALE (cached, unverified), DELAY/PROBE (verifying), FAILED (no answer). A FAILED gateway entry means layer 2 is broken, whatever the interface says.
Sockets with ss #
ss -ltnp # listening TCP with process
ss -tunap # TCP + UDP, all states, with process
ss -tn state established '( dport = :443 )'
ss -tn dst 10.0.5.0/24
ss -ti # rtt, cwnd, retrans, bytes_acked per socket
ss -tno # timers: retransmit, keepalive, timewait
ss -s # summary by state
ss -tlm # socket memory
ss -K dst 10.0.5.7 # forcibly close matching sockets (needs privileges)ss -ti is the fastest way to tell a slow network from a slow server: high rtt and growing retrans is the network; small cwnd with no retransmits and a full send queue is the application.
Namespaces #
ip netns list
ip netns add ns1
ip netns exec ns1 ip addr
ip link add veth0 type veth peer name veth1
ip link set veth1 netns ns1
ip netns exec ns1 ip addr add 10.10.0.2/24 dev veth1
ip netns exec ns1 ip link set veth1 up
ip netns exec ns1 ip route add default via 10.10.0.1
nsenter -t "$(pgrep -f myapp)" -n ss -ltnp # look inside a container's network
ip netns del ns1A container’s network namespace is reachable through its PID, which is how you run tcpdump or ss against a container that has neither installed.
Tunnels #
ip link add wg0 type wireguard
ip link add gre1 type gre local 203.0.113.1 remote 198.51.100.1 ttl 255
ip link add vxlan0 type vxlan id 100 dev eth0 dstport 4789 group 239.1.1.1
ip link add ipip0 type ipip local 203.0.113.1 remote 198.51.100.1
ip -d link show vxlan0 # -d prints tunnel parametersEvery tunnel costs header bytes: GRE 24, VXLAN 50, WireGuard 60 or so. Set the tunnel MTU below the underlay MTU by that amount, or large packets fail while pings succeed — the classic “some sites load, others hang”.
ip link set vxlan0 mtu 1450
ping -M do -s 1422 10.0.5.7 # 1450 minus 28 bytes of ICMP+IP headerTraffic control #
tc qdisc show dev eth0
tc -s qdisc show dev eth0 # with drop/backlog statistics
tc qdisc replace dev eth0 root fq_codel # sane default, fights bufferbloat
tc qdisc add dev eth0 root tbf rate 100mbit burst 32kbit latency 400ms # shape
tc qdisc add dev eth0 root netem delay 100ms 10ms loss 0.1% # simulate
tc qdisc del dev eth0 root # removenetem on a test host reproduces latency and loss faithfully enough to find timeout bugs before users do. Never leave it on a production interface.
Oneliners #
# Interfaces that are up with addresses, one line each
ip -br addr | awk '$2 == "UP"'
# Default gateway and the interface that reaches it
ip route get 1.1.1.1 | awk '{print "via", $3, "dev", $5, "src", $7; exit}'
# All routes as JSON, filtered
ip -j route | jq -r '.[] | select(.dev=="eth0") | [.dst, .gateway//"-"] | @tsv'
# Interface error and drop counters
ip -s -j link | jq -r '.[] | [.ifname, .stats64.rx.errors, .stats64.rx.dropped, .stats64.tx.errors] | @tsv'
# Top remote addresses by connection count
ss -tn state established | awk 'NR>1 {split($5,a,":"); print a[1]}' | sort | uniq -c | sort -rn | head
# Which process owns a port
ss -ltnp 'sport = :8080'
# Sockets with retransmissions right now
ss -ti | grep -B1 retrans | head -20
# Watch routing and link changes as they happen
ip monitor route link addr
# MTU to a destination, without guessing
tracepath -n 10.0.5.7 | tail -3
# Move a running container's network into scope for tcpdump
nsenter -t "$(docker inspect -f '{{.State.Pid}}' myapp)" -n tcpdump -ni any -c 50
# Every address on the box, including namespaces
ip -br addr; for ns in $(ip netns list | awk '{print $1}'); do echo "== $ns"; ip netns exec "$ns" ip -br addr; done
# Flush and reapply a default route atomically
ip route replace default via 10.0.0.254 dev eth0