summaryrefslogtreecommitdiffstats
path: root/nixos
diff options
context:
space:
mode:
authorMichele Guerini Rocco <rnhmjoj@users.noreply.github.com>2021-08-13 11:02:15 +0200
committerGitHub <noreply@github.com>2021-08-13 11:02:15 +0200
commit95c7f7ed587025f4f65eaf2b623d09ff9333a963 (patch)
tree2bb5abfc32f41195b8102c171d713e39ccae1e4b /nixos
parenta9725665477f725a5f886fc41d552b340fa40199 (diff)
parent0d082b464dcc303a95e01a37e0b117eeeddfc04e (diff)
Merge pull request #118583 from rnhmjoj/roaming
nixos/wireless: add options for better roaming
Diffstat (limited to 'nixos')
-rw-r--r--nixos/modules/services/networking/connman.nix1
-rw-r--r--nixos/modules/services/networking/wpa_supplicant.nix298
2 files changed, 206 insertions, 93 deletions
diff --git a/nixos/modules/services/networking/connman.nix b/nixos/modules/services/networking/connman.nix
index 11f66b05df12..608672c6446c 100644
--- a/nixos/modules/services/networking/connman.nix
+++ b/nixos/modules/services/networking/connman.nix
@@ -150,6 +150,7 @@ in {
useDHCP = false;
wireless = {
enable = mkIf (!enableIwd) true;
+ dbusControlled = true;
iwd = mkIf enableIwd {
enable = true;
};
diff --git a/nixos/modules/services/networking/wpa_supplicant.nix b/nixos/modules/services/networking/wpa_supplicant.nix
index 6238a351b998..155c6fdd0ab0 100644
--- a/nixos/modules/services/networking/wpa_supplicant.nix
+++ b/nixos/modules/services/networking/wpa_supplicant.nix
@@ -8,28 +8,108 @@ let
else pkgs.wpa_supplicant;
cfg = config.networking.wireless;
- configFile = if cfg.networks != {} || cfg.extraConfig != "" || cfg.userControlled.enable then pkgs.writeText "wpa_supplicant.conf" ''
- ${optionalString cfg.userControlled.enable ''
- ctrl_interface=DIR=/run/wpa_supplicant GROUP=${cfg.userControlled.group}
- update_config=1''}
- ${cfg.extraConfig}
- ${concatStringsSep "\n" (mapAttrsToList (ssid: config: with config; let
- key = if psk != null
- then ''"${psk}"''
- else pskRaw;
- baseAuth = if key != null
- then "psk=${key}"
- else "key_mgmt=NONE";
- in ''
- network={
- ssid="${ssid}"
- ${optionalString (priority != null) ''priority=${toString priority}''}
- ${optionalString hidden "scan_ssid=1"}
- ${if (auth != null) then auth else baseAuth}
- ${extraConfig}
- }
- '') cfg.networks)}
- '' else "/etc/wpa_supplicant.conf";
+
+ # Content of wpa_supplicant.conf
+ generatedConfig = concatStringsSep "\n" (
+ (mapAttrsToList mkNetwork cfg.networks)
+ ++ optional cfg.userControlled.enable (concatStringsSep "\n"
+ [ "ctrl_interface=/run/wpa_supplicant"
+ "ctrl_interface_group=${cfg.userControlled.group}"
+ "update_config=1"
+ ])
+ ++ optional cfg.scanOnLowSignal ''bgscan="simple:30:-70:3600"''
+ ++ optional (cfg.extraConfig != "") cfg.extraConfig);
+
+ configFile =
+ if cfg.networks != {} || cfg.extraConfig != "" || cfg.userControlled.enable
+ then pkgs.writeText "wpa_supplicant.conf" generatedConfig
+ else "/etc/wpa_supplicant.conf";
+
+ # Creates a network block for wpa_supplicant.conf
+ mkNetwork = ssid: opts:
+ let
+ quote = x: ''"${x}"'';
+ indent = x: " " + x;
+
+ pskString = if opts.psk != null
+ then quote opts.psk
+ else opts.pskRaw;
+
+ options = [
+ "ssid=${quote ssid}"
+ (if pskString != null || opts.auth != null
+ then "key_mgmt=${concatStringsSep " " opts.authProtocols}"
+ else "key_mgmt=NONE")
+ ] ++ optional opts.hidden "scan_ssid=1"
+ ++ optional (pskString != null) "psk=${pskString}"
+ ++ optionals (opts.auth != null) (filter (x: x != "") (splitString "\n" opts.auth))
+ ++ optional (opts.priority != null) "priority=${toString opts.priority}"
+ ++ optional (opts.extraConfig != "") opts.extraConfig;
+ in ''
+ network={
+ ${concatMapStringsSep "\n" indent options}
+ }
+ '';
+
+ # Creates a systemd unit for wpa_supplicant bound to a given (or any) interface
+ mkUnit = iface:
+ let
+ deviceUnit = optional (iface != null) "sys-subsystem-net-devices-${utils.escapeSystemdPath iface}.device";
+ configStr = if cfg.allowAuxiliaryImperativeNetworks
+ then "-c /etc/wpa_supplicant.conf -I ${configFile}"
+ else "-c ${configFile}";
+ in {
+ description = "WPA Supplicant instance" + optionalString (iface != null) " for interface ${iface}";
+
+ after = deviceUnit;
+ before = [ "network.target" ];
+ wants = [ "network.target" ];
+ requires = deviceUnit;
+ wantedBy = [ "multi-user.target" ];
+ stopIfChanged = false;
+
+ path = [ package ];
+
+ script =
+ ''
+ if [ -f /etc/wpa_supplicant.conf -a "/etc/wpa_supplicant.conf" != "${configFile}" ]; then
+ echo >&2 "<3>/etc/wpa_supplicant.conf present but ignored. Generated ${configFile} is used instead."
+ fi
+
+ iface_args="-s ${optionalString cfg.dbusControlled "-u"} -D${cfg.driver} ${configStr}"
+
+ ${if iface == null then ''
+ # detect interfaces automatically
+
+ # check if there are no wireless interfaces
+ if ! find -H /sys/class/net/* -name wireless | grep -q .; then
+ # if so, wait until one appears
+ echo "Waiting for wireless interfaces"
+ grep -q '^ACTION=add' < <(stdbuf -oL -- udevadm monitor -s net/wlan -pu)
+ # Note: the above line has been carefully written:
+ # 1. The process substitution avoids udevadm hanging (after grep has quit)
+ # until it tries to write to the pipe again. Not even pipefail works here.
+ # 2. stdbuf is needed because udevadm output is buffered by default and grep
+ # may hang until more udev events enter the pipe.
+ fi
+
+ # add any interface found to the daemon arguments
+ for name in $(find -H /sys/class/net/* -name wireless | cut -d/ -f 5); do
+ echo "Adding interface $name"
+ args+="''${args:+ -N} -i$name $iface_args"
+ done
+ '' else ''
+ # add known interface to the daemon arguments
+ args="-i${iface} $iface_args"
+ ''}
+
+ # finally start daemon
+ exec wpa_supplicant $args
+ '';
+ };
+
+ systemctl = "/run/current-system/systemd/bin/systemctl";
+
in {
options = {
networking.wireless = {
@@ -42,6 +122,10 @@ in {
description = ''
The interfaces <command>wpa_supplicant</command> will use. If empty, it will
automatically use all wireless interfaces.
+
+ <note><para>
+ A separate wpa_supplicant instance will be started for each interface.
+ </para></note>
'';
};
@@ -61,6 +145,16 @@ in {
'';
};
+ scanOnLowSignal = mkOption {
+ type = types.bool;
+ default = true;
+ description = ''
+ Whether to periodically scan for (better) networks when the signal of
+ the current one is low. This will make roaming between access points
+ faster, but will consume more power.
+ '';
+ };
+
networks = mkOption {
type = types.attrsOf (types.submodule {
options = {
@@ -89,11 +183,52 @@ in {
'';
};
+ authProtocols = mkOption {
+ default = [
+ # WPA2 and WPA3
+ "WPA-PSK" "WPA-EAP" "SAE"
+ # 802.11r variants of the above
+ "FT-PSK" "FT-EAP" "FT-SAE"
+ ];
+ # The list can be obtained by running this command
+ # awk '
+ # /^# key_mgmt: /{ run=1 }
+ # /^#$/{ run=0 }
+ # /^# [A-Z0-9-]{2,}/{ if(run){printf("\"%s\"\n", $2)} }
+ # ' /run/current-system/sw/share/doc/wpa_supplicant/wpa_supplicant.conf.example
+ type = types.listOf (types.enum [
+ "WPA-PSK"
+ "WPA-EAP"
+ "IEEE8021X"
+ "NONE"
+ "WPA-NONE"
+ "FT-PSK"
+ "FT-EAP"
+ "FT-EAP-SHA384"
+ "WPA-PSK-SHA256"
+ "WPA-EAP-SHA256"
+ "SAE"
+ "FT-SAE"
+ "WPA-EAP-SUITE-B"
+ "WPA-EAP-SUITE-B-192"
+ "OSEN"
+ "FILS-SHA256"
+ "FILS-SHA384"
+ "FT-FILS-SHA256"
+ "FT-FILS-SHA384"
+ "OWE"
+ "DPP"
+ ]);
+ description = ''
+ The list of authentication protocols accepted by this network.
+ This corresponds to the <literal>key_mgmt</literal> option in wpa_supplicant.
+ '';
+ };
+
auth = mkOption {
type = types.nullOr types.str;
default = null;
example = ''
- key_mgmt=WPA-EAP
eap=PEAP
identity="user@example.com"
password="secret"
@@ -200,6 +335,16 @@ in {
description = "Members of this group can control wpa_supplicant.";
};
};
+
+ dbusControlled = mkOption {
+ type = types.bool;
+ default = lib.length cfg.interfaces < 2;
+ description = ''
+ Whether to enable the DBus control interface.
+ This is only needed when using NetworkManager or connman.
+ '';
+ };
+
extraConfig = mkOption {
type = types.str;
default = "";
@@ -223,80 +368,47 @@ in {
assertions = flip mapAttrsToList cfg.networks (name: cfg: {
assertion = with cfg; count (x: x != null) [ psk pskRaw auth ] <= 1;
message = ''options networking.wireless."${name}".{psk,pskRaw,auth} are mutually exclusive'';
- });
-
- environment.systemPackages = [ package ];
-
- services.dbus.packages = [ package ];
+ }) ++ [
+ {
+ assertion = length cfg.interfaces > 1 -> !cfg.dbusControlled;
+ message =
+ let daemon = if config.networking.networkmanager.enable then "NetworkManager" else
+ if config.services.connman.enable then "connman" else null;
+ n = toString (length cfg.interfaces);
+ in ''
+ It's not possible to run multiple wpa_supplicant instances with DBus support.
+ Note: you're seeing this error because `networking.wireless.interfaces` has
+ ${n} entries, implying an equal number of wpa_supplicant instances.
+ '' + optionalString (daemon != null) ''
+ You don't need to change `networking.wireless.interfaces` when using ${daemon}:
+ in this case the interfaces will be configured automatically for you.
+ '';
+ }
+ ];
hardware.wirelessRegulatoryDatabase = true;
- # FIXME: start a separate wpa_supplicant instance per interface.
- systemd.services.wpa_supplicant = let
- ifaces = cfg.interfaces;
- deviceUnit = interface: [ "sys-subsystem-net-devices-${utils.escapeSystemdPath interface}.device" ];
- in {
- description = "WPA Supplicant";
-
- after = lib.concatMap deviceUnit ifaces;
- before = [ "network.target" ];
- wants = [ "network.target" ];
- requires = lib.concatMap deviceUnit ifaces;
- wantedBy = [ "multi-user.target" ];
- stopIfChanged = false;
-
- path = [ package pkgs.udev ];
-
- script = let
- configStr = if cfg.allowAuxiliaryImperativeNetworks
- then "-c /etc/wpa_supplicant.conf -I ${configFile}"
- else "-c ${configFile}";
- in ''
- if [ -f /etc/wpa_supplicant.conf -a "/etc/wpa_supplicant.conf" != "${configFile}" ]; then
- echo >&2 "<3>/etc/wpa_supplicant.conf present but ignored. Generated ${configFile} is used instead."
- fi
-
- iface_args="-s -u -D${cfg.driver} ${configStr}"
-
- ${if ifaces == [] then ''
- # detect interfaces automatically
-
- # check if there are no wireless interface
- if ! find -H /sys/class/net/* -name wireless | grep -q .; then
- # if so, wait until one appears
- echo "Waiting for wireless interfaces"
- grep -q '^ACTION=add' < <(stdbuf -oL -- udevadm monitor -s net/wlan -pu)
- # Note: the above line has been carefully written:
- # 1. The process substitution avoids udevadm hanging (after grep has quit)
- # until it tries to write to the pipe again. Not even pipefail works here.
- # 2. stdbuf is needed because udevadm output is buffered by default and grep
- # may hang until more udev events enter the pipe.
- fi
-
- # add any interface found to the daemon arguments
- for name in $(find -H /sys/class/net/* -name wireless | cut -d/ -f 5); do
- echo "Adding interface $name"
- args+="''${args:+ -N} -i$name $iface_args"
- done
- '' else ''
- # add known interfaces to the daemon arguments
- args="${concatMapStringsSep " -N " (i: "-i${i} $iface_args") ifaces}"
- ''}
-
- # finally start daemon
- exec wpa_supplicant $args
- '';
- };
-
- powerManagement.resumeCommands = ''
- /run/current-system/systemd/bin/systemctl try-restart wpa_supplicant
- '';
-
- # Restart wpa_supplicant when a wlan device appears or disappears.
- services.udev.extraRules = ''
- ACTION=="add|remove", SUBSYSTEM=="net", ENV{DEVTYPE}=="wlan", RUN+="/run/current-system/systemd/bin/systemctl try-restart wpa_supplicant.service"
+ environment.systemPackages = [ package ];
+ services.dbus.packages = optional cfg.dbusControlled package;
+
+ systemd.services =
+ if cfg.interfaces == []
+ then { wpa_supplicant = mkUnit null; }
+ else listToAttrs (map (i: nameValuePair "wpa_supplicant-${i}" (mkUnit i)) cfg.interfaces);
+
+ # Restart wpa_supplicant after resuming from sleep
+ powerManagement.resumeCommands = concatStringsSep "\n" (
+ optional (cfg.interfaces == []) "${systemctl} try-restart wpa_supplicant"
+ ++ map (i: "${systemctl} try-restart wpa_supplicant-${i}") cfg.interfaces
+ );
+
+ # Restart wpa_supplicant when a wlan device appears or disappears. This is
+ # only needed when an interface hasn't been specified by the user.
+ services.udev.extraRules = optionalString (cfg.interfaces == []) ''
+ ACTION=="add|remove", SUBSYSTEM=="net", ENV{DEVTYPE}=="wlan", \
+ RUN+="${systemctl} try-restart wpa_supplicant.service"
'';
};
- meta.maintainers = with lib.maintainers; [ globin ];
+ meta.maintainers = with lib.maintainers; [ globin rnhmjoj ];
}