Linux Wireless Stack Overview

Categories: Linux


This article gives a brief overview of how wireless networking works on Linux (as a client), and in particular which userspace applications communicate with the kernel in which way.

Only the typical client->access-point (ie “AP mode” or “infrastructure mode”) connectivity is discussed here, where some central device (often a router) acts as the hub of a wireless network, and devices (eg laptops) connect to it as clients.

For information on rarer setups such as a Linux server being an access-point, or ad-hoc networks, you’ll have to look elsewhere..

The Components

A rough diagram of the general architecture (see later text for more detail):

A wireless driver plugs in to the network stack like wired drivers do - a source and sink of layer-2 network packets.

The “state” of the wireless link (bringing it up) is managed outside of the wireless stack. Selecting which base-station to connect to, and negotiating connection parameters (in particular, encryption keys) is done by the userspace wpa_supplicant application. Allocating an address dynamically is done by a dhcp client (mostly same as with fixed networks, with a few quirks).

There is usually another layer over wpa_supplicant and the dhcp client which manages “policy” for connections, ie which connection to choose when (wpa_cli, systemd-networkd, NetworkManager, Wicd, etc).

User interaction (eg prompting for a password) is done by yet another layer.

The ifupdown tools and related scripts are discussed, but are rarely used for managing wireless connections these days.

Device Drivers

The kernel scans the PCI bus on startup, will detect wireless devices (even on-board ones will be accessible via the PCI bus), and will create sysfs entries for them. Normally, udev will then load the relevant drivers automatically, those drivers will load any necessary firmware, and the drivers will register the “physical network interfaces” with the network stack. However these interfaces will be in a “down” state until userspace takes relevant action to activate them.

The following device-drivers are needed for wireless:

  • mac80211 - provides a common framework for wireless drivers - ie used by the device-specific driver, and provides file /proc/net/wireless
  • cfg80211 - provides generic configuration API for wireless drivers; see nl80211/cfg80211 later in this document.
  • device-specific drivers, eg for modern intel wireless chips, iwlwifi and iwlmvm.

Once all device-drivers have been correctly initialised:

  • file /proc/net/wireless will have an entry for the interface
  • file /proc/net/dev will have an entry for the interface
  • command ifconfig --all will show the available interfaces

There are two main APIs used by userspace to communicate with wifi drivers: nl80211/cfg80211 (new) and wext (old). Most actively-maintained drivers support nl80211/cfg80211; most also support the older wext API.

The nl80211/cfg80211 interface is based around passing messages over a netlink socket (thus the ‘nl’ prefix). The advantage of a netlink-based design is that it is well suited for streaming a sequence of events from kernel to userspace. Userspace can also send messages in the other direction to perform configuration operations. See /usr/include/linux/nl80211.h for more details.

The older wext (“wireless extensions”) interface is based on ioctl operations on a network socket filedescriptor. The socket state doesn’t matter much, and is usually never bound to anything or otherwise used; it is just a handle which causes the ioctl operations to be forwarded to the kernel network stack. The datastructure passed to the ioctl normally specifies the name of the interface which the operation is to be applied to. Unlike the nl80211 API, there is no easy way to “subscribe” to events from the kernel. The wext interface has fewer features than the nl80211 interface, and is effectively deprecated.

Userspace tools (eg wpa_supplicant) then use these APIs to talk to compliant device-drivers.

The physical radio-handling circuitry of a wifi network device is often referred to as a “PHY”.

One important part of wireless driver management is “regulation compliance” which ensures that the frequencies and broadcast strengths used by the driver comply with the laws of the location in which the device currently is.

SSIDs and BSSIDs and other networking details

Before we go further, here’s some quick background/definition-of-terms for important wireless terminology.

An SSID is a “logical network name”. Multiple access points (APs) can provide the same logical SSID, but each AP will have a unique BSSID address. Normally, a client attaches to an SSID and the wireless software automatically chooses the strongest signal (ie the best BSSID).

A station is any device capable of performing wifi operations (whether as client or server).

Wireless networks are usually based around access points which act as “servers” that client devices connect to; this is also called “infrastructure mode”. In this setup, each access point is the hub for multiple clients, and the access point normally has a wired connection over which it forwards data to the internet. Each access-point has a unique MAC address. An access-point periodically sends out beacon frames which announce its existence to other nearby devices (clients); these frames are sent on on the channel (frequency) that the access-point uses for its network. Clients are expected to listen for a short period on each channel in turn, in order to detect these beacon frames. Clients can also “actively probe”, ie send out a “probe request” without having seen a beacon frame, and the access-point will respond if there.

A beacon frame includes the BSSID (aka MAC address) of the access-point and the SSID (logical network name) that it offers.

An access-point can be configured with a “hidden” SSID, in which case the beacon frames are sent with an empty SSID field. Any client device which wishes to connect to a hidden network must look for beacon frames without SSIDs and then perform a connection attempt to each one, until it finds the right access-point. During the connection-attempt, the SSID is sent by the client in plain-text, thus revealing its existence; as a result hidden SSIDs are not very effective.


The Kernel IP Network Stack

The Linux kernel has a large subsystem dedicated to managing MAC-based and IP-based network traffic. Describing this layer is out-of-scope for this document, except to mention that:

  • it exists
  • drivers for network devices register with it
  • it provides a number of files in sysfs that provide information about the network state
  • opening a “network socket” causes the network stack to return a file-descriptor
  • syscalls which operate on a network-related file-descriptor are forwarded to the networking code
  • it provides asynchronous notifications of some interface changes via netlink sockets

In general, drivers take care of “layer 2” details (MAC layer), and leave the responsibility of building or parsing layer-3 packets to the networking stack.

The network stack maintains a set of “state flags” for each interface which userspace can query via ioctl or sysfs/procfs (see headerfile linux/if.h):

  • IFF_UP : the “administrative status”, ie whether the system/sysadmin wants the interface to be active
  • IFF_LOWER_UP : physical layer active, sometimes referred to as “carrier present”
  • IFF_RUNNING; the “operative status”, ie actual interface state - whether it is correctly initialised (when IFF_UP is false, this should be false too).

Tools may set/clear IFF_UP. Flag IFF_LOWER_UP is read-only. I’m still not sure about IFF_RUNNING..

The current flags for an interface can be seen in the output of /sbin/ifconfig -a.

TODO: can DHCP only be done on interfaces with IFF_MULTICAST?

See: Kernel docs: networking operstates


Udev is responsible for loading the correct wireless drivers for the available hardware, as usual.

Udev also has rules to assign “stable” names to interfaces; it (somehow) allocates names like sit0 (wired) or wlp3s0 (wireless) to the interfaces, where these names are based on which physical location the devices are attached to the PCI bus at, rather than the order that they happened to be registered with (as was the case with the old eth* and wlan* style names). Prefix “wl” stands for “wireless lan”; “p” identifies the PCI bus, and “3s0” is the address on the bus (try “lspci”). Prefix “ww” is for wireless wan (not very common). Within a container, virtual (“veth”) interfaces are named ve*.

The old-style names can, however, be restored (stably) by defining relevant (per-machine) udev rules which map the stable names assigned above to any desired strings (eg sit0->eth0 and wlp3s0->wlan0). Alternatively, “.link” files can be created under /etc/systemd/network on any udev-based system (doesn’t require systemd-init) which can assign a name based on many criteria including pci-path and/or mac-address; see “man”.

Udev does not create any device-nodes in /dev (or anywhere elsewhere) for network drivers; userspace interacts with network interfaces via the socket call, the bind/listen/etc calls, ioctl calls and netlink sockets.

If certain network interfaces require parameters to be set which other tools (eg wpa_supplicant or dhcp-client) don’t set themselves, then a custom udev rule can be written; the rule runs when the interface is first registered by the kernel and can use tools like ifconfig to push any desired settings into the interface/driver.

Network-related Syscalls

The socket systemcall allows connections to many different kernel components. When the “domain” parameter is AF_INET, then the file-descriptor returned is associated with the IP network stack. Later invocations of socket-related syscalls such as bind, accept, listen, read or write on that file-descriptor will be forwarded to the network stack for handling.

Applications using the socket calls to send or receive data over a network don’t need to be aware of the fact that the network is carried over a wireless link.

Note that applications do not interact with the wireless driver via a device-node; instead they use the socket-related system calls. Neither the udev daemon nor any other tool creates any device-nodes for wired or wireless network interfaces.

The ioctl systemcall can be applied to any filedescriptor, and specifies the “type of request” as an integer that is interpreted by whichever subsystem is associated with that filedescriptor. When the file-descriptor is associated with the networking subsystem then there is a large number of possible request-types, including operations to set the IP address and network mask, to set the interface “state flags”, etc.

A userspace application can get asynchronous notifications of changes to interfaces by creating a netlink socket; first create a filedescriptor with socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE) then subscribe to notifications via bind(sock, struct sockaddr, size) where the sockaddr has family=AF_NETLINK and groups=RTMGRP_LINK. In particular, change of link state from !IFF_UP to IFF_UP triggers a notification message. A userspace app can also update some driver properties by sending netlink messages to the kernel. AFAICT, this is done to set the IFF_RUNNING flag. See the following references for more detail:

Wifi Scanning

To connect to an access point, it is first necessary to find it.

As noted, access points periodically send out “beacon frames” with a BSSID (ie MAC address) and SSID (logical network identifier); the interval is typically 100msecs. A wireless driver can passively scan by listening for a short period (100msec or so) on each channel (frequency) the physical radio circuits support. A wireless driver can also actively scan by sending out probe frames on each channel; the probe can contain an SSID in which case just that access-point will respond or can include an empty SSID in which case every access-point on that frequency will respond (“wildcard scan”).

When performing an active scan, the driver needs to be given a list of SSIDs to probe for (marked as scan_ssid=1 in wpa_supplicant.conf, see later). There is a (driver-specific) maximum number of such SSIDs that can be specified per scan.

As noted, a beacon frame for a hidden network will have an empty SSID, which isn’t much use. To find hidden networks, an active scan must be used rather than a passive one, ie send out probes for specific SSIDs.

Scanning (passive or active) can be triggered over the nl80211 API via netlink messages NL80211_CMD_TRIGGER_SCAN or NL80211_START_SCHED_SCAN; the SSIDs to actively probe for can be specified with optional nested NL80211_ATTR_SCAN_SSIDS values.

Scanning can be triggered over the wext interface via an ioctl, but the nl80211 API is more flexible/featureful.

TODO: it is a common case for a hidden network to be provided by just a single device/BSSID (home router scenario). In this case, it would be nice to probe for that SSID only if a passive-scan reveals that the BSSID is present (to avoid probing for that SSID when it is not in range). I wonder if this is supported by wpa_supplicant…

Activating an Interface

Making a wireless interface “active” involves a careful dance of ifconfig, iwconfig and scripts (often managed via ifup).


This command comes from the ‘inetutils’ package, and is maintained by the GNU foundation.

The ifconfig uses procfs (/proc/net/dev and /proc/net/wireless) to view the state of the network stack, eg which interfaces exist and which are “up”.

ifconfig can also be used to configure properties associated with an interface, eg:

  • mark an interface as “up”; this just sets a flag in the kernel network stack to indicate the system wants the interface to be active
  • assign static IP addresses to interfaces: ifconfig eth0 will initialise eth0 (and implicitly mark it “up”). Other address-types can be specified too, by providing an “aftype” parameter.
  • many other properties

The ifconfig program uses ioctl syscalls of form ioctl(int sfd, SIOCSIF*, struct ifreq) to update the driver parameters. The “struct ifreq” is reused multiple times, but one thing it always has set is the ->name field which is the network interface name. The “sfd” value is an arbitrary AF_INET socket filedescriptor.

Calling “ifconfig someiface up” triggers an ioctl call with SIOCSIFFLAGS, passing a “struct ifreq” containing the interface-name and the new set of flags including IFF_UP. Calling ifconfig someiface triggers an ioctl with SIOCSIFADDR, passing a “struct ifreq” containing the interface-name and the new IP address for that interface. The file-descriptor used in that ioctl call must be associated with the networking stack (ie be a network socket filedescriptor) but doesn’t need to be “bound” to anything.

Marking an interface as “up” doesn’t actually achieve much. The network stack won’t let the interface be used unless it is marked “up”, but marking an interface as “up” isn’t enough to make it usable - it must also have appropriate configuration parameters set, have an IP address, and in the case of wireless have a valid encrypted “session”.

Interestingly, on some systems the “loopback” interface (normally called “lo”) is configured on startup via: ifconfig lo up

ifup and ifdown

The “ifupdown” package provides applications named ifup,ifdown,ifquery (these are all actually symlinks to the same executable). These applications purely manage invocation of a set of “hook scripts”; they do not communicate with the kernel in any way, do not interact with device-nodes, etc. - though the scripts they execute might. Usually, some script will invoke “ifconfig someiface up” to actually enable an interface.

NOTE: ifupdown is not widely used in modern desktop or mobile systems (replaced by NetworkManager, ConnMan, Wicd, etc); in particular it does not work very well with wireless. It might be useful in some servers. It is interesting background but if your interest is primarily on wireless-for-laptops then you can skip this section (and ignore future references to ifupdown) if you wish.

The file /etc/network/interfaces defines the set of interfaces that ifup and family will manage, and the associated parameters.

The ifupdown commands are always passed physical interface names on the commandline; the config file can have entries of form “mapping {phys_iface_name}” which allows a user-defined script to be specified which can map a physical interface name to a logical name. If no “mapping” entry matches the physical interface specified on the commandline, then logical-name=physical-name.

Each “iface {logical-name} {addrfamily} {method}” entry is followed by zero or more lines holding a (key,value) pair. These define “options” that will be passed to scripts as environment-variables. Each line gets transformed to an environment variable of form “IF_key=value”. However if the option-name matches a phase (see below) then it must instead have a value which is the name of a script to run.

ifupdown defines the following “phases” at which scripts are run: “pre-up”, “up”, “post-up”, “pre-down”, “down”, “post-down”. When a command like “ifup eth0” is run:

  • the ifupdown “state cache file” /run/network/ifstate is checked to see whether the interface is currently up (if no entry is there, then it is down).
  • the mappings from the config-file are applied to convert physical name eth0 to a logical name (if none, logical=physical)
  • the iface entry for that logical name is located, and the keywords on the same line plus the following list of “options” are read
  • for each “phase” (in this case “pre-up”, “up”, “post-up”), invoke every script under /etc/network/if-{phase}.d, passing it the following environment variables:
    • IFACE: the physical interface name
    • LOGICAL: the logical interface name
    • ADDRFAM: the address family (usually “inet”)
    • METHOD: how to allocate a network address: “dhcp” or “manual”
    • MODE: “start”
    • PHASE: the phase
    • some other standard parameters including VERBOSITY,PATH
    • the “options” from the iface definition in the config-file (with key prefixed with IF, eg IF_ADDRESS)
  • finally, a new entry is added to /run/network/ifstate of form “phys_iface_name=logical_iface_name”, to mark that the interface is “up”.

If extra “option” parameters are passed on the commandline (eg “-o FOO=bar”) then those are treated just as if they were in the config-file, ie converted to environment-vars of form “IF_FOO=bar” - unless the key is one of the magic “run a script” names.

Running ifdown is similar; the mode is “stop” and the phases are (“pre-down”,”down”,”post-down”). At the end, the corresponding line is removed from /run/network/ifstate.

When invoked as “ifup -a”, the above process will be applied to every “iface” definition in the config file which is marked as “auto”. The standard boot process invokes (or used to invoke?) “ifup -a”. This is similar to auto-mount entries in /etc/fstab.

Note that /run/network/ifstate can potentially get out-of-sync with the real state of the network interface (as reported by ifconfig which really queries the kernel). I don’t know why ifupdown bothers to keep its own cache of interface state when it is so easy to query the real thing…

Note that although ifupdown supports the METHOD parameter (“dhcp” or “manual”), it doesn’t itself do anything with it; that is just a string that some script might be able to do something with.

NetworkManager replaces the ifupdown tools, but does runs some of the ifupdown scripts (at least partially); see section on NetworkManager for more details.

See “man interfaces” for more information.

Wireless Extensions (aka “wireless-tools”)

There is a software package called wireless extensions for Linux which provides programs named iwconfig, iwspy, etc. This is partly related to the “wext api” used to communicate with wifi drivers: the applications included in this package use the wext ioctl operations. However other tools also use that API (and the api deprecated anyway, in favour of nl80211).

As with ifupdown, this software is not usually used on modern desktop systems, so this section can be skipped if you are interested in wireless-on-laptops.

This package also provides some ifup/ifdown scripts related to wireless, which depend on iwconfig, ifconfig. These scripts get installed under /etc/network/if-{phase}.d. On “ifup” these scripts execute the following during phase “pre-up”:

  • check whether the specified interface is wireless; if not it does nothing
  • set driver parameters using iwconfig
  • tell the kernel to enable the interface via “ifconfig iface up” (which, as mentioned earlier, performs an ioctl with request-type SIOCSIFFLAGS to update the “flags” associated with the specified interface).

Here’s a nice comment from the script “/etc/network/if-pre-up.d/wireless-tools” on my debian system:

# The wireless driver madness:
# - Some drivers want everything to be configured
#   before bringing the interface up
# - Some drivers want everything to be configured
#   after bringing the interface up
# - Some drivers want some parameters before, other
#   parameters after bringing the interface up
# So, we try to set every parameter when the interface is still down,
# and remember which ones failed to be configured properly.
# If some failed, we bring the interface up, and try the failed ones again.

That script then uses iwconfig to set parameters, ifconfig up to activate the interface, then iwconfig to set all parameters again.

All the parameters necessary for the wireless driver (eg WIRELESS_MODE, WIRELESS_RATE, WIRELESS_CHANNEL) can be specified in the “iface” block in file /etc/network/interfaces; they will be passed to the wireless-extensions script as environment variables and the script passes them on to the driver. Alternatively, such “static” settings can often be performed from a udev rule when the interface is first registered.

Note that this step doesn’t actually select a “base station” to connect to, ie the network interface is “up” but not yet connected to anything. That is the job of wpa_supplicant..

If a fixed IP-address is desired for the wireless interface, then that address should simply be defined in the /etc/network/interfaces file, and the wireless-tools script will pass those values through to the network stack via iwconfig/ifconfig. However in most cases, DHCP is desired for wireless networks, in which case a later layer needs to take care of that.


wpa_supplicant handles choosing and signing in to a wireless access-point. Once this process is complete, wpa_supplicant sets the IFF_RUNNING flag on the interface state then sits “dormant” (ie the wireless driver does the actual encryption/decryption) unless something significant happens such as loss of connection to the access-point.

wpa_supplicant has a number of other interesting features that will not be discussed further in this article:

  • it can manage the server side of a wireless connection, ie be the access point that other wireless devices connect to. This requires wpa_supplicant to be compiled with the CONFIG_AP option. The functionality is limited (more featureful tools such as hostapd are recommended by the wpa_supplicant documentation), but it is apparently sufficient to support using a normal Linux PC as a wireless access point.

  • it can set up ad-hoc/peer-to-peer/mesh wireless networks in which a group of stations network themselves without needing a “master access point”. This is particularly interesting for devices like mobile phones and tablets.

  • it can be used to perform EAP (Extensible Authentication Protocol) over a wired network; EAP was originally invented for wired networks. The documentation mentions the special configuration settings needed for this, in particular “ap_scan=0”.

AFAIK, there are no competing open-source projects to wpa_supplicant. It is used in systems based on ifup/ifdown, spawned by dhcp-clients, and used by NetworkManager/Wicd/etc. It has a very clearly-delimited role (connect to access-point, encrypt, login) and every higher-level network connection tool relies on it for this role. It is therefore important to understand it.

The authors of this software explicitly intend it to be portable across many operating systems (including Windows); it therefore has some features that feel slightly odd in a Linux environment.

This software package includes the main “wpa_supplicant” application, a couple of helpers, and some scripts for use by ifup/ifdown. The scripts are installed into /etc/wpa_supplicant and some symlinks to those scripts into /etc/network/if-{phase}.d such that they get executed immediately after the “wireless extensions” scripts when “ifup” is executed. When a wireless interface is “started” (ifup), the wpa_supplicant-related script does the following during phase “pre-up” (but after wireless-tools has run, so the interface is actually marked up):

  • starts a new (background) instance of wpa_supplicant to manage that interface (see later)
  • if ACTION_SCRIPT is defined then starts a new (background) instance of wpa_cli to manage the “policy” part of roaming (choosing a new network to connect to after disconnect)

In other words, an instance of this application is created when a wireless interface becomes active, and remains running in the background until the interface terminates.

The running wpa_supplicant instance which is managing an interface can be communicated with via a local socket; client applications use this to send commands and reconfigure it. The concept is somewhat like dbus, but app-specific as wpa_supplicant is cross-platform. This socket is dynamically created in the directory specified by commandline option “-C”, or by entry “ctrl_interface” in the wpa_supplicant configuration file. By default, this inter-process-communication socket is named /run/wpa_supplicant/{ifacename}. Multiple “client apps” may connect to a wpa_supplicant instance via its socket concurrently.

An alternative approach is to run wpa_supplicant (usually as a daemon) without specifying any interfaces; it will then auto-detect and manage all available wireless interfaces which have been set to “up” (something the man-page doesn’t make very clear). In this case, a netlink socket is used to get notifications from the kernel when new interfaces appear, or existing ones are marked up (eg via “ifconfig iface up”). When a single wpa_supplicant process is managing multiple interfaces, it creates a separate “ctrl interface” socket for each network interface it is currently managing. Optionally, it can create a “global control socket”, but this isn’t the default.

When compiled on a platform that has DBUS, wpa_supplicant also provides a dbus-based interface for queries and commands. See commandline option -u.

wpa_supplicant can be given the location of a configuration file on its commandline; the standard location is /etc/wpa_supplicant.conf. The configuration-file specifies the “ctrl_interface” directory in which the comms sockets are created. It also has a network{..} block for each wireless network SSID of interest, holding the credentials associated with each one (a password in the simplest case).

When authenticating via password (ie using WPA_PSK), command wpa_passphrase can be used to encrypt the original plaintext password and generate a suitable wpa_supplicant configuration file.

The daemon does its work of scanning radio frequencies, negotiating keys, submitting passwords, etc. via a set of “pluggable back ends” (which it calls “drivers”) so that the same software can work on multiple operating-systems. The backends used on Linux are “wext” (old) and “nl80211” (new). As described earlier, nl80211 is based around sending/receiving packets over a netlink socket while wext is based around ioctl syscalls. See “main event loop” later in this section.

If wpa_supplicant is started with the explicit name of an interface, then it automatically sets interface flag IFF_UP (“up”) - clearly the system “wants” the interface up. When started without any interface names (“autodetect mode”), it does not set interfaces to up automatically; something else must mark them as “up” before they will be managed.

To configure an interface once it has been marked “up”, wpa_supplicant selects an SSID (possibly scanning all radio-frequencies first), tells the driver to connect to that SSID, sets up encryption, authenticates, and finally sets the interface state to IFF_RUNNING (“running”). When wpa_supplicant has completed its job, the wireless interface has a functioning layer-2 connection to a specific base-station, with an authenticated (and encrypted) session. However it does not yet have an IP (layer-3) address.

If the wireless connection should have a fixed IP address (rare) then the settings can be been defined in /etc/network/interfaces in which case the wireless-tools ifup script will already have passed those values through to the network stack. Alternatively the address (and netmask) can be assigned via “ifconfig iface address ...”. Tools systemd-networkd, NetworkManager, etc have their own ways of achieving the same goal. If a dynamically-allocated address (DHCP) is desired, then see the section on dhcp clients below.

wpa_supplicant handles switching between access-points that provide the same SSID, ie when one access-point goes out of range it can switch to another. The config-file also allows “priorities” to be assigned to networks, to cover the case where multiple known networks are available at the same time. However its configuration-file is not capable of expressing higher-level policy decisions; tools such as wpa_cli, systemd-networkd, NetworkManager/Wicd/ConnMan/etc can be used to perform that role. Of course if a system uses wireless but is always in the same location with the same networks available then those higher-level tools are not necessary.

As described earlier, the “backends” use a combination of ioctl operations (eg SIOCSIFLAGS), a subscription to NETLINK_ROUTE netlink messages (to detect iface state changes), and sending NETLINK_ROUTE messages (eg IF_OPER_UP to set IFF_RUNNING) to manage the interface state within the kernel. wpa_supplicant has a “main event loop” in which it blocks on a set of sockets until one of them has data available (select). The set of sockets includes the “ctrl interface” socket and the netlink socket subscribed to RTNETLINK events, ie changes in the status of kernel interfaces.

The wpa_supplicant configuration file has a number of options:

# path to directory in which to create "ctrl interface" sockets

# Should wpa_supplicant modify this file?
# Optional. Boolean (0|1), default=0 (ie false).
# When a running wpa_supplicant process receives a command via its
# "ctrl interface socket" which modifies the configuration, should
# it write that change back to this file?

# Specifies how to choose from the available networks
# Optional (default recommended). Integer(0..2), default=1
#   1: wpa_supplicant requests scan results from the driver then chooses the
#      "best match" from the list of network entries below. Note: works with
#      hidden SSIDs too (as long as they are marked scan_ssid=1)
#   0: Let driver choose the network to connect to. Used mostly for encryption
#      over a wired network - where the driver doesn't scan at all.
#   2: Just try connecting to the networks below in order until one succeeds
#      (no scanning). Not recommended with nl80211.

# Which version of the EAP authentication protocol should be used?
# EAPOL = Extensible Authentication Protocol Over LAN (aka IEEE802.1X),
# originally designed for wired networks but later applied to wireless.
# Optional (default recommended). Integer(1..2), default=2
#   1:  use old eapol for backwards compatibility with old devices
#   2:  use current eapol version 

# define a known network that might be available
        # Specifies the order in which scanned networks should be used when
        # multiple match; higher is better. For ties (multiple entries with
        # the same priority) network is chosen based on security-policy and
        # signal strength.
        # Optional. Integer.
        # Ignored when ap_scan=2; instead entries are tried in the order they
        # are defined in the config-file.

        # The logical network to connect to.
        # Mandatory.

        # Specify exact access-point device.
        # Optional (not normally needed).
        # Specifies which physical device to connect to, in case multiple devices
        # provide access to the same ssid. Of course this means that connection
        # to the SSID may fail if there is an access-point for that SSID within
        # range but it is not the one specified here.
	bssid=<M.A.C. of Access Point>

        # A whitespace-separated list of acceptable authentication protocols;
        # client selects a protocol that it and the server both accept.
        # If the server supports none of the listed protocols then the client
        # will not connect (a safety measure).
        # For each protocol specified here, corresponding entries containing the
        # related credentials must also be specified. When "WPA-PSK" is
        # specified, there must be a "psk=" line. For other protocol types,
        # other types of credentials are needed.
        # Mandatory: one of the supported protocols (see docs)

        # The PSK password (may be encrypted via wpa_passphrase or unencrypted).
        # May be a hex string (unquoted) or a plaintext string (quoted)

        # Whitespace-separated list of acceptable encryption protocols; client
        # selects a protocol that it and the server both accept. If the server
        # supports none of the listed protocols then the client will not connect
        # (a safety measure). Defaults to all protocols known to the client.
        # Optional but recommended (for safety)

        # Whether to "actively probe" for this SSID. Not needed for broadcast
        # SSIDs (though they can also be found this way); mandatory for hidden
        # SSIDs.
        # Optional: boolean(0|1): default=0

        # Specifies the kind of wireless connection to establish. Should always
        # be left at the default (0) unless you really know what you are doing!
        # Optional (default recommended). Integer(0..3).
        #  0: connect to an access-point (managed mode)
        #  1: set up adhoc network (peer-to-peer)
        #  2: be an access-point
        #  3: be a p2p group owner

        # More encryption protocol settings. Rarely needed.
        # Optional. Whitespace-separated list of acceptable protocols. 
	# pairwise=CCMP TKIP
	# group=CCMP TKIP

The primary source of the above information was files “config_ssid.h” and “config.h” from wpa_supplicant source-code.

The “ap_scan” and “scan_ssid” fields are not well described in the wpa_supplicant man-pages, and there appears to be a lot of bad advice on internet forums; a brief summary may therefore be useful.

As described earlier, a driver may perform a passive scan or an active scan. When ap_scan=1 and there are no networks with scan_ssid, then a passive scan is all that is necessary - ie the driver just needs to tune in to the different channels in sequence and listen long enough to receive the beacon-frames which access-points regularly transmit (normally every 100msecs or so); the frames contain both BSSID and SSID values. If an access-point has a hidden SSID, then the SSID in the beacon frame is blank. Alternatively, the driver may be given a list of SSIDs to scan for, in which case it switches to “active scanning” and for each channel it supports it sends out a “probe” for each SSID on the list; each access-point supporting that SSID will send a response (there may be multiple, each with a different BSSID) - even if the SSID is a hidden one. Note that the SSID is in “plain text” because every access-point needs to be able to read it to determine if the probe is for it. The list of SSIDs may contain an empty string, in which case a “wildcard probe” is sent which triggers a response from every access-point - but not for hidden networks.

wpa_supplicant retrieves the results of the scan, chooses a BSSID and tells the driver to connect to it. The choice is guided by the “priority” field in the network-definitions from its configuration file, the signal strength, the encryption protocols available, etc.

The above explains why a network{..} block describing a hidden SSID must use “scan_ssid=1”; it needs to turn on active scanning in order to see whether any access-points serving that SSID are in range.

It also explains why a “hidden network” is not so hidden; probes for that SSID are sent in plain text.

TODO: I can imagine several ways in which a connection to a hidden network could be made more efficient. None of these ideas are complicated, so I presume there are good reasons why they are not implemented but would very much like to know why:

  • When the BSSID for the hidden network is known (common in the case of a home-router, where there is only one BSSID for that SSID), passively scan for frames with that BSSID and only connect if that is present (prevents probing for hidden networks that aren’t there, thus revealing their existence when they are out-of-range)

  • Scan all beacons to find all BSSIDs where the SSID is hidden, then send a connection request to each one in turn. The beacon frame should be able to provide an encryption key that can hide the SSID used in the connection request.

Option ap_scan=0 tells the driver to “connect to a network” and lets the driver do the rest. This assumes the driver has been pre-configured with all the necessary details. Used mostly with wired connections where the driver isn’t actually connecting to anything. Option ap_scan=2 tells wpa_supplicant to iterate over all networks defined in the config file, telling the driver “try connecting to this SSID”. There is no control over which BSSID is chosen. Mode ap_scan=2 was really designed to support the few devices/drivers which cannot handle “active scanning” with a list of SSIDs; however these are considered “broken” and the nl80211 protocol does not (currently) support the ap_scan=2 mode.

If wpa_supplicant reports an error, eg “denied association (code=n)”, then the code-definitions can be found in the Linux kernel source, under /include/linux/ieee80211.h. In particular, leaving out the “key_mgmt” entry leads to “denied association (code=43)”.

It can be interesting to try running wpa_supplicant manually. Shut down the wireless interface, ensure that no other app is managing it (eg kill any background wpa_supplicant or dhcpcd instances), write a wpa_supplicant config-file, then run:

ifconfig -a

# -d or -dd gives more output
# the -D is optional if only one "back-end" has been compiled-in, or the desired
# backend happens to be the first one (see wpa_supplicant --help).
w(pa_supplicant -i someiface -c /path/to/config/file -Dwext 

and in another window run /sbin/ifconfig; the interface should be UP and RUNNING with a MAC address but no IP address.

The easiest way to shut down a wpa_supplicant instance is via wpa_cli -i {iface} terminate. Command killall wpa_supplicant also works fine (and does a clean shutdown).

If using systemd, you may need to do the following (stop isn’t sufficient as the service could just be triggered again):

systemctl --runtime disable systemd-networkd.service
systemctl stop systemd-networkd.service
systemctl --runtime disable wpa_supplicant.service
systemctl stop wpa_supplicant.service

Notes on systemd: wpa_supplicant may be run in two different ways:

  • as unit “wpa_supplicant.service” which starts one global daemon instance at boottime to manage all interfaces;
  • by creating an interface-specific service which starts an wpa_supplicant instance for that interface.

The “global daemon” approach is the common case (and possibly is required when using NetworkManager). A global daemon ignores all interfaces until they have been (somehow) marked as “up” (which means system-wants-this-interface-up). On systems using dhcpcd, the dhcpcd hook-scripts can start wpa_supplicant - but that step is skipped if an wpa_supplicant instance is already active for that interface (eg the interface has been marked up and so the global daemon is managing it).

The “interface-specific” services can be created using the “wpa_supplicant@.service” template, ie via:

systemctl disable wpa_supplicant.service
systemctl stop wpa_supplicant.service
systemctl enable wpa_supplicant@wlan0.service
systemctl start wpa_supplicant@wlan0.service

This creates a symlink “wpa_supplicant@wlan0.service” which points at the “wpa_supplicant@.service” file. At runtime the file will be invoked with “wlan0” as a parameter, which the template-file then uses. Alternatively, a real “wpa_supplicant@wlan0.service” file can be created by hand, and tuned as desired. Note that the global daemon has been disabled; there have been reports that having the global service and interface-specific services running at the same time causes problems.


dhcp clients

There are two commonly-used tools for allocating IP addresses from a remote DHCP server: dhclient and dhcpcd. Both do the same job. In fact, writing dhcp-clients seems to be a widespread hobby; there are quite a few implementations to choose from.

All DHCP clients will create or overwrite file /etc/resolv.conf as soon as they receive network config parameters from a remote DHCP server - unless the resolvconf daemon is running, in which case they send a message to that daemon instead. The glibc functions which look up addresses all use /etc/resolv.conf.

One of the easiest ways of getting wireless up and running with DHCP is to use dhcpcd as described at the end of that section below.


dhcpcd must be started with the name of the interface it is to configure. It then runs in the background, managing the address of that interface.

dhcpcd has its own “event scripts” system, similar to ifupdown. It provides some that integrate with wpa_supplicant:

  • when dhcpcd starts, the script tries to start wpa_supplicant if an instance isn’t already managing that interface (ie if no “ctrl interface” socket exists for that interface)
  • when dhcpcd exits, the script stops wpa_supplicant
  • when the address changes, the script sends a “reconfigure” message to wpa_supplicant

At least in the “standard” dhcpcd download, the software package does not include any scripts to hook it into the ifupdown system (so that “ifup” on an interface also runs dhcpcd, which seems desirable). Maybe in the distro-specific versions? However for most people this is not important as there are better ways of managing wireless interfaces than through ifupdown.

After running its hook-scripts on startup, dhcpcd waits until the specified interface reaches RUNNING state. This is normally the responsibility of wpa_supplicant - either the interface-specific instance which a hook-script just started, or an already-running background daemon. In the case where a background-daemon exists, the interface needs to be somehow set to “up” first (eg via ifconfig {iface} up) so that wpa_supplicant will start managing it. When no background daemon exists, then the dhcpcd hook-script starts an instance which will automatically set the interface to up then try to manage it (select an access-point, etc).

WARNING: Having a background-daemon active, then running dhcpcd before the interface has been marked as up seems to cause problems. At that point, there is no wpa_supplicant “ctrl interface” socket for that particular interface (a wpa_supplicant daemon only creates that after the interface has been marked up), so dhcpcd spawns a new wpa_supplicant instance. But there are now two instances, which doesn’t seem to work well.

If environment variable $wpa_supplicant_conf is defined before running dhcpcd, then the new wpa_supplicant instance is started with that file. Otherwise dhcpcd looks for wpa_supplicant files in the following places and starts wpa_supplicant with the first file found:

  • /etc/wpa_supplicant/wpa_supplicant-$interface.conf
  • /etc/wpa_supplicant/wpa_supplicant.conf
  • /etc/wpa_supplicant-$interface.conf
  • /etc/wpa_supplicant.conf

dhcpcd has a very small amount of wireless-specific code in it; it uses an ioctl to fetches the SSID of the base-station the interface is connected to. This ssid is then used when selecting an appropriate “profile” from the dhcpcd config-file - ie this allows different settings to be used depending on which base-stations the interface has connected to.

dhcpcd uses ioctl calls for the following purposes:

  • detecting if the interface exists at all (if_carrier)
  • reading the “FLAGS” field of the interface
  • setting the IFF_UP flag of the interface (once dhcp detects that “carrier” is available)
  • reading the wireless SSID that the interface is connected to
  • to get or set the “maximum data frame size” (MTU)
  • to set the IP address once one has been obtained from the dhcp-server

Note that running dhcpcd on an interface implies that you want the interface up, ie ifconfig {iface} up is not necessary - unless a “global wpa_supplicant daemon” exists.

The DHCP IPv4 protocol involves sending IP datagram messages with source address of and destination of - but this is done before the kernel interface is initialised with an IP address which is tricky. dhcpcd opens sockets of type AF_PACKET and uses the packet syscall to send manually-built IP datapackets across the network.

As noted earlier, dhcpcd waits for the interface to reach state IFF_RUNNING before attempting to contact a DHCP server. The waiting is done by blocking on the NETROUTE netlink socket, ie waiting for the kernel to notify dhcpcd of a change to the interface (see “main event loop”). A wired interface will go to IFF_RUNNING all by itself, while a wireless interface needs wpa_supplicant to complete its job then set the interface state to show that it is done.

dhcpcd has a “main event loop” in which it blocks waiting for activity on a set of sockets (select). The sockets include the netlink socket. see function eloop_event_add(ctx, fd, fn, data) where a socket is added to the list of sockets to wait on, and “fn” is a function-pointer that should be invoked to handle data present on that socket.

dhcpcd and wpa_supplicant together are sufficient to manage wireless networking on a simple system - ie “higher levels” of software are not absolutely necessary. All that is necessary is:

  • create a suitable wpa_supplicant config-file and save it as /etc/wpa_supplicant/wpa_supplicant-{iface}.conf
  • run dhcpcd {iface}

You should first ensure nothing else is messing with interface, eg run all of the relevant steps below (as root):

# for systemd
systemctl stop systemd-networkd.service
systemctl stop wpa_supplicant.service

ifconfig {iface} down
killall wpa_supplicant


dhclient is similar to dhcpcd. However it supports a list of interfaces to manage, ie a single instance can manage multiple interfaces. And if no interfaces are listed on the commandline, dhclient will try to autodetect the list of interfaces.


The wpa_cli application is part of the wpa_supplicant package. It has three quite different “modes” of operation:

  • a CLI tool which sends a command or query to a running wpa_supplicant instance then exits;
  • an interactive tool for configuring or querying a running wpa_supplicant instance (ie it accepts a sequence of commands from the user and doesn’t exit until the user tells it to).
  • a background daemon that executes an “action script” when the state of a wireless interface changes;

The first two modes are reasonably obvious; the third is more interesting. As noted in the section on wpa_supplicant, basic disconnect/reconnect is handled but the wpa_supplicant config-file doesn’t provide any way for the sysadmin or end-user to encode more complex “policy” about which networks to connect to when. The “action script” which is invoked when network connection is lost gives a place for a sysadmin to encode any logic they like, to influence the choice of network.

When /etc/network/interfaces includes a variable WPA_ROAM in the network {..} section for a wireless interface, then the wpa_supplicant ifup script will:

  • use the file pointed to by WPA_ROAM as the wpa_supplicant config file; and also
  • start an instance of wpa_cli as a daemon process with /sbin/wpa_action as its “action-file”.

In all cases, wpa_cli connects to the wpa_supplicant instance via its “ctrl interface” socket, which is usually named /run/wpa_supplicant/{ifacename}.

If a wireless interface “goes down”, eg the driver loses connectivity to the base-station, then wpa_supplicant will send a DISCONNECTED message over its “ctrl interface” socket. A wpa_cli instance which has been started with “-a some-action-file” will then execute the specified scriptfile, passing some parameters. Similarly, when wpa_supplicant detects that the driver has restored connectivity to a base-station, it sends a CONNECTED message, and the connected wpa_cli daemon (if any) will invoke its “action script”.

Interestingly, the standard /sbin/wpa_action action-script responds to CONNECTED by:

  • invoking ifup someiface!
  • and on ifup-failure, invoking wpa_cli -i someiface reassociate - ie using wpa_cli in “run once” mode to send a message to the wpa_supplicant instance!

I’m still not entirely clear on the details of wpa_cli and its action-script - but I don’t care all that much as most modern systems instead use NetworkManager, ConnMan, Wicd or similar to manage networks rather than this rather complex approach.

Question: if I understand the scripts correctly, WPA_ROAM is only supported when the iface definition in /etc/network/interfaces specifies METHOD (ie ip-allocation method) to be “manual”. I presume that means that something in the wpa_cli/action-script environment will trigger dhcp-based address allocation (instead of the usual ifupdown scripts, ie METHOD=manual disables normal dhcp processing). However I can’t find anything in the action-script or elsewhere that runs a dhcp-client. The wpa_supplicant program certainly does not do it.


This is the most recent of the higher-level network management daemons. The primary motivation for its creation was to have a simple method of getting wired networking configured in a container. However it can be useful in other environments too, as long as the networking is not too complex.

A container has its own independent user-space, but shares the kernel with the host. Nevertheless, a Linux kernel network namespace is normally used so that a container does not see the standard network interfaces defined in the host. Instead it sees only one (or more) interfaces which are actually “veth” interfaces which effectively act as a network card connected to a virtual network switch in the host. All a container needs to do is set this interface “up”, and send DHCP requests over this interface to allocate an IP address (the host will provide a simple DHCP server at the other end). Containers should be lightweight and quick to start, so bypassing things like ifupdown or even NetworkManager is a good idea.

If systemd-networkd is running, and an interface appears or changes then it consults configuration files under /etc/systemd/network/ and /lib/systemd/network/. When such a network interface appears in the kernel, networkd iterates over all files named “*.network” (by convention, the files’ base name is the interface-name). When any file whose [match] block matches the interface, then the interface is marked “up” (ie systems-wants-it-up) and the rest of the file is applied; the most common matching-term is the interface name. The “body” of the configuration can set static network address/netmask, gateways, etc. Or it can specify “DHCP=yes”. When a wpa_supplicant global daemon is running, then marking the interface up will get its attention; the wpa_supplicant configuration file will be used to determine which access-point to connect to (with or without scanning), and on success the interface will be moved to state RUNNING at which point an address can be assigned via DHCP. Alternatively, when a wpa_suppplicant@someiface.service exists, then an interface-specific wpa_supplicant instance will be started as the specified iface is registered (without needing it to be moved to “up” first).

systemd-networkd has its own internal DHCP client (and DHCP server too, for use in a host to configure containers, but that is irrelevant here). See file “src/libsystemd-network/sd-dhcp-client.c” within the systemd sourcecode.

AFAIK, there is no nice GUI interface for configuring systemd-networkd. The *.network files need to be written by hand, and for wireless support the wpa_supplicant.conf file also needs to be written by hand. So for “normal users”, one of the friendlier tools such as NetworkManager are better. However for servers and containers this is efficient and appropriate.

Special systemd/network/*.link” files can be used to define properties to be set as soon as an interface is registered by the kernel, and are basically an alterative to writing custom udev rules for interfaces. They are structured like .network files, with a match section first; there are several options including by-mac-address and by-pci-path. These “link files” are not often needed. A single udev rule which specifies “net_setup_link” causes these rules to be executed, ie they are (indirectly) executed as part of udev processing, not systemd-networkd. Note that udev does not run in containers, so this functionality won’t work there - though networkd still does.

networkd has a number of other features which are not relevant to wireless-networking.

Using networkd is not absolutely necessary, even in a systemd-based system; wpa_supplicant + dhcpcd do the job fine. Nevertheless, the systemd-networkd approach is fairly minimal while still being powerful.

To temporarily stop systemd-networkd etc (so you can play with dhcpcd and wpa_supplicant manually):

systemctl stop systemd-networkd.service
systemctl stop wpa_supplicant.service



Most modern desktop systems use the NetworkManager daemon to manage network interfaces. This application runs in the background (as root), trying to keep all relevant interfaces working based on the set of configuration files in /etc/NetworkManager/system-connections (which are similar to systemd-networkd “.network” files, and similar in purpose to ifup’s /etc/network/interface). NetworkManager does not directly provide a user-interface or commandline interface; it provides a DBUS interface which allows external commandline or GUI tools to talk to it.

As noted in the wpa_supplicant section, wpa_supplicant handles basic disconnect/reconnects when roaming but doesn’t support “policy” decisions. NetworkManager tries to provide “sensible heuristics” which connect the machine to the network that the user is most likely to want - eg:

  • when a wired interface is available, it doesn’t bother to bring up wireless too
  • when it brings up a wireless interface, it tries to connect to the interface you were most recently connected to
  • when many wireless networks are available, it tries those you have previously logged into first

It also emits dbus messages which allow user interfaces to pop up and prompt for network passwords when needed. However dbus is not mandatory; when dbus is not available then NetworkManager can still manage networks based on the information in its configuration files. TODO: I’m not quite sure how it communicates with wpa_supplicant in this case.

There are two commonly-used commandline clients (nmcli and cnetworkmanager), and several GUI clients (including a gnome and two kde interfaces), all of which talk to NetworkManager over dbus.

NetworkManager executes scripts on interface state-change, in a manner very similar to ifupdown - except that the scripts are in /etc/NetworkManager/dispatcher.d. The process is very well documented in the NetworkManager man-page. Note in particular that scripts don’t get put into separate “if-{phase}” directories, but instead into a single directory; scripts should check the phase and just exit (successfully) if the phase doesn’t interest them. This facility isn’t widely used, and not needed for simple wireless-on-the-desktop.

By default, there is exactly one script in the NetworkManager dispatcher.d directory: one that emulates ifup/ifdown by executing the scripts under /etc/network/if-{phase}.d. However the emulation is only partial; in particular it only executes ifupdown scripts for phases “post-up” and “post-down”. The wirelesstools scripts hook into the “pre-up” phase, and the wpa_supplicant scripts hook into the “up” phase so NetworkManager doesn’t actually run either of those scripts even if they are installed. NetworkManager also fails to pass many of the environment-variables that ifupdown would pass, so the up/down scripts that test for these variables will behave somewhat differently (eg the wpa_supplicant script runs on post-up, but just exits because IF_WPA_CONF is not defined). This integration is actually pretty useless, and can mostly be ignored.

When a GUI or commandline tool sends a DBUS message to the daemon to “add a network”, the daemon saves the details in a file under /etc/NetworkManager/system-connections. Such files can also be created by hand. For WIFI connections, the file includes the authentication password (in plain text, but the conf-files are only readable by root).

General NetworkManager settings are stored in /var/lib/NetworkManager/NetworkManager.state.

When network-manager decides to “bring up” a wireless interface, it starts an instance of wpa_supplicant to manage the connection. All necessary parameters are passed to wpa_supplicant on its commandline, ie in this situation wpa_supplicant does not use a configuration-file (and therefore there is no need to write such files by hand). NetworkManager can talk to wpa_supplicant after it is started via the application’s dedicated communications socket.

When NetworkManager is managing networks, the ifup/ifdown commands should not be used.

NetworkManager “plugins” allow it to read different formats of configuration-files. The default “keyfile” plugin reads ini-style configuration files from /etc/NetworkManager/system-connections; these files define which interfaces should be managed and how. There is a plugin to read traditional redhat network config files, and one that parses /etc/network/interfaces (the equivalent definition used by ifupdown scripts).

NetworkManager was originally created, and is mostly maintained, by RedHat. It is implemented in C.

NetworkManager performs DHCP configuration by executing either dhclient or dhcpcd as a separate process. Unfortunately I cannot find any documentation on this, and the code is completely uncommented so exactly how this integration works is not clear. It’s also glib-based code, so where/if processes are getting spawned is not clear to a non-gnome developer like myself.

NetworkManager relies on wpa_supplicant for setting up wireless interfaces, but as with DHCP it is not clear whether NetworkManager spawns instances of it or not. Probably best to have a global wpa_supplicant daemon running - though it is still unclear how network-manager passes the wireless details (ssid, authentication, etc) to a global instance - and particularly in the case where dbus is not running (which NetworkManager claims to support).



According to the wikipedia article on wicd, it performs the same tasks as NetworkManager, and has a similar architecture (daemon <-> dbus <-> client). And AFAICT, it also depends on wpa_supplicant.

It seems to be actively maintained, and is apparently implemented in Python.


The ConnMan application is also similar to NetworkManager and WICD. It was originally written by Intel, and is used primarily in mobile devices (SailfishOS, Jolla, etc).

The architecture of ConnMan is also similar to NetworkManager/WICD (daemon<->dbus<->client + wpa_supplicant). Apparently the prime motiviations for creating ConnMan were related to the code-structure, extensibility, and some of the libraries that NetworkManager depends on (particularly in a small mobile device).

Pesonally, having seen the glib-style code in NetworkManager, and the lack of comments/documentation, I can see some justification for the creation of ConnMan.

See connman pros and cons.

Other Comments

The primary difference between WPA2-personal vs WPA2-enterprise authentication is that for the “personal” authentication there is a single password per network while the enterprise version supports a password per user. However administering “per user” passwords is obviously more complex; in most cases it requires setting up a separate RADIUS authentication server that the access-point consults.

I’m a little surprised that nobody has created a dhcp-client-library that apps can use rather than excuting an external process. Systemd does include such functionality in the libsystemd-network library, but that dhcp-client is reasonably limited in features and that is probably not a general-purpose library. wpa_supplicant runs as a system service (ie global daemon) which is reasonable; other apps can then communicate with the already-running daemon. However dhclient/dhcpcd are neither system services nor libraries, making them IMO awkward to integrate with. For example, I can imagine NetworkManager would be improved by using a library rather than executing/communicating-with an external process.

Now for a little personal rant! Why don’t open-source projects provide better documentation and comment their code? As a professional software developer, the projects I work on almost always have a document describing the overall architecture, and then the code is heavily commented to describe what it is doing and why. Neither of these appear to be common in most projects I looked at. One of the mottos of open-source is that “many eyes make bugs shallow” but thousands of lines of complex source-code without any comments at all, and no guide to the general design of the application, does not make it easy to review or contribute patches!


See links within individual sections.

Other useful pages: