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