Beyond ifconfig: The ip Command Reference Every Network Engineer Needs
Most Linux networking guides introduce ip as a simple replacement for ifconfig: swap ifconfig eth0 for ip addr show eth0 and move on. That framing undersells the tool dramatically. ip is the user-space interface to the entire Linux networking stack — interfaces, addresses, routing tables, policy routing rules, ARP/neighbour cache, tunnels, VRFs, and real-time kernel event streams. Understanding it properly makes every other Linux networking task easier.
This post covers the full model, with practical examples aimed at network engineers rather than sysadmins.
Why iproute2 Replaced net-tools
The old tools — ifconfig, route, arp, netstat — were built around the original BSD socket API and have not kept pace with the kernel. They miss VRFs, policy routing, network namespaces, and various modern interface types. They are also deprecated on most distributions and absent from many minimal installs.
iproute2 (the package that provides ip, tc, ss, bridge, and others) talks directly to the kernel via netlink sockets. Everything you can do in the kernel’s networking subsystem, you can do through iproute2.
The ip Command Structure
ip [ OPTIONS ] OBJECT { COMMAND | help }
Objects: link, addr, route, rule, neigh, netns, tunnel, vrf, monitor, and more.
Useful global options:
-br/--brief— compact one-line-per-object output, great for scripting-c/--color— colour output (human-facing use)-j/--json— machine-readable JSON output-4/-6— restrict to IPv4 or IPv6-n <netns>— run the command inside a named network namespace
ip link — Interface Management
ip link manages layer 2 objects: physical interfaces, VLANs, bonds, bridges, VEths, dummies, VRFs, and more.
Listing interfaces
# Verbose list
ip link show
# Brief one-liner per interface
ip -br link show
# Only interfaces that are UP
ip link show up
# A specific interface
ip link show dev eth0
Brief output format: <name> <state> <MAC>. State is UP, DOWN, or UNKNOWN.
Bringing interfaces up and down
ip link set dev eth0 up
ip link set dev eth0 down
Changing MTU
ip link set dev eth0 mtu 9000
Jumbo frames on a lab segment — always verify the upstream switch port matches before blaming the host.
Setting a MAC address
ip link set dev eth0 address 02:00:00:00:00:01
Useful for testing MAC-based policies or cloning an upstream device’s address.
Creating virtual interfaces
# VLAN subinterface
ip link add link eth0 name eth0.100 type vlan id 100
# Dummy interface (software loopback, useful for stable addresses in labs)
ip link add name dummy0 type dummy
# Veth pair (virtual Ethernet — used extensively with namespaces)
ip link add veth0 type veth peer name veth1
# Bridge
ip link add name br0 type bridge
ip link set dev eth0 master br0
Deleting an interface
ip link delete dev eth0.100
ip addr — Address Management
ip addr manages layer 3 addresses assigned to interfaces. An interface can have multiple addresses — this is normal, not a misconfiguration.
Listing addresses
# All interfaces
ip addr show
# Brief format
ip -br addr show
# One interface
ip addr show dev eth0
# Only IPv4
ip -4 addr show
Adding and removing addresses
# Add with prefix length
ip addr add 192.168.1.10/24 dev eth0
# Add with an explicit broadcast address
ip addr add 10.0.0.5/30 broadcast 10.0.0.7 dev eth0
# Remove
ip addr del 192.168.1.10/24 dev eth0
# Remove all addresses from an interface
ip addr flush dev eth0
Secondary addresses
Every address after the first on an interface is a secondary address. They behave identically for routing purposes but are labelled differently in ip addr show output. This matters when you flush: ip addr flush dev eth0 removes all addresses including secondaries.
Labels
Addresses can carry a label — useful for identifying their purpose in scripts:
ip addr add 10.0.0.1/24 dev eth0 label eth0:mgmt
ip route — Routing Table Management
ip route manages the main routing table (and others — see ip rule below).
Listing routes
# Full table
ip route show
# Brief
ip -br route show
# Only IPv6
ip -6 route show
# Routes for a specific prefix
ip route show 10.0.0.0/8
# What route would be used for a destination?
ip route get 8.8.8.8
ip route get is particularly useful for troubleshooting — it shows exactly which route the kernel would select, including the source address and outgoing interface, which show alone does not tell you.
8.8.8.8 via 192.168.1.1 dev eth0 src 192.168.1.10 uid 1000
Adding and removing routes
# Default gateway
ip route add default via 192.168.1.1
# Static host route
ip route add 10.10.10.10/32 via 192.168.2.1 dev eth1
# Blackhole (silent drop)
ip route add blackhole 192.0.2.0/24
# Unreachable (ICMP unreachable reply)
ip route add unreachable 192.0.2.0/24
# Remove
ip route del 10.10.10.10/32 via 192.168.2.1
ip route del default
Route attributes
# Set metric (lower wins)
ip route add 10.0.0.0/8 via 192.168.1.1 metric 100
# Per-route MTU (useful when a path has a lower MTU than the interface)
ip route add 10.0.0.0/8 via 192.168.1.1 mtu 1400
# Next-hop weight for ECMP (equal-cost multi-path)
ip route add 10.0.0.0/8 \
nexthop via 192.168.1.1 dev eth0 weight 1 \
nexthop via 192.168.2.1 dev eth1 weight 1
ECMP is the Linux equivalent of SD-WAN load balancing at the routing level — traffic hashes across both next-hops by default using a flow hash (src IP + dst IP + protocol).
ip rule — Policy Routing
This is where ip route ends and proper network engineering begins. Linux supports up to 2^32 independent routing tables. ip rule defines a priority-ordered list of rules; for each packet, the kernel walks the rule list top-to-bottom and consults the first matching table.
The default rule set:
ip rule show
# 0: from all lookup local
# 32766: from all lookup main
# 32767: from all lookup default
- local (table 255): kernel-managed, loopback and broadcast routes. Never touch this.
- main (table 254): the normal routing table that
ip routemanipulates by default. - default (table 253): empty by default, a last-resort catch-all.
Source-based routing — the classic use case
You have two ISPs on eth0 and eth1, and you need traffic from 192.168.10.0/24 to leave via ISP-A (eth0) and traffic from 192.168.20.0/24 to leave via ISP-B (eth1), regardless of the main routing table.
# Create two tables (add to /etc/iproute2/rt_tables for named access)
# Table 10 = ISP-A, Table 20 = ISP-B
# Populate each table with its own default route
ip route add default via 203.0.113.1 dev eth0 table 10
ip route add default via 198.51.100.1 dev eth1 table 20
# Also add the connected routes so return traffic resolves
ip route add 192.168.10.0/24 dev eth0 table 10
ip route add 192.168.20.0/24 dev eth1 table 20
# Rules: match source prefix → consult the right table
ip rule add from 192.168.10.0/24 lookup 10 priority 100
ip rule add from 192.168.20.0/24 lookup 20 priority 200
Any packet from 192.168.10.x hits rule priority 100, consults table 10, and exits via eth0. Traffic from 192.168.20.x hits priority 200 and exits via eth1. Everything else falls through to the main table.
Matching on more than source address
Rules can match on:
# Inbound interface (useful on a multi-homed host)
ip rule add iif eth1 lookup 20 priority 150
# Firewall mark (set by iptables/nftables before routing)
ip rule add fwmark 0x2 lookup 20 priority 160
# Destination prefix
ip rule add to 10.0.0.0/8 lookup 10 priority 170
# TOS field
ip rule add tos 0x10 lookup 10 priority 180
Combining fwmark with nftables gives you routing decisions based on any packet attribute nftables can match — application port, connection state, DSCP — without the complexity of u32 filter syntax.
Named routing tables
Rather than bare numbers, add names to /etc/iproute2/rt_tables:
echo "10 isp-a" >> /etc/iproute2/rt_tables
echo "20 isp-b" >> /etc/iproute2/rt_tables
Then use names everywhere:
ip route add default via 203.0.113.1 dev eth0 table isp-a
ip rule add from 192.168.10.0/24 lookup isp-a priority 100
Listing and removing rules
ip rule show
ip rule del from 192.168.10.0/24 lookup 10 priority 100
Rules do not persist across reboots. For persistence, add the ip rule add and ip route add table commands to a networkd configuration, a post-up hook in /etc/network/interfaces, or a systemd unit that runs after network-online.target.
ip neigh — ARP and Neighbour Cache
ip neigh (short for neighbour) manages the ARP table for IPv4 and the NDP cache for IPv6.
Listing the neighbour table
ip neigh show
# Brief
ip -br neigh show
# Only a specific interface
ip neigh show dev eth0
# Filter by state
ip neigh show nud reachable
States to know: REACHABLE (confirmed working), STALE (not recently confirmed but still usable), FAILED (resolution failed), PERMANENT (static, never expires), NOARP (no ARP needed, e.g. point-to-point).
Adding a static ARP entry
ip neigh add 192.168.1.1 lladdr 00:11:22:33:44:55 dev eth0 nud permanent
Useful in environments where ARP is unreliable or for locking down a gateway’s MAC.
Removing and flushing entries
# Remove one entry
ip neigh del 192.168.1.1 dev eth0
# Flush all stale entries on an interface
ip neigh flush dev eth0 nud stale
# Flush everything on an interface (careful)
ip neigh flush dev eth0
Triggering an ARP request
ping -c1 192.168.1.254 && ip neigh show dev eth0
ip monitor — Live Kernel Events
ip monitor streams netlink events from the kernel in real time. It is the fastest way to watch what the network stack is doing as you make changes.
# Watch everything
ip monitor all
# Watch only routing table changes
ip monitor route
# Watch address changes (useful when debugging DHCP)
ip monitor address
# Watch neighbour (ARP) events
ip monitor neigh
# Watch link state changes
ip monitor link
Example output during a link bounce:
[LINK] 3: eth0: <BROADCAST,MULTICAST> mtu 1500 state DOWN
[LINK] 3: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 state UP
[ADDR] 3: eth0 inet 192.168.1.10/24 brd 192.168.1.255 scope global eth0
[ROUTE] default via 192.168.1.1 dev eth0 proto dhcp src 192.168.1.10 metric 100
Run ip monitor route in one terminal while making routing changes in another — it makes cause-and-effect immediately visible.
Useful One-liners
Find which interface owns an IP address
ip addr show | grep -w "192.168.1.10"
Show the exact route taken to a destination, with source IP
ip route get 8.8.8.8 from 192.168.1.10
Specifying from forces the kernel to evaluate policy routing rules for that source address — essential when debugging multi-table setups.
List only IPv4 addresses in CIDR notation
ip -4 -br addr show | awk '{print $3}'
Watch for routing table changes in the background
ip monitor route >> /tmp/route-events.log &
Dump routing state as JSON for scripting
ip -j route show | python3 -m json.tool
The -j flag makes iproute2 output parse-friendly without any text manipulation. Combine with jq:
# All interface names that are UP
ip -j link show | jq -r '.[] | select(.operstate=="UP") | .ifname'
# Gateway for the default route
ip -j route show default | jq -r '.[0].gateway'
Persistence
ip commands are not persistent by default — a reboot clears everything. Persistence is distribution-dependent:
- Debian/Ubuntu with ifupdown:
/etc/network/interfaces,upandpost-updirectives - systemd-networkd:
.networkfiles in/etc/systemd/network/, with native support for addresses, routes, and routing policy rules - NetworkManager:
nmclior connection profiles in/etc/NetworkManager/system-connections/ - RHEL/Fedora:
nmclior interface scripts under/etc/sysconfig/network-scripts/
For lab hosts, a simple systemd unit that calls a shell script after network-online.target is often the cleanest option.
What to Read Next
- Linux tc — traffic shaping, HTB class hierarchies, and netem impairments that sit on top of the interfaces and routes configured here
- Linux network namespaces —
ip netnsis the object not covered in depth here; the namespaces post covers the full pattern of building isolated topologies with veth pairs and theip -nflag