@@ -10,9 +10,35 @@ let
1010 cfg = config.networking.wireless;
1111 opt = options.networking.wireless;
1212
13+ wpa3Protocols = [ "SAE" "FT-SAE" ];
14+ hasWPA3 = opts: !mutuallyExclusive opts.authProtocols wpa3Protocols;
15+
16+ # Gives a WPA3 network higher priority
17+ increaseWPA3Priority = opts:
18+ opts // optionalAttrs (hasWPA3 opts)
19+ { priority = if opts.priority == null
20+ then 1
21+ else opts.priority + 1;
22+ };
23+
24+ # Creates a WPA2 fallback network
25+ mkWPA2Fallback = opts:
26+ opts // { authProtocols = subtractLists wpa3Protocols opts.authProtocols; };
27+
28+ # Networks attrset as a list
29+ networkList = mapAttrsToList (ssid: opts: opts // { inherit ssid; })
30+ cfg.networks;
31+
32+ # List of all networks (normal + generated fallbacks)
33+ allNetworks =
34+ if cfg.fallbackToWPA2
35+ then map increaseWPA3Priority networkList
36+ ++ map mkWPA2Fallback (filter hasWPA3 networkList)
37+ else networkList;
38+
1339 # Content of wpa_supplicant.conf
1440 generatedConfig = concatStringsSep "\n" (
15− (mapAttrsToList mkNetwork cfg.networks)
41+ (map mkNetwork allNetworks)
1642 ++ optional cfg.userControlled.enable (concatStringsSep "\n"
1743 [ "ctrl_interface=/run/wpa_supplicant"
1844 "ctrl_interface_group=${cfg.userControlled.group}"
@@ -34,7 +60,7 @@ let
3460 finalConfig = ''"$RUNTIME_DIRECTORY"/wpa_supplicant.conf'';
3561
3662 # Creates a network block for wpa_supplicant.conf
37− mkNetwork = ssid: opts:
63+ mkNetwork = opts:
3864 let
3965 quote = x: ''"${x}"'';
4066 indent = x: " " + x;
@@ -44,7 +70,7 @@ let
4470 else opts.pskRaw;
4571
4672 options = [
47− "ssid=${quote ssid}"
73+ "ssid=${quote opts.ssid}"
4874 (if pskString != null || opts.auth != null
4975 then "key_mgmt=${concatStringsSep " " opts.authProtocols}"
5076 else "key_mgmt=NONE")
@@ -176,6 +202,18 @@ in {
176202 '';
177203 };
178204
205+ fallbackToWPA2 = mkOption {
206+ type = types.bool;
207+ default = true;
208+ description = ''
209+ Whether to fall back to WPA2 authentication protocols if WPA3 failed.
210+ This allows old wireless cards (that lack recent features required by
211+ WPA3) to connect to mixed WPA2/WPA3 access points.
212+
213+ To avoid possible downgrade attacks, disable this options.
214+ '';
215+ };
216+
179217 environmentFile = mkOption {
180218 type = types.nullOr types.path;
181219 default = null;