Getting the active network interface(s) in a script on Ubuntu 22.04

October 12, 2023

Suppose, not entirely hypothetically, that we want to start using systemd-resolved on our Ubuntu 22.04 machines. One of the challenges of this is that the whole networking environment is configured through netplan, and in order for systemd-resolved to work well this means that your netplan configuration must have your full list of DNS resolvers and DNS search domains. We don't normally set these in netplan, because it's kind of a pain; instead we copy in an /etc/resolv.conf afterward.

It is possible to make automated changes to your netplan setup through netplan set. However, this needs to know the name of your specific Ethernet device, which varies from system to system in these modern days. This opens up the question of how do you get this name, and how do you get the right name on multi-homed machines (you want the Ethernet device that already has a 'nameservers:' line).

Netplan has netplan get but by itself it's singularly unhelpful. There are probably clever ways to get a list of fully qualified YAML keys, so you could grep for 'ethernets.<name>.nameservers' and fish out the necessary name there. Since netplan in our Ubuntu 22.04 server setup is relying on systemd-networkd, we could ask it for information through networkctl, but there's no straightforward way to get the necessary information.

(Networkctl does have a JSON output for 'networkctl list', but it's both too much and too little information. The 'networkctl status' output is sort of what you want but it's clearly intended for human consumption, not scripts.)

In practice our best bet is probably to look at where the default route points, which we can find with 'ip route show default':

; ip route show default
default via 128.100.X.Y dev enp68s0f0 proto static

Alternately, we could ask for the route to one of our resolvers, especially if they're all on the same network:

; ip route get 128.100.X.M
128.100.X.M dev enp68s0f0 src 128.100.3.X.Q uid ...

In both cases we can pluck the 'dev <what>' out with something (for example awk, or 'egrep -o' if you feel conservative). This will give us the device name and we can then 'netplan set ethernets.<name>...' as appropriate.

If you have JSON-processing tools handy, modern versions of ip support JSON output via '-json'. This reduces things to:

; ip -json route show default | jq -r .[0].dev
; ip -json route get 128.100.X.M | jq -r .[0].dev

These days, I think it's increasingly safe to assume you have jq or some equivalent installed, and this illustrates why.

In the world of systemd-resolved, we probably want Netplan's 'nameservers:' section attached to the Ethernet interface that we use to talk to the DNS resolvers even if our default route goes elsewhere. Fortunately in our environment it generally doesn't matter because our Ubuntu servers almost never have more than one active network interface.

(The physical servers generally come with at least two, but most machines only use one.)

If we want all interfaces, we can reach for either 'ip -br addr' or 'ip -br link', although in both cases we'll need to screen out DOWN links and 'lo', the loopback interface. If we know that all interesting interfaces have an IPv4 (or IPv6) address, we can use this to automatically exclude down interfaces:

; ip -4 -br addr
lo               UNKNOWN 
enp68s0f0        UP             128.100.X.Q/24

(For IPv6, use -6.)

On some machines this may include a 'virbr1' interface that exists due to (local) virtual machines.

(In some environments the answer is 'your servers all get this information through DHCP'. In our environment all servers have static IPs and static network configurations, partly because that way they don't need a DHCP server to boot and get on the network.)

Sidebar: the weird option of looking at the networkd configuration

Netplan writes its systemd-networkd configuration to /run/systemd/network in files that in Ubuntu 22.04 are called '10-netplan-<device>.network'. Generally, even on a multi-interface machine exactly one of those files will have a 'Gateway=' line and some 'DNS=' and 'Domains=' lines. This file's name has the network device you want to 'netplan set'.

Actually relying on this file naming pattern is probably a bad idea. On the other hand, you could find this file and extract the interface name from it (it appears as 'Name=' in the '[Match]' section, due to how Netplan sets up basic fixed networking).

Written on 12 October 2023.
« The wisdom of being selective about python-lsp-server plugins
OpenBSD PF-based firewalls suffer differently from denial of service attacks »

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

Last modified: Thu Oct 12 23:37:04 2023
This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.