Be careful when matching on Ethernet addresses in systemd-networkd

June 28, 2021

A not uncommon pattern in networkd is to write a <link>.link or <network>.network file that selects the hardware to work on by MAC address, because that's often more stable than many of the other alternatives. For instance, you might write a .link file for your motherboard like this:

[Match]
MACAddress=2c:fd:a1:xx:xx:xx

[Link]
Description=Onboard motherboard port
MACAddressPolicy=persistent
Name=em0

Unfortunately this is dangerous, because some virtual devices inherit Ethernet addresses from their parent device and networkd will allow virtual devices to match against just Ethernet addresses. In particular VLANs inherit the Ethernet address from their underlying network device, so if you have one or more VLANs on top of em0, they will all match this (and then they'll try to rename themselves to em0). The same can happen if you have a .network file that matches with MACAddress in order to deal with variable network names for the same underlying connection.

(If you have a real device that matches this way and creates VLANs on top of itself, networkd may be smart enough to recognize that it has a recursive situation, or it may blow up. I haven't tested.)

In other words, if you tell networkd that a .link or a .network file applies to anything with a specific Ethernet address, networkd takes that to really mean anything. You may have meant this to apply (only) to your actual Ethernet device, but the .link file doesn't say that and networkd won't infer it.

In systemd v245 or later, what you probably want is to restrict any Ethernet hardware matches to real Ethernet devices with the additional requirement of 'Type=ether':

[Match]
MACAddress=2c:fd:a1:xx:xx:xx
Type=ether

(Systemd v245 was released in February of 2020 and is in Ubuntu 20.04 and the current versions of Fedora, but isn't in Debian stable. Support for the current meaning of Type= that allows matching 'ether' was added in this commit as a result of issue #14952. To my surprise, this significant improvement doesn't seem to have been noted in the NEWS for v245.)

The 'ether' type applies to both PCI Ethernet ports and USB Ethernet devices, but it doesn't apply to wireless devices; those are 'wlan'. As the manpage covers, 'networkctl list' can tell you what your devices are. VLANs are type 'vlan'.

If you have a systemd (and thus a systemd-networkd) that's older than v245, I think the only thing you can do is match on a property of the device, obtained from 'udevadm info /sys/class/net/<what>'. For a lot of physical hardware, the obvious property is that it's on a PCI bus:

[Match]
MACAddress=2c:fd:a1:xx:xx:xx
Property=ID_BUS=pci

(I have to say that I haven't tested this, I'm just following the manpage.)

However, USB Ethernet devices are 'ID_BUS=usb', not PCI, while a laptop's onboard wireless most likely is a PCI device, which is the case on my Dell XPS 13. My laptop's wireless device is also 'DEVTYPE=wlan', while even now real Ethernet devices have no DEVTYPE (as of systemd v248 on a Fedora 34 virtual machine).

(This elaborates on a tweet of mine.)

PS: I'm not sure whether the matching here is being done by systemd-networkd, the systemd version of udevd, or both of them. It's quite possible that both programs and subsystems are doing it at different times and in different circumstances.


Comments on this page:

From 31.220.42.129 at 2021-06-29 10:45:59:

.link files are processed entirely by udevd (by the same net_setup_link builtin that also handles "predictable interface names"), while .network files are processed by networkd. (The "Link File" field in networkctl is just taken from ID_NET_LINK_FILE that was set by udev previously.)

The DEVTYPE is usually provided by the kernel, and it's "normal" for Ethernet devices to lack one. One of the mailing list threads linked from #14952 has something like an explanation.

From 193.219.181.219 at 2021-06-29 10:53:36:

(Ah, I forgot my usual "name".)

On older systemd, two more options are to match by the driver, as in Driver= or the ID_NET_DRIVER udev property, or by the OriginalName= which is the kernel-assigned name and will (almost) always be eth* for Ethernet interfaces.

It should also be possible to use Type=!?* to match devices with no devtype, which would be only Ethernet.

Written on 28 June 2021.
« I should keep track of what Python packages I install through pip
Monitoring the status of Linux network interfaces with Prometheus »

Page tools: View Source, View Normal, Add Comment.
Search:
Login: Password:
Atom Syndication: Recent Comments.

Last modified: Mon Jun 28 23:45:06 2021
This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.