at 15.09-beta 23 kB view raw
1{ config, 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 17 slaveIfs = map (i: cfg.interfaces.${i}) (filter (i: cfg.interfaces ? ${i}) slaves); 18 19 rstpBridges = flip filterAttrs cfg.bridges (_: { rstp, ... }: rstp); 20 21 needsMstpd = rstpBridges != { }; 22 23 bridgeStp = optional needsMstpd (pkgs.writeTextFile { 24 name = "bridge-stp"; 25 executable = true; 26 destination = "/bin/bridge-stp"; 27 text = '' 28 #!${pkgs.stdenv.shell} -e 29 export PATH="${pkgs.mstpd}/bin" 30 31 BRIDGES=(${concatStringsSep " " (attrNames rstpBridges)}) 32 for BRIDGE in $BRIDGES; do 33 if [ "$BRIDGE" = "$1" ]; then 34 if [ "$2" = "start" ]; then 35 mstpctl addbridge "$BRIDGE" 36 exit 0 37 elif [ "$2" = "stop" ]; then 38 mstpctl delbridge "$BRIDGE" 39 exit 0 40 fi 41 exit 1 42 fi 43 done 44 exit 1 45 ''; 46 }); 47 48 # We must escape interfaces due to the systemd interpretation 49 subsystemDevice = interface: 50 "sys-subsystem-net-devices-${escapeSystemdPath interface}.device"; 51 52 addrOpts = v: 53 assert v == 4 || v == 6; 54 { 55 address = mkOption { 56 type = types.str; 57 description = '' 58 IPv${toString v} address of the interface. Leave empty to configure the 59 interface using DHCP. 60 ''; 61 }; 62 63 prefixLength = mkOption { 64 type = types.addCheck types.int (n: n >= 0 && n <= (if v == 4 then 32 else 128)); 65 description = '' 66 Subnet mask of the interface, specified as the number of 67 bits in the prefix (<literal>${if v == 4 then "24" else "64"}</literal>). 68 ''; 69 }; 70 }; 71 72 interfaceOpts = { name, ... }: { 73 74 options = { 75 76 name = mkOption { 77 example = "eth0"; 78 type = types.str; 79 description = "Name of the interface."; 80 }; 81 82 useDHCP = mkOption { 83 type = types.nullOr types.bool; 84 default = null; 85 description = '' 86 Whether this interface should be configured with dhcp. 87 Null implies the old behavior which depends on whether ip addresses 88 are specified or not. 89 ''; 90 }; 91 92 ip4 = mkOption { 93 default = [ ]; 94 example = [ 95 { address = "10.0.0.1"; prefixLength = 16; } 96 { address = "192.168.1.1"; prefixLength = 24; } 97 ]; 98 type = types.listOf types.optionSet; 99 options = addrOpts 4; 100 description = '' 101 List of IPv4 addresses that will be statically assigned to the interface. 102 ''; 103 }; 104 105 ip6 = mkOption { 106 default = [ ]; 107 example = [ 108 { address = "fdfd:b3f0:482::1"; prefixLength = 48; } 109 { address = "2001:1470:fffd:2098::e006"; prefixLength = 64; } 110 ]; 111 type = types.listOf types.optionSet; 112 options = addrOpts 6; 113 description = '' 114 List of IPv6 addresses that will be statically assigned to the interface. 115 ''; 116 }; 117 118 ipAddress = mkOption { 119 default = null; 120 example = "10.0.0.1"; 121 type = types.nullOr types.str; 122 description = '' 123 IP address of the interface. Leave empty to configure the 124 interface using DHCP. 125 ''; 126 }; 127 128 prefixLength = mkOption { 129 default = null; 130 example = 24; 131 type = types.nullOr types.int; 132 description = '' 133 Subnet mask of the interface, specified as the number of 134 bits in the prefix (<literal>24</literal>). 135 ''; 136 }; 137 138 subnetMask = mkOption { 139 default = null; 140 description = '' 141 Defunct, supply the prefix length instead. 142 ''; 143 }; 144 145 ipv6Address = mkOption { 146 default = null; 147 example = "2001:1470:fffd:2098::e006"; 148 type = types.nullOr types.str; 149 description = '' 150 IPv6 address of the interface. Leave empty to configure the 151 interface using NDP. 152 ''; 153 }; 154 155 ipv6PrefixLength = mkOption { 156 default = 64; 157 example = 64; 158 type = types.int; 159 description = '' 160 Subnet mask of the interface, specified as the number of 161 bits in the prefix (<literal>64</literal>). 162 ''; 163 }; 164 165 macAddress = mkOption { 166 default = null; 167 example = "00:11:22:33:44:55"; 168 type = types.nullOr (types.str); 169 description = '' 170 MAC address of the interface. Leave empty to use the default. 171 ''; 172 }; 173 174 mtu = mkOption { 175 default = null; 176 example = 9000; 177 type = types.nullOr types.int; 178 description = '' 179 MTU size for packets leaving the interface. Leave empty to use the default. 180 ''; 181 }; 182 183 virtual = mkOption { 184 default = false; 185 type = types.bool; 186 description = '' 187 Whether this interface is virtual and should be created by tunctl. 188 This is mainly useful for creating bridges between a host a virtual 189 network such as VPN or a virtual machine. 190 ''; 191 }; 192 193 virtualOwner = mkOption { 194 default = "root"; 195 type = types.str; 196 description = '' 197 In case of a virtual device, the user who owns it. 198 ''; 199 }; 200 201 virtualType = mkOption { 202 default = null; 203 type = types.nullOr (types.addCheck types.str (v: v == "tun" || v == "tap")); 204 description = '' 205 The explicit type of interface to create. Accepts tun or tap strings. 206 Also accepts null to implicitly detect the type of device. 207 ''; 208 }; 209 210 proxyARP = mkOption { 211 default = false; 212 type = types.bool; 213 description = '' 214 Turn on proxy_arp for this device (and proxy_ndp for ipv6). 215 This is mainly useful for creating pseudo-bridges between a real 216 interface and a virtual network such as VPN or a virtual machine for 217 interfaces that don't support real bridging (most wlan interfaces). 218 As ARP proxying acts slightly above the link-layer, below-ip traffic 219 isn't bridged, so things like DHCP won't work. The advantage above 220 using NAT lies in the fact that no IP addresses are shared, so all 221 hosts are reachable/routeable. 222 223 WARNING: turns on ip-routing, so if you have multiple interfaces, you 224 should think of the consequence and setup firewall rules to limit this. 225 ''; 226 }; 227 228 }; 229 230 config = { 231 name = mkDefault name; 232 }; 233 234 }; 235 236 hexChars = stringToCharacters "0123456789abcdef"; 237 238 isHexString = s: all (c: elem c hexChars) (stringToCharacters (toLower s)); 239 240in 241 242{ 243 244 ###### interface 245 246 options = { 247 248 networking.hostName = mkOption { 249 default = "nixos"; 250 type = types.str; 251 description = '' 252 The name of the machine. Leave it empty if you want to obtain 253 it from a DHCP server (if using DHCP). 254 ''; 255 }; 256 257 networking.hostId = mkOption { 258 default = null; 259 example = "4e98920d"; 260 type = types.nullOr types.str; 261 description = '' 262 The 32-bit host ID of the machine, formatted as 8 hexadecimal characters. 263 264 You should try to make this ID unique among your machines. You can 265 generate a random 32-bit ID using the following commands: 266 267 <literal>cksum /etc/machine-id | while read c rest; do printf "%x" $c; done</literal> 268 269 (this derives it from the machine-id that systemd generates) or 270 271 <literal>head -c4 /dev/urandom | od -A none -t x4</literal> 272 ''; 273 }; 274 275 networking.enableIPv6 = mkOption { 276 default = true; 277 type = types.bool; 278 description = '' 279 Whether to enable support for IPv6. 280 ''; 281 }; 282 283 networking.defaultGateway = mkOption { 284 default = null; 285 example = "131.211.84.1"; 286 type = types.nullOr types.str; 287 description = '' 288 The default gateway. It can be left empty if it is auto-detected through DHCP. 289 ''; 290 }; 291 292 networking.defaultGateway6 = mkOption { 293 default = null; 294 example = "2001:4d0:1e04:895::1"; 295 type = types.nullOr types.str; 296 description = '' 297 The default ipv6 gateway. It can be left empty if it is auto-detected through DHCP. 298 ''; 299 }; 300 301 networking.defaultGatewayWindowSize = mkOption { 302 default = null; 303 example = 524288; 304 type = types.nullOr types.int; 305 description = '' 306 The window size of the default gateway. It limits maximal data bursts that TCP peers 307 are allowed to send to us. 308 ''; 309 }; 310 311 networking.nameservers = mkOption { 312 default = []; 313 example = ["130.161.158.4" "130.161.33.17"]; 314 description = '' 315 The list of nameservers. It can be left empty if it is auto-detected through DHCP. 316 ''; 317 }; 318 319 networking.search = mkOption { 320 default = []; 321 example = [ "example.com" "local.domain" ]; 322 type = types.listOf types.str; 323 description = '' 324 The list of search paths used when resolving domain names. 325 ''; 326 }; 327 328 networking.domain = mkOption { 329 default = null; 330 example = "home"; 331 type = types.nullOr types.str; 332 description = '' 333 The domain. It can be left empty if it is auto-detected through DHCP. 334 ''; 335 }; 336 337 networking.useHostResolvConf = mkOption { 338 type = types.bool; 339 default = false; 340 description = '' 341 In containers, whether to use the 342 <filename>resolv.conf</filename> supplied by the host. 343 ''; 344 }; 345 346 networking.localCommands = mkOption { 347 default = ""; 348 example = "text=anything; echo You can put $text here."; 349 description = '' 350 Shell commands to be executed at the end of the 351 <literal>network-setup</literal> systemd service. Note that if 352 you are using DHCP to obtain the network configuration, 353 interfaces may not be fully configured yet. 354 ''; 355 }; 356 357 networking.interfaces = mkOption { 358 default = {}; 359 example = 360 { eth0.ip4 = [ { 361 address = "131.211.84.78"; 362 prefixLength = 25; 363 } ]; 364 }; 365 description = '' 366 The configuration for each network interface. If 367 <option>networking.useDHCP</option> is true, then every 368 interface not listed here will be configured using DHCP. 369 ''; 370 type = types.loaOf types.optionSet; 371 options = [ interfaceOpts ]; 372 }; 373 374 networking.bridges = mkOption { 375 default = { }; 376 example = 377 { br0.interfaces = [ "eth0" "eth1" ]; 378 br1.interfaces = [ "eth2" "wlan0" ]; 379 }; 380 description = 381 '' 382 This option allows you to define Ethernet bridge devices 383 that connect physical networks together. The value of this 384 option is an attribute set. Each attribute specifies a 385 bridge, with the attribute name specifying the name of the 386 bridge's network interface. 387 ''; 388 389 type = types.attrsOf types.optionSet; 390 391 options = { 392 393 interfaces = mkOption { 394 example = [ "eth0" "eth1" ]; 395 type = types.listOf types.str; 396 description = 397 "The physical network interfaces connected by the bridge."; 398 }; 399 400 rstp = mkOption { 401 example = true; 402 default = false; 403 type = types.bool; 404 description = "Whether the bridge interface should enable rstp."; 405 }; 406 407 }; 408 409 }; 410 411 networking.bonds = mkOption { 412 default = { }; 413 example = { 414 bond0 = { 415 interfaces = [ "eth0" "wlan0" ]; 416 miimon = 100; 417 mode = "active-backup"; 418 }; 419 fatpipe.interfaces = [ "enp4s0f0" "enp4s0f1" "enp5s0f0" "enp5s0f1" ]; 420 }; 421 description = '' 422 This option allows you to define bond devices that aggregate multiple, 423 underlying networking interfaces together. The value of this option is 424 an attribute set. Each attribute specifies a bond, with the attribute 425 name specifying the name of the bond's network interface 426 ''; 427 428 type = types.attrsOf types.optionSet; 429 430 options = { 431 432 interfaces = mkOption { 433 example = [ "enp4s0f0" "enp4s0f1" "wlan0" ]; 434 type = types.listOf types.str; 435 description = "The interfaces to bond together"; 436 }; 437 438 lacp_rate = mkOption { 439 default = null; 440 example = "fast"; 441 type = types.nullOr types.str; 442 description = '' 443 Option specifying the rate in which we'll ask our link partner 444 to transmit LACPDU packets in 802.3ad mode. 445 ''; 446 }; 447 448 miimon = mkOption { 449 default = null; 450 example = 100; 451 type = types.nullOr types.int; 452 description = '' 453 Miimon is the number of millisecond in between each round of polling 454 by the device driver for failed links. By default polling is not 455 enabled and the driver is trusted to properly detect and handle 456 failure scenarios. 457 ''; 458 }; 459 460 mode = mkOption { 461 default = null; 462 example = "active-backup"; 463 type = types.nullOr types.str; 464 description = '' 465 The mode which the bond will be running. The default mode for 466 the bonding driver is balance-rr, optimizing for throughput. 467 More information about valid modes can be found at 468 https://www.kernel.org/doc/Documentation/networking/bonding.txt 469 ''; 470 }; 471 472 xmit_hash_policy = mkOption { 473 default = null; 474 example = "layer2+3"; 475 type = types.nullOr types.str; 476 description = '' 477 Selects the transmit hash policy to use for slave selection in 478 balance-xor, 802.3ad, and tlb modes. 479 ''; 480 }; 481 482 }; 483 }; 484 485 networking.macvlans = mkOption { 486 type = types.attrsOf types.optionSet; 487 default = { }; 488 example = { 489 wan = { 490 interface = "enp2s0"; 491 mode = "vepa"; 492 }; 493 }; 494 description = '' 495 This option allows you to define macvlan interfaces which should 496 be automatically created. 497 ''; 498 options = { 499 500 interface = mkOption { 501 example = "enp4s0"; 502 type = types.string; 503 description = "The interface the macvlan will transmit packets through."; 504 }; 505 506 mode = mkOption { 507 default = null; 508 type = types.nullOr types.str; 509 example = "vepa"; 510 description = "The mode of the macvlan device."; 511 }; 512 513 }; 514 }; 515 516 networking.sits = mkOption { 517 type = types.attrsOf types.optionSet; 518 default = { }; 519 example = { 520 hurricane = { 521 remote = "10.0.0.1"; 522 local = "10.0.0.22"; 523 ttl = 255; 524 }; 525 msipv6 = { 526 remote = "192.168.0.1"; 527 dev = "enp3s0"; 528 ttl = 127; 529 }; 530 }; 531 description = '' 532 This option allows you to define 6-to-4 interfaces which should be automatically created. 533 ''; 534 options = { 535 536 remote = mkOption { 537 type = types.nullOr types.str; 538 default = null; 539 example = "10.0.0.1"; 540 description = '' 541 The address of the remote endpoint to forward traffic over. 542 ''; 543 }; 544 545 local = mkOption { 546 type = types.nullOr types.str; 547 default = null; 548 example = "10.0.0.22"; 549 description = '' 550 The address of the local endpoint which the remote 551 side should send packets to. 552 ''; 553 }; 554 555 ttl = mkOption { 556 type = types.nullOr types.int; 557 default = null; 558 example = 255; 559 description = '' 560 The time-to-live of the connection to the remote tunnel endpoint. 561 ''; 562 }; 563 564 dev = mkOption { 565 type = types.nullOr types.str; 566 default = null; 567 example = "enp4s0f0"; 568 description = '' 569 The underlying network device on which the tunnel resides. 570 ''; 571 }; 572 573 }; 574 }; 575 576 networking.vlans = mkOption { 577 default = { }; 578 example = { 579 vlan0 = { 580 id = 3; 581 interface = "enp3s0"; 582 }; 583 vlan1 = { 584 id = 1; 585 interface = "wlan0"; 586 }; 587 }; 588 description = 589 '' 590 This option allows you to define vlan devices that tag packets 591 on top of a physical interface. The value of this option is an 592 attribute set. Each attribute specifies a vlan, with the name 593 specifying the name of the vlan interface. 594 ''; 595 596 type = types.attrsOf types.optionSet; 597 598 options = { 599 600 id = mkOption { 601 example = 1; 602 type = types.int; 603 description = "The vlan identifier"; 604 }; 605 606 interface = mkOption { 607 example = "enp4s0"; 608 type = types.string; 609 description = "The interface the vlan will transmit packets through."; 610 }; 611 612 }; 613 }; 614 615 networking.useDHCP = mkOption { 616 type = types.bool; 617 default = true; 618 description = '' 619 Whether to use DHCP to obtain an IP address and other 620 configuration for all network interfaces that are not manually 621 configured. 622 ''; 623 }; 624 625 networking.useNetworkd = mkOption { 626 default = false; 627 type = types.bool; 628 description = '' 629 Whether we should use networkd as the network configuration backend or 630 the legacy script based system. Note that this option is experimental, 631 enable at your own risk. 632 ''; 633 }; 634 635 }; 636 637 638 ###### implementation 639 640 config = { 641 642 assertions = 643 (flip map interfaces (i: { 644 assertion = i.subnetMask == null; 645 message = "The networking.interfaces.${i.name}.subnetMask option is defunct. Use prefixLength instead."; 646 })) ++ (flip map slaveIfs (i: { 647 assertion = i.ip4 == [ ] && i.ipAddress == null && i.ip6 == [ ] && i.ipv6Address == null; 648 message = "The networking.interfaces.${i.name} must not have any defined ips when it is a slave."; 649 })) ++ [ 650 { 651 assertion = cfg.hostId == null || (stringLength cfg.hostId == 8 && isHexString cfg.hostId); 652 message = "Invalid value given to the networking.hostId option."; 653 } 654 ]; 655 656 boot.kernelModules = [ ] 657 ++ optional cfg.enableIPv6 "ipv6" 658 ++ optional hasVirtuals "tun" 659 ++ optional hasSits "sit" 660 ++ optional hasBonds "bonding"; 661 662 boot.extraModprobeConfig = 663 # This setting is intentional as it prevents default bond devices 664 # from being created. 665 optionalString hasBonds "options bonding max_bonds=0"; 666 667 boot.kernel.sysctl = { 668 "net.net.ipv4.conf.all.promote_secondaries" = true; 669 "net.ipv6.conf.all.disable_ipv6" = mkDefault (!cfg.enableIPv6); 670 "net.ipv6.conf.default.disable_ipv6" = mkDefault (!cfg.enableIPv6); 671 "net.ipv4.conf.all_forwarding" = mkDefault (any (i: i.proxyARP) interfaces); 672 "net.ipv6.conf.all.forwarding" = mkDefault (any (i: i.proxyARP) interfaces); 673 } // listToAttrs (concatLists (flip map (filter (i: i.proxyARP) interfaces) 674 (i: flip map [ "4" "6" ] (v: nameValuePair "net.ipv${v}.conf.${i.name}.proxy_arp" true)) 675 )); 676 677 security.setuidPrograms = [ "ping" "ping6" ]; 678 679 # Set the host and domain names in the activation script. Don't 680 # clear it if it's not configured in the NixOS configuration, 681 # since it may have been set by dhcpcd in the meantime. 682 system.activationScripts.hostname = 683 optionalString (cfg.hostName != "") '' 684 hostname "${cfg.hostName}" 685 ''; 686 system.activationScripts.domain = 687 optionalString (cfg.domain != null) '' 688 domainname "${cfg.domain}" 689 ''; 690 691 environment.etc = mkIf (cfg.hostId != null) 692 [ 693 { 694 target = "hostid"; 695 source = pkgs.runCommand "gen-hostid" {} '' 696 hi="${cfg.hostId}" 697 ${if pkgs.stdenv.isBigEndian then '' 698 echo -ne "\x''${hi:0:2}\x''${hi:2:2}\x''${hi:4:2}\x''${hi:6:2}" > $out 699 '' else '' 700 echo -ne "\x''${hi:6:2}\x''${hi:4:2}\x''${hi:2:2}\x''${hi:0:2}" > $out 701 ''} 702 ''; 703 } 704 ]; 705 706 environment.systemPackages = 707 [ pkgs.host 708 pkgs.iproute 709 pkgs.iputils 710 pkgs.nettools 711 pkgs.openresolv 712 ] 713 ++ optionals (!config.boot.isContainer) [ 714 pkgs.wirelesstools # FIXME: obsolete? 715 pkgs.iw 716 pkgs.rfkill 717 ] 718 ++ bridgeStp; 719 720 systemd.targets."network-interfaces" = 721 { description = "All Network Interfaces"; 722 wantedBy = [ "network.target" ]; 723 before = [ "network.target" ]; 724 after = [ "network-pre.target" ]; 725 unitConfig.X-StopOnReconfiguration = true; 726 }; 727 728 systemd.services = { 729 network-local-commands = { 730 description = "Extra networking commands."; 731 before = [ "network.target" ]; 732 wantedBy = [ "network.target" ]; 733 after = [ "network-pre.target" ]; 734 unitConfig.ConditionCapability = "CAP_NET_ADMIN"; 735 path = [ pkgs.iproute ]; 736 serviceConfig.Type = "oneshot"; 737 serviceConfig.RemainAfterExit = true; 738 script = '' 739 # Run any user-specified commands. 740 ${cfg.localCommands} 741 ''; 742 }; 743 } // (listToAttrs (flip map interfaces (i: 744 nameValuePair "network-link-${i.name}" 745 { description = "Link configuration of ${i.name}"; 746 wantedBy = [ "network-interfaces.target" ]; 747 before = [ "network-interfaces.target" ]; 748 bindsTo = [ (subsystemDevice i.name) ]; 749 after = [ (subsystemDevice i.name) "network-pre.target" ]; 750 path = [ pkgs.iproute ]; 751 serviceConfig = { 752 Type = "oneshot"; 753 RemainAfterExit = true; 754 }; 755 script = 756 '' 757 echo "Configuring link..." 758 '' + optionalString (i.macAddress != null) '' 759 echo "setting MAC address to ${i.macAddress}..." 760 ip link set "${i.name}" address "${i.macAddress}" 761 '' + optionalString (i.mtu != null) '' 762 echo "setting MTU to ${toString i.mtu}..." 763 ip link set "${i.name}" mtu "${toString i.mtu}" 764 ''; 765 }))); 766 767 services.mstpd = mkIf needsMstpd { enable = true; }; 768 769 }; 770 771}