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