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