ifconfig, route, netstat: Legacy net-tools to iproute2

The full command map, and the same host through both toolsets so you can see exactly what the legacy tools hide from you.

Dark terminal graphic comparing ifconfig and ip addr output on the same Linux interface

You type ifconfig, get "command not found", and briefly consider a career change. It happens on a fresh container, a minimal cloud image, or any recent distribution that stopped installing net-tools by default. The commands are not gone, exactly. They are just no longer the ones the system expects you to use, and they have not been able to show you the whole truth for about twenty years.

This article is the translation layer. It maps ifconfig, route, netstat and arp onto their iproute2 and ss replacements, shows the same host through both sets of tools so you can see exactly what the old ones omit, and is honest about the one thing the new tools do not replace. Everything below was captured on a Debian 13 host running net-tools 2.10 and iproute2 6.15, wired into a Cisco Modeling Labs topology. It is part of the Linux networking commands cluster.

Why the change happened

net-tools is genuinely old. The package on this host reports:

j@llmbits:~$ dpkg -s net-tools | grep -E '^(Package|Version|Status)'
Package: net-tools
Status: install ok installed
Version: 2.10-1.3

Version 2.10 was released in 2021, but that release was largely maintenance. The design dates to the mid nineties and the tools read kernel state through /proc text files and legacy ioctls. Those interfaces still exist for compatibility, and the kernel still populates them, but nothing added to Linux networking since roughly 2001 is exposed through them.

iproute2 talks netlink, which is structured, extensible and where all the new features land. That is the whole argument. It is not that ifconfig is broken. It is that ifconfig is reading an API that stopped growing, so anything modern is either missing or displayed misleadingly.

The practical consequence is that you cannot trust old tools on modern configurations, and the rest of this article shows you where.

The commands, mapped

Legacy to modern, command by command
ifconfigip addr show or ip -br addr
ifconfig eth0 10.0.0.1/24ip addr add 10.0.0.1/24 dev eth0
ifconfig eth0 up / downip link set dev eth0 up / down
ifconfig eth0 mtu 9000ip link set dev eth0 mtu 9000
route -nip route show
route add default gw 10.0.0.1ip route add default via 10.0.0.1
arp -nip neigh show
arp -s 10.0.0.5 aa:bb:cc:dd:ee:ffip neigh add 10.0.0.5 lladdr aa:bb:cc:dd:ee:ff dev eth0 nud permanent
netstat -tulpnss -tulpn
netstat -rnip route show
netstat -iip -s link show
vconfig add eth0 50ip link add link eth0 name eth0.50 type vlan id 50
netstat -sNo direct replacement. Keep netstat -s or read /proc/net/snmp.

Memorize the top row and the rest follows, because ip uses one grammar throughout: object, command, arguments. The full object model is in the Linux ip command guide.

ifconfig against ip addr, on the same interface

j@llmbits:~$ ifconfig ens224
ens224: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 10.77.0.100  netmask 255.255.255.0  broadcast 0.0.0.0
        ether 00:0c:29:b1:cc:47  txqueuelen 1000  (Ethernet)
        RX packets 3690  bytes 250868 (244.9 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 3398  bytes 9332942 (8.9 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

Two things in that output are worth stopping on.

First, broadcast 0.0.0.0. That is not a bug in this capture. The address was set with ip addr add 10.77.0.100/24 dev ens224 and no brd +, so no broadcast address was ever installed on the interface. ifconfig prints the empty field without comment, so it reads as though the interface has a broadcast address of 0.0.0.0. It does not have one at all. This is exactly the class of misleading display the old tools produce on configurations they were not designed for.

Second, the netmask is presented in dotted decimal. Fine for a /24, considerably less fine for a /22 or a /26 at three in the morning.

Now the same interface through ip:

j@llmbits:~$ ip addr show dev ens224
3: ens224: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether 00:0c:29:b1:cc:47 brd ff:ff:ff:ff:ff:ff
    altname enp19s0
    altname enx000c29b1cc47
    inet 10.77.0.100/24 scope global ens224
       valid_lft forever preferred_lft forever

What ifconfig did not show you, in order: the interface index (3), the LOWER_UP flag that distinguishes "admin up" from "carrier present", the queueing discipline, the alternative interface names the kernel also answers to, the address scope, and the address lifetimes.

The LOWER_UP omission is the one that costs time. ifconfig shows RUNNING, which is derived from the same flag, but an interface that is administratively up with no carrier simply drops RUNNING and there is nothing in the output telling you those are two separate conditions. ip shows UP and LOWER_UP as distinct flags, which is the Linux equivalent of reading "up / up" versus "up / down" on a Cisco interface.

The lifetimes matter on any DHCP-managed interface. Look at the management NIC on the same host:

    inet 192.168.88.156/24 brd 192.168.88.255 scope global dynamic noprefixroute ens192
       valid_lft 6409sec preferred_lft 6409sec

That address expires in 6409 seconds unless the lease is renewed. Through ifconfig, a host about to lose its address looks identical to one with a static configuration.

The secondary address problem

This is where ifconfig stops being merely incomplete and starts being actively wrong.

Linux has supported multiple addresses per interface natively for decades. ifconfig has no model for that, so it invented aliases: eth0:0, eth0:1, presented as separate interfaces. They are not separate interfaces. They are labels on additional addresses of one interface, kept only so that ifconfig can see them at all.

j@llmbits:~$ sudo ip addr add 10.77.0.200/24 dev ens224 label ens224:1
j@llmbits:~$ ip addr show dev ens224 | grep inet
    inet 10.77.0.100/24 scope global ens224
    inet 10.77.0.200/24 scope global secondary ens224:1

One interface, two addresses, and ip marks the second as secondary so you know which is primary for source address selection. Add an address without a label, which is the normal way to do it, and ifconfig will not show it at all. On a host with a handful of service addresses configured properly, ifconfig reports one address per interface and you conclude the others are missing.

That is the strongest single reason not to trust ifconfig for diagnosis: it does not just omit detail, it can tell you an address is absent when it is configured and working.

The routing table, three ways

j@llmbits:~$ route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         192.168.88.1    0.0.0.0         UG    101    0        0 ens192
10.77.0.0       0.0.0.0         255.255.255.0   U     0      0        0 ens224
10.77.0.0       10.77.0.1       255.255.0.0     UG    0      0        0 ens224
192.168.88.0    0.0.0.0         255.255.255.0   U     101    0        0 ens192

j@llmbits:~$ netstat -rn
Kernel IP routing table
Destination     Gateway         Genmask         Flags   MSS Window  irtt Iface
0.0.0.0         192.168.88.1    0.0.0.0         UG        0 0          0 ens192
10.77.0.0       0.0.0.0         255.255.255.0   U         0 0          0 ens224
10.77.0.0       10.77.0.1       255.255.0.0     UG        0 0          0 ens224
192.168.88.0    0.0.0.0         255.255.255.0   U         0 0          0 ens192

j@llmbits:~$ ip route show
default via 192.168.88.1 dev ens192 proto dhcp src 192.168.88.156 metric 101
10.77.0.0/24 dev ens224 proto kernel scope link src 10.77.0.100
10.77.0.0/16 via 10.77.0.1 dev ens224
192.168.88.0/24 dev ens192 proto kernel scope link src 192.168.88.156 metric 101

Read the middle two lines of each. In route -n and netstat -rn they both start 10.77.0.0 and differ only in the Genmask column, so you have to convert 255.255.255.0 and 255.255.0.0 in your head to notice one is a /24 and the other a /16. In ip route they read 10.77.0.0/24 and 10.77.0.0/16. On an overlapping-prefix problem, that difference is the whole diagnosis.

Then the fields. netstat -rn silently dropped the metric column that route -n at least shows, so two routes with different metrics look identical. Neither shows proto, so you cannot tell that the default route came from DHCP and the lab route was typed by hand. Neither shows src, so you cannot tell which source address the host will use. And neither can see a second routing table at all, which means a policy-routed host is completely opaque to both. That side is covered in ip route: managing the Linux routing table.

Interface counters

j@llmbits:~$ netstat -i
Kernel Interface table
Iface             MTU    RX-OK RX-ERR RX-DRP RX-OVR    TX-OK TX-ERR TX-DRP TX-OVR Flg
ens192           1500    24431      0   2969 0          5901      0      0      0 BMRU
ens224           1500     3690      0      0 0          3398      0      0      0 BMRU
lo              65536       30      0      0 0            30      0      0      0 LRU

j@llmbits:~$ ip -s link show dev ens224
3: ens224: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000
    link/ether 00:0c:29:b1:cc:47 brd ff:ff:ff:ff:ff:ff
    RX:  bytes packets errors dropped  missed   mcast
        250454    3685      0       0       0       0
    TX:  bytes packets errors dropped carrier collsns
       9332264    3388      0       0       0       0

netstat -i is a decent one-screen summary and gives you packet counts but no byte counts. ip -s link gives you both, plus missed and mcast, and doubling the flag to -s -s expands the errors into per-cause columns (CRC, frame, FIFO, overrun, carrier transitions) that tell you which layer to suspect. For anything below that (speed, duplex, offloads, driver counters) neither tool helps and you want ethtool.

Sockets: netstat against ss

j@llmbits:~$ netstat -tulpn
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      -
tcp        0      0 127.0.0.1:631           0.0.0.0:*               LISTEN      -
tcp6       0      0 :::22                   :::*                    LISTEN      -
tcp6       0      0 ::1:631                 :::*                    LISTEN      -
udp        0      0 0.0.0.0:57029           0.0.0.0:*                           -
udp        0      0 0.0.0.0:5353            0.0.0.0:*                           -
udp6       0      0 :::43891                :::*                                -
udp6       0      0 :::5353                 :::*                                -

j@llmbits:~$ ss -tulpn
Netid State  Recv-Q Send-Q Local Address:Port  Peer Address:PortProcess
udp   UNCONN 0      0            0.0.0.0:57029      0.0.0.0:*
udp   UNCONN 0      0            0.0.0.0:5353       0.0.0.0:*
udp   UNCONN 0      0               [::]:43891         [::]:*
udp   UNCONN 0      0               [::]:5353          [::]:*
tcp   LISTEN 0      128          0.0.0.0:22         0.0.0.0:*
tcp   LISTEN 0      4096       127.0.0.1:631        0.0.0.0:*
tcp   LISTEN 0      128             [::]:22            [::]:*
tcp   LISTEN 0      4096           [::1]:631           [::]:*

The flags are deliberately identical, which is the single kindest thing about this migration: -t TCP, -u UDP, -l listening, -p process, -n numeric. Muscle memory transfers intact.

What you gain is the Send-Q column, which on a listening socket is the accept backlog: 128 for sshd, 4096 for cups. A service whose backlog is saturated under load is a real production failure mode and netstat has no column for it. You also gain filtering that reads like a language rather than a pipeline into grep: ss -t state established '( dport = :443 or sport = :443 )', or ss -ti for per-connection congestion window, RTT and retransmit counts pulled straight from the kernel's TCP state.

The performance difference matters at scale too. netstat reads and parses /proc/net/tcp, which is a text file the kernel regenerates on every read; on a box with a hundred thousand sockets it is genuinely slow. ss queries the socket layer directly over netlink.

The one thing that does not have a replacement

netstat -s prints per-protocol counters, and nothing in iproute2 reproduces it:

j@llmbits:~$ netstat -s | head -25
Ip:
    Forwarding: 2
    16250 total packets received
    2 with invalid addresses
    0 forwarded
    0 incoming packets discarded
    16236 incoming packets delivered
    8951 requests sent out
    132 dropped because of missing route
    OutTransmits: 8972
Icmp:
    52 ICMP messages received
    3 input ICMP message failed
    ICMP input histogram:
        destination unreachable: 7
        timeout in transit: 24
        echo replies: 21
    38 ICMP messages sent
    0 ICMP messages failed
    ICMP output histogram:
        destination unreachable: 1
        echo requests: 37
IcmpMsg:
        InType0: 21
        InType3: 7

That output is doing real work. "132 dropped because of missing route" is a routing gap you would otherwise have to catch in a capture. The ICMP histogram distinguishes 24 time exceeded messages (that traceroute run) from 7 destination unreachables. TCP retransmission and out-of-order counters live further down the same output.

Reaching for netstat -s is correct. Just be aware you are reading /proc/net/snmp and /proc/net/netstat through a formatter, which is exactly what nstat does with better delta handling if you want counters between two points in time rather than since boot.

ARP

j@llmbits:~$ arp -n
Address                  HWtype  HWaddress           Flags Mask            Iface
192.168.88.124           ether   34:9f:7b:7f:ca:ee   C                     ens192
10.77.0.1                ether   aa:bb:cc:00:04:00   C                     ens224
192.168.88.1             ether   98:ba:5f:11:1b:3a   C                     ens192
192.168.88.125           ether   58:ef:68:e7:26:1a   C                     ens192
10.77.0.55                       (incomplete)                              ens224

j@llmbits:~$ ip neigh show
192.168.88.124 dev ens192 lladdr 34:9f:7b:7f:ca:ee STALE
10.77.0.1 dev ens224 lladdr aa:bb:cc:00:04:00 STALE
192.168.88.1 dev ens192 lladdr 98:ba:5f:11:1b:3a REACHABLE
192.168.88.125 dev ens192 lladdr 58:ef:68:e7:26:1a REACHABLE

Same table, same moment. arp collapses every resolved entry to the flag C. ip neigh distinguishes REACHABLE (confirmed within the last thirty seconds) from STALE (cached, unverified), which is precisely the distinction you want when something is intermittently unreachable. And arp shows no IPv6 entries at all, because it predates neighbor discovery. The states, the tunables and the failure modes are covered in ARP on Linux: ip neigh, arp and neighbor states.

What to do about it in practice

Do not alias ifconfig to ip addr. The output formats are different enough that the alias will confuse you at the worst moment, and you will still hit hosts where the real ifconfig exists and behaves differently.

Do learn ip -br addr, ip -br link, ip route show, ip route get, ip neigh show and ss -tulpn. That is six commands and it covers the overwhelming majority of what the old set did.

Do keep net-tools installed where you can, for netstat -s and for the times you are handed a runbook written in 2009.

Do remember that on a minimal container image none of the old tools exist and ip frequently does not either. ip -j from the host into the container's namespace, or nsenter, is a more reliable habit than expecting a debugging toolchain inside the image.

FAQ

Has ifconfig been removed from Linux?

No. It has been deprecated, meaning it is no longer developed and several distributions stopped installing net-tools by default. Install the package and it works exactly as it always did. What it cannot do is show you anything the kernel added after its API stopped being extended.

Why does ifconfig not show all my IP addresses?

Because Linux supports multiple addresses per interface and ifconfig has no model for that. It only sees additional addresses that carry an alias label like eth0:1. Addresses added with ip addr add and no label are invisible to it while being fully configured and working. Use ip addr show.

Is ss really faster than netstat?

Substantially, on any host with a lot of sockets. netstat reads and parses /proc/net/tcp, a text file the kernel regenerates per read. ss queries the socket layer over netlink and can filter kernel-side. On a small host you will not notice; on a busy load balancer the difference is seconds against milliseconds.

What replaces netstat -s?

Nothing in iproute2, and that is the honest answer. Keep using netstat -s, or use nstat, which reads the same /proc/net/snmp and /proc/net/netstat counters and is better at showing deltas between two moments rather than totals since boot.

Do I need to relearn all the flags?

For ss, no: -t, -u, -l, -p and -n mean the same things they did in netstat. For ip, yes, but the grammar is regular. Once you internalize object then command, the rest is guessable, and every object accepts help.

Key takeaways

  • net-tools reads a kernel API that stopped being extended around 2001. It is not broken, it is blind to anything modern.
  • ifconfig cannot see addresses added without an alias label, so it can report an address as absent when it is configured and working.
  • ifconfig does not distinguish admin-up from carrier-present, does not show address lifetimes, and prints empty fields as though they held values (broadcast 0.0.0.0).
  • route -n and netstat -rn print netmasks in dotted decimal and hide proto, src and any table other than main.
  • ss keeps netstat's flags, adds the accept backlog in Send-Q, supports real filter expressions, and is far faster at scale.
  • netstat -s has no iproute2 replacement. Keep it, or use nstat for deltas.
  • Six commands replace nearly all of it: ip -br addr, ip -br link, ip route show, ip route get, ip neigh show, ss -tulpn.

Next, go one layer down to the NIC itself with ethtool: link speed, duplex, offloads and NIC stats, or make your configuration survive a reboot with nmcli and nmtui. The full grammar of the replacement tool is in the Linux ip command guide, and everything in this cluster is indexed on the Linux networking commands guide.

Read next