at 23.05-pre 55 kB view raw
1{ config, options, lib, pkgs, utils, ... }: 2 3with lib; 4with utils; 5 6let 7 8 cfg = config.networking; 9 opt = options.networking; 10 interfaces = attrValues cfg.interfaces; 11 hasVirtuals = any (i: i.virtual) interfaces; 12 hasSits = cfg.sits != { }; 13 hasGres = cfg.greTunnels != { }; 14 hasBonds = cfg.bonds != { }; 15 hasFous = cfg.fooOverUDP != { } 16 || filterAttrs (_: s: s.encapsulation != null) cfg.sits != { }; 17 18 slaves = concatMap (i: i.interfaces) (attrValues cfg.bonds) 19 ++ concatMap (i: i.interfaces) (attrValues cfg.bridges) 20 ++ concatMap (i: attrNames (filterAttrs (name: config: ! (config.type == "internal" || hasAttr name cfg.interfaces)) i.interfaces)) (attrValues cfg.vswitches); 21 22 slaveIfs = map (i: cfg.interfaces.${i}) (filter (i: cfg.interfaces ? ${i}) slaves); 23 24 rstpBridges = flip filterAttrs cfg.bridges (_: { rstp, ... }: rstp); 25 26 needsMstpd = rstpBridges != { }; 27 28 bridgeStp = optional needsMstpd (pkgs.writeTextFile { 29 name = "bridge-stp"; 30 executable = true; 31 destination = "/bin/bridge-stp"; 32 text = '' 33 #!${pkgs.runtimeShell} -e 34 export PATH="${pkgs.mstpd}/bin" 35 36 BRIDGES=(${concatStringsSep " " (attrNames rstpBridges)}) 37 for BRIDGE in $BRIDGES; do 38 if [ "$BRIDGE" = "$1" ]; then 39 if [ "$2" = "start" ]; then 40 mstpctl addbridge "$BRIDGE" 41 exit 0 42 elif [ "$2" = "stop" ]; then 43 mstpctl delbridge "$BRIDGE" 44 exit 0 45 fi 46 exit 1 47 fi 48 done 49 exit 1 50 ''; 51 }); 52 53 # We must escape interfaces due to the systemd interpretation 54 subsystemDevice = interface: 55 "sys-subsystem-net-devices-${escapeSystemdPath interface}.device"; 56 57 addrOpts = v: 58 assert v == 4 || v == 6; 59 { options = { 60 address = mkOption { 61 type = types.str; 62 description = lib.mdDoc '' 63 IPv${toString v} address of the interface. Leave empty to configure the 64 interface using DHCP. 65 ''; 66 }; 67 68 prefixLength = mkOption { 69 type = types.addCheck types.int (n: n >= 0 && n <= (if v == 4 then 32 else 128)); 70 description = lib.mdDoc '' 71 Subnet mask of the interface, specified as the number of 72 bits in the prefix (`${if v == 4 then "24" else "64"}`). 73 ''; 74 }; 75 }; 76 }; 77 78 routeOpts = v: 79 { options = { 80 address = mkOption { 81 type = types.str; 82 description = lib.mdDoc "IPv${toString v} address of the network."; 83 }; 84 85 prefixLength = mkOption { 86 type = types.addCheck types.int (n: n >= 0 && n <= (if v == 4 then 32 else 128)); 87 description = lib.mdDoc '' 88 Subnet mask of the network, specified as the number of 89 bits in the prefix (`${if v == 4 then "24" else "64"}`). 90 ''; 91 }; 92 93 type = mkOption { 94 type = types.nullOr (types.enum [ 95 "unicast" "local" "broadcast" "multicast" 96 ]); 97 default = null; 98 description = lib.mdDoc '' 99 Type of the route. See the `Route types` section 100 in the `ip-route(8)` manual page for the details. 101 102 Note that `prohibit`, `blackhole`, 103 `unreachable`, and `throw` cannot 104 be configured per device, so they are not available here. Similarly, 105 `nat` hasn't been supported since kernel 2.6. 106 ''; 107 }; 108 109 via = mkOption { 110 type = types.nullOr types.str; 111 default = null; 112 description = lib.mdDoc "IPv${toString v} address of the next hop."; 113 }; 114 115 options = mkOption { 116 type = types.attrsOf types.str; 117 default = { }; 118 example = { mtu = "1492"; window = "524288"; }; 119 description = lib.mdDoc '' 120 Other route options. See the symbol `OPTIONS` 121 in the `ip-route(8)` manual page for the details. 122 You may also specify `metric`, 123 `src`, `protocol`, 124 `scope`, `from` 125 and `table`, which are technically 126 not route options, in the sense used in the manual. 127 ''; 128 }; 129 130 }; 131 }; 132 133 gatewayCoerce = address: { inherit address; }; 134 135 gatewayOpts = { ... }: { 136 137 options = { 138 139 address = mkOption { 140 type = types.str; 141 description = lib.mdDoc "The default gateway address."; 142 }; 143 144 interface = mkOption { 145 type = types.nullOr types.str; 146 default = null; 147 example = "enp0s3"; 148 description = lib.mdDoc "The default gateway interface."; 149 }; 150 151 metric = mkOption { 152 type = types.nullOr types.int; 153 default = null; 154 example = 42; 155 description = lib.mdDoc "The default gateway metric/preference."; 156 }; 157 158 }; 159 160 }; 161 162 interfaceOpts = { name, ... }: { 163 164 options = { 165 name = mkOption { 166 example = "eth0"; 167 type = types.str; 168 description = lib.mdDoc "Name of the interface."; 169 }; 170 171 tempAddress = mkOption { 172 type = types.enum (lib.attrNames tempaddrValues); 173 default = cfg.tempAddresses; 174 defaultText = literalExpression ''config.networking.tempAddresses''; 175 description = lib.mdDoc '' 176 When IPv6 is enabled with SLAAC, this option controls the use of 177 temporary address (aka privacy extensions) on this 178 interface. This is used to reduce tracking. 179 180 See also the global option 181 [](#opt-networking.tempAddresses), which 182 applies to all interfaces where this is not set. 183 184 Possible values are: 185 ${tempaddrDoc} 186 ''; 187 }; 188 189 useDHCP = mkOption { 190 type = types.nullOr types.bool; 191 default = null; 192 description = lib.mdDoc '' 193 Whether this interface should be configured with dhcp. 194 Null implies the old behavior which depends on whether ip addresses 195 are specified or not. 196 ''; 197 }; 198 199 ipv4.addresses = mkOption { 200 default = [ ]; 201 example = [ 202 { address = "10.0.0.1"; prefixLength = 16; } 203 { address = "192.168.1.1"; prefixLength = 24; } 204 ]; 205 type = with types; listOf (submodule (addrOpts 4)); 206 description = lib.mdDoc '' 207 List of IPv4 addresses that will be statically assigned to the interface. 208 ''; 209 }; 210 211 ipv6.addresses = mkOption { 212 default = [ ]; 213 example = [ 214 { address = "fdfd:b3f0:482::1"; prefixLength = 48; } 215 { address = "2001:1470:fffd:2098::e006"; prefixLength = 64; } 216 ]; 217 type = with types; listOf (submodule (addrOpts 6)); 218 description = lib.mdDoc '' 219 List of IPv6 addresses that will be statically assigned to the interface. 220 ''; 221 }; 222 223 ipv4.routes = mkOption { 224 default = []; 225 example = [ 226 { address = "10.0.0.0"; prefixLength = 16; } 227 { address = "192.168.2.0"; prefixLength = 24; via = "192.168.1.1"; } 228 ]; 229 type = with types; listOf (submodule (routeOpts 4)); 230 description = lib.mdDoc '' 231 List of extra IPv4 static routes that will be assigned to the interface. 232 233 ::: {.warning} 234 If the route type is the default `unicast`, then the scope 235 is set differently depending on the value of {option}`networking.useNetworkd`: 236 the script-based backend sets it to `link`, while networkd sets 237 it to `global`. 238 ::: 239 240 If you want consistency between the two implementations, 241 set the scope of the route manually with 242 `networking.interfaces.eth0.ipv4.routes = [{ options.scope = "global"; }]` 243 for example. 244 ''; 245 }; 246 247 ipv6.routes = mkOption { 248 default = []; 249 example = [ 250 { address = "fdfd:b3f0::"; prefixLength = 48; } 251 { address = "2001:1470:fffd:2098::"; prefixLength = 64; via = "fdfd:b3f0::1"; } 252 ]; 253 type = with types; listOf (submodule (routeOpts 6)); 254 description = lib.mdDoc '' 255 List of extra IPv6 static routes that will be assigned to the interface. 256 ''; 257 }; 258 259 macAddress = mkOption { 260 default = null; 261 example = "00:11:22:33:44:55"; 262 type = types.nullOr (types.str); 263 description = lib.mdDoc '' 264 MAC address of the interface. Leave empty to use the default. 265 ''; 266 }; 267 268 mtu = mkOption { 269 default = null; 270 example = 9000; 271 type = types.nullOr types.int; 272 description = lib.mdDoc '' 273 MTU size for packets leaving the interface. Leave empty to use the default. 274 ''; 275 }; 276 277 virtual = mkOption { 278 default = false; 279 type = types.bool; 280 description = lib.mdDoc '' 281 Whether this interface is virtual and should be created by tunctl. 282 This is mainly useful for creating bridges between a host and a virtual 283 network such as VPN or a virtual machine. 284 ''; 285 }; 286 287 virtualOwner = mkOption { 288 default = "root"; 289 type = types.str; 290 description = lib.mdDoc '' 291 In case of a virtual device, the user who owns it. 292 ''; 293 }; 294 295 virtualType = mkOption { 296 default = if hasPrefix "tun" name then "tun" else "tap"; 297 defaultText = literalExpression ''if hasPrefix "tun" name then "tun" else "tap"''; 298 type = with types; enum [ "tun" "tap" ]; 299 description = lib.mdDoc '' 300 The type of interface to create. 301 The default is TUN for an interface name starting 302 with "tun", otherwise TAP. 303 ''; 304 }; 305 306 proxyARP = mkOption { 307 default = false; 308 type = types.bool; 309 description = lib.mdDoc '' 310 Turn on proxy_arp for this device. 311 This is mainly useful for creating pseudo-bridges between a real 312 interface and a virtual network such as VPN or a virtual machine for 313 interfaces that don't support real bridging (most wlan interfaces). 314 As ARP proxying acts slightly above the link-layer, below-ip traffic 315 isn't bridged, so things like DHCP won't work. The advantage above 316 using NAT lies in the fact that no IP addresses are shared, so all 317 hosts are reachable/routeable. 318 319 WARNING: turns on ip-routing, so if you have multiple interfaces, you 320 should think of the consequence and setup firewall rules to limit this. 321 ''; 322 }; 323 324 wakeOnLan = { 325 enable = mkOption { 326 type = types.bool; 327 default = false; 328 description = lib.mdDoc "Whether to enable wol on this interface."; 329 }; 330 }; 331 }; 332 333 config = { 334 name = mkDefault name; 335 }; 336 337 # Renamed or removed options 338 imports = 339 let 340 defined = x: x != "_mkMergedOptionModule"; 341 in [ 342 (mkChangedOptionModule [ "preferTempAddress" ] [ "tempAddress" ] 343 (config: 344 let bool = getAttrFromPath [ "preferTempAddress" ] config; 345 in if bool then "default" else "enabled" 346 )) 347 (mkRenamedOptionModule [ "ip4" ] [ "ipv4" "addresses"]) 348 (mkRenamedOptionModule [ "ip6" ] [ "ipv6" "addresses"]) 349 (mkRemovedOptionModule [ "subnetMask" ] '' 350 Supply a prefix length instead; use option 351 networking.interfaces.<name>.ipv{4,6}.addresses'') 352 (mkMergedOptionModule 353 [ [ "ipAddress" ] [ "prefixLength" ] ] 354 [ "ipv4" "addresses" ] 355 (cfg: with cfg; 356 optional (defined ipAddress && defined prefixLength) 357 { address = ipAddress; prefixLength = prefixLength; })) 358 (mkMergedOptionModule 359 [ [ "ipv6Address" ] [ "ipv6PrefixLength" ] ] 360 [ "ipv6" "addresses" ] 361 (cfg: with cfg; 362 optional (defined ipv6Address && defined ipv6PrefixLength) 363 { address = ipv6Address; prefixLength = ipv6PrefixLength; })) 364 365 ({ options.warnings = options.warnings; options.assertions = options.assertions; }) 366 ]; 367 368 }; 369 370 vswitchInterfaceOpts = {name, ...}: { 371 372 options = { 373 374 name = mkOption { 375 description = lib.mdDoc "Name of the interface"; 376 example = "eth0"; 377 type = types.str; 378 }; 379 380 vlan = mkOption { 381 description = lib.mdDoc "Vlan tag to apply to interface"; 382 example = 10; 383 type = types.nullOr types.int; 384 default = null; 385 }; 386 387 type = mkOption { 388 description = lib.mdDoc "Openvswitch type to assign to interface"; 389 example = "internal"; 390 type = types.nullOr types.str; 391 default = null; 392 }; 393 }; 394 }; 395 396 hexChars = stringToCharacters "0123456789abcdef"; 397 398 isHexString = s: all (c: elem c hexChars) (stringToCharacters (toLower s)); 399 400 tempaddrValues = { 401 disabled = { 402 sysctl = "0"; 403 description = "completely disable IPv6 temporary addresses"; 404 }; 405 enabled = { 406 sysctl = "1"; 407 description = "generate IPv6 temporary addresses but still use EUI-64 addresses as source addresses"; 408 }; 409 default = { 410 sysctl = "2"; 411 description = "generate IPv6 temporary addresses and use these as source addresses in routing"; 412 }; 413 }; 414 tempaddrDoc = concatStringsSep "\n" 415 (mapAttrsToList 416 (name: { description, ... }: ''- `"${name}"` to ${description};'') 417 tempaddrValues); 418 419 hostidFile = pkgs.runCommand "gen-hostid" { preferLocalBuild = true; } '' 420 hi="${cfg.hostId}" 421 ${if pkgs.stdenv.isBigEndian then '' 422 echo -ne "\x''${hi:0:2}\x''${hi:2:2}\x''${hi:4:2}\x''${hi:6:2}" > $out 423 '' else '' 424 echo -ne "\x''${hi:6:2}\x''${hi:4:2}\x''${hi:2:2}\x''${hi:0:2}" > $out 425 ''} 426 ''; 427 428in 429 430{ 431 432 ###### interface 433 434 options = { 435 436 networking.hostName = mkOption { 437 default = "nixos"; 438 # Only allow hostnames without the domain name part (i.e. no FQDNs, see 439 # e.g. "man 5 hostname") and require valid DNS labels (recommended 440 # syntax). Note: We also allow underscores for compatibility/legacy 441 # reasons (as undocumented feature): 442 type = types.strMatching 443 "^$|^[[:alnum:]]([[:alnum:]_-]{0,61}[[:alnum:]])?$"; 444 description = lib.mdDoc '' 445 The name of the machine. Leave it empty if you want to obtain it from a 446 DHCP server (if using DHCP). The hostname must be a valid DNS label (see 447 RFC 1035 section 2.3.1: "Preferred name syntax", RFC 1123 section 2.1: 448 "Host Names and Numbers") and as such must not contain the domain part. 449 This means that the hostname must start with a letter or digit, 450 end with a letter or digit, and have as interior characters only 451 letters, digits, and hyphen. The maximum length is 63 characters. 452 Additionally it is recommended to only use lower-case characters. 453 If (e.g. for legacy reasons) a FQDN is required as the Linux kernel 454 network node hostname (uname --nodename) the option 455 boot.kernel.sysctl."kernel.hostname" can be used as a workaround (but 456 the 64 character limit still applies). 457 458 WARNING: Do not use underscores (_) or you may run into unexpected issues. 459 ''; 460 # warning until the issues in https://github.com/NixOS/nixpkgs/pull/138978 461 # are resolved 462 }; 463 464 networking.fqdn = mkOption { 465 readOnly = true; 466 type = types.str; 467 default = if (cfg.hostName != "" && cfg.domain != null) 468 then "${cfg.hostName}.${cfg.domain}" 469 else throw '' 470 The FQDN is required but cannot be determined. Please make sure that 471 both networking.hostName and networking.domain are set properly. 472 ''; 473 defaultText = literalExpression ''"''${networking.hostName}.''${networking.domain}"''; 474 description = lib.mdDoc '' 475 The fully qualified domain name (FQDN) of this host. It is the result 476 of combining `networking.hostName` and `networking.domain.` Using this 477 option will result in an evaluation error if the hostname is empty or 478 no domain is specified. 479 480 Modules that accept a mere `networing.hostName` but prefer a fully qualified 481 domain name may use `networking.fqdnOrHostName` instead. 482 ''; 483 }; 484 485 networking.fqdnOrHostName = mkOption { 486 readOnly = true; 487 type = types.str; 488 default = if cfg.domain == null then cfg.hostName else cfg.fqdn; 489 defaultText = literalExpression '' 490 if cfg.domain == null then cfg.hostName else cfg.fqdn 491 ''; 492 description = lib.mdDoc '' 493 Either the fully qualified domain name (FQDN), or just the host name if 494 it does not exists. 495 496 This is a convenience option for modules to read instead of `fqdn` when 497 a mere `hostName` is also an acceptable value; this option does not 498 throw an error when `domain` is unset. 499 ''; 500 }; 501 502 networking.hostId = mkOption { 503 default = null; 504 example = "4e98920d"; 505 type = types.nullOr types.str; 506 description = lib.mdDoc '' 507 The 32-bit host ID of the machine, formatted as 8 hexadecimal characters. 508 509 You should try to make this ID unique among your machines. You can 510 generate a random 32-bit ID using the following commands: 511 512 `head -c 8 /etc/machine-id` 513 514 (this derives it from the machine-id that systemd generates) or 515 516 `head -c4 /dev/urandom | od -A none -t x4` 517 518 The primary use case is to ensure when using ZFS that a pool isn't imported 519 accidentally on a wrong machine. 520 ''; 521 }; 522 523 networking.enableIPv6 = mkOption { 524 default = true; 525 type = types.bool; 526 description = lib.mdDoc '' 527 Whether to enable support for IPv6. 528 ''; 529 }; 530 531 networking.defaultGateway = mkOption { 532 default = null; 533 example = { 534 address = "131.211.84.1"; 535 interface = "enp3s0"; 536 }; 537 type = types.nullOr (types.coercedTo types.str gatewayCoerce (types.submodule gatewayOpts)); 538 description = lib.mdDoc '' 539 The default gateway. It can be left empty if it is auto-detected through DHCP. 540 It can be specified as a string or an option set along with a network interface. 541 ''; 542 }; 543 544 networking.defaultGateway6 = mkOption { 545 default = null; 546 example = { 547 address = "2001:4d0:1e04:895::1"; 548 interface = "enp3s0"; 549 }; 550 type = types.nullOr (types.coercedTo types.str gatewayCoerce (types.submodule gatewayOpts)); 551 description = lib.mdDoc '' 552 The default ipv6 gateway. It can be left empty if it is auto-detected through DHCP. 553 It can be specified as a string or an option set along with a network interface. 554 ''; 555 }; 556 557 networking.defaultGatewayWindowSize = mkOption { 558 default = null; 559 example = 524288; 560 type = types.nullOr types.int; 561 description = lib.mdDoc '' 562 The window size of the default gateway. It limits maximal data bursts that TCP peers 563 are allowed to send to us. 564 ''; 565 }; 566 567 networking.nameservers = mkOption { 568 type = types.listOf types.str; 569 default = []; 570 example = ["130.161.158.4" "130.161.33.17"]; 571 description = lib.mdDoc '' 572 The list of nameservers. It can be left empty if it is auto-detected through DHCP. 573 ''; 574 }; 575 576 networking.search = mkOption { 577 default = []; 578 example = [ "example.com" "home.arpa" ]; 579 type = types.listOf types.str; 580 description = lib.mdDoc '' 581 The list of search paths used when resolving domain names. 582 ''; 583 }; 584 585 networking.domain = mkOption { 586 default = null; 587 example = "home.arpa"; 588 type = types.nullOr types.str; 589 description = lib.mdDoc '' 590 The domain. It can be left empty if it is auto-detected through DHCP. 591 ''; 592 }; 593 594 networking.useHostResolvConf = mkOption { 595 type = types.bool; 596 default = false; 597 description = lib.mdDoc '' 598 In containers, whether to use the 599 {file}`resolv.conf` supplied by the host. 600 ''; 601 }; 602 603 networking.localCommands = mkOption { 604 type = types.lines; 605 default = ""; 606 example = "text=anything; echo You can put $text here."; 607 description = lib.mdDoc '' 608 Shell commands to be executed at the end of the 609 `network-setup` systemd service. Note that if 610 you are using DHCP to obtain the network configuration, 611 interfaces may not be fully configured yet. 612 ''; 613 }; 614 615 networking.interfaces = mkOption { 616 default = {}; 617 example = 618 { eth0.ipv4.addresses = [ { 619 address = "131.211.84.78"; 620 prefixLength = 25; 621 } ]; 622 }; 623 description = lib.mdDoc '' 624 The configuration for each network interface. If 625 {option}`networking.useDHCP` is true, then every 626 interface not listed here will be configured using DHCP. 627 628 Please note that {option}`systemd.network.netdevs` has more features 629 and is better maintained. When building new things, it is advised to 630 use that instead. 631 ''; 632 type = with types; attrsOf (submodule interfaceOpts); 633 }; 634 635 networking.vswitches = mkOption { 636 default = { }; 637 example = 638 { vs0.interfaces = { eth0 = { }; lo1 = { type="internal"; }; }; 639 vs1.interfaces = [ { name = "eth2"; } { name = "lo2"; type="internal"; } ]; 640 }; 641 description = 642 lib.mdDoc '' 643 This option allows you to define Open vSwitches that connect 644 physical networks together. The value of this option is an 645 attribute set. Each attribute specifies a vswitch, with the 646 attribute name specifying the name of the vswitch's network 647 interface. 648 ''; 649 650 type = with types; attrsOf (submodule { 651 652 options = { 653 654 interfaces = mkOption { 655 description = lib.mdDoc "The physical network interfaces connected by the vSwitch."; 656 type = with types; attrsOf (submodule vswitchInterfaceOpts); 657 }; 658 659 controllers = mkOption { 660 type = types.listOf types.str; 661 default = []; 662 example = [ "ptcp:6653:[::1]" ]; 663 description = lib.mdDoc '' 664 Specify the controller targets. For the allowed options see `man 8 ovs-vsctl`. 665 ''; 666 }; 667 668 openFlowRules = mkOption { 669 type = types.lines; 670 default = ""; 671 example = '' 672 actions=normal 673 ''; 674 description = lib.mdDoc '' 675 OpenFlow rules to insert into the Open vSwitch. All `openFlowRules` are 676 loaded with `ovs-ofctl` within one atomic operation. 677 ''; 678 }; 679 680 # TODO: custom "openflow version" type, with list from existing openflow protocols 681 supportedOpenFlowVersions = mkOption { 682 type = types.listOf types.str; 683 example = [ "OpenFlow10" "OpenFlow13" "OpenFlow14" ]; 684 default = [ "OpenFlow13" ]; 685 description = lib.mdDoc '' 686 Supported versions to enable on this switch. 687 ''; 688 }; 689 690 # TODO: use same type as elements from supportedOpenFlowVersions 691 openFlowVersion = mkOption { 692 type = types.str; 693 default = "OpenFlow13"; 694 description = lib.mdDoc '' 695 Version of OpenFlow protocol to use when communicating with the switch internally (e.g. with `openFlowRules`). 696 ''; 697 }; 698 699 extraOvsctlCmds = mkOption { 700 type = types.lines; 701 default = ""; 702 example = '' 703 set-fail-mode <switch_name> secure 704 set Bridge <switch_name> stp_enable=true 705 ''; 706 description = lib.mdDoc '' 707 Commands to manipulate the Open vSwitch database. Every line executed with `ovs-vsctl`. 708 All commands are bundled together with the operations for adding the interfaces 709 into one atomic operation. 710 ''; 711 }; 712 713 }; 714 715 }); 716 717 }; 718 719 networking.bridges = mkOption { 720 default = { }; 721 example = 722 { br0.interfaces = [ "eth0" "eth1" ]; 723 br1.interfaces = [ "eth2" "wlan0" ]; 724 }; 725 description = 726 lib.mdDoc '' 727 This option allows you to define Ethernet bridge devices 728 that connect physical networks together. The value of this 729 option is an attribute set. Each attribute specifies a 730 bridge, with the attribute name specifying the name of the 731 bridge's network interface. 732 ''; 733 734 type = with types; attrsOf (submodule { 735 736 options = { 737 738 interfaces = mkOption { 739 example = [ "eth0" "eth1" ]; 740 type = types.listOf types.str; 741 description = 742 lib.mdDoc "The physical network interfaces connected by the bridge."; 743 }; 744 745 rstp = mkOption { 746 default = false; 747 type = types.bool; 748 description = lib.mdDoc "Whether the bridge interface should enable rstp."; 749 }; 750 751 }; 752 753 }); 754 755 }; 756 757 networking.bonds = 758 let 759 driverOptionsExample = '' 760 { 761 miimon = "100"; 762 mode = "active-backup"; 763 } 764 ''; 765 in mkOption { 766 default = { }; 767 example = literalExpression '' 768 { 769 bond0 = { 770 interfaces = [ "eth0" "wlan0" ]; 771 driverOptions = ${driverOptionsExample}; 772 }; 773 anotherBond.interfaces = [ "enp4s0f0" "enp4s0f1" "enp5s0f0" "enp5s0f1" ]; 774 } 775 ''; 776 description = lib.mdDoc '' 777 This option allows you to define bond devices that aggregate multiple, 778 underlying networking interfaces together. The value of this option is 779 an attribute set. Each attribute specifies a bond, with the attribute 780 name specifying the name of the bond's network interface 781 ''; 782 783 type = with types; attrsOf (submodule { 784 785 options = { 786 787 interfaces = mkOption { 788 example = [ "enp4s0f0" "enp4s0f1" "wlan0" ]; 789 type = types.listOf types.str; 790 description = lib.mdDoc "The interfaces to bond together"; 791 }; 792 793 driverOptions = mkOption { 794 type = types.attrsOf types.str; 795 default = {}; 796 example = literalExpression driverOptionsExample; 797 description = lib.mdDoc '' 798 Options for the bonding driver. 799 Documentation can be found in 800 <https://www.kernel.org/doc/Documentation/networking/bonding.txt> 801 ''; 802 803 }; 804 805 lacp_rate = mkOption { 806 default = null; 807 example = "fast"; 808 type = types.nullOr types.str; 809 description = lib.mdDoc '' 810 DEPRECATED, use `driverOptions`. 811 Option specifying the rate in which we'll ask our link partner 812 to transmit LACPDU packets in 802.3ad mode. 813 ''; 814 }; 815 816 miimon = mkOption { 817 default = null; 818 example = 100; 819 type = types.nullOr types.int; 820 description = lib.mdDoc '' 821 DEPRECATED, use `driverOptions`. 822 Miimon is the number of millisecond in between each round of polling 823 by the device driver for failed links. By default polling is not 824 enabled and the driver is trusted to properly detect and handle 825 failure scenarios. 826 ''; 827 }; 828 829 mode = mkOption { 830 default = null; 831 example = "active-backup"; 832 type = types.nullOr types.str; 833 description = lib.mdDoc '' 834 DEPRECATED, use `driverOptions`. 835 The mode which the bond will be running. The default mode for 836 the bonding driver is balance-rr, optimizing for throughput. 837 More information about valid modes can be found at 838 https://www.kernel.org/doc/Documentation/networking/bonding.txt 839 ''; 840 }; 841 842 xmit_hash_policy = mkOption { 843 default = null; 844 example = "layer2+3"; 845 type = types.nullOr types.str; 846 description = lib.mdDoc '' 847 DEPRECATED, use `driverOptions`. 848 Selects the transmit hash policy to use for slave selection in 849 balance-xor, 802.3ad, and tlb modes. 850 ''; 851 }; 852 853 }; 854 855 }); 856 }; 857 858 networking.macvlans = mkOption { 859 default = { }; 860 example = literalExpression '' 861 { 862 wan = { 863 interface = "enp2s0"; 864 mode = "vepa"; 865 }; 866 } 867 ''; 868 description = lib.mdDoc '' 869 This option allows you to define macvlan interfaces which should 870 be automatically created. 871 ''; 872 type = with types; attrsOf (submodule { 873 options = { 874 875 interface = mkOption { 876 example = "enp4s0"; 877 type = types.str; 878 description = lib.mdDoc "The interface the macvlan will transmit packets through."; 879 }; 880 881 mode = mkOption { 882 default = null; 883 type = types.nullOr types.str; 884 example = "vepa"; 885 description = lib.mdDoc "The mode of the macvlan device."; 886 }; 887 888 }; 889 890 }); 891 }; 892 893 networking.fooOverUDP = mkOption { 894 default = { }; 895 example = 896 { 897 primary = { port = 9001; local = { address = "192.0.2.1"; dev = "eth0"; }; }; 898 backup = { port = 9002; }; 899 }; 900 description = lib.mdDoc '' 901 This option allows you to configure Foo Over UDP and Generic UDP Encapsulation 902 endpoints. See {manpage}`ip-fou(8)` for details. 903 ''; 904 type = with types; attrsOf (submodule { 905 options = { 906 port = mkOption { 907 type = port; 908 description = lib.mdDoc '' 909 Local port of the encapsulation UDP socket. 910 ''; 911 }; 912 913 protocol = mkOption { 914 type = nullOr (ints.between 1 255); 915 default = null; 916 description = lib.mdDoc '' 917 Protocol number of the encapsulated packets. Specifying `null` 918 (the default) creates a GUE endpoint, specifying a protocol number will create 919 a FOU endpoint. 920 ''; 921 }; 922 923 local = mkOption { 924 type = nullOr (submodule { 925 options = { 926 address = mkOption { 927 type = types.str; 928 description = lib.mdDoc '' 929 Local address to bind to. The address must be available when the FOU 930 endpoint is created, using the scripted network setup this can be achieved 931 either by setting `dev` or adding dependency information to 932 `systemd.services.<name>-fou-encap`; it isn't supported 933 when using networkd. 934 ''; 935 }; 936 937 dev = mkOption { 938 type = nullOr str; 939 default = null; 940 example = "eth0"; 941 description = lib.mdDoc '' 942 Network device to bind to. 943 ''; 944 }; 945 }; 946 }); 947 default = null; 948 example = { address = "203.0.113.22"; }; 949 description = lib.mdDoc '' 950 Local address (and optionally device) to bind to using the given port. 951 ''; 952 }; 953 }; 954 }); 955 }; 956 957 networking.sits = mkOption { 958 default = { }; 959 example = literalExpression '' 960 { 961 hurricane = { 962 remote = "10.0.0.1"; 963 local = "10.0.0.22"; 964 ttl = 255; 965 }; 966 msipv6 = { 967 remote = "192.168.0.1"; 968 dev = "enp3s0"; 969 ttl = 127; 970 }; 971 } 972 ''; 973 description = lib.mdDoc '' 974 This option allows you to define 6-to-4 interfaces which should be automatically created. 975 ''; 976 type = with types; attrsOf (submodule { 977 options = { 978 979 remote = mkOption { 980 type = types.nullOr types.str; 981 default = null; 982 example = "10.0.0.1"; 983 description = lib.mdDoc '' 984 The address of the remote endpoint to forward traffic over. 985 ''; 986 }; 987 988 local = mkOption { 989 type = types.nullOr types.str; 990 default = null; 991 example = "10.0.0.22"; 992 description = lib.mdDoc '' 993 The address of the local endpoint which the remote 994 side should send packets to. 995 ''; 996 }; 997 998 ttl = mkOption { 999 type = types.nullOr types.int; 1000 default = null; 1001 example = 255; 1002 description = lib.mdDoc '' 1003 The time-to-live of the connection to the remote tunnel endpoint. 1004 ''; 1005 }; 1006 1007 dev = mkOption { 1008 type = types.nullOr types.str; 1009 default = null; 1010 example = "enp4s0f0"; 1011 description = lib.mdDoc '' 1012 The underlying network device on which the tunnel resides. 1013 ''; 1014 }; 1015 1016 encapsulation = with types; mkOption { 1017 type = nullOr (submodule { 1018 options = { 1019 type = mkOption { 1020 type = enum [ "fou" "gue" ]; 1021 description = lib.mdDoc '' 1022 Selects encapsulation type. See 1023 {manpage}`ip-link(8)` for details. 1024 ''; 1025 }; 1026 1027 port = mkOption { 1028 type = port; 1029 example = 9001; 1030 description = lib.mdDoc '' 1031 Destination port for encapsulated packets. 1032 ''; 1033 }; 1034 1035 sourcePort = mkOption { 1036 type = nullOr types.port; 1037 default = null; 1038 example = 9002; 1039 description = lib.mdDoc '' 1040 Source port for encapsulated packets. Will be chosen automatically by 1041 the kernel if unset. 1042 ''; 1043 }; 1044 }; 1045 }); 1046 default = null; 1047 example = { type = "fou"; port = 9001; }; 1048 description = lib.mdDoc '' 1049 Configures encapsulation in UDP packets. 1050 ''; 1051 }; 1052 1053 }; 1054 1055 }); 1056 }; 1057 1058 networking.greTunnels = mkOption { 1059 default = { }; 1060 example = literalExpression '' 1061 { 1062 greBridge = { 1063 remote = "10.0.0.1"; 1064 local = "10.0.0.22"; 1065 dev = "enp4s0f0"; 1066 type = "tap"; 1067 ttl = 255; 1068 }; 1069 gre6Tunnel = { 1070 remote = "fd7a:5634::1"; 1071 local = "fd7a:5634::2"; 1072 dev = "enp4s0f0"; 1073 type = "tun6"; 1074 ttl = 255; 1075 }; 1076 } 1077 ''; 1078 description = lib.mdDoc '' 1079 This option allows you to define Generic Routing Encapsulation (GRE) tunnels. 1080 ''; 1081 type = with types; attrsOf (submodule { 1082 options = { 1083 1084 remote = mkOption { 1085 type = types.nullOr types.str; 1086 default = null; 1087 example = "10.0.0.1"; 1088 description = lib.mdDoc '' 1089 The address of the remote endpoint to forward traffic over. 1090 ''; 1091 }; 1092 1093 local = mkOption { 1094 type = types.nullOr types.str; 1095 default = null; 1096 example = "10.0.0.22"; 1097 description = lib.mdDoc '' 1098 The address of the local endpoint which the remote 1099 side should send packets to. 1100 ''; 1101 }; 1102 1103 dev = mkOption { 1104 type = types.nullOr types.str; 1105 default = null; 1106 example = "enp4s0f0"; 1107 description = lib.mdDoc '' 1108 The underlying network device on which the tunnel resides. 1109 ''; 1110 }; 1111 1112 ttl = mkOption { 1113 type = types.nullOr types.int; 1114 default = null; 1115 example = 255; 1116 description = lib.mdDoc '' 1117 The time-to-live/hoplimit of the connection to the remote tunnel endpoint. 1118 ''; 1119 }; 1120 1121 type = mkOption { 1122 type = with types; enum [ "tun" "tap" "tun6" "tap6" ]; 1123 default = "tap"; 1124 example = "tap"; 1125 apply = v: { 1126 tun = "gre"; 1127 tap = "gretap"; 1128 tun6 = "ip6gre"; 1129 tap6 = "ip6gretap"; 1130 }.${v}; 1131 description = lib.mdDoc '' 1132 Whether the tunnel routes layer 2 (tap) or layer 3 (tun) traffic. 1133 ''; 1134 }; 1135 }; 1136 }); 1137 }; 1138 1139 networking.vlans = mkOption { 1140 default = { }; 1141 example = literalExpression '' 1142 { 1143 vlan0 = { 1144 id = 3; 1145 interface = "enp3s0"; 1146 }; 1147 vlan1 = { 1148 id = 1; 1149 interface = "wlan0"; 1150 }; 1151 } 1152 ''; 1153 description = 1154 lib.mdDoc '' 1155 This option allows you to define vlan devices that tag packets 1156 on top of a physical interface. The value of this option is an 1157 attribute set. Each attribute specifies a vlan, with the name 1158 specifying the name of the vlan interface. 1159 ''; 1160 1161 type = with types; attrsOf (submodule { 1162 1163 options = { 1164 1165 id = mkOption { 1166 example = 1; 1167 type = types.int; 1168 description = lib.mdDoc "The vlan identifier"; 1169 }; 1170 1171 interface = mkOption { 1172 example = "enp4s0"; 1173 type = types.str; 1174 description = lib.mdDoc "The interface the vlan will transmit packets through."; 1175 }; 1176 1177 }; 1178 1179 }); 1180 1181 }; 1182 1183 networking.wlanInterfaces = mkOption { 1184 default = { }; 1185 example = literalExpression '' 1186 { 1187 wlan-station0 = { 1188 device = "wlp6s0"; 1189 }; 1190 wlan-adhoc0 = { 1191 type = "ibss"; 1192 device = "wlp6s0"; 1193 mac = "02:00:00:00:00:01"; 1194 }; 1195 wlan-p2p0 = { 1196 device = "wlp6s0"; 1197 mac = "02:00:00:00:00:02"; 1198 }; 1199 wlan-ap0 = { 1200 device = "wlp6s0"; 1201 mac = "02:00:00:00:00:03"; 1202 }; 1203 } 1204 ''; 1205 description = 1206 lib.mdDoc '' 1207 Creating multiple WLAN interfaces on top of one physical WLAN device (NIC). 1208 1209 The name of the WLAN interface corresponds to the name of the attribute. 1210 A NIC is referenced by the persistent device name of the WLAN interface that 1211 `udev` assigns to a NIC by default. 1212 If a NIC supports multiple WLAN interfaces, then the one NIC can be used as 1213 `device` for multiple WLAN interfaces. 1214 If a NIC is used for creating WLAN interfaces, then the default WLAN interface 1215 with a persistent device name form `udev` is not created. 1216 A WLAN interface with the persistent name assigned from `udev` 1217 would have to be created explicitly. 1218 ''; 1219 1220 type = with types; attrsOf (submodule { 1221 1222 options = { 1223 1224 device = mkOption { 1225 type = types.str; 1226 example = "wlp6s0"; 1227 description = lib.mdDoc "The name of the underlying hardware WLAN device as assigned by `udev`."; 1228 }; 1229 1230 type = mkOption { 1231 type = types.enum [ "managed" "ibss" "monitor" "mesh" "wds" ]; 1232 default = "managed"; 1233 example = "ibss"; 1234 description = lib.mdDoc '' 1235 The type of the WLAN interface. 1236 The type has to be supported by the underlying hardware of the device. 1237 ''; 1238 }; 1239 1240 meshID = mkOption { 1241 type = types.nullOr types.str; 1242 default = null; 1243 description = lib.mdDoc "MeshID of interface with type `mesh`."; 1244 }; 1245 1246 flags = mkOption { 1247 type = with types; nullOr (enum [ "none" "fcsfail" "control" "otherbss" "cook" "active" ]); 1248 default = null; 1249 example = "control"; 1250 description = lib.mdDoc '' 1251 Flags for interface of type `monitor`. 1252 ''; 1253 }; 1254 1255 fourAddr = mkOption { 1256 type = types.nullOr types.bool; 1257 default = null; 1258 description = lib.mdDoc "Whether to enable `4-address mode` with type `managed`."; 1259 }; 1260 1261 mac = mkOption { 1262 type = types.nullOr types.str; 1263 default = null; 1264 example = "02:00:00:00:00:01"; 1265 description = lib.mdDoc '' 1266 MAC address to use for the device. If `null`, then the MAC of the 1267 underlying hardware WLAN device is used. 1268 1269 INFO: Locally administered MAC addresses are of the form: 1270 - x2:xx:xx:xx:xx:xx 1271 - x6:xx:xx:xx:xx:xx 1272 - xA:xx:xx:xx:xx:xx 1273 - xE:xx:xx:xx:xx:xx 1274 ''; 1275 }; 1276 1277 }; 1278 1279 }); 1280 1281 }; 1282 1283 networking.useDHCP = mkOption { 1284 type = types.bool; 1285 default = true; 1286 description = lib.mdDoc '' 1287 Whether to use DHCP to obtain an IP address and other 1288 configuration for all network interfaces that are not manually 1289 configured. 1290 ''; 1291 }; 1292 1293 networking.useNetworkd = mkOption { 1294 default = false; 1295 type = types.bool; 1296 description = lib.mdDoc '' 1297 Whether we should use networkd as the network configuration backend or 1298 the legacy script based system. Note that this option is experimental, 1299 enable at your own risk. 1300 ''; 1301 }; 1302 1303 networking.tempAddresses = mkOption { 1304 default = if cfg.enableIPv6 then "default" else "disabled"; 1305 defaultText = literalExpression '' 1306 if ''${config.${opt.enableIPv6}} then "default" else "disabled" 1307 ''; 1308 type = types.enum (lib.attrNames tempaddrValues); 1309 description = lib.mdDoc '' 1310 Whether to enable IPv6 Privacy Extensions for interfaces not 1311 configured explicitly in 1312 [](#opt-networking.interfaces._name_.tempAddress). 1313 1314 This sets the ipv6.conf.*.use_tempaddr sysctl for all 1315 interfaces. Possible values are: 1316 1317 ${tempaddrDoc} 1318 ''; 1319 }; 1320 1321 }; 1322 1323 1324 ###### implementation 1325 1326 config = { 1327 1328 warnings = concatMap (i: i.warnings) interfaces; 1329 1330 assertions = 1331 (forEach interfaces (i: { 1332 # With the linux kernel, interface name length is limited by IFNAMSIZ 1333 # to 16 bytes, including the trailing null byte. 1334 # See include/linux/if.h in the kernel sources 1335 assertion = stringLength i.name < 16; 1336 message = '' 1337 The name of networking.interfaces."${i.name}" is too long, it needs to be less than 16 characters. 1338 ''; 1339 })) ++ (forEach slaveIfs (i: { 1340 assertion = i.ipv4.addresses == [ ] && i.ipv6.addresses == [ ]; 1341 message = '' 1342 The networking.interfaces."${i.name}" must not have any defined ips when it is a slave. 1343 ''; 1344 })) ++ (forEach interfaces (i: { 1345 assertion = i.tempAddress != "disabled" -> cfg.enableIPv6; 1346 message = '' 1347 Temporary addresses are only needed when IPv6 is enabled. 1348 ''; 1349 })) ++ (forEach interfaces (i: { 1350 assertion = (i.virtual && i.virtualType == "tun") -> i.macAddress == null; 1351 message = '' 1352 Setting a MAC Address for tun device ${i.name} isn't supported. 1353 ''; 1354 })) ++ [ 1355 { 1356 assertion = cfg.hostId == null || (stringLength cfg.hostId == 8 && isHexString cfg.hostId); 1357 message = "Invalid value given to the networking.hostId option."; 1358 } 1359 ]; 1360 1361 boot.kernelModules = [ ] 1362 ++ optional hasVirtuals "tun" 1363 ++ optional hasSits "sit" 1364 ++ optional hasGres "gre" 1365 ++ optional hasBonds "bonding" 1366 ++ optional hasFous "fou"; 1367 1368 boot.extraModprobeConfig = 1369 # This setting is intentional as it prevents default bond devices 1370 # from being created. 1371 optionalString hasBonds "options bonding max_bonds=0"; 1372 1373 boot.kernel.sysctl = { 1374 "net.ipv4.conf.all.forwarding" = mkDefault (any (i: i.proxyARP) interfaces); 1375 "net.ipv6.conf.all.disable_ipv6" = mkDefault (!cfg.enableIPv6); 1376 "net.ipv6.conf.default.disable_ipv6" = mkDefault (!cfg.enableIPv6); 1377 # networkmanager falls back to "/proc/sys/net/ipv6/conf/default/use_tempaddr" 1378 "net.ipv6.conf.default.use_tempaddr" = tempaddrValues.${cfg.tempAddresses}.sysctl; 1379 } // listToAttrs (forEach interfaces 1380 (i: nameValuePair "net.ipv4.conf.${replaceChars ["."] ["/"] i.name}.proxy_arp" i.proxyARP)) 1381 // listToAttrs (forEach interfaces 1382 (i: let 1383 opt = i.tempAddress; 1384 val = tempaddrValues.${opt}.sysctl; 1385 in nameValuePair "net.ipv6.conf.${replaceChars ["."] ["/"] i.name}.use_tempaddr" val)); 1386 1387 security.wrappers = { 1388 ping = { 1389 owner = "root"; 1390 group = "root"; 1391 capabilities = "cap_net_raw+p"; 1392 source = "${pkgs.iputils.out}/bin/ping"; 1393 }; 1394 }; 1395 security.apparmor.policies."bin.ping".profile = lib.mkIf config.security.apparmor.policies."bin.ping".enable (lib.mkAfter '' 1396 /run/wrappers/bin/ping { 1397 include <abstractions/base> 1398 include <nixos/security.wrappers> 1399 rpx /run/wrappers/wrappers.*/ping, 1400 } 1401 /run/wrappers/wrappers.*/ping { 1402 include <abstractions/base> 1403 include <nixos/security.wrappers> 1404 r /run/wrappers/wrappers.*/ping.real, 1405 mrpx ${config.security.wrappers.ping.source}, 1406 capability net_raw, 1407 capability setpcap, 1408 } 1409 ''); 1410 1411 # Set the host and domain names in the activation script. Don't 1412 # clear it if it's not configured in the NixOS configuration, 1413 # since it may have been set by dhcpcd in the meantime. 1414 system.activationScripts.hostname = 1415 optionalString (cfg.hostName != "") '' 1416 hostname "${cfg.hostName}" 1417 ''; 1418 system.activationScripts.domain = 1419 optionalString (cfg.domain != null) '' 1420 domainname "${cfg.domain}" 1421 ''; 1422 1423 environment.etc.hostid = mkIf (cfg.hostId != null) { source = hostidFile; }; 1424 boot.initrd.systemd.contents."/etc/hostid" = mkIf (cfg.hostId != null) { source = hostidFile; }; 1425 1426 # static hostname configuration needed for hostnamectl and the 1427 # org.freedesktop.hostname1 dbus service (both provided by systemd) 1428 environment.etc.hostname = mkIf (cfg.hostName != "") 1429 { 1430 text = cfg.hostName + "\n"; 1431 }; 1432 1433 environment.systemPackages = 1434 [ pkgs.host 1435 pkgs.iproute2 1436 pkgs.iputils 1437 pkgs.nettools 1438 ] 1439 ++ optionals config.networking.wireless.enable [ 1440 pkgs.wirelesstools # FIXME: obsolete? 1441 pkgs.iw 1442 ] 1443 ++ bridgeStp; 1444 1445 # The network-interfaces target is kept for backwards compatibility. 1446 # New modules must NOT use it. 1447 systemd.targets.network-interfaces = 1448 { description = "All Network Interfaces (deprecated)"; 1449 wantedBy = [ "network.target" ]; 1450 before = [ "network.target" ]; 1451 after = [ "network-pre.target" ]; 1452 unitConfig.X-StopOnReconfiguration = true; 1453 }; 1454 1455 systemd.services = { 1456 network-local-commands = { 1457 description = "Extra networking commands."; 1458 before = [ "network.target" ]; 1459 wantedBy = [ "network.target" ]; 1460 after = [ "network-pre.target" ]; 1461 unitConfig.ConditionCapability = "CAP_NET_ADMIN"; 1462 path = [ pkgs.iproute2 ]; 1463 serviceConfig.Type = "oneshot"; 1464 serviceConfig.RemainAfterExit = true; 1465 script = '' 1466 # Run any user-specified commands. 1467 ${cfg.localCommands} 1468 ''; 1469 }; 1470 }; 1471 services.mstpd = mkIf needsMstpd { enable = true; }; 1472 1473 virtualisation.vswitch = mkIf (cfg.vswitches != { }) { enable = true; }; 1474 1475 services.udev.packages = [ 1476 (pkgs.writeTextFile rec { 1477 name = "ipv6-privacy-extensions.rules"; 1478 destination = "/etc/udev/rules.d/98-${name}"; 1479 text = let 1480 sysctl-value = tempaddrValues.${cfg.tempAddresses}.sysctl; 1481 in '' 1482 # enable and prefer IPv6 privacy addresses by default 1483 ACTION=="add", SUBSYSTEM=="net", RUN+="${pkgs.bash}/bin/sh -c 'echo ${sysctl-value} > /proc/sys/net/ipv6/conf/$name/use_tempaddr'" 1484 ''; 1485 }) 1486 (pkgs.writeTextFile rec { 1487 name = "ipv6-privacy-extensions.rules"; 1488 destination = "/etc/udev/rules.d/99-${name}"; 1489 text = concatMapStrings (i: 1490 let 1491 opt = i.tempAddress; 1492 val = tempaddrValues.${opt}.sysctl; 1493 msg = tempaddrValues.${opt}.description; 1494 in 1495 '' 1496 # override to ${msg} for ${i.name} 1497 ACTION=="add", SUBSYSTEM=="net", RUN+="${pkgs.procps}/bin/sysctl net.ipv6.conf.${replaceChars ["."] ["/"] i.name}.use_tempaddr=${val}" 1498 '') (filter (i: i.tempAddress != cfg.tempAddresses) interfaces); 1499 }) 1500 ] ++ lib.optional (cfg.wlanInterfaces != {}) 1501 (pkgs.writeTextFile { 1502 name = "99-zzz-40-wlanInterfaces.rules"; 1503 destination = "/etc/udev/rules.d/99-zzz-40-wlanInterfaces.rules"; 1504 text = 1505 let 1506 # Collect all interfaces that are defined for a device 1507 # as device:interface key:value pairs. 1508 wlanDeviceInterfaces = 1509 let 1510 allDevices = unique (mapAttrsToList (_: v: v.device) cfg.wlanInterfaces); 1511 interfacesOfDevice = d: filterAttrs (_: v: v.device == d) cfg.wlanInterfaces; 1512 in 1513 genAttrs allDevices (d: interfacesOfDevice d); 1514 1515 # Convert device:interface key:value pairs into a list, and if it exists, 1516 # place the interface which is named after the device at the beginning. 1517 wlanListDeviceFirst = device: interfaces: 1518 if hasAttr device interfaces 1519 then mapAttrsToList (n: v: v//{_iName=n;}) (filterAttrs (n: _: n==device) interfaces) ++ mapAttrsToList (n: v: v//{_iName=n;}) (filterAttrs (n: _: n!=device) interfaces) 1520 else mapAttrsToList (n: v: v // {_iName = n;}) interfaces; 1521 1522 # Udev script to execute for the default WLAN interface with the persistend udev name. 1523 # The script creates the required, new WLAN interfaces interfaces and configures the 1524 # existing, default interface. 1525 curInterfaceScript = device: current: new: pkgs.writeScript "udev-run-script-wlan-interfaces-${device}.sh" '' 1526 #!${pkgs.runtimeShell} 1527 # Change the wireless phy device to a predictable name. 1528 ${pkgs.iw}/bin/iw phy `${pkgs.coreutils}/bin/cat /sys/class/net/$INTERFACE/phy80211/name` set name ${device} 1529 1530 # Add new WLAN interfaces 1531 ${flip concatMapStrings new (i: '' 1532 ${pkgs.iw}/bin/iw phy ${device} interface add ${i._iName} type managed 1533 '')} 1534 1535 # Configure the current interface 1536 ${pkgs.iw}/bin/iw dev ${device} set type ${current.type} 1537 ${optionalString (current.type == "mesh" && current.meshID!=null) "${pkgs.iw}/bin/iw dev ${device} set meshid ${current.meshID}"} 1538 ${optionalString (current.type == "monitor" && current.flags!=null) "${pkgs.iw}/bin/iw dev ${device} set monitor ${current.flags}"} 1539 ${optionalString (current.type == "managed" && current.fourAddr!=null) "${pkgs.iw}/bin/iw dev ${device} set 4addr ${if current.fourAddr then "on" else "off"}"} 1540 ${optionalString (current.mac != null) "${pkgs.iproute2}/bin/ip link set dev ${device} address ${current.mac}"} 1541 ''; 1542 1543 # Udev script to execute for a new WLAN interface. The script configures the new WLAN interface. 1544 newInterfaceScript = new: pkgs.writeScript "udev-run-script-wlan-interfaces-${new._iName}.sh" '' 1545 #!${pkgs.runtimeShell} 1546 # Configure the new interface 1547 ${pkgs.iw}/bin/iw dev ${new._iName} set type ${new.type} 1548 ${optionalString (new.type == "mesh" && new.meshID!=null) "${pkgs.iw}/bin/iw dev ${new._iName} set meshid ${new.meshID}"} 1549 ${optionalString (new.type == "monitor" && new.flags!=null) "${pkgs.iw}/bin/iw dev ${new._iName} set monitor ${new.flags}"} 1550 ${optionalString (new.type == "managed" && new.fourAddr!=null) "${pkgs.iw}/bin/iw dev ${new._iName} set 4addr ${if new.fourAddr then "on" else "off"}"} 1551 ${optionalString (new.mac != null) "${pkgs.iproute2}/bin/ip link set dev ${new._iName} address ${new.mac}"} 1552 ''; 1553 1554 # Udev attributes for systemd to name the device and to create a .device target. 1555 systemdAttrs = n: ''NAME:="${n}", ENV{INTERFACE}="${n}", ENV{SYSTEMD_ALIAS}="/sys/subsystem/net/devices/${n}", TAG+="systemd"''; 1556 in 1557 flip (concatMapStringsSep "\n") (attrNames wlanDeviceInterfaces) (device: 1558 let 1559 interfaces = wlanListDeviceFirst device wlanDeviceInterfaces.${device}; 1560 curInterface = elemAt interfaces 0; 1561 newInterfaces = drop 1 interfaces; 1562 in '' 1563 # It is important to have that rule first as overwriting the NAME attribute also prevents the 1564 # next rules from matching. 1565 ${flip (concatMapStringsSep "\n") (wlanListDeviceFirst device wlanDeviceInterfaces.${device}) (interface: 1566 ''ACTION=="add", SUBSYSTEM=="net", ENV{DEVTYPE}=="wlan", ENV{INTERFACE}=="${interface._iName}", ${systemdAttrs interface._iName}, RUN+="${newInterfaceScript interface}"'')} 1567 1568 # Add the required, new WLAN interfaces to the default WLAN interface with the 1569 # persistent, default name as assigned by udev. 1570 ACTION=="add", SUBSYSTEM=="net", ENV{DEVTYPE}=="wlan", NAME=="${device}", ${systemdAttrs curInterface._iName}, RUN+="${curInterfaceScript device curInterface newInterfaces}" 1571 # Generate the same systemd events for both 'add' and 'move' udev events. 1572 ACTION=="move", SUBSYSTEM=="net", ENV{DEVTYPE}=="wlan", NAME=="${device}", ${systemdAttrs curInterface._iName} 1573 ''); 1574 }); 1575 }; 1576 1577}