at 17.09-beta 6.3 kB view raw
1{ config, lib, pkgs, ... }: 2 3with lib; 4 5let 6 cfg = config.networking.wireless; 7 configFile = if cfg.networks != {} then pkgs.writeText "wpa_supplicant.conf" '' 8 ${optionalString cfg.userControlled.enable '' 9 ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=${cfg.userControlled.group} 10 update_config=1''} 11 ${concatStringsSep "\n" (mapAttrsToList (ssid: networkConfig: let 12 psk = if networkConfig.psk != null 13 then ''"${networkConfig.psk}"'' 14 else networkConfig.pskRaw; 15 priority = networkConfig.priority; 16 in '' 17 network={ 18 ssid="${ssid}" 19 ${optionalString (psk != null) ''psk=${psk}''} 20 ${optionalString (psk == null) ''key_mgmt=NONE''} 21 ${optionalString (priority != null) ''priority=${toString priority}''} 22 } 23 '') cfg.networks)} 24 '' else "/etc/wpa_supplicant.conf"; 25in { 26 options = { 27 networking.wireless = { 28 enable = mkEnableOption "wpa_supplicant"; 29 30 interfaces = mkOption { 31 type = types.listOf types.str; 32 default = []; 33 example = [ "wlan0" "wlan1" ]; 34 description = '' 35 The interfaces <command>wpa_supplicant</command> will use. If empty, it will 36 automatically use all wireless interfaces. 37 ''; 38 }; 39 40 driver = mkOption { 41 type = types.str; 42 default = "nl80211,wext"; 43 description = "Force a specific wpa_supplicant driver."; 44 }; 45 46 networks = mkOption { 47 type = types.attrsOf (types.submodule { 48 options = { 49 psk = mkOption { 50 type = types.nullOr types.str; 51 default = null; 52 description = '' 53 The network's pre-shared key in plaintext defaulting 54 to being a network without any authentication. 55 56 Be aware that these will be written to the nix store 57 in plaintext! 58 59 Mutually exclusive with <varname>pskRaw</varname>. 60 ''; 61 }; 62 63 pskRaw = mkOption { 64 type = types.nullOr types.str; 65 default = null; 66 description = '' 67 The network's pre-shared key in hex defaulting 68 to being a network without any authentication. 69 70 Mutually exclusive with <varname>psk</varname>. 71 ''; 72 }; 73 priority = mkOption { 74 type = types.nullOr types.int; 75 default = null; 76 description = '' 77 By default, all networks will get same priority group (0). If some of the 78 networks are more desirable, this field can be used to change the order in 79 which wpa_supplicant goes through the networks when selecting a BSS. The 80 priority groups will be iterated in decreasing priority (i.e., the larger the 81 priority value, the sooner the network is matched against the scan results). 82 Within each priority group, networks will be selected based on security 83 policy, signal strength, etc. 84 ''; 85 }; 86 }; 87 }); 88 description = '' 89 The network definitions to automatically connect to when 90 <command>wpa_supplicant</command> is running. If this 91 parameter is left empty wpa_supplicant will use 92 /etc/wpa_supplicant.conf as the configuration file. 93 ''; 94 default = {}; 95 example = literalExample '' 96 { echelon = { 97 psk = "abcdefgh"; 98 }; 99 "free.wifi" = {}; 100 } 101 ''; 102 }; 103 104 userControlled = { 105 enable = mkOption { 106 type = types.bool; 107 default = false; 108 description = '' 109 Allow normal users to control wpa_supplicant through wpa_gui or wpa_cli. 110 This is useful for laptop users that switch networks a lot and don't want 111 to depend on a large package such as NetworkManager just to pick nearby 112 access points. 113 114 When using a declarative network specification you cannot persist any 115 settings via wpa_gui or wpa_cli. 116 ''; 117 }; 118 119 group = mkOption { 120 type = types.str; 121 default = "wheel"; 122 example = "network"; 123 description = "Members of this group can control wpa_supplicant."; 124 }; 125 }; 126 }; 127 }; 128 129 config = mkIf cfg.enable { 130 assertions = flip mapAttrsToList cfg.networks (name: cfg: { 131 assertion = cfg.psk == null || cfg.pskRaw == null; 132 message = ''networking.wireless."${name}".psk and networking.wireless."${name}".pskRaw are mutually exclusive''; 133 }); 134 135 environment.systemPackages = [ pkgs.wpa_supplicant ]; 136 137 services.dbus.packages = [ pkgs.wpa_supplicant ]; 138 139 # FIXME: start a separate wpa_supplicant instance per interface. 140 systemd.services.wpa_supplicant = let 141 ifaces = cfg.interfaces; 142 deviceUnit = interface: [ "sys-subsystem-net-devices-${interface}.device" ]; 143 in { 144 description = "WPA Supplicant"; 145 146 after = lib.concatMap deviceUnit ifaces; 147 before = [ "network.target" ]; 148 wants = [ "network.target" ]; 149 requires = lib.concatMap deviceUnit ifaces; 150 wantedBy = [ "multi-user.target" ]; 151 152 path = [ pkgs.wpa_supplicant ]; 153 154 script = '' 155 ${if ifaces == [] then '' 156 for i in $(cd /sys/class/net && echo *); do 157 DEVTYPE= 158 source /sys/class/net/$i/uevent 159 if [ "$DEVTYPE" = "wlan" -o -e /sys/class/net/$i/wireless ]; then 160 ifaces="$ifaces''${ifaces:+ -N} -i$i" 161 fi 162 done 163 '' else '' 164 ifaces="${concatStringsSep " -N " (map (i: "-i${i}") ifaces)}" 165 ''} 166 exec wpa_supplicant -s -u -D${cfg.driver} -c ${configFile} $ifaces 167 ''; 168 }; 169 170 powerManagement.resumeCommands = '' 171 ${config.systemd.package}/bin/systemctl try-restart wpa_supplicant 172 ''; 173 174 # Restart wpa_supplicant when a wlan device appears or disappears. 175 services.udev.extraRules = '' 176 ACTION=="add|remove", SUBSYSTEM=="net", ENV{DEVTYPE}=="wlan", RUN+="${config.systemd.package}/bin/systemctl try-restart wpa_supplicant.service" 177 ''; 178 }; 179 180 meta.maintainers = with lib.maintainers; [ globin ]; 181}