at 18.03-beta 42 kB view raw
1{ config, options, lib, pkgs, utils, stdenv, ... }: 2 3with lib; 4with utils; 5 6let 7 8 cfg = config.networking; 9 interfaces = attrValues cfg.interfaces; 10 hasVirtuals = any (i: i.virtual) interfaces; 11 hasSits = cfg.sits != { }; 12 hasBonds = cfg.bonds != { }; 13 14 slaves = concatMap (i: i.interfaces) (attrValues cfg.bonds) 15 ++ concatMap (i: i.interfaces) (attrValues cfg.bridges) 16 ++ concatMap (i: i.interfaces) (attrValues cfg.vswitches); 17 18 slaveIfs = map (i: cfg.interfaces.${i}) (filter (i: cfg.interfaces ? ${i}) slaves); 19 20 rstpBridges = flip filterAttrs cfg.bridges (_: { rstp, ... }: rstp); 21 22 needsMstpd = rstpBridges != { }; 23 24 bridgeStp = optional needsMstpd (pkgs.writeTextFile { 25 name = "bridge-stp"; 26 executable = true; 27 destination = "/bin/bridge-stp"; 28 text = '' 29 #!${pkgs.stdenv.shell} -e 30 export PATH="${pkgs.mstpd}/bin" 31 32 BRIDGES=(${concatStringsSep " " (attrNames rstpBridges)}) 33 for BRIDGE in $BRIDGES; do 34 if [ "$BRIDGE" = "$1" ]; then 35 if [ "$2" = "start" ]; then 36 mstpctl addbridge "$BRIDGE" 37 exit 0 38 elif [ "$2" = "stop" ]; then 39 mstpctl delbridge "$BRIDGE" 40 exit 0 41 fi 42 exit 1 43 fi 44 done 45 exit 1 46 ''; 47 }); 48 49 # Collect all interfaces that are defined for a device 50 # as device:interface key:value pairs. 51 wlanDeviceInterfaces = 52 let 53 allDevices = unique (mapAttrsToList (_: v: v.device) cfg.wlanInterfaces); 54 interfacesOfDevice = d: filterAttrs (_: v: v.device == d) cfg.wlanInterfaces; 55 in 56 genAttrs allDevices (d: interfacesOfDevice d); 57 58 # Convert device:interface key:value pairs into a list, and if it exists, 59 # place the interface which is named after the device at the beginning. 60 wlanListDeviceFirst = device: interfaces: 61 if hasAttr device interfaces 62 then mapAttrsToList (n: v: v//{_iName=n;}) (filterAttrs (n: _: n==device) interfaces) ++ mapAttrsToList (n: v: v//{_iName=n;}) (filterAttrs (n: _: n!=device) interfaces) 63 else mapAttrsToList (n: v: v // {_iName = n;}) interfaces; 64 65 # udev script that configures a physical wlan device and adds virtual interfaces 66 wlanDeviceUdevScript = device: interfaceList: pkgs.writeScript "wlan-${device}-udev-script" '' 67 #!${pkgs.stdenv.shell} 68 69 # Change the wireless phy device to a predictable name. 70 if [ -e "/sys/class/net/${device}/phy80211/name" ]; then 71 ${pkgs.iw}/bin/iw phy `${pkgs.coreutils}/bin/cat /sys/class/net/${device}/phy80211/name` set name ${device} || true 72 fi 73 74 # Crate new, virtual interfaces and configure them at the same time 75 ${flip concatMapStrings (drop 1 interfaceList) (i: '' 76 ${pkgs.iw}/bin/iw dev ${device} interface add ${i._iName} type ${i.type} \ 77 ${optionalString (i.type == "mesh" && i.meshID != null) "mesh_id ${i.meshID}"} \ 78 ${optionalString (i.type == "monitor" && i.flags != null) "flags ${i.flags}"} \ 79 ${optionalString (i.type == "managed" && i.fourAddr != null) "4addr ${if i.fourAddr then "on" else "off"}"} \ 80 ${optionalString (i.mac != null) "addr ${i.mac}"} 81 '')} 82 83 # Reconfigure and rename the default interface that already exists 84 ${flip concatMapStrings (take 1 interfaceList) (i: '' 85 ${pkgs.iw}/bin/iw dev ${device} set type ${i.type} 86 ${optionalString (i.type == "mesh" && i.meshID != null) "${pkgs.iw}/bin/iw dev ${device} set meshid ${i.meshID}"} 87 ${optionalString (i.type == "monitor" && i.flags != null) "${pkgs.iw}/bin/iw dev ${device} set monitor ${i.flags}"} 88 ${optionalString (i.type == "managed" && i.fourAddr != null) "${pkgs.iw}/bin/iw dev ${device} set 4addr ${if i.fourAddr then "on" else "off"}"} 89 ${optionalString (i.mac != null) "${pkgs.iproute}/bin/ip link set dev ${device} address ${i.mac}"} 90 ${optionalString (device != i._iName) "${pkgs.iproute}/bin/ip link set dev ${device} name ${i._iName}"} 91 '')} 92 ''; 93 94 # We must escape interfaces due to the systemd interpretation 95 subsystemDevice = interface: 96 "sys-subsystem-net-devices-${escapeSystemdPath interface}.device"; 97 98 addrOpts = v: 99 assert v == 4 || v == 6; 100 { options = { 101 address = mkOption { 102 type = types.str; 103 description = '' 104 IPv${toString v} address of the interface. Leave empty to configure the 105 interface using DHCP. 106 ''; 107 }; 108 109 prefixLength = mkOption { 110 type = types.addCheck types.int (n: n >= 0 && n <= (if v == 4 then 32 else 128)); 111 description = '' 112 Subnet mask of the interface, specified as the number of 113 bits in the prefix (<literal>${if v == 4 then "24" else "64"}</literal>). 114 ''; 115 }; 116 }; 117 }; 118 119 routeOpts = v: 120 { options = { 121 address = mkOption { 122 type = types.str; 123 description = "IPv${toString v} address of the network."; 124 }; 125 126 prefixLength = mkOption { 127 type = types.addCheck types.int (n: n >= 0 && n <= (if v == 4 then 32 else 128)); 128 description = '' 129 Subnet mask of the network, specified as the number of 130 bits in the prefix (<literal>${if v == 4 then "24" else "64"}</literal>). 131 ''; 132 }; 133 134 via = mkOption { 135 type = types.nullOr types.str; 136 default = null; 137 description = "IPv${toString v} address of the next hop."; 138 }; 139 140 options = mkOption { 141 type = types.attrsOf types.str; 142 default = { }; 143 example = { mtu = "1492"; window = "524288"; }; 144 description = '' 145 Other route options. See the symbol <literal>OPTION</literal> 146 in the <literal>ip-route(8)</literal> manual page for the details. 147 ''; 148 }; 149 150 }; 151 }; 152 153 gatewayCoerce = address: { inherit address; }; 154 155 gatewayOpts = { ... }: { 156 157 options = { 158 159 address = mkOption { 160 type = types.str; 161 description = "The default gateway address."; 162 }; 163 164 interface = mkOption { 165 type = types.nullOr types.str; 166 default = null; 167 example = "enp0s3"; 168 description = "The default gateway interface."; 169 }; 170 171 metric = mkOption { 172 type = types.nullOr types.int; 173 default = null; 174 example = 42; 175 description = "The default gateway metric/preference."; 176 }; 177 178 }; 179 180 }; 181 182 interfaceOpts = { name, ... }: { 183 184 options = { 185 name = mkOption { 186 example = "eth0"; 187 type = types.str; 188 description = "Name of the interface."; 189 }; 190 191 preferTempAddress = mkOption { 192 type = types.bool; 193 default = cfg.enableIPv6; 194 defaultText = literalExample "config.networking.enableIpv6"; 195 description = '' 196 When using SLAAC prefer a temporary (IPv6) address over the EUI-64 197 address for originating connections. This is used to reduce tracking. 198 ''; 199 }; 200 201 useDHCP = mkOption { 202 type = types.nullOr types.bool; 203 default = null; 204 description = '' 205 Whether this interface should be configured with dhcp. 206 Null implies the old behavior which depends on whether ip addresses 207 are specified or not. 208 ''; 209 }; 210 211 ipv4.addresses = mkOption { 212 default = [ ]; 213 example = [ 214 { address = "10.0.0.1"; prefixLength = 16; } 215 { address = "192.168.1.1"; prefixLength = 24; } 216 ]; 217 type = with types; listOf (submodule (addrOpts 4)); 218 description = '' 219 List of IPv4 addresses that will be statically assigned to the interface. 220 ''; 221 }; 222 223 ipv6.addresses = mkOption { 224 default = [ ]; 225 example = [ 226 { address = "fdfd:b3f0:482::1"; prefixLength = 48; } 227 { address = "2001:1470:fffd:2098::e006"; prefixLength = 64; } 228 ]; 229 type = with types; listOf (submodule (addrOpts 6)); 230 description = '' 231 List of IPv6 addresses that will be statically assigned to the interface. 232 ''; 233 }; 234 235 ipv4.routes = mkOption { 236 default = []; 237 example = [ 238 { address = "10.0.0.0"; prefixLength = 16; } 239 { address = "192.168.2.0"; prefixLength = 24; via = "192.168.1.1"; } 240 ]; 241 type = with types; listOf (submodule (routeOpts 4)); 242 description = '' 243 List of extra IPv4 static routes that will be assigned to the interface. 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 = '' 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 = '' 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 = '' 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 = '' 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 = '' 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 = literalExample ''if hasPrefix "tun" name then "tun" else "tap"''; 298 type = with types; enum [ "tun" "tap" ]; 299 description = '' 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 = '' 310 Turn on proxy_arp for this device (and proxy_ndp for ipv6). 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 }; 325 326 config = { 327 name = mkDefault name; 328 }; 329 330 # Renamed or removed options 331 imports = 332 let 333 defined = x: x != "_mkMergedOptionModule"; 334 in [ 335 (mkRenamedOptionModule [ "ip4" ] [ "ipv4" "addresses"]) 336 (mkRenamedOptionModule [ "ip6" ] [ "ipv6" "addresses"]) 337 (mkRemovedOptionModule [ "subnetMask" ] '' 338 Supply a prefix length instead; use option 339 networking.interfaces.<name>.ipv{4,6}.addresses'') 340 (mkMergedOptionModule 341 [ [ "ipAddress" ] [ "prefixLength" ] ] 342 [ "ipv4" "addresses" ] 343 (cfg: with cfg; 344 optional (defined ipAddress && defined prefixLength) 345 { address = ipAddress; prefixLength = prefixLength; })) 346 (mkMergedOptionModule 347 [ [ "ipv6Address" ] [ "ipv6PrefixLength" ] ] 348 [ "ipv6" "addresses" ] 349 (cfg: with cfg; 350 optional (defined ipv6Address && defined ipv6PrefixLength) 351 { address = ipv6Address; prefixLength = ipv6PrefixLength; })) 352 353 ({ options.warnings = options.warnings; }) 354 ]; 355 356 }; 357 358 hexChars = stringToCharacters "0123456789abcdef"; 359 360 isHexString = s: all (c: elem c hexChars) (stringToCharacters (toLower s)); 361 362in 363 364{ 365 366 ###### interface 367 368 options = { 369 370 networking.hostName = mkOption { 371 default = "nixos"; 372 type = types.str; 373 description = '' 374 The name of the machine. Leave it empty if you want to obtain 375 it from a DHCP server (if using DHCP). 376 ''; 377 }; 378 379 networking.hostId = mkOption { 380 default = null; 381 example = "4e98920d"; 382 type = types.nullOr types.str; 383 description = '' 384 The 32-bit host ID of the machine, formatted as 8 hexadecimal characters. 385 386 You should try to make this ID unique among your machines. You can 387 generate a random 32-bit ID using the following commands: 388 389 <literal>cksum /etc/machine-id | while read c rest; do printf "%x" $c; done</literal> 390 391 (this derives it from the machine-id that systemd generates) or 392 393 <literal>head -c4 /dev/urandom | od -A none -t x4</literal> 394 ''; 395 }; 396 397 networking.enableIPv6 = mkOption { 398 default = true; 399 type = types.bool; 400 description = '' 401 Whether to enable support for IPv6. 402 ''; 403 }; 404 405 networking.defaultGateway = mkOption { 406 default = null; 407 example = { 408 address = "131.211.84.1"; 409 interface = "enp3s0"; 410 }; 411 type = types.nullOr (types.coercedTo types.str gatewayCoerce (types.submodule gatewayOpts)); 412 description = '' 413 The default gateway. It can be left empty if it is auto-detected through DHCP. 414 It can be specified as a string or an option set along with a network interface. 415 ''; 416 }; 417 418 networking.defaultGateway6 = mkOption { 419 default = null; 420 example = { 421 address = "2001:4d0:1e04:895::1"; 422 interface = "enp3s0"; 423 }; 424 type = types.nullOr (types.coercedTo types.str gatewayCoerce (types.submodule gatewayOpts)); 425 description = '' 426 The default ipv6 gateway. It can be left empty if it is auto-detected through DHCP. 427 It can be specified as a string or an option set along with a network interface. 428 ''; 429 }; 430 431 networking.defaultGatewayWindowSize = mkOption { 432 default = null; 433 example = 524288; 434 type = types.nullOr types.int; 435 description = '' 436 The window size of the default gateway. It limits maximal data bursts that TCP peers 437 are allowed to send to us. 438 ''; 439 }; 440 441 networking.nameservers = mkOption { 442 type = types.listOf types.str; 443 default = []; 444 example = ["130.161.158.4" "130.161.33.17"]; 445 description = '' 446 The list of nameservers. It can be left empty if it is auto-detected through DHCP. 447 ''; 448 }; 449 450 networking.search = mkOption { 451 default = []; 452 example = [ "example.com" "local.domain" ]; 453 type = types.listOf types.str; 454 description = '' 455 The list of search paths used when resolving domain names. 456 ''; 457 }; 458 459 networking.domain = mkOption { 460 default = null; 461 example = "home"; 462 type = types.nullOr types.str; 463 description = '' 464 The domain. It can be left empty if it is auto-detected through DHCP. 465 ''; 466 }; 467 468 networking.useHostResolvConf = mkOption { 469 type = types.bool; 470 default = false; 471 description = '' 472 In containers, whether to use the 473 <filename>resolv.conf</filename> supplied by the host. 474 ''; 475 }; 476 477 networking.localCommands = mkOption { 478 type = types.lines; 479 default = ""; 480 example = "text=anything; echo You can put $text here."; 481 description = '' 482 Shell commands to be executed at the end of the 483 <literal>network-setup</literal> systemd service. Note that if 484 you are using DHCP to obtain the network configuration, 485 interfaces may not be fully configured yet. 486 ''; 487 }; 488 489 networking.interfaces = mkOption { 490 default = {}; 491 example = 492 { eth0.ipv4 = [ { 493 address = "131.211.84.78"; 494 prefixLength = 25; 495 } ]; 496 }; 497 description = '' 498 The configuration for each network interface. If 499 <option>networking.useDHCP</option> is true, then every 500 interface not listed here will be configured using DHCP. 501 ''; 502 type = with types; loaOf (submodule interfaceOpts); 503 }; 504 505 networking.vswitches = mkOption { 506 default = { }; 507 example = 508 { vs0.interfaces = [ "eth0" "eth1" ]; 509 vs1.interfaces = [ "eth2" "wlan0" ]; 510 }; 511 description = 512 '' 513 This option allows you to define Open vSwitches that connect 514 physical networks together. The value of this option is an 515 attribute set. Each attribute specifies a vswitch, with the 516 attribute name specifying the name of the vswitch's network 517 interface. 518 ''; 519 520 type = with types; attrsOf (submodule { 521 522 options = { 523 524 interfaces = mkOption { 525 example = [ "eth0" "eth1" ]; 526 type = types.listOf types.str; 527 description = 528 "The physical network interfaces connected by the vSwitch."; 529 }; 530 531 controllers = mkOption { 532 type = types.listOf types.str; 533 default = []; 534 example = [ "ptcp:6653:[::1]" ]; 535 description = '' 536 Specify the controller targets. For the allowed options see <literal>man 8 ovs-vsctl</literal>. 537 ''; 538 }; 539 540 openFlowRules = mkOption { 541 type = types.lines; 542 default = ""; 543 example = '' 544 actions=normal 545 ''; 546 description = '' 547 OpenFlow rules to insert into the Open vSwitch. All <literal>openFlowRules</literal> are 548 loaded with <literal>ovs-ofctl</literal> within one atomic operation. 549 ''; 550 }; 551 552 extraOvsctlCmds = mkOption { 553 type = types.lines; 554 default = ""; 555 example = '' 556 set-fail-mode <switch_name> secure 557 set Bridge <switch_name> stp_enable=true 558 ''; 559 description = '' 560 Commands to manipulate the Open vSwitch database. Every line executed with <literal>ovs-vsctl</literal>. 561 All commands are bundled together with the operations for adding the interfaces 562 into one atomic operation. 563 ''; 564 }; 565 566 }; 567 568 }); 569 570 }; 571 572 networking.bridges = mkOption { 573 default = { }; 574 example = 575 { br0.interfaces = [ "eth0" "eth1" ]; 576 br1.interfaces = [ "eth2" "wlan0" ]; 577 }; 578 description = 579 '' 580 This option allows you to define Ethernet bridge devices 581 that connect physical networks together. The value of this 582 option is an attribute set. Each attribute specifies a 583 bridge, with the attribute name specifying the name of the 584 bridge's network interface. 585 ''; 586 587 type = with types; attrsOf (submodule { 588 589 options = { 590 591 interfaces = mkOption { 592 example = [ "eth0" "eth1" ]; 593 type = types.listOf types.str; 594 description = 595 "The physical network interfaces connected by the bridge."; 596 }; 597 598 rstp = mkOption { 599 default = false; 600 type = types.bool; 601 description = "Whether the bridge interface should enable rstp."; 602 }; 603 604 }; 605 606 }); 607 608 }; 609 610 networking.bonds = 611 let 612 driverOptionsExample = { 613 miimon = "100"; 614 mode = "active-backup"; 615 }; 616 in mkOption { 617 default = { }; 618 example = literalExample { 619 bond0 = { 620 interfaces = [ "eth0" "wlan0" ]; 621 driverOptions = driverOptionsExample; 622 }; 623 anotherBond.interfaces = [ "enp4s0f0" "enp4s0f1" "enp5s0f0" "enp5s0f1" ]; 624 }; 625 description = '' 626 This option allows you to define bond devices that aggregate multiple, 627 underlying networking interfaces together. The value of this option is 628 an attribute set. Each attribute specifies a bond, with the attribute 629 name specifying the name of the bond's network interface 630 ''; 631 632 type = with types; attrsOf (submodule { 633 634 options = { 635 636 interfaces = mkOption { 637 example = [ "enp4s0f0" "enp4s0f1" "wlan0" ]; 638 type = types.listOf types.str; 639 description = "The interfaces to bond together"; 640 }; 641 642 driverOptions = mkOption { 643 type = types.attrsOf types.str; 644 default = {}; 645 example = literalExample driverOptionsExample; 646 description = '' 647 Options for the bonding driver. 648 Documentation can be found in 649 <link xlink:href="https://www.kernel.org/doc/Documentation/networking/bonding.txt" /> 650 ''; 651 652 }; 653 654 lacp_rate = mkOption { 655 default = null; 656 example = "fast"; 657 type = types.nullOr types.str; 658 description = '' 659 DEPRECATED, use `driverOptions`. 660 Option specifying the rate in which we'll ask our link partner 661 to transmit LACPDU packets in 802.3ad mode. 662 ''; 663 }; 664 665 miimon = mkOption { 666 default = null; 667 example = 100; 668 type = types.nullOr types.int; 669 description = '' 670 DEPRECATED, use `driverOptions`. 671 Miimon is the number of millisecond in between each round of polling 672 by the device driver for failed links. By default polling is not 673 enabled and the driver is trusted to properly detect and handle 674 failure scenarios. 675 ''; 676 }; 677 678 mode = mkOption { 679 default = null; 680 example = "active-backup"; 681 type = types.nullOr types.str; 682 description = '' 683 DEPRECATED, use `driverOptions`. 684 The mode which the bond will be running. The default mode for 685 the bonding driver is balance-rr, optimizing for throughput. 686 More information about valid modes can be found at 687 https://www.kernel.org/doc/Documentation/networking/bonding.txt 688 ''; 689 }; 690 691 xmit_hash_policy = mkOption { 692 default = null; 693 example = "layer2+3"; 694 type = types.nullOr types.str; 695 description = '' 696 DEPRECATED, use `driverOptions`. 697 Selects the transmit hash policy to use for slave selection in 698 balance-xor, 802.3ad, and tlb modes. 699 ''; 700 }; 701 702 }; 703 704 }); 705 }; 706 707 networking.macvlans = mkOption { 708 default = { }; 709 example = literalExample { 710 wan = { 711 interface = "enp2s0"; 712 mode = "vepa"; 713 }; 714 }; 715 description = '' 716 This option allows you to define macvlan interfaces which should 717 be automatically created. 718 ''; 719 type = with types; attrsOf (submodule { 720 options = { 721 722 interface = mkOption { 723 example = "enp4s0"; 724 type = types.str; 725 description = "The interface the macvlan will transmit packets through."; 726 }; 727 728 mode = mkOption { 729 default = null; 730 type = types.nullOr types.str; 731 example = "vepa"; 732 description = "The mode of the macvlan device."; 733 }; 734 735 }; 736 737 }); 738 }; 739 740 networking.sits = mkOption { 741 default = { }; 742 example = literalExample { 743 hurricane = { 744 remote = "10.0.0.1"; 745 local = "10.0.0.22"; 746 ttl = 255; 747 }; 748 msipv6 = { 749 remote = "192.168.0.1"; 750 dev = "enp3s0"; 751 ttl = 127; 752 }; 753 }; 754 description = '' 755 This option allows you to define 6-to-4 interfaces which should be automatically created. 756 ''; 757 type = with types; attrsOf (submodule { 758 options = { 759 760 remote = mkOption { 761 type = types.nullOr types.str; 762 default = null; 763 example = "10.0.0.1"; 764 description = '' 765 The address of the remote endpoint to forward traffic over. 766 ''; 767 }; 768 769 local = mkOption { 770 type = types.nullOr types.str; 771 default = null; 772 example = "10.0.0.22"; 773 description = '' 774 The address of the local endpoint which the remote 775 side should send packets to. 776 ''; 777 }; 778 779 ttl = mkOption { 780 type = types.nullOr types.int; 781 default = null; 782 example = 255; 783 description = '' 784 The time-to-live of the connection to the remote tunnel endpoint. 785 ''; 786 }; 787 788 dev = mkOption { 789 type = types.nullOr types.str; 790 default = null; 791 example = "enp4s0f0"; 792 description = '' 793 The underlying network device on which the tunnel resides. 794 ''; 795 }; 796 797 }; 798 799 }); 800 }; 801 802 networking.vlans = mkOption { 803 default = { }; 804 example = literalExample { 805 vlan0 = { 806 id = 3; 807 interface = "enp3s0"; 808 }; 809 vlan1 = { 810 id = 1; 811 interface = "wlan0"; 812 }; 813 }; 814 description = 815 '' 816 This option allows you to define vlan devices that tag packets 817 on top of a physical interface. The value of this option is an 818 attribute set. Each attribute specifies a vlan, with the name 819 specifying the name of the vlan interface. 820 ''; 821 822 type = with types; attrsOf (submodule { 823 824 options = { 825 826 id = mkOption { 827 example = 1; 828 type = types.int; 829 description = "The vlan identifier"; 830 }; 831 832 interface = mkOption { 833 example = "enp4s0"; 834 type = types.str; 835 description = "The interface the vlan will transmit packets through."; 836 }; 837 838 }; 839 840 }); 841 842 }; 843 844 networking.wlanInterfaces = mkOption { 845 default = { }; 846 example = literalExample { 847 "wlan-station0" = { 848 device = "wlp6s0"; 849 }; 850 "wlan-adhoc0" = { 851 type = "ibss"; 852 device = "wlp6s0"; 853 mac = "02:00:00:00:00:01"; 854 }; 855 "wlan-p2p0" = { 856 device = "wlp6s0"; 857 mac = "02:00:00:00:00:02"; 858 }; 859 "wlan-ap0" = { 860 device = "wlp6s0"; 861 mac = "02:00:00:00:00:03"; 862 }; 863 }; 864 description = 865 '' 866 Creating multiple WLAN interfaces on top of one physical WLAN device (NIC). 867 868 The name of the WLAN interface corresponds to the name of the attribute. 869 A NIC is referenced by the persistent device name of the WLAN interface that 870 <literal>udev</literal> assigns to a NIC by default. 871 If a NIC supports multiple WLAN interfaces, then the one NIC can be used as 872 <literal>device</literal> for multiple WLAN interfaces. 873 If a NIC is used for creating WLAN interfaces, then the default WLAN interface 874 with a persistent device name form <literal>udev</literal> is not created. 875 A WLAN interface with the persistent name assigned from <literal>udev</literal> 876 would have to be created explicitly. 877 ''; 878 879 type = with types; attrsOf (submodule { 880 881 options = { 882 883 device = mkOption { 884 type = types.string; 885 example = "wlp6s0"; 886 description = "The name of the underlying hardware WLAN device as assigned by <literal>udev</literal>."; 887 }; 888 889 type = mkOption { 890 type = types.enum [ "managed" "ibss" "monitor" "mesh" "wds" ]; 891 default = "managed"; 892 example = "ibss"; 893 description = '' 894 The type of the WLAN interface. 895 The type has to be supported by the underlying hardware of the device. 896 ''; 897 }; 898 899 meshID = mkOption { 900 type = types.nullOr types.string; 901 default = null; 902 description = "MeshID of interface with type <literal>mesh</literal>."; 903 }; 904 905 flags = mkOption { 906 type = with types; nullOr (enum [ "none" "fcsfail" "control" "otherbss" "cook" "active" ]); 907 default = null; 908 example = "control"; 909 description = '' 910 Flags for interface of type <literal>monitor</literal>. 911 ''; 912 }; 913 914 fourAddr = mkOption { 915 type = types.nullOr types.bool; 916 default = null; 917 description = "Whether to enable <literal>4-address mode</literal> with type <literal>managed</literal>."; 918 }; 919 920 mac = mkOption { 921 type = types.nullOr types.str; 922 default = null; 923 example = "02:00:00:00:00:01"; 924 description = '' 925 MAC address to use for the device. If <literal>null</literal>, then the MAC of the 926 underlying hardware WLAN device is used. 927 928 INFO: Locally administered MAC addresses are of the form: 929 <itemizedlist> 930 <listitem><para>x2:xx:xx:xx:xx:xx</para></listitem> 931 <listitem><para>x6:xx:xx:xx:xx:xx</para></listitem> 932 <listitem><para>xA:xx:xx:xx:xx:xx</para></listitem> 933 <listitem><para>xE:xx:xx:xx:xx:xx</para></listitem> 934 </itemizedlist> 935 ''; 936 }; 937 938 }; 939 940 }); 941 942 }; 943 944 networking.useDHCP = mkOption { 945 type = types.bool; 946 default = true; 947 description = '' 948 Whether to use DHCP to obtain an IP address and other 949 configuration for all network interfaces that are not manually 950 configured. 951 ''; 952 }; 953 954 networking.useNetworkd = mkOption { 955 default = false; 956 type = types.bool; 957 description = '' 958 Whether we should use networkd as the network configuration backend or 959 the legacy script based system. Note that this option is experimental, 960 enable at your own risk. 961 ''; 962 }; 963 964 }; 965 966 967 ###### implementation 968 969 config = { 970 971 warnings = concatMap (i: i.warnings) interfaces; 972 973 assertions = 974 (flip map interfaces (i: { 975 # With the linux kernel, interface name length is limited by IFNAMSIZ 976 # to 16 bytes, including the trailing null byte. 977 # See include/linux/if.h in the kernel sources 978 assertion = stringLength i.name < 16; 979 message = '' 980 The name of networking.interfaces."${i.name}" is too long, it needs to be less than 16 characters. 981 ''; 982 })) ++ (flip map slaveIfs (i: { 983 assertion = i.ipv4.addresses == [ ] && i.ipv6.addresses == [ ]; 984 message = '' 985 The networking.interfaces."${i.name}" must not have any defined ips when it is a slave. 986 ''; 987 })) ++ (flip map interfaces (i: { 988 assertion = i.preferTempAddress -> cfg.enableIPv6; 989 message = '' 990 Temporary addresses are only needed when IPv6 is enabled. 991 ''; 992 })) ++ [ 993 { 994 assertion = cfg.hostId == null || (stringLength cfg.hostId == 8 && isHexString cfg.hostId); 995 message = "Invalid value given to the networking.hostId option."; 996 } 997 ]; 998 999 boot.kernelModules = [ ] 1000 ++ optional cfg.enableIPv6 "ipv6" 1001 ++ optional hasVirtuals "tun" 1002 ++ optional hasSits "sit" 1003 ++ optional hasBonds "bonding"; 1004 1005 boot.extraModprobeConfig = 1006 # This setting is intentional as it prevents default bond devices 1007 # from being created. 1008 optionalString hasBonds "options bonding max_bonds=0"; 1009 1010 boot.kernel.sysctl = { 1011 "net.ipv6.conf.all.disable_ipv6" = mkDefault (!cfg.enableIPv6); 1012 "net.ipv6.conf.default.disable_ipv6" = mkDefault (!cfg.enableIPv6); 1013 "net.ipv6.conf.all.forwarding" = mkDefault (any (i: i.proxyARP) interfaces); 1014 } // listToAttrs (flip concatMap (filter (i: i.proxyARP) interfaces) 1015 (i: flip map [ "4" "6" ] (v: nameValuePair "net.ipv${v}.conf.${i.name}.proxy_arp" true))) 1016 // listToAttrs (flip map (filter (i: i.preferTempAddress) interfaces) 1017 (i: nameValuePair "net.ipv6.conf.${i.name}.use_tempaddr" 2)); 1018 1019 # Capabilities won't work unless we have at-least a 4.3 Linux 1020 # kernel because we need the ambient capability 1021 security.wrappers = if (versionAtLeast (getVersion config.boot.kernelPackages.kernel) "4.3") then { 1022 ping = { 1023 source = "${pkgs.iputils.out}/bin/ping"; 1024 capabilities = "cap_net_raw+p"; 1025 }; 1026 } else { 1027 ping.source = "${pkgs.iputils.out}/bin/ping"; 1028 }; 1029 1030 # Set the host and domain names in the activation script. Don't 1031 # clear it if it's not configured in the NixOS configuration, 1032 # since it may have been set by dhcpcd in the meantime. 1033 system.activationScripts.hostname = 1034 optionalString (cfg.hostName != "") '' 1035 hostname "${cfg.hostName}" 1036 ''; 1037 system.activationScripts.domain = 1038 optionalString (cfg.domain != null) '' 1039 domainname "${cfg.domain}" 1040 ''; 1041 1042 environment.etc."hostid" = mkIf (cfg.hostId != null) 1043 { source = pkgs.runCommand "gen-hostid" {} '' 1044 hi="${cfg.hostId}" 1045 ${if pkgs.stdenv.isBigEndian then '' 1046 echo -ne "\x''${hi:0:2}\x''${hi:2:2}\x''${hi:4:2}\x''${hi:6:2}" > $out 1047 '' else '' 1048 echo -ne "\x''${hi:6:2}\x''${hi:4:2}\x''${hi:2:2}\x''${hi:0:2}" > $out 1049 ''} 1050 ''; 1051 }; 1052 1053 # static hostname configuration needed for hostnamectl and the 1054 # org.freedesktop.hostname1 dbus service (both provided by systemd) 1055 environment.etc."hostname" = mkIf (cfg.hostName != "") 1056 { 1057 text = cfg.hostName + "\n"; 1058 }; 1059 1060 environment.systemPackages = 1061 [ pkgs.host 1062 pkgs.iproute 1063 pkgs.iputils 1064 pkgs.nettools 1065 pkgs.openresolv 1066 ] 1067 ++ optionals config.networking.wireless.enable [ 1068 pkgs.wirelesstools # FIXME: obsolete? 1069 pkgs.iw 1070 pkgs.rfkill 1071 ] 1072 ++ bridgeStp; 1073 1074 # The network-interfaces target is kept for backwards compatibility. 1075 # New modules must NOT use it. 1076 systemd.targets."network-interfaces" = 1077 { description = "All Network Interfaces (deprecated)"; 1078 wantedBy = [ "network.target" ]; 1079 before = [ "network.target" ]; 1080 after = [ "network-pre.target" ]; 1081 unitConfig.X-StopOnReconfiguration = true; 1082 }; 1083 1084 systemd.services = { 1085 network-local-commands = { 1086 description = "Extra networking commands."; 1087 before = [ "network.target" ]; 1088 wantedBy = [ "network.target" ]; 1089 after = [ "network-pre.target" ]; 1090 unitConfig.ConditionCapability = "CAP_NET_ADMIN"; 1091 path = [ pkgs.iproute ]; 1092 serviceConfig.Type = "oneshot"; 1093 serviceConfig.RemainAfterExit = true; 1094 script = '' 1095 # Run any user-specified commands. 1096 ${cfg.localCommands} 1097 ''; 1098 }; 1099 } // (listToAttrs (flip map interfaces (i: 1100 let 1101 deviceDependency = if config.boot.isContainer 1102 then [] 1103 else [ (subsystemDevice i.name) ]; 1104 in 1105 nameValuePair "network-link-${i.name}" 1106 { description = "Link configuration of ${i.name}"; 1107 wantedBy = [ "network-interfaces.target" ]; 1108 before = [ "network-interfaces.target" ]; 1109 bindsTo = deviceDependency; 1110 after = [ "network-pre.target" ] ++ deviceDependency; 1111 path = [ pkgs.iproute ]; 1112 serviceConfig = { 1113 Type = "oneshot"; 1114 RemainAfterExit = true; 1115 }; 1116 script = 1117 '' 1118 echo "Configuring link..." 1119 '' + optionalString (i.macAddress != null) '' 1120 echo "setting MAC address to ${i.macAddress}..." 1121 ip link set "${i.name}" address "${i.macAddress}" 1122 '' + optionalString (i.mtu != null) '' 1123 echo "setting MTU to ${toString i.mtu}..." 1124 ip link set "${i.name}" mtu "${toString i.mtu}" 1125 '' + '' 1126 echo -n "bringing up interface... " 1127 ip link set "${i.name}" up && echo "done" || (echo "failed"; exit 1) 1128 ''; 1129 }))); 1130 1131 services.mstpd = mkIf needsMstpd { enable = true; }; 1132 1133 virtualisation.vswitch = mkIf (cfg.vswitches != { }) { enable = true; }; 1134 1135 services.udev.packages = mkIf (cfg.wlanInterfaces != {}) [ 1136 (pkgs.writeTextFile { 1137 name = "99-zzz-40-wlanInterfaces.rules"; 1138 destination = "/etc/udev/rules.d/99-zzz-40-wlanInterfaces.rules"; 1139 text = 1140 let 1141 # Collect all interfaces that are defined for a device 1142 # as device:interface key:value pairs. 1143 wlanDeviceInterfaces = 1144 let 1145 allDevices = unique (mapAttrsToList (_: v: v.device) cfg.wlanInterfaces); 1146 interfacesOfDevice = d: filterAttrs (_: v: v.device == d) cfg.wlanInterfaces; 1147 in 1148 genAttrs allDevices (d: interfacesOfDevice d); 1149 1150 # Convert device:interface key:value pairs into a list, and if it exists, 1151 # place the interface which is named after the device at the beginning. 1152 wlanListDeviceFirst = device: interfaces: 1153 if hasAttr device interfaces 1154 then mapAttrsToList (n: v: v//{_iName=n;}) (filterAttrs (n: _: n==device) interfaces) ++ mapAttrsToList (n: v: v//{_iName=n;}) (filterAttrs (n: _: n!=device) interfaces) 1155 else mapAttrsToList (n: v: v // {_iName = n;}) interfaces; 1156 1157 # Udev script to execute for the default WLAN interface with the persistend udev name. 1158 # The script creates the required, new WLAN interfaces interfaces and configures the 1159 # existing, default interface. 1160 curInterfaceScript = device: current: new: pkgs.writeScript "udev-run-script-wlan-interfaces-${device}.sh" '' 1161 #!${pkgs.stdenv.shell} 1162 # Change the wireless phy device to a predictable name. 1163 ${pkgs.iw}/bin/iw phy `${pkgs.coreutils}/bin/cat /sys/class/net/$INTERFACE/phy80211/name` set name ${device} 1164 1165 # Add new WLAN interfaces 1166 ${flip concatMapStrings new (i: '' 1167 ${pkgs.iw}/bin/iw phy ${device} interface add ${i._iName} type managed 1168 '')} 1169 1170 # Configure the current interface 1171 ${pkgs.iw}/bin/iw dev ${device} set type ${current.type} 1172 ${optionalString (current.type == "mesh" && current.meshID!=null) "${pkgs.iw}/bin/iw dev ${device} set meshid ${current.meshID}"} 1173 ${optionalString (current.type == "monitor" && current.flags!=null) "${pkgs.iw}/bin/iw dev ${device} set monitor ${current.flags}"} 1174 ${optionalString (current.type == "managed" && current.fourAddr!=null) "${pkgs.iw}/bin/iw dev ${device} set 4addr ${if current.fourAddr then "on" else "off"}"} 1175 ${optionalString (current.mac != null) "${pkgs.iproute}/bin/ip link set dev ${device} address ${current.mac}"} 1176 ''; 1177 1178 # Udev script to execute for a new WLAN interface. The script configures the new WLAN interface. 1179 newInterfaceScript = device: new: pkgs.writeScript "udev-run-script-wlan-interfaces-${new._iName}.sh" '' 1180 #!${pkgs.stdenv.shell} 1181 # Configure the new interface 1182 ${pkgs.iw}/bin/iw dev ${new._iName} set type ${new.type} 1183 ${optionalString (new.type == "mesh" && new.meshID!=null) "${pkgs.iw}/bin/iw dev ${device} set meshid ${new.meshID}"} 1184 ${optionalString (new.type == "monitor" && new.flags!=null) "${pkgs.iw}/bin/iw dev ${device} set monitor ${new.flags}"} 1185 ${optionalString (new.type == "managed" && new.fourAddr!=null) "${pkgs.iw}/bin/iw dev ${device} set 4addr ${if new.fourAddr then "on" else "off"}"} 1186 ${optionalString (new.mac != null) "${pkgs.iproute}/bin/ip link set dev ${device} address ${new.mac}"} 1187 ''; 1188 1189 # Udev attributes for systemd to name the device and to create a .device target. 1190 systemdAttrs = n: ''NAME:="${n}", ENV{INTERFACE}:="${n}", ENV{SYSTEMD_ALIAS}:="/sys/subsystem/net/devices/${n}", TAG+="systemd"''; 1191 in 1192 flip (concatMapStringsSep "\n") (attrNames wlanDeviceInterfaces) (device: 1193 let 1194 interfaces = wlanListDeviceFirst device wlanDeviceInterfaces."${device}"; 1195 curInterface = elemAt interfaces 0; 1196 newInterfaces = drop 1 interfaces; 1197 in '' 1198 # It is important to have that rule first as overwriting the NAME attribute also prevents the 1199 # next rules from matching. 1200 ${flip (concatMapStringsSep "\n") (wlanListDeviceFirst device wlanDeviceInterfaces."${device}") (interface: 1201 ''ACTION=="add", SUBSYSTEM=="net", ENV{DEVTYPE}=="wlan", ENV{INTERFACE}=="${interface._iName}", ${systemdAttrs interface._iName}, RUN+="${newInterfaceScript device interface}"'')} 1202 1203 # Add the required, new WLAN interfaces to the default WLAN interface with the 1204 # persistent, default name as assigned by udev. 1205 ACTION=="add", SUBSYSTEM=="net", ENV{DEVTYPE}=="wlan", NAME=="${device}", ${systemdAttrs curInterface._iName}, RUN+="${curInterfaceScript device curInterface newInterfaces}" 1206 # Generate the same systemd events for both 'add' and 'move' udev events. 1207 ACTION=="move", SUBSYSTEM=="net", ENV{DEVTYPE}=="wlan", NAME=="${device}", ${systemdAttrs curInterface._iName} 1208 ''); 1209 }) ]; 1210 1211 }; 1212 1213}