Steam In-home Streaming lags with NetworkManager revisited: Replacing NetworkManager with systemd-networkd

Back in early 2015 I encountered an issue with NetworkManager: Even if you have ever only told it about your one single wireless network (or AP, access point) it insists on scanning the airwaves at regular intervals (about every two minutes) looking for a better connection. I thought that was dumb. This behaviour is known to the developer and is working-as-intended. There is no off-switch. I thought that was really, seriously dumb. I still do.

It was not an issue for me until I tried live streaming, specifically using Steam’s in-home streaming that allows you to play a game on a different computer to the one you’re sitting at and streaming the ‘playback’ to your current machine. Then NetworkManager cuts into the smooth streaming every two minutes, turning it into a screenshot slideshow instead for 3-5 seconds. If you’re playing a game that requires attention to not dying/crashing/clicking-the-wrong-button, this is a bad thing for a long time.

Now, my gaming desktop died shortly afterwards (note to self: Never buy another (cheap) Corsair PSU as long as you live) so it quickly became a non-issue. I recently got a new gaming computer, dual-booting Windows 10 and Ubuntu 16.04 17.04.  Coupled with a new 802.11ac wireless network  it seemed obvious to try to make in-home streaming work with the desktop-to-HTPC streaming, essentially making the HTPC a high-end Steam Link, rather than a low-end gaming computer in it’s own right. Running the streaming ‘server’ from Windows, everything works. Streaming from linux I was instantly reminded of the problems I had two years ago.

About half a year ago, commenter William Bernting kindly added some information to my original post suggesting how to make NetworkManager stop the scanning routine. I hope it works for whoever finds this information and I should probably have tested his solution before forging ahead with my own, more radical one. However, I have no patience with NetworkManager anymore, I do only connect to one network and I had the impetus last night so here is what works for me: Rip out NetworkManager, replace with systemd-networkd.

A word of warning: systemd-networkd is a more obvious solution for when you just have an ethernet cable sticking into your pc. For those cases NetworkManager is massively overkill and networkd is a five line config file and you’re done. It’s a bit more finnicky with a wireless connection, requiring three different services and writing your own systemd service files. It’s not rocket science, though. I experimented a bit and muddled through in about an hour and half.

Second word of warning: GNOME is getting more and more integrated and NetworkManager is a core part of GNOME. Some network integration might depend on NetworkManager. I haven’t encountered any issues so far but they may still be there.

As I said, if this was a wired connection we would just write a quick config file for networkd and start that service. As it is we need to start at a somewhat lower level. We are going to need two systemd managed services to replace NetworkManager’s packaged one, of which systemd-networkd is only the top one:

  • First, we need to run wpa_supplicant. wpa_supplicant is, unsurprisingly, a supplicant. That means it takes care of actually talking to the designated network and authenticating with whatever credentials you have given it. We’re not ‘connected to the network’, though, as we still don’t have an IP address. What we have is an authenticated channel to talk to the router and ask it for one.
  • Which is what the second service does: systemd-networkd runs a dhcp client, asking the router’s dhcp server for an IP address. Now we’re online but a little lost, as we don’t have any DNS lookup.
  • Finally we need DNS. You can run systemd-resolved.service which by default relays any DNS enquiries to the network and in effect to your router. However, a manually configured resolv.conf will most likely work just as well. One advantage to using the resolved.service is if you have special DNS preferences like OpenDNS (see here why you might want to stop using OpenDNS) or Google’s DNS servers, you can put them in your router’s settings with systemd- rather than repeating them on each machine/OS connected to you network.

This may seem complicated and messy but seemingly simpler services that do it all in one swoop, do the exact same things, only they hide the internals behind one clean service file, similar to NetworkManager.

Note that all or almost all of the following commands assume root.

Step 1: Getting the network device name

We are going to need to know the name of the network device we are configuring for, so to start us off, we run the following command.

nmcli device status

This will tells us simply what active network devices are recognised on the system and which are managed by NetworkManager. Most probably you only have one ‘real’ network device, your wireless card, and the loopback device (lo). This is assuming NetworkManager is still up and running. If not, you can use ip addr show or ifconfig -a, as well. The device naming scheme changed with systemd (v197, to be precise) so where you used to get names like wlan0, you now get wlp3s0 and the like. Make a note of the name of your wireless device.

Step 2: Disabling NetworkManager

We obviously need to stop and disable NetworkManager so it will not run now or when we reboot. Using systemd this is fairly straight-forward:

systemctl stop NetworkManager.service
systemctl disable NetworkManager.service

This should also stop/disable any services that depend on NetworkManager, like the dispatcher service that takes actions when the network is down/up.

EDIT: Sadly it doesn’t do the reverse, i.e. take out any services that themselves are hardcoded to depend on NetworkManager and will trigger it in spite of any disabling or masking. So as of late the following is a necessity too, to prevent NetworkManager rising from the grave:

NetworkManager-wait-online.service

Step 3: Running wpa_supplicant

First up we’re going to run wpa_supplicant with a basic configuration files, just to get things off the ground. Stealing from the Arch Wiki, we put this in a fresh config file called /etc/wpa_supplicant/example.conf:

ctrl_interface=/run/wpa_supplicant
 update_config=1

The ctrl interface tis simply the socket-like file that is used to talk to wpa_supplicant. Setting update_config to 1 allows for us to write back to and update the config file at a later stage when we have the needed information.

Make sure that there is not already a wpa_supplicant running in the background somewhere. For me Ubuntu 17.04 introduced this issue. Despite having stopped and disabled NetworkManager, wpa_supplicant.service was still running. You can check for it by running

pidof wpa_supplicant

Then run the following to trace the pid back to the service that ran it (in case there’s any doubt);

systemctl status $(pidof wpa_supplicant)

And kill it:

systemctl stop wpa_supplicant.service

Now we run wpa_supplicant manually as root:

wpa_supplicant -i [interface found earlier] -c /etc/wpa_supplicant/example.conf

Adding a -B flag will cause it to run in the background and free up your terminal. I prefer to keep it in the foreground when we’re at this stage. If you want more info the -d (for debug) flag will supply plenty. In case you get warnings or errors about a p2p-dev-[interface] device: This is apparently a known thing with the current versions of wpa_supplicant although accounts vary (search ‘p2p dev interface’). As far as I can tell it is safe to ignore as long as you’re wary of not picking the p2p-dev-[interface] device in any of the following.

Now, in another terminal (in case you didn’t daemonize), we run wpa_cli. wpa_cli opens up a command line with it’s own set of command. Pay attention to what interface is automatically selected and change it (by issuing the in-program command interface [interface]) in case it picks the aforementioned p2p-dev-thing or something else irrelevant.

Then scan the airwaves by issuing the ‘scan’ command. If you only get an OK back but no indication of results (usually in the form of a line saying <3>CTRL-EVENT-SCAN-RESULTS) something’s amiss. Else you can simply have it inform you of the results by issuing the command scan_results.

Assuming that you have spotted your network in the list of scan_results, we now proceed to interactively configure that network while we’re in wpa_cli. This is a rather ingenious method: Instead of writing your config file, then run the program and hope that everything works, you configure things interactively and get told the second something doesn’t work. If everything is okay you can simply write the resulting configuration back to the example.conf file (remember how we set update_config to 1?).

Start the process:

add_network

Seeing as it’s the first network, it gets numbered 0. This is then how we refer to it in the following. Indicate the SSID or name of the wireless network you wish to connect to.

set_network 0 ssid "MY_NETWORK_NAME"

Input the password for the network:

set_network 0 psk "PASSWORD"

The we connect/test that we can connect given what you have just entered.

enable_network 0
CTRL-EVENT-CONNECTED - Connection to [routers mac address] completed

should indicate success. If so, save the configuration back to example.conf with:

save_config

Quit wpa_cli and rename example.conf to wpa_supplicant-[interface].conf where [interface] is the name of your interface.

All we need for wpa_supplicant to work properly now is a custom systemd service file. We could hack a custom version of wpa_supplicant.service by copying over the one in /lib/systemd/system and editing our interface in. A slightly more kosher way to do it is to use an interface agnostic version like this wpa_supplicant@.service:

[Unit]
 Description=WPA Supplicant for device %i
 Before=network.target
 Wants=network.target

[Service]
 Type=simple
 ExecStart=/sbin/wpa_supplicant -i%i -c /etc/wpa_supplicant/wpa_supplicant-%i.conf

[Install]
 WantedBy=multi-user.target

The idea is obviously that this will work with any device as long as we name our configuration file appropriately (note the -c /etc/wpa_supplicant/wpa_supplicant-%i.conf) Copy paste it into /etc/systemd/system/wpa_supplicant@.service and enable it by adding the name of your interface after the at sign, like so

systemctl enable wpa_supplicant@wlp5s0.service

Start it and check it’s status at the same time to check for errors.

You may recall that we stopped the general wpa_supplicant.service earlier on. You can try disabling it but it seems that  it’s triggered by something else that is resistant to our disabling it. I haven’t encountered any problems with allowing two instances running on the pc. If you do or just dislike the idea, you can consider masking the general wpa_supplicant.service.

Step 4: DHCP and systemd-networkd

As I said earlier, this is only the first step to getting internet access. We still do not have the actual IP address necessary for access to any other machine. There are ways to do this semi-manually as well but there’s not much point to it so I’m going to skip ahead to the simpple systemd-networkd configuration. With a single .network file in /etc/systemd/network you should be good to go. Here are the basics:

[Match]
Name=wlp5s0

[Network]
DHCP=ipv4

Match the device/interface, run dhcp for an ip v4 address. Simple as. You can assign whatever name to the .network file you want. Be aware that if you place more than one .network file in the directory, networkd will try them out in alphanumerical order, so 10-xxxxx.network comes before 30-xxxxxx.network.

Enable the service as usual:

systemctl enable systemd-networkd.service

Test it by checking the service’s status and look for an assigned ip address or ping another machine on the network.

Step 4b: MAC spoofing

I also added some MAC spoofing at this point to be able to distinguish between the same network interface in Windows and in linux. You simply add another file with the same ‘first name’ as the .network file in the same directory and use the suffix .link. Here’s an example:

[Match]
 MACAddress=xx:xx:xx:xx:xx:4a

[Link]
 MACAddress=xx:xx:xx:xx:xx:4b
 NamePolicy=kernel database onboard slot path

The first MAC address (under Match) is the ‘real’ one. The second one (under Link)  is the spoofed one. Simple. There are more details on the Arch Wiki.

Step 5: Resolved and Ready

All that’s left is to take care of DNS. Check if systemd-resolved.service is running and disable and stop it if so. Remove the symlink /etc/resolv.conf and create a text file in its stead. The format is extremely simple:

nameserver [ip address]

I believe there is a limit of three servers. Following the advice of the Arch Wiki I went to https://www.opennic.org and picked the three suggested servers based on proximity.

There should be no need to restart anything after this edit although it may be advisable to restart the machine to flush any cached DNS.

That’s it. Enjoy a non-interrupted network experience.

2 Comments

It looks like those 2 mins periodic scans by NM can be disabled with explicitly set BSSID (= AP’s MAC) in the connection’s properties.

But there are also 5mins periodic scans. I don’t know where from does it come since it scans even when I issue a STOP signal to NM.

Here is an iwevent log. You can see 2mins- and 5mins periodic scans.
12:02:24.775587 wlan0 Scan request completed
12:02:36.171466 wlan0 Scan request completed
12:04:36.175501 wlan0 Scan request completed
12:06:36.171988 wlan0 Scan request completed
12:07:24.783494 wlan0 Scan request completed
12:08:36.171545 wlan0 Scan request completed
12:10:36.171501 wlan0 Scan request completed
12:12:24.783498 wlan0 Scan request completed
12:12:36.171543 wlan0 Scan request completed
12:14:36.171607 wlan0 Scan request completed
12:16:36.173097 wlan0 Scan request completed
12:17:24.787530 wlan0 Scan request completed
12:18:36.171560 wlan0 Scan request completed
12:20:36.171531 wlan0 Scan request completed
12:22:24.787529 wlan0 Scan request completed
12:22:36.171528 wlan0 Scan request completed
12:24:36.172065 wlan0 Scan request completed
12:26:36.171520 wlan0 Scan request completed
12:27:24.787509 wlan0 Scan request completed

Any idea about the 5mins periodic scans?

No, sorry. But then as the article hints, I don’t think the solution is to work around NetworkManager but to replace it.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.