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