Minimalistic Wireless Networking With systemd

Why

There are already many guides out there for setting up systemd-networkd and friends. But I want to talk about going beyond just simple networking here and describe my setup. Hopefully it will be useful to others!

systemd-networkd and friends

Setting up systemd-networkd and friends is pretty easy.

Configure systemd-networkd to manage your interface

First, run networkctl to determine which interfaces exist on your system. On mine, for example, the output is as follows:

Anything listed as "configuring" or "configured" is being managed by networkctl. Wired interfaces are listed with type "ether" and wireless interfaces are listed with type "wlan". If you are using something like Network Manager or wicd, you will probably see everything listed as unmanaged.

The essence of configuring systemd-networkd is so-called "network files" in /etc/systemd/network/. So go ahead and create something like /etc/systemd/network/25-wireless.network (it can have an arbitrary name) and open it up in your favorite text editor (as root or with sudoedit). The contents of this file are fairly simple:

Make sure to replace wlp60s0 with your wireless interface name from above! Feel free to play around with the options or add your own (see man systemd.network for all of the available options — there are lots!). At this point, don't quite reload any services just yet — we need to set up wpa_supplicant first.

Configure wpa_supplicant to connect to a wireless network

This part is definitely the trickiest part. You have to create a configuration file at /etc/wpa_supplicant/wpa_supplicant-<interface>.conf. So in my case, the file is called /etc/wpa_supplicant/wpa_supplicant-wlp60s0.conf. At the top of the file, put

Next, you need to add network configuration blocks for the networks you want wpa_supplicant to connect to. The key thing to remember here is that defaults are your friend 😉 You can use my "iw enhanced" script from the Some Scripts & Programs page to figure out what the authentication scheme is for your particular wireless network. Some examples are:

WPA-PSK

This type shows up as "Authentication Suites: PSK" in the output from iw and is the simplest to setup. A configuration block for this type looks like:

WPA-EAP

This type shows up for me at school (username/password combo). A configuration block for this type looks like:

Other

There are countless more examples in /usr/share/doc/wpa_supplicant/examples, and the documentation in general is pretty good. Go take a look there or search the web for tutorials on configuring wpa_supplicant if you're confused.

Testing

Take a deep breath. Now stop your network manager if you have one (Network Manager, wicd, connman, etc) using the systemctl command (this will differ based on which one your using — tab completion is your friend here). Once you have done that, you should restart systemd-networkd and start wpa_supplicant@<interface> (so in my case, it's wpa_supplicant@wlp60s0) and systemd-resolved. Within about a second or two, wpa_supplicant should connect to your network (you can verify by checking if networkctl shows "configured" and "routable" for your wireless interface or just trying to browse the internet).

If this didn't work, check to make sure systemd-networkd is actually managing your interface (check the output of networkctl). If it is, next check to see that wpa_supplicant@<interface> is running (if it shows "failed", then something's probably wrong with your configuration file). If wpa_supplicant is running but not connected, it means the network configuration is incorrect (wrong password, wrong ssid, etc).

Running scripts upon connection

So previously, one would often run scripts using if-up,if-down, etc. However, there is a cleaner way to run stuff on network connection, which is often what one wants anyway. For example, in my case, I want to bring up a VPN when I'm connected to most wireless networks…except my home network (long story short, I run a VPN at the router level on my home network, so a device-level VPN is unnecessary). A wpa_supplicant action script can be used to do this kind of stuff very easily and conveniently, especially when combined with systemd.

Setting up the systemd unit file

In order to do this, we need to create a new service, wpa_cli@.service. Why? Because action scripts are run by wpa_cli and not the main wpa_supplicant process. So open up /etc/systemd/system/wpa_cli@.service and put in the following:

(The ExecStartPre= makes sure the socket exists before starting the service, and StartLimitBurst ensures it is restarted if the socket doesn't yet exist).

Setting up the script

I called the script /usr/local/bin/launch_vpn, but you're free to call it whatever you want (just make sure to change the service file above accordingly!). So go ahead and open up that file in your favorite editor and put in something like the following:

Now of course, you can change this to be whatever you want — I'm just providing a template. One nice thing to note here is that if you're starting systemd units, you don't need to worry about conditionally stopping them — calling stop on a stopped unit does nothing and exits cleanly.

Testing

Now if you start wpa_cli@<interface>.service, that script should automatically run on connect and disconnect! You can easily try this by suspending and resuming your computer or by toggling the rfkill for your wireless interface. You should be able to debug the script by checking the journal, so if it's not doing what you expect, check there first.

Final Notes

Of course, systemd haters will note that this was always possible, and that's certainly true. Personally, the impetus for me to even check out running wpa_supplicant as a standalone process was the introduction of systemd-networkd — until then, I had always relied on wicd or Network Manager to carry me along; those tools can sometimes get in the way of things like this (I remember I tried something similar a while back using if-up scripts, but those don't get the network SSID for obvious reasons, so it became too much of a hassle).

If everything works, go ahead and disable your previous network manager from starting at boot and enable starting systemd-networkd, wpa_supplicant@<interface>, wpa_cli@<interface>, and systemd-resolved at boot. As one final step, you should link /etc/resolv.conf to /var/run/systemd/resolve/stub-resolv.conf to let systemd-resolve manage it (that way when you switch networks, everything will work as you expect).