How to filter physical ethernet links using rtnetlink

A rtnetlink RTM_GETLINK dump returns every network interface on the system: loopback, physical NICs, bridges, veth pairs, VLANs, WireGuard tunnels, TUN devices, and so on. If you only want physical wired Ethernet adapters, you need to filter out three categories:

  1. Non-Ethernet link types — loopback (ARPHRD_LOOPBACK), WireGuard/TUN (ARPHRD_NONE), etc.
  2. Virtual Ethernet interfaces — bridges, veths, VLANs, bonds, macvlan, etc. These all report ifi_type == ARPHRD_ETHER but carry an IFLA_LINKINFO attribute identifying their kind.
  3. Wireless interfaces — Wi-Fi adapters also report ARPHRD_ETHER and have no IFLA_LINKINFO, so they are indistinguishable from wired Ethernet via netlink alone.

This post shows a compact C++ program that applies all three filters and prints only the physical wired Ethernet links.

The three filters

Filter 1: ifi_type == ARPHRD_ETHER

The ifi_type field in struct ifinfomsg holds the ARP hardware type. Wired Ethernet, Wi-Fi, bridges, and veths all report ARPHRD_ETHER (1). Loopback reports 772 (ARPHRD_LOOPBACK), and tunnel interfaces (WireGuard, TUN) report 65534 (ARPHRD_NONE). This filter removes everything that is not Ethernet-framed at L2.

Filter 2: no IFLA_LINKINFO

Virtual Ethernet interfaces — bridges (Kind: bridge), veths (Kind: veth), VLANs (Kind: vlan), bonds (Kind: bond), macvlan, etc. — all carry the nested IFLA_LINKINFO attribute with an IFLA_INFO_KIND inside it. Physical interfaces (both wired and wireless) do not have IFLA_LINKINFO at all.

So: if IFLA_LINKINFO is present in the attribute list, the interface is virtual and should be skipped.

Filter 3: no /sys/class/net/<name>/wireless

Wi-Fi adapters are physical devices with no IFLA_LINKINFO, so they pass filters 1 and 2. The kernel does not expose a “is wireless” flag in the rtnetlink link dump (the IFLA_WIRELESS attribute is only sent in wireless extension events, not in dump replies).

The reliable way to detect Wi-Fi is to check for the existence of /sys/class/net/<name>/wireless — the kernel creates this directory for wireless interfaces only. This requires a stat() call per candidate interface, which is negligible since there are typically only a handful.

The program

Note: <linux/if_arp.h> and <net/if.h> both define the IFF_* flags and conflict if included together. This program only needs ARPHRD_ETHER from <linux/if_arp.h>, so <net/if.h> is omitted. If you need IFF_* flag constants, include only <net/if.h> and hardcode ARPHRD_ETHER as 1.

Building and running

No special privileges are required.

Example output

On a host with loopback, Wi-Fi, several Docker bridges and veth pairs, WireGuard tunnels, and a single USB Ethernet adapter, the program prints only the physical wired Ethernet link:

For comparison, the same host has 30+ interfaces in the full rtnetlink dump — all filtered out by the three checks above.

Why not just check the interface name?

Interface names follow conventions (enX = wired Ethernet, wlX = Wi-Fi, brX = bridge, vethX = veth pair), but these are conventions, not guarantees. USB Ethernet adapters use enx<MAC> by default but can be renamed with udev rules or ip link set name. The IFLA_LINKINFO + sysfs approach is robust against renaming because it inspects what the kernel reports about the interface, not what the user called it.

Also see How to list all network links using rtnetlink API for the full link-dump program this filter is derived from, How to filter VLAN interfaces using rtnetlink for the complementary VLAN filter, and How to iterate rtnetlink rtattr attributes for the attribute-walking idiom.


Check out similar posts by category: C/C++ Linux