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