Merge pull request #5043 from wkennington/master.networkd

nixos/networking: Revamp networking configuration and add an experimental networkd option.

+1851 -375
+28 -1
nixos/modules/config/networking.nix
··· 84 84 dnsmasq_conf=/etc/dnsmasq-conf.conf 85 85 dnsmasq_resolv=/etc/dnsmasq-resolv.conf 86 86 ''; 87 - }; 87 + 88 + } // (optionalAttrs config.services.resolved.enable ( 89 + if dnsmasqResolve then { 90 + "dnsmasq-resolv.conf".source = "/run/systemd/resolve/resolv.conf"; 91 + } else { 92 + "resolv.conf".source = "/run/systemd/resolve/resolv.conf"; 93 + } 94 + )); 88 95 89 96 # The ‘ip-up’ target is started when we have IP connectivity. So 90 97 # services that depend on IP connectivity (like ntpd) should be 91 98 # pulled in by this target. 92 99 systemd.targets.ip-up.description = "Services Requiring IP Connectivity"; 100 + 101 + # This is needed when /etc/resolv.conf is being overriden by networkd 102 + # and other configurations. If the file is destroyed by an environment 103 + # activation then it must be rebuilt so that applications which interface 104 + # with /etc/resolv.conf directly don't break. 105 + system.activationScripts.resolvconf = stringAfter [ "etc" "tmpfs" "var" ] 106 + '' 107 + # Systemd resolved controls its own resolv.conf 108 + rm -f /run/resolvconf/interfaces/systemd 109 + ${optionalString config.services.resolved.enable '' 110 + rm -rf /run/resolvconf/interfaces 111 + mkdir -p /run/resolvconf/interfaces 112 + ln -s /run/systemd/resolve/resolv.conf /run/resolvconf/interfaces/systemd 113 + ''} 114 + 115 + # Make sure resolv.conf is up to date if not managed by systemd 116 + ${optionalString (!config.services.resolved.enable) '' 117 + ${pkgs.openresolv}/bin/resolvconf -u 118 + ''} 119 + ''; 93 120 94 121 }; 95 122
+2
nixos/modules/module-list.nix
··· 388 388 ./tasks/kbd.nix 389 389 ./tasks/lvm.nix 390 390 ./tasks/network-interfaces.nix 391 + ./tasks/network-interfaces-systemd.nix 392 + ./tasks/network-interfaces-scripted.nix 391 393 ./tasks/scsi-link-power-management.nix 392 394 ./tasks/swraid.nix 393 395 ./tasks/trackpoint.nix
+2 -2
nixos/modules/services/networking/chrony.nix
··· 100 100 jobs.chronyd = 101 101 { description = "chrony daemon"; 102 102 103 - wantedBy = [ "ip-up.target" ]; 104 - partOf = [ "ip-up.target" ]; 103 + wantedBy = [ "multi-user.target" ]; 104 + after = [ "network.target" ]; 105 105 106 106 path = [ chrony ]; 107 107
+17 -3
nixos/modules/services/networking/dhcpcd.nix
··· 8 8 9 9 cfg = config.networking.dhcpcd; 10 10 11 + interfaces = attrValues config.networking.interfaces; 12 + 13 + enableDHCP = config.networking.useDHCP || any (i: i.useDHCP == true) interfaces; 14 + 11 15 # Don't start dhcpcd on explicitly configured interfaces or on 12 16 # interfaces that are part of a bridge, bond or sit device. 13 17 ignoredInterfaces = 14 - map (i: i.name) (filter (i: i.ip4 != [ ] || i.ipAddress != null) (attrValues config.networking.interfaces)) 18 + map (i: i.name) (filter (i: if i.useDHCP != null then !i.useDHCP else i.ip4 != [ ] || i.ipAddress != null) interfaces) 15 19 ++ mapAttrsToList (i: _: i) config.networking.sits 16 20 ++ concatLists (attrValues (mapAttrs (n: v: v.interfaces) config.networking.bridges)) 17 21 ++ concatLists (attrValues (mapAttrs (n: v: v.interfaces) config.networking.bonds)) 18 22 ++ config.networking.dhcpcd.denyInterfaces; 23 + 24 + arrayAppendOrNull = a1: a2: if a1 == null && a2 == null then null 25 + else if a1 == null then a2 else if a2 == null then a1 26 + else a1 ++ a2; 27 + 28 + # If dhcp is disabled but explicit interfaces are enabled, 29 + # we need to provide dhcp just for those interfaces. 30 + allowInterfaces = arrayAppendOrNull cfg.allowInterfaces 31 + (if !config.networking.useDHCP && enableDHCP then 32 + map (i: i.name) (filter (i: i.useDHCP == true) interfaces) else null); 19 33 20 34 # Config file adapted from the one that ships with dhcpcd. 21 35 dhcpcdConf = pkgs.writeText "dhcpcd.conf" ··· 41 55 denyinterfaces ${toString ignoredInterfaces} lo peth* vif* tap* tun* virbr* vnet* vboxnet* sit* 42 56 43 57 # Use the list of allowed interfaces if specified 44 - ${optionalString (cfg.allowInterfaces != null) "allowinterfaces ${toString cfg.allowInterfaces}"} 58 + ${optionalString (allowInterfaces != null) "allowinterfaces ${toString allowInterfaces}"} 45 59 46 60 ${cfg.extraConfig} 47 61 ''; ··· 132 146 133 147 ###### implementation 134 148 135 - config = mkIf config.networking.useDHCP { 149 + config = mkIf enableDHCP { 136 150 137 151 systemd.services.dhcpcd = 138 152 { description = "DHCP Client";
+1 -1
nixos/modules/services/networking/dnsmasq.nix
··· 82 82 83 83 systemd.services.dnsmasq = { 84 84 description = "dnsmasq daemon"; 85 - after = [ "network.target" ]; 85 + after = [ "network.target" "systemd-resolved.conf" ]; 86 86 wantedBy = [ "multi-user.target" ]; 87 87 path = [ dnsmasq ]; 88 88 preStart = ''
+1 -2
nixos/modules/services/networking/gogoclient.nix
··· 76 76 exec ${pkgs.gogoclient}/bin/gogoc -y -f /var/lib/gogoc/gogoc.conf 77 77 ''; 78 78 } // optionalAttrs cfg.autorun { 79 - wantedBy = [ "ip-up.target" ]; 80 - partOf = [ "ip-up.target" ]; 79 + wantedBy = [ "multi-user.target" ]; 81 80 }; 82 81 83 82 };
+1
nixos/modules/services/networking/networkmanager.nix
··· 52 52 #!/bin/sh 53 53 if test "$2" = "up"; then 54 54 ${config.systemd.package}/bin/systemctl start ip-up.target 55 + ${config.systemd.package}/bin/systemctl start network-online.target 55 56 fi 56 57 ''; 57 58
+1 -2
nixos/modules/services/networking/ntpd.nix
··· 77 77 jobs.ntpd = 78 78 { description = "NTP Daemon"; 79 79 80 - wantedBy = [ "ip-up.target" ]; 81 - partOf = [ "ip-up.target" ]; 80 + wantedBy = [ "multi-user.target" ]; 82 81 83 82 path = [ ntp ]; 84 83
+1 -2
nixos/modules/services/networking/openntpd.nix
··· 41 41 42 42 systemd.services.openntpd = { 43 43 description = "OpenNTP Server"; 44 - wantedBy = [ "ip-up.target" ]; 45 - partOf = [ "ip-up.target" ]; 44 + wantedBy = [ "multi-user.target" ]; 46 45 serviceConfig.ExecStart = "${package}/sbin/ntpd -d -f ${cfgFile}"; 47 46 }; 48 47 };
-2
nixos/modules/system/boot/stage-2-init.sh
··· 141 141 # Use /etc/resolv.conf supplied by systemd-nspawn, if applicable. 142 142 if [ -n "@useHostResolvConf@" -a -e /etc/resolv.conf ]; then 143 143 cat /etc/resolv.conf | resolvconf -m 1000 -a host 144 - else 145 - touch /etc/resolv.conf 146 144 fi 147 145 148 146
+528 -9
nixos/modules/system/boot/systemd-unit-options.nix
··· 4 4 5 5 let 6 6 7 - checkService = v: 8 - let assertValueOneOf = name: values: attr: 9 - let val = attr.${name}; 10 - in optional (attr ? ${name} && !elem val values) "Systemd service field `${name}' cannot have value `${val}'."; 11 - checkType = assertValueOneOf "Type" ["simple" "forking" "oneshot" "dbus" "notify" "idle"]; 12 - checkRestart = assertValueOneOf "Restart" ["no" "on-success" "on-failure" "on-abort" "always"]; 13 - errors = concatMap (c: c v) [checkType checkRestart]; 14 - in if errors == [] then true 15 - else builtins.trace (concatStringsSep "\n" errors) false; 7 + boolValues = [true false "yes" "no"]; 8 + 9 + assertValueOneOf = name: values: group: attr: 10 + optional (attr ? ${name} && !elem attr.${name} values) 11 + "Systemd ${group} field `${name}' cannot have value `${attr.${name}}'."; 12 + 13 + assertHasField = name: group: attr: 14 + optional (!(attr ? ${name})) 15 + "Systemd ${group} field `${name}' must exist."; 16 + 17 + assertOnlyFields = fields: group: attr: 18 + let badFields = filter (name: ! elem name fields) (attrNames attr); in 19 + optional (badFields != [ ]) 20 + "Systemd ${group} has extra fields [${concatStringsSep " " badFields}]."; 21 + 22 + assertRange = name: min: max: group: attr: 23 + optional (attr ? ${name} && !(min <= attr.${name} && max >= attr.${name})) 24 + "Systemd ${group} field `${name}' is outside the range [${toString min},${toString max}]"; 25 + 26 + digits = map toString (range 0 9); 27 + 28 + isByteFormat = s: 29 + let 30 + l = reverseList (stringToCharacters s); 31 + suffix = head l; 32 + nums = tail l; 33 + in elem suffix (["K" "M" "G" "T"] ++ digits) 34 + && all (num: elem num digits) nums; 35 + 36 + assertByteFormat = name: group: attr: 37 + optional (attr ? ${name} && ! isByteFormat attr.${name}) 38 + "Systemd ${group} field `${name}' must be in byte format [0-9]+[KMGT]."; 39 + 40 + hexChars = stringToCharacters "0123456789abcdefABCDEF"; 41 + 42 + isMacAddress = s: stringLength s == 17 43 + && flip all (splitString ":" s) (bytes: 44 + all (byte: elem byte hexChars) (stringToCharacters bytes) 45 + ); 46 + 47 + assertMacAddress = name: group: attr: 48 + optional (attr ? ${name} && ! isMacAddress attr.${name}) 49 + "Systemd ${group} field `${name}' must be a valid mac address."; 50 + 51 + checkUnitConfig = group: checks: v: 52 + let errors = concatMap (c: c group v) checks; in 53 + if errors == [] then true 54 + else builtins.trace (concatStringsSep "\n" errors) false; 55 + 56 + checkService = checkUnitConfig "Service" [ 57 + (assertValueOneOf "Type" [ 58 + "simple" "forking" "oneshot" "dbus" "notify" "idle" 59 + ]) 60 + (assertValueOneOf "Restart" [ 61 + "no" "on-success" "on-failure" "on-abort" "always" 62 + ]) 63 + ]; 64 + 65 + checkLink = checkUnitConfig "Link" [ 66 + (assertOnlyFields [ 67 + "Description" "Alias" "MACAddressPolicy" "MACAddress" "NamePolicy" "Name" 68 + "MTUBytes" "BitsPerSecond" "Duplex" "WakeOnLan" 69 + ]) 70 + (assertValueOneOf "MACAddressPolicy" ["persistent" "random"]) 71 + (assertMacAddress "MACAddress") 72 + (assertValueOneOf "NamePolicy" [ 73 + "kernel" "database" "onboard" "slot" "path" "mac" 74 + ]) 75 + (assertByteFormat "MTUBytes") 76 + (assertByteFormat "BitsPerSecond") 77 + (assertValueOneOf "Duplex" ["half" "full"]) 78 + (assertValueOneOf "WakeOnLan" ["phy" "magic" "off"]) 79 + ]; 80 + 81 + checkNetdev = checkUnitConfig "Netdev" [ 82 + (assertOnlyFields [ 83 + "Description" "Name" "Kind" "MTUBytes" "MACAddress" 84 + ]) 85 + (assertHasField "Name") 86 + (assertHasField "Kind") 87 + (assertValueOneOf "Kind" [ 88 + "bridge" "bond" "vlan" "macvlan" "vxlan" "ipip" 89 + "gre" "sit" "vti" "veth" "tun" "tap" "dummy" 90 + ]) 91 + (assertByteFormat "MTUBytes") 92 + (assertMacAddress "MACAddress") 93 + ]; 94 + 95 + checkVlan = checkUnitConfig "VLAN" [ 96 + (assertOnlyFields ["Id"]) 97 + (assertRange "Id" 0 4094) 98 + ]; 99 + 100 + checkMacvlan = checkUnitConfig "MACVLAN" [ 101 + (assertOnlyFields ["Mode"]) 102 + (assertValueOneOf "Mode" ["private" "vepa" "bridge" "passthru"]) 103 + ]; 104 + 105 + checkVxlan = checkUnitConfig "VXLAN" [ 106 + (assertOnlyFields ["Id" "Group" "TOS" "TTL" "MacLearning"]) 107 + (assertRange "TTL" 0 255) 108 + (assertValueOneOf "MacLearning" boolValues) 109 + ]; 110 + 111 + checkTunnel = checkUnitConfig "Tunnel" [ 112 + (assertOnlyFields ["Local" "Remote" "TOS" "TTL" "DiscoverPathMTU"]) 113 + (assertRange "TTL" 0 255) 114 + (assertValueOneOf "DiscoverPathMTU" boolValues) 115 + ]; 116 + 117 + checkPeer = checkUnitConfig "Peer" [ 118 + (assertOnlyFields ["Name" "MACAddress"]) 119 + (assertMacAddress "MACAddress") 120 + ]; 121 + 122 + tunTapChecks = [ 123 + (assertOnlyFields ["OneQueue" "MultiQueue" "PacketInfo" "User" "Group"]) 124 + (assertValueOneOf "OneQueue" boolValues) 125 + (assertValueOneOf "MultiQueue" boolValues) 126 + (assertValueOneOf "PacketInfo" boolValues) 127 + ]; 128 + 129 + checkTun = checkUnitConfig "Tun" tunTapChecks; 130 + 131 + checkTap = checkUnitConfig "Tap" tunTapChecks; 132 + 133 + checkBond = checkUnitConfig "Bond" [ 134 + (assertOnlyFields [ 135 + "Mode" "TransmitHashPolicy" "LACPTransmitRate" "MIIMonitorSec" 136 + "UpDelaySec" "DownDelaySec" 137 + ]) 138 + (assertValueOneOf "Mode" [ 139 + "balance-rr" "active-backup" "balance-xor" 140 + "broadcast" "802.3ad" "balance-tlb" "balance-alb" 141 + ]) 142 + (assertValueOneOf "TransmitHashPolicy" [ 143 + "layer2" "layer3+4" "layer2+3" "encap2+3" "802.3ad" "encap3+4" 144 + ]) 145 + (assertValueOneOf "LACPTransmitRate" ["slow" "fast"]) 146 + ]; 147 + 148 + checkNetwork = checkUnitConfig "Network" [ 149 + (assertOnlyFields [ 150 + "Description" "DHCP" "DHCPServer" "IPv4LL" "IPv4LLRoute" 151 + "LLMNR" "Domains" "Bridge" "Bond" 152 + ]) 153 + (assertValueOneOf "DHCP" ["both" "none" "v4" "v6"]) 154 + (assertValueOneOf "DHCPServer" boolValues) 155 + (assertValueOneOf "IPv4LL" boolValues) 156 + (assertValueOneOf "IPv4LLRoute" boolValues) 157 + (assertValueOneOf "LLMNR" boolValues) 158 + ]; 159 + 160 + checkAddress = checkUnitConfig "Address" [ 161 + (assertOnlyFields ["Address" "Peer" "Broadcast" "Label"]) 162 + (assertHasField "Address") 163 + ]; 164 + 165 + checkRoute = checkUnitConfig "Route" [ 166 + (assertOnlyFields ["Gateway" "Destination" "Metric"]) 167 + (assertHasField "Gateway") 168 + ]; 169 + 170 + checkDhcp = checkUnitConfig "DHCP" [ 171 + (assertOnlyFields [ 172 + "UseDNS" "UseMTU" "SendHostname" "UseHostname" "UseDomains" "UseRoutes" 173 + "CriticalConnections" "VendorClassIdentifier" "RequestBroadcast" 174 + "RouteMetric" 175 + ]) 176 + (assertValueOneOf "UseDNS" boolValues) 177 + (assertValueOneOf "UseMTU" boolValues) 178 + (assertValueOneOf "SendHostname" boolValues) 179 + (assertValueOneOf "UseHostname" boolValues) 180 + (assertValueOneOf "UseDomains" boolValues) 181 + (assertValueOneOf "UseRoutes" boolValues) 182 + (assertValueOneOf "CriticalConnections" boolValues) 183 + (assertValueOneOf "RequestBroadcast" boolValues) 184 + ]; 16 185 17 186 unitOption = mkOptionType { 18 187 name = "systemd option"; ··· 137 306 description = '' 138 307 If the specified units are started, then this unit is stopped 139 308 and vice versa. 309 + ''; 310 + }; 311 + 312 + requisite = mkOption { 313 + default = []; 314 + type = types.listOf types.str; 315 + description = '' 316 + Similar to requires. However if the units listed are not started, 317 + they will not be started and the transaction will fail. 140 318 ''; 141 319 }; 142 320 ··· 440 618 }; 441 619 442 620 targetOptions = commonUnitOptions; 621 + 622 + commonNetworkOptions = { 623 + 624 + enable = mkOption { 625 + default = true; 626 + type = types.bool; 627 + description = '' 628 + If set to false, this unit will be a symlink to 629 + /dev/null. 630 + ''; 631 + }; 632 + 633 + matchConfig = mkOption { 634 + default = {}; 635 + example = { Name = "eth0"; }; 636 + type = types.attrsOf unitOption; 637 + description = '' 638 + Each attribute in this set specifies an option in the 639 + <literal>[Match]</literal> section of the unit. See 640 + <citerefentry><refentrytitle>systemd.link</refentrytitle><manvolnum>5</manvolnum></citerefentry> 641 + <citerefentry><refentrytitle>systemd.netdev</refentrytitle><manvolnum>5</manvolnum></citerefentry> 642 + <citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry> 643 + for details. 644 + ''; 645 + }; 646 + 647 + }; 648 + 649 + linkOptions = commonNetworkOptions // { 650 + 651 + linkConfig = mkOption { 652 + default = {}; 653 + example = { MACAddress = "00:ff:ee:aa:cc:dd"; }; 654 + type = types.addCheck (types.attrsOf unitOption) checkLink; 655 + description = '' 656 + Each attribute in this set specifies an option in the 657 + <literal>[Link]</literal> section of the unit. See 658 + <citerefentry><refentrytitle>systemd.link</refentrytitle> 659 + <manvolnum>5</manvolnum></citerefentry> for details. 660 + ''; 661 + }; 662 + 663 + }; 664 + 665 + netdevOptions = commonNetworkOptions // { 666 + 667 + netdevConfig = mkOption { 668 + default = {}; 669 + example = { Name = "mybridge"; Kind = "bridge"; }; 670 + type = types.addCheck (types.attrsOf unitOption) checkNetdev; 671 + description = '' 672 + Each attribute in this set specifies an option in the 673 + <literal>[Netdev]</literal> section of the unit. See 674 + <citerefentry><refentrytitle>systemd.netdev</refentrytitle> 675 + <manvolnum>5</manvolnum></citerefentry> for details. 676 + ''; 677 + }; 678 + 679 + vlanConfig = mkOption { 680 + default = {}; 681 + example = { Id = "4"; }; 682 + type = types.addCheck (types.attrsOf unitOption) checkVlan; 683 + description = '' 684 + Each attribute in this set specifies an option in the 685 + <literal>[VLAN]</literal> section of the unit. See 686 + <citerefentry><refentrytitle>systemd.netdev</refentrytitle> 687 + <manvolnum>5</manvolnum></citerefentry> for details. 688 + ''; 689 + }; 690 + 691 + macvlanConfig = mkOption { 692 + default = {}; 693 + example = { Mode = "private"; }; 694 + type = types.addCheck (types.attrsOf unitOption) checkMacvlan; 695 + description = '' 696 + Each attribute in this set specifies an option in the 697 + <literal>[MACVLAN]</literal> section of the unit. See 698 + <citerefentry><refentrytitle>systemd.netdev</refentrytitle> 699 + <manvolnum>5</manvolnum></citerefentry> for details. 700 + ''; 701 + }; 702 + 703 + vxlanConfig = mkOption { 704 + default = {}; 705 + example = { Id = "4"; }; 706 + type = types.addCheck (types.attrsOf unitOption) checkVxlan; 707 + description = '' 708 + Each attribute in this set specifies an option in the 709 + <literal>[VXLAN]</literal> section of the unit. See 710 + <citerefentry><refentrytitle>systemd.netdev</refentrytitle> 711 + <manvolnum>5</manvolnum></citerefentry> for details. 712 + ''; 713 + }; 714 + 715 + tunnelConfig = mkOption { 716 + default = {}; 717 + example = { Remote = "192.168.1.1"; }; 718 + type = types.addCheck (types.attrsOf unitOption) checkTunnel; 719 + description = '' 720 + Each attribute in this set specifies an option in the 721 + <literal>[Tunnel]</literal> section of the unit. See 722 + <citerefentry><refentrytitle>systemd.netdev</refentrytitle> 723 + <manvolnum>5</manvolnum></citerefentry> for details. 724 + ''; 725 + }; 726 + 727 + peerConfig = mkOption { 728 + default = {}; 729 + example = { Name = "veth2"; }; 730 + type = types.addCheck (types.attrsOf unitOption) checkPeer; 731 + description = '' 732 + Each attribute in this set specifies an option in the 733 + <literal>[Peer]</literal> section of the unit. See 734 + <citerefentry><refentrytitle>systemd.netdev</refentrytitle> 735 + <manvolnum>5</manvolnum></citerefentry> for details. 736 + ''; 737 + }; 738 + 739 + tunConfig = mkOption { 740 + default = {}; 741 + example = { User = "openvpn"; }; 742 + type = types.addCheck (types.attrsOf unitOption) checkTun; 743 + description = '' 744 + Each attribute in this set specifies an option in the 745 + <literal>[Tun]</literal> section of the unit. See 746 + <citerefentry><refentrytitle>systemd.netdev</refentrytitle> 747 + <manvolnum>5</manvolnum></citerefentry> for details. 748 + ''; 749 + }; 750 + 751 + tapConfig = mkOption { 752 + default = {}; 753 + example = { User = "openvpn"; }; 754 + type = types.addCheck (types.attrsOf unitOption) checkTap; 755 + description = '' 756 + Each attribute in this set specifies an option in the 757 + <literal>[Tap]</literal> section of the unit. See 758 + <citerefentry><refentrytitle>systemd.netdev</refentrytitle> 759 + <manvolnum>5</manvolnum></citerefentry> for details. 760 + ''; 761 + }; 762 + 763 + bondConfig = mkOption { 764 + default = {}; 765 + example = { Mode = "802.3ad"; }; 766 + type = types.addCheck (types.attrsOf unitOption) checkBond; 767 + description = '' 768 + Each attribute in this set specifies an option in the 769 + <literal>[Bond]</literal> section of the unit. See 770 + <citerefentry><refentrytitle>systemd.netdev</refentrytitle> 771 + <manvolnum>5</manvolnum></citerefentry> for details. 772 + ''; 773 + }; 774 + 775 + }; 776 + 777 + addressOptions = { 778 + 779 + addressConfig = mkOption { 780 + default = {}; 781 + example = { Address = "192.168.0.100/24"; }; 782 + type = types.addCheck (types.attrsOf unitOption) checkAddress; 783 + description = '' 784 + Each attribute in this set specifies an option in the 785 + <literal>[Address]</literal> section of the unit. See 786 + <citerefentry><refentrytitle>systemd.network</refentrytitle> 787 + <manvolnum>5</manvolnum></citerefentry> for details. 788 + ''; 789 + }; 790 + 791 + }; 792 + 793 + routeOptions = { 794 + 795 + routeConfig = mkOption { 796 + default = {}; 797 + example = { Gateway = "192.168.0.1"; }; 798 + type = types.addCheck (types.attrsOf unitOption) checkRoute; 799 + description = '' 800 + Each attribute in this set specifies an option in the 801 + <literal>[Route]</literal> section of the unit. See 802 + <citerefentry><refentrytitle>systemd.network</refentrytitle> 803 + <manvolnum>5</manvolnum></citerefentry> for details. 804 + ''; 805 + }; 806 + 807 + }; 808 + 809 + networkOptions = commonNetworkOptions // { 810 + 811 + networkConfig = mkOption { 812 + default = {}; 813 + example = { Description = "My Network"; }; 814 + type = types.addCheck (types.attrsOf unitOption) checkNetwork; 815 + description = '' 816 + Each attribute in this set specifies an option in the 817 + <literal>[Network]</literal> section of the unit. See 818 + <citerefentry><refentrytitle>systemd.network</refentrytitle> 819 + <manvolnum>5</manvolnum></citerefentry> for details. 820 + ''; 821 + }; 822 + 823 + dhcpConfig = mkOption { 824 + default = {}; 825 + example = { UseDNS = true; UseRoutes = true; }; 826 + type = types.addCheck (types.attrsOf unitOption) checkDhcp; 827 + description = '' 828 + Each attribute in this set specifies an option in the 829 + <literal>[DHCP]</literal> section of the unit. See 830 + <citerefentry><refentrytitle>systemd.network</refentrytitle> 831 + <manvolnum>5</manvolnum></citerefentry> for details. 832 + ''; 833 + }; 834 + 835 + name = mkOption { 836 + type = types.nullOr types.str; 837 + default = null; 838 + description = '' 839 + The name of the network interface to match against. 840 + ''; 841 + }; 842 + 843 + DHCP = mkOption { 844 + type = types.nullOr types.str; 845 + default = null; 846 + description = '' 847 + Whether to enable DHCP on the interfaces matched. 848 + ''; 849 + }; 850 + 851 + domains = mkOption { 852 + type = types.nullOr (types.listOf types.str); 853 + default = null; 854 + description = '' 855 + A list of domains to pass to the network config. 856 + ''; 857 + }; 858 + 859 + address = mkOption { 860 + default = [ ]; 861 + type = types.listOf types.str; 862 + description = '' 863 + A list of addresses to be added to the network section of the 864 + unit. See <citerefentry><refentrytitle>systemd.network</refentrytitle> 865 + <manvolnum>5</manvolnum></citerefentry> for details. 866 + ''; 867 + }; 868 + 869 + gateway = mkOption { 870 + default = [ ]; 871 + type = types.listOf types.str; 872 + description = '' 873 + A list of gateways to be added to the network section of the 874 + unit. See <citerefentry><refentrytitle>systemd.network</refentrytitle> 875 + <manvolnum>5</manvolnum></citerefentry> for details. 876 + ''; 877 + }; 878 + 879 + dns = mkOption { 880 + default = [ ]; 881 + type = types.listOf types.str; 882 + description = '' 883 + A list of dns servers to be added to the network section of the 884 + unit. See <citerefentry><refentrytitle>systemd.network</refentrytitle> 885 + <manvolnum>5</manvolnum></citerefentry> for details. 886 + ''; 887 + }; 888 + 889 + ntp = mkOption { 890 + default = [ ]; 891 + type = types.listOf types.str; 892 + description = '' 893 + A list of ntp servers to be added to the network section of the 894 + unit. See <citerefentry><refentrytitle>systemd.network</refentrytitle> 895 + <manvolnum>5</manvolnum></citerefentry> for details. 896 + ''; 897 + }; 898 + 899 + vlan = mkOption { 900 + default = [ ]; 901 + type = types.listOf types.str; 902 + description = '' 903 + A list of vlan interfaces to be added to the network section of the 904 + unit. See <citerefentry><refentrytitle>systemd.network</refentrytitle> 905 + <manvolnum>5</manvolnum></citerefentry> for details. 906 + ''; 907 + }; 908 + 909 + macvlan = mkOption { 910 + default = [ ]; 911 + type = types.listOf types.str; 912 + description = '' 913 + A list of macvlan interfaces to be added to the network section of the 914 + unit. See <citerefentry><refentrytitle>systemd.network</refentrytitle> 915 + <manvolnum>5</manvolnum></citerefentry> for details. 916 + ''; 917 + }; 918 + 919 + vxlan = mkOption { 920 + default = [ ]; 921 + type = types.listOf types.str; 922 + description = '' 923 + A list of vxlan interfaces to be added to the network section of the 924 + unit. See <citerefentry><refentrytitle>systemd.network</refentrytitle> 925 + <manvolnum>5</manvolnum></citerefentry> for details. 926 + ''; 927 + }; 928 + 929 + tunnel = mkOption { 930 + default = [ ]; 931 + type = types.listOf types.str; 932 + description = '' 933 + A list of tunnel interfaces to be added to the network section of the 934 + unit. See <citerefentry><refentrytitle>systemd.network</refentrytitle> 935 + <manvolnum>5</manvolnum></citerefentry> for details. 936 + ''; 937 + }; 938 + 939 + addresses = mkOption { 940 + default = [ ]; 941 + type = types.listOf types.optionSet; 942 + options = [ addressOptions ]; 943 + description = '' 944 + A list of address sections to be added to the unit. See 945 + <citerefentry><refentrytitle>systemd.network</refentrytitle> 946 + <manvolnum>5</manvolnum></citerefentry> for details. 947 + ''; 948 + }; 949 + 950 + routes = mkOption { 951 + default = [ ]; 952 + type = types.listOf types.optionSet; 953 + options = [ routeOptions ]; 954 + description = '' 955 + A list of route sections to be added to the unit. See 956 + <citerefentry><refentrytitle>systemd.network</refentrytitle> 957 + <manvolnum>5</manvolnum></citerefentry> for details. 958 + ''; 959 + }; 960 + 961 + }; 443 962 444 963 }
+250 -4
nixos/modules/system/boot/systemd.nix
··· 96 96 "systemd-modules-load.service" 97 97 "kmod-static-nodes.service" 98 98 99 + # Networking 100 + "systemd-networkd.service" 101 + "systemd-networkd-wait-online.service" 102 + "systemd-resolved.service" 103 + "systemd-timesyncd.service" 104 + 99 105 # Filesystems. 100 106 "systemd-fsck@.service" 101 107 "systemd-fsck-root.service" ··· 212 218 { PartOf = toString config.partOf; } 213 219 // optionalAttrs (config.conflicts != []) 214 220 { Conflicts = toString config.conflicts; } 221 + // optionalAttrs (config.requisite != []) 222 + { Requisite = toString config.requisite; } 215 223 // optionalAttrs (config.restartTriggers != []) 216 224 { X-Restart-Triggers = toString config.restartTriggers; } 217 225 // optionalAttrs (config.description != "") { ··· 292 300 }; 293 301 }; 294 302 303 + networkConfig = { name, config, ... }: { 304 + config = { 305 + matchConfig = optionalAttrs (config.name != null) { 306 + Name = config.name; 307 + }; 308 + networkConfig = optionalAttrs (config.DHCP != null) { 309 + DHCP = config.DHCP; 310 + } // optionalAttrs (config.domains != null) { 311 + Domains = concatStringsSep " " config.domains; 312 + }; 313 + }; 314 + }; 315 + 295 316 toOption = x: 296 317 if x == true then "true" 297 318 else if x == false then "false" ··· 384 405 ''; 385 406 }; 386 407 408 + commonMatchText = def: '' 409 + [Match] 410 + ${attrsToSection def.matchConfig} 411 + ''; 412 + 413 + linkToUnit = name: def: 414 + { inherit (def) enable; 415 + text = commonMatchText def + 416 + '' 417 + [Link] 418 + ${attrsToSection def.linkConfig} 419 + ''; 420 + }; 421 + 422 + netdevToUnit = name: def: 423 + { inherit (def) enable; 424 + text = commonMatchText def + 425 + '' 426 + [NetDev] 427 + ${attrsToSection def.netdevConfig} 428 + 429 + ${optionalString (def.vlanConfig != { }) '' 430 + [VLAN] 431 + ${attrsToSection def.vlanConfig} 432 + 433 + ''} 434 + ${optionalString (def.macvlanConfig != { }) '' 435 + [MACVLAN] 436 + ${attrsToSection def.macvlanConfig} 437 + 438 + ''} 439 + ${optionalString (def.vxlanConfig != { }) '' 440 + [VXLAN] 441 + ${attrsToSection def.vxlanConfig} 442 + 443 + ''} 444 + ${optionalString (def.tunnelConfig != { }) '' 445 + [Tunnel] 446 + ${attrsToSection def.tunnelConfig} 447 + 448 + ''} 449 + ${optionalString (def.peerConfig != { }) '' 450 + [Peer] 451 + ${attrsToSection def.peerConfig} 452 + 453 + ''} 454 + ${optionalString (def.tunConfig != { }) '' 455 + [Tun] 456 + ${attrsToSection def.tunConfig} 457 + 458 + ''} 459 + ${optionalString (def.tapConfig != { }) '' 460 + [Tap] 461 + ${attrsToSection def.tapConfig} 462 + 463 + ''} 464 + ${optionalString (def.bondConfig != { }) '' 465 + [Bond] 466 + ${attrsToSection def.bondConfig} 467 + 468 + ''} 469 + ''; 470 + }; 471 + 472 + networkToUnit = name: def: 473 + { inherit (def) enable; 474 + text = commonMatchText def + 475 + '' 476 + [Network] 477 + ${attrsToSection def.networkConfig} 478 + ${concatStringsSep "\n" (map (s: "Address=${s}") def.address)} 479 + ${concatStringsSep "\n" (map (s: "Gateway=${s}") def.gateway)} 480 + ${concatStringsSep "\n" (map (s: "DNS=${s}") def.dns)} 481 + ${concatStringsSep "\n" (map (s: "NTP=${s}") def.ntp)} 482 + ${concatStringsSep "\n" (map (s: "VLAN=${s}") def.vlan)} 483 + ${concatStringsSep "\n" (map (s: "MACVLAN=${s}") def.macvlan)} 484 + ${concatStringsSep "\n" (map (s: "VXLAN=${s}") def.vxlan)} 485 + ${concatStringsSep "\n" (map (s: "Tunnel=${s}") def.tunnel)} 486 + 487 + ${optionalString (def.dhcpConfig != { }) '' 488 + [DHCP] 489 + ${attrsToSection def.dhcpConfig} 490 + 491 + ''} 492 + ${flip concatMapStrings def.addresses (x: '' 493 + [Address] 494 + ${attrsToSection x.addressConfig} 495 + 496 + '')} 497 + ${flip concatMapStrings def.routes (x: '' 498 + [Route] 499 + ${attrsToSection x.routeConfig} 500 + 501 + '')} 502 + ''; 503 + }; 504 + 387 505 generateUnits = type: units: upstreamUnits: upstreamWants: 388 506 pkgs.runCommand "${type}-units" { preferLocalBuild = true; } '' 389 507 mkdir -p $out ··· 468 586 mkdir -p $out/getty.target.wants/ 469 587 ln -s ../autovt@tty1.service $out/getty.target.wants/ 470 588 471 - ln -s ../local-fs.target ../remote-fs.target ../network.target ../nss-lookup.target \ 472 - ../nss-user-lookup.target ../swap.target $out/multi-user.target.wants/ 589 + ln -s ../local-fs.target ../remote-fs.target ../network.target \ 590 + ../nss-lookup.target ../nss-user-lookup.target ../swap.target \ 591 + $out/multi-user.target.wants/ 473 592 ''} 474 593 ''; # */ 475 594 ··· 562 681 ''; 563 682 }; 564 683 684 + systemd.network.enable = mkOption { 685 + default = false; 686 + type = types.bool; 687 + description = '' 688 + Whether to enable networkd or not. 689 + ''; 690 + }; 691 + 692 + systemd.network.links = mkOption { 693 + default = {}; 694 + type = types.attrsOf types.optionSet; 695 + options = [ linkOptions ]; 696 + description = "Definiton of systemd network links."; 697 + }; 698 + 699 + systemd.network.netdevs = mkOption { 700 + default = {}; 701 + type = types.attrsOf types.optionSet; 702 + options = [ netdevOptions ]; 703 + description = "Definiton of systemd network devices."; 704 + }; 705 + 706 + systemd.network.networks = mkOption { 707 + default = {}; 708 + type = types.attrsOf types.optionSet; 709 + options = [ networkOptions networkConfig ]; 710 + description = "Definiton of systemd networks."; 711 + }; 712 + 713 + systemd.network.units = mkOption { 714 + description = "Definition of networkd units."; 715 + default = {}; 716 + type = types.attrsOf types.optionSet; 717 + options = { name, config, ... }: 718 + { options = concreteUnitOptions; 719 + config = { 720 + unit = mkDefault (makeUnit name config); 721 + }; 722 + }; 723 + }; 724 + 565 725 systemd.defaultUnit = mkOption { 566 726 default = "multi-user.target"; 567 727 type = types.str; ··· 645 805 ''; 646 806 }; 647 807 808 + services.resolved.enable = mkOption { 809 + default = false; 810 + type = types.bool; 811 + description = '' 812 + Enables the systemd dns resolver daemon. 813 + ''; 814 + }; 815 + 816 + services.timesyncd.enable = mkOption { 817 + default = false; 818 + type = types.bool; 819 + description = '' 820 + Enables the systemd ntp client daemon. 821 + ''; 822 + }; 823 + 648 824 systemd.tmpfiles.rules = mkOption { 649 825 type = types.listOf types.str; 650 826 default = []; ··· 701 877 702 878 ###### implementation 703 879 704 - config = { 880 + config = mkMerge [ { 705 881 706 882 warnings = concatLists (mapAttrsToList (name: service: 707 883 optional (service.serviceConfig.Type or "" == "oneshot" && service.serviceConfig.Restart or "no" != "no") ··· 714 890 environment.etc."systemd/system".source = 715 891 generateUnits "system" cfg.units upstreamSystemUnits upstreamSystemWants; 716 892 893 + environment.etc."systemd/network".source = 894 + generateUnits "network" cfg.network.units [] []; 895 + 717 896 environment.etc."systemd/user".source = 718 897 generateUnits "user" cfg.user.units upstreamUserUnits []; 719 898 ··· 766 945 unitConfig.X-StopOnReconfiguration = true; 767 946 }; 768 947 948 + systemd.targets.network-online.after = [ "ip-up.target" ]; 949 + 769 950 systemd.units = 770 951 mapAttrs' (n: v: nameValuePair "${n}.target" (targetToUnit n v)) cfg.targets 771 952 // mapAttrs' (n: v: nameValuePair "${n}.service" (serviceToUnit n v)) cfg.services ··· 779 960 (v: let n = escapeSystemdPath v.where; 780 961 in nameValuePair "${n}.automount" (automountToUnit n v)) cfg.automounts); 781 962 963 + systemd.network.units = 964 + mapAttrs' (n: v: nameValuePair "${n}.link" (linkToUnit n v)) cfg.network.links 965 + // mapAttrs' (n: v: nameValuePair "${n}.netdev" (netdevToUnit n v)) cfg.network.netdevs 966 + // mapAttrs' (n: v: nameValuePair "${n}.network" (networkToUnit n v)) cfg.network.networks; 967 + 782 968 systemd.user.units = 783 969 mapAttrs' (n: v: nameValuePair "${n}.service" (serviceToUnit n v)) cfg.user.services 784 970 // mapAttrs' (n: v: nameValuePair "${n}.socket" (socketToUnit n v)) cfg.user.sockets; ··· 833 1019 systemd.services.systemd-remount-fs.restartIfChanged = false; 834 1020 systemd.services.systemd-journal-flush.restartIfChanged = false; 835 1021 836 - }; 1022 + } 1023 + (mkIf config.systemd.network.enable { 1024 + users.extraUsers.systemd-network.uid = config.ids.uids.systemd-network; 1025 + users.extraGroups.systemd-network.gid = config.ids.gids.systemd-network; 1026 + 1027 + systemd.services.systemd-networkd = { 1028 + wantedBy = [ "multi-user.target" ]; 1029 + restartTriggers = [ config.environment.etc."systemd/network".source ]; 1030 + }; 1031 + 1032 + systemd.services.systemd-networkd-wait-online = { 1033 + before = [ "network-online.target" "ip-up.target" ]; 1034 + wantedBy = [ "network-online.target" "ip-up.target" ]; 1035 + }; 1036 + 1037 + systemd.services."systemd-network-wait-online@" = { 1038 + description = "Wait for Network Interface %I to be Configured"; 1039 + conflicts = [ "shutdown.target" ]; 1040 + requisite = [ "systemd-networkd.service" ]; 1041 + after = [ "systemd-networkd.service" ]; 1042 + serviceConfig = { 1043 + Type = "oneshot"; 1044 + RemainAfterExit = true; 1045 + ExecStart = "${config.systemd.package}/lib/systemd/systemd-networkd-wait-online -i %I"; 1046 + }; 1047 + }; 1048 + 1049 + services.resolved.enable = mkDefault true; 1050 + services.timesyncd.enable = mkDefault config.services.ntp.enable; 1051 + }) 1052 + (mkIf config.services.resolved.enable { 1053 + users.extraUsers.systemd-resolve.uid = config.ids.uids.systemd-resolve; 1054 + users.extraGroups.systemd-resolve.gid = config.ids.gids.systemd-resolve; 1055 + 1056 + systemd.services.systemd-resolved = { 1057 + wantedBy = [ "multi-user.target" ]; 1058 + restartTriggers = [ config.environment.etc."systemd/resolved.conf".source ]; 1059 + }; 1060 + 1061 + environment.etc."systemd/resolved.conf".text = '' 1062 + [Resolve] 1063 + DNS=${concatStringsSep " " config.networking.nameservers} 1064 + ''; 1065 + }) 1066 + (mkIf config.services.timesyncd.enable { 1067 + users.extraUsers.systemd-timesync.uid = config.ids.uids.systemd-timesync; 1068 + users.extraGroups.systemd-timesync.gid = config.ids.gids.systemd-timesync; 1069 + 1070 + systemd.services.systemd-timesyncd = { 1071 + wantedBy = [ "sysinit.target" ]; 1072 + restartTriggers = [ config.environment.etc."systemd/timesyncd.conf".source ]; 1073 + }; 1074 + 1075 + environment.etc."systemd/timesyncd.conf".text = '' 1076 + [Time] 1077 + NTP=${concatStringsSep " " config.services.ntp.servers} 1078 + ''; 1079 + 1080 + systemd.services.ntpd.enable = false; 1081 + }) 1082 + ]; 837 1083 }
+2 -4
nixos/modules/tasks/filesystems/nfs.nix
··· 73 73 74 74 path = [ pkgs.nfsUtils pkgs.sysvtools pkgs.utillinux ]; 75 75 76 - wantedBy = [ "network-online.target" "multi-user.target" ]; 77 - before = [ "network-online.target" ]; 76 + wantedBy = [ "multi-user.target" ]; 78 77 requires = [ "basic.target" "rpcbind.service" ]; 79 78 after = [ "basic.target" "rpcbind.service" "network.target" ]; 80 79 ··· 100 99 101 100 path = [ pkgs.sysvtools pkgs.utillinux ]; 102 101 103 - wantedBy = [ "network-online.target" "multi-user.target" ]; 104 - before = [ "network-online.target" ]; 102 + wantedBy = [ "multi-user.target" ]; 105 103 requires = [ "rpcbind.service" ]; 106 104 after = [ "rpcbind.service" ]; 107 105
+340
nixos/modules/tasks/network-interfaces-scripted.nix
··· 1 + { config, lib, pkgs, utils, ... }: 2 + 3 + with lib; 4 + with utils; 5 + 6 + let 7 + 8 + cfg = config.networking; 9 + interfaces = attrValues cfg.interfaces; 10 + hasVirtuals = any (i: i.virtual) interfaces; 11 + 12 + # We must escape interfaces due to the systemd interpretation 13 + subsystemDevice = interface: 14 + "sys-subsystem-net-devices-${escapeSystemdPath interface}.device"; 15 + 16 + interfaceIps = i: 17 + i.ip4 ++ optionals cfg.enableIPv6 i.ip6 18 + ++ optional (i.ipAddress != null) { 19 + address = i.ipAddress; 20 + prefixLength = i.prefixLength; 21 + } ++ optional (cfg.enableIPv6 && i.ipv6Address != null) { 22 + address = i.ipv6Address; 23 + prefixLength = i.ipv6PrefixLength; 24 + }; 25 + 26 + destroyBond = i: '' 27 + while true; do 28 + UPDATED=1 29 + SLAVES=$(ip link | grep 'master ${i}' | awk -F: '{print $2}') 30 + for I in $SLAVES; do 31 + UPDATED=0 32 + ip link set "$I" nomaster 33 + done 34 + [ "$UPDATED" -eq "1" ] && break 35 + done 36 + ip link set "${i}" down || true 37 + ip link del "${i}" || true 38 + ''; 39 + 40 + in 41 + 42 + { 43 + 44 + config = mkIf (!cfg.useNetworkd) { 45 + 46 + systemd.targets."network-interfaces" = 47 + { description = "All Network Interfaces"; 48 + wantedBy = [ "network.target" ]; 49 + unitConfig.X-StopOnReconfiguration = true; 50 + }; 51 + 52 + systemd.services = 53 + let 54 + 55 + networkLocalCommands = { 56 + after = [ "network-setup.service" ]; 57 + bindsTo = [ "network-setup.service" ]; 58 + }; 59 + 60 + networkSetup = 61 + { description = "Networking Setup"; 62 + 63 + after = [ "network-interfaces.target" ]; 64 + before = [ "network.target" ]; 65 + wantedBy = [ "network.target" ]; 66 + 67 + unitConfig.ConditionCapability = "CAP_NET_ADMIN"; 68 + 69 + path = [ pkgs.iproute ]; 70 + 71 + serviceConfig.Type = "oneshot"; 72 + serviceConfig.RemainAfterExit = true; 73 + 74 + script = 75 + (optionalString (!config.services.resolved.enable) '' 76 + # Set the static DNS configuration, if given. 77 + ${pkgs.openresolv}/sbin/resolvconf -m 1 -a static <<EOF 78 + ${optionalString (cfg.nameservers != [] && cfg.domain != null) '' 79 + domain ${cfg.domain} 80 + ''} 81 + ${optionalString (cfg.search != []) ("search " + concatStringsSep " " cfg.search)} 82 + ${flip concatMapStrings cfg.nameservers (ns: '' 83 + nameserver ${ns} 84 + '')} 85 + EOF 86 + '') + '' 87 + # Set the default gateway. 88 + ${optionalString (cfg.defaultGateway != null) '' 89 + # FIXME: get rid of "|| true" (necessary to make it idempotent). 90 + ip route add default via "${cfg.defaultGateway}" ${ 91 + optionalString (cfg.defaultGatewayWindowSize != null) 92 + "window ${cfg.defaultGatewayWindowSize}"} || true 93 + ''} 94 + ''; 95 + }; 96 + 97 + # For each interface <foo>, create a job ‘network-addresses-<foo>.service" 98 + # that performs static address configuration. It has a "wants" 99 + # dependency on ‘<foo>.service’, which is supposed to create 100 + # the interface and need not exist (i.e. for hardware 101 + # interfaces). It has a binds-to dependency on the actual 102 + # network device, so it only gets started after the interface 103 + # has appeared, and it's stopped when the interface 104 + # disappears. 105 + configureAddrs = i: 106 + let 107 + ips = interfaceIps i; 108 + in 109 + nameValuePair "network-addresses-${i.name}" 110 + { description = "Addresss configuration of ${i.name}"; 111 + wantedBy = [ "network-interfaces.target" ]; 112 + before = [ "network-interfaces.target" ]; 113 + bindsTo = [ (subsystemDevice i.name) ]; 114 + after = [ (subsystemDevice i.name) ]; 115 + serviceConfig.Type = "oneshot"; 116 + serviceConfig.RemainAfterExit = true; 117 + path = [ pkgs.iproute ]; 118 + script = 119 + '' 120 + echo "bringing up interface..." 121 + ip link set "${i.name}" up 122 + 123 + restart_network_interfaces=false 124 + '' + flip concatMapStrings (ips) (ip: 125 + let 126 + address = "${ip.address}/${toString ip.prefixLength}"; 127 + in 128 + '' 129 + echo "checking ip ${address}..." 130 + if out=$(ip addr add "${address}" dev "${i.name}" 2>&1); then 131 + echo "added ip ${address}..." 132 + restart_network_setup=true 133 + elif ! echo "$out" | grep "File exists" >/dev/null 2>&1; then 134 + echo "failed to add ${address}" 135 + exit 1 136 + fi 137 + '') 138 + + optionalString (ips != [ ]) 139 + '' 140 + if [ "$restart_network_setup" = "true" ]; then 141 + # Ensure that the default gateway remains set. 142 + # (Flushing this interface may have removed it.) 143 + ${config.systemd.package}/bin/systemctl try-restart --no-block network-setup.service 144 + fi 145 + ${config.systemd.package}/bin/systemctl start ip-up.target 146 + ''; 147 + preStop = 148 + '' 149 + echo "releasing configured ip's..." 150 + '' + flip concatMapStrings (ips) (ip: 151 + let 152 + address = "${ip.address}/${toString ip.prefixLength}"; 153 + in 154 + '' 155 + echo -n "Deleting ${address}..." 156 + ip addr del "${address}" dev "${i.name}" >/dev/null 2>&1 || echo -n " Failed" 157 + echo "" 158 + ''); 159 + }; 160 + 161 + createTunDevice = i: nameValuePair "${i.name}-netdev" 162 + { description = "Virtual Network Interface ${i.name}"; 163 + requires = [ "dev-net-tun.device" ]; 164 + after = [ "dev-net-tun.device" ]; 165 + wantedBy = [ "network.target" (subsystemDevice i.name) ]; 166 + path = [ pkgs.iproute ]; 167 + serviceConfig = { 168 + Type = "oneshot"; 169 + RemainAfterExit = true; 170 + }; 171 + script = '' 172 + ip tuntap add dev "${i.name}" \ 173 + ${optionalString (i.virtualType != null) "mode ${i.virtualType}"} \ 174 + user "${i.virtualOwner}" 175 + ''; 176 + postStop = '' 177 + ip link del ${i.name} 178 + ''; 179 + }; 180 + 181 + createBridgeDevice = n: v: nameValuePair "${n}-netdev" 182 + (let 183 + deps = map subsystemDevice v.interfaces; 184 + in 185 + { description = "Bridge Interface ${n}"; 186 + wantedBy = [ "network.target" (subsystemDevice n) ]; 187 + bindsTo = deps; 188 + after = deps; 189 + serviceConfig.Type = "oneshot"; 190 + serviceConfig.RemainAfterExit = true; 191 + path = [ pkgs.iproute ]; 192 + script = '' 193 + # Remove Dead Interfaces 194 + echo "Removing old bridge ${n}..." 195 + ip link show "${n}" >/dev/null 2>&1 && ip link del "${n}" 196 + 197 + echo "Adding bridge ${n}..." 198 + ip link add name "${n}" type bridge 199 + 200 + # Enslave child interfaces 201 + ${flip concatMapStrings v.interfaces (i: '' 202 + ip link set "${i}" master "${n}" 203 + ip link set "${i}" up 204 + '')} 205 + 206 + ip link set "${n}" up 207 + ''; 208 + postStop = '' 209 + ip link set "${n}" down || true 210 + ip link del "${n}" || true 211 + ''; 212 + }); 213 + 214 + createBondDevice = n: v: nameValuePair "${n}-netdev" 215 + (let 216 + deps = map subsystemDevice v.interfaces; 217 + in 218 + { description = "Bond Interface ${n}"; 219 + wantedBy = [ "network.target" (subsystemDevice n) ]; 220 + bindsTo = deps; 221 + after = deps; 222 + before = [ "${n}-cfg.service" ]; 223 + serviceConfig.Type = "oneshot"; 224 + serviceConfig.RemainAfterExit = true; 225 + path = [ pkgs.iproute pkgs.gawk ]; 226 + script = '' 227 + echo "Destroying old bond ${n}..." 228 + ${destroyBond n} 229 + 230 + echo "Creating new bond ${n}..." 231 + ip link add name "${n}" type bond \ 232 + ${optionalString (v.mode != null) "mode ${toString v.mode}"} \ 233 + ${optionalString (v.miimon != null) "miimon ${toString v.miimon}"} \ 234 + ${optionalString (v.xmit_hash_policy != null) "xmit_hash_policy ${toString v.xmit_hash_policy}"} \ 235 + ${optionalString (v.lacp_rate != null) "lacp_rate ${toString v.lacp_rate}"} 236 + 237 + # !!! There must be a better way to wait for the interface 238 + while [ ! -d "/sys/class/net/${n}" ]; do sleep 0.1; done; 239 + 240 + # Bring up the bond and enslave the specified interfaces 241 + ip link set "${n}" up 242 + ${flip concatMapStrings v.interfaces (i: '' 243 + ip link set "${i}" master "${n}" 244 + '')} 245 + ''; 246 + postStop = destroyBond n; 247 + }); 248 + 249 + createMacvlanDevice = n: v: nameValuePair "${n}-netdev" 250 + (let 251 + deps = [ (subsystemDevice v.interface) ]; 252 + in 253 + { description = "Vlan Interface ${n}"; 254 + wantedBy = [ "network.target" (subsystemDevice n) ]; 255 + bindsTo = deps; 256 + after = deps; 257 + serviceConfig.Type = "oneshot"; 258 + serviceConfig.RemainAfterExit = true; 259 + path = [ pkgs.iproute ]; 260 + script = '' 261 + # Remove Dead Interfaces 262 + ip link show "${n}" >/dev/null 2>&1 && ip link delete "${n}" 263 + ip link add link "${v.interface}" name "${n}" type macvlan \ 264 + ${optionalString (v.mode != null) "mode ${v.mode}"} 265 + ip link set "${n}" up 266 + ''; 267 + postStop = '' 268 + ip link delete "${n}" 269 + ''; 270 + }); 271 + 272 + createSitDevice = n: v: nameValuePair "${n}-netdev" 273 + (let 274 + deps = optional (v.dev != null) (subsystemDevice v.dev); 275 + in 276 + { description = "6-to-4 Tunnel Interface ${n}"; 277 + wantedBy = [ "network.target" (subsystemDevice n) ]; 278 + bindsTo = deps; 279 + after = deps; 280 + serviceConfig.Type = "oneshot"; 281 + serviceConfig.RemainAfterExit = true; 282 + path = [ pkgs.iproute ]; 283 + script = '' 284 + # Remove Dead Interfaces 285 + ip link show "${n}" >/dev/null 2>&1 && ip link delete "${n}" 286 + ip link add name "${n}" type sit \ 287 + ${optionalString (v.remote != null) "remote \"${v.remote}\""} \ 288 + ${optionalString (v.local != null) "local \"${v.local}\""} \ 289 + ${optionalString (v.ttl != null) "ttl ${toString v.ttl}"} \ 290 + ${optionalString (v.dev != null) "dev \"${v.dev}\""} 291 + ip link set "${n}" up 292 + ''; 293 + postStop = '' 294 + ip link delete "${n}" 295 + ''; 296 + }); 297 + 298 + createVlanDevice = n: v: nameValuePair "${n}-netdev" 299 + (let 300 + deps = [ (subsystemDevice v.interface) ]; 301 + in 302 + { description = "Vlan Interface ${n}"; 303 + wantedBy = [ "network.target" (subsystemDevice n) ]; 304 + bindsTo = deps; 305 + after = deps; 306 + serviceConfig.Type = "oneshot"; 307 + serviceConfig.RemainAfterExit = true; 308 + path = [ pkgs.iproute ]; 309 + script = '' 310 + # Remove Dead Interfaces 311 + ip link show "${n}" >/dev/null 2>&1 && ip link delete "${n}" 312 + ip link add link "${v.interface}" name "${n}" type vlan id "${toString v.id}" 313 + ip link set "${n}" up 314 + ''; 315 + postStop = '' 316 + ip link delete "${n}" 317 + ''; 318 + }); 319 + 320 + in listToAttrs ( 321 + map configureAddrs interfaces ++ 322 + map createTunDevice (filter (i: i.virtual) interfaces)) 323 + // mapAttrs' createBridgeDevice cfg.bridges 324 + // mapAttrs' createBondDevice cfg.bonds 325 + // mapAttrs' createMacvlanDevice cfg.macvlans 326 + // mapAttrs' createSitDevice cfg.sits 327 + // mapAttrs' createVlanDevice cfg.vlans 328 + // { 329 + "network-setup" = networkSetup; 330 + "network-local-commands" = networkLocalCommands; 331 + }; 332 + 333 + services.udev.extraRules = 334 + '' 335 + KERNEL=="tun", TAG+="systemd" 336 + ''; 337 + 338 + }; 339 + 340 + }
+174
nixos/modules/tasks/network-interfaces-systemd.nix
··· 1 + { config, lib, pkgs, utils, ... }: 2 + 3 + with lib; 4 + with utils; 5 + 6 + let 7 + 8 + cfg = config.networking; 9 + interfaces = attrValues cfg.interfaces; 10 + 11 + interfaceIps = i: 12 + i.ip4 ++ optionals cfg.enableIPv6 i.ip6 13 + ++ optional (i.ipAddress != null) { 14 + address = i.ipAddress; 15 + prefixLength = i.prefixLength; 16 + } ++ optional (cfg.enableIPv6 && i.ipv6Address != null) { 17 + address = i.ipv6Address; 18 + prefixLength = i.ipv6PrefixLength; 19 + }; 20 + 21 + dhcpStr = useDHCP: if useDHCP then "both" else "none"; 22 + 23 + slaves = 24 + concatLists (map (bond: bond.interfaces) (attrValues cfg.bonds)) 25 + ++ concatLists (map (bridge: bridge.interfaces) (attrValues cfg.bridges)) 26 + ++ map (sit: sit.dev) (attrValues cfg.sits) 27 + ++ map (vlan: vlan.interface) (attrValues cfg.vlans); 28 + 29 + in 30 + 31 + { 32 + 33 + config = mkIf cfg.useNetworkd { 34 + 35 + assertions = [ { 36 + assertion = cfg.defaultGatewayWindowSize == null; 37 + message = "networking.defaultGatewayWindowSize is not supported by networkd."; 38 + } ]; 39 + 40 + systemd.services.dhcpcd.enable = mkDefault false; 41 + 42 + systemd.services.network-local-commands = { 43 + after = [ "systemd-networkd.service" ]; 44 + bindsTo = [ "systemd-networkd.service" ]; 45 + }; 46 + 47 + systemd.network = 48 + let 49 + domains = cfg.search ++ (optional (cfg.domain != null) cfg.domain); 50 + genericNetwork = override: { 51 + DHCP = override (dhcpStr cfg.useDHCP); 52 + } // optionalAttrs (cfg.defaultGateway != null) { 53 + gateway = override [ cfg.defaultGateway ]; 54 + } // optionalAttrs (domains != [ ]) { 55 + domains = override domains; 56 + }; 57 + in mkMerge [ { 58 + enable = true; 59 + networks."99-main" = genericNetwork mkDefault; 60 + } 61 + (mkMerge (flip map interfaces (i: { 62 + netdevs = mkIf i.virtual ( 63 + let 64 + devType = if i.virtualType != null then i.virtualType 65 + else (if hasPrefix "tun" i.name then "tun" else "tap"); 66 + in { 67 + "40-${i.name}" = { 68 + netdevConfig = { 69 + Name = i.name; 70 + Kind = devType; 71 + }; 72 + "${devType}Config" = optionalAttrs (i.virtualOwner != null) { 73 + User = i.virtualOwner; 74 + }; 75 + }; 76 + }); 77 + networks."40-${i.name}" = mkMerge [ (genericNetwork mkDefault) { 78 + name = mkDefault i.name; 79 + DHCP = mkForce (dhcpStr 80 + (if i.useDHCP != null then i.useDHCP else interfaceIps i == [ ])); 81 + address = flip map (interfaceIps i) 82 + (ip: "${ip.address}/${toString ip.prefixLength}"); 83 + } ]; 84 + }))) 85 + (mkMerge (flip mapAttrsToList cfg.bridges (name: bridge: { 86 + netdevs."40-${name}" = { 87 + netdevConfig = { 88 + Name = name; 89 + Kind = "bridge"; 90 + }; 91 + }; 92 + networks = listToAttrs (flip map bridge.interfaces (bi: 93 + nameValuePair "40-${bi}" (mkMerge [ (genericNetwork (mkOverride 999)) { 94 + DHCP = mkOverride 0 (dhcpStr false); 95 + networkConfig.Bridge = name; 96 + } ]))); 97 + }))) 98 + (mkMerge (flip mapAttrsToList cfg.bonds (name: bond: { 99 + netdevs."40-${name}" = { 100 + netdevConfig = { 101 + Name = name; 102 + Kind = "bond"; 103 + }; 104 + bondConfig = 105 + (optionalAttrs (bond.lacp_rate != null) { 106 + LACPTransmitRate = bond.lacp_rate; 107 + }) // (optionalAttrs (bond.miimon != null) { 108 + MIIMonitorSec = bond.miimon; 109 + }) // (optionalAttrs (bond.mode != null) { 110 + Mode = bond.mode; 111 + }) // (optionalAttrs (bond.xmit_hash_policy != null) { 112 + TransmitHashPolicy = bond.xmit_hash_policy; 113 + }); 114 + }; 115 + networks = listToAttrs (flip map bond.interfaces (bi: 116 + nameValuePair "40-${bi}" (mkMerge [ (genericNetwork (mkOverride 999)) { 117 + DHCP = mkOverride 0 (dhcpStr false); 118 + networkConfig.Bond = name; 119 + } ]))); 120 + }))) 121 + (mkMerge (flip mapAttrsToList cfg.macvlans (name: macvlan: { 122 + netdevs."40-${name}" = { 123 + netdevConfig = { 124 + Name = name; 125 + Kind = "macvlan"; 126 + }; 127 + macvlanConfig.Mode = macvlan.mode; 128 + }; 129 + networks."40-${macvlan.interface}" = (mkMerge [ (genericNetwork (mkOverride 999)) { 130 + macvlan = [ name ]; 131 + } ]); 132 + }))) 133 + (mkMerge (flip mapAttrsToList cfg.sits (name: sit: { 134 + netdevs."40-${name}" = { 135 + netdevConfig = { 136 + Name = name; 137 + Kind = "sit"; 138 + }; 139 + tunnelConfig = 140 + (optionalAttrs (sit.remote != null) { 141 + Remote = sit.remote; 142 + }) // (optionalAttrs (sit.local != null) { 143 + Local = sit.local; 144 + }) // (optionalAttrs (sit.ttl != null) { 145 + TTL = sit.ttl; 146 + }); 147 + }; 148 + networks = mkIf (sit.dev != null) { 149 + "40-${sit.dev}" = (mkMerge [ (genericNetwork (mkOverride 999)) { 150 + tunnel = [ name ]; 151 + } ]); 152 + }; 153 + }))) 154 + (mkMerge (flip mapAttrsToList cfg.vlans (name: vlan: { 155 + netdevs."40-${name}" = { 156 + netdevConfig = { 157 + Name = name; 158 + Kind = "vlan"; 159 + }; 160 + vlanConfig.Id = vlan.id; 161 + }; 162 + networks."40-${vlan.interface}" = (mkMerge [ (genericNetwork (mkOverride 999)) { 163 + vlan = [ name ]; 164 + } ]); 165 + }))) 166 + ]; 167 + 168 + # We need to prefill the slaved devices with networking options 169 + # This forces the network interface creator to initialize slaves. 170 + networking.interfaces = listToAttrs (map (i: nameValuePair i { }) slaves); 171 + 172 + }; 173 + 174 + }
+114 -343
nixos/modules/tasks/network-interfaces.nix
··· 45 45 description = "Name of the interface."; 46 46 }; 47 47 48 + useDHCP = mkOption { 49 + type = types.nullOr types.bool; 50 + default = null; 51 + description = '' 52 + Whether this interface should be configured with dhcp. 53 + Null implies the old behavior which depends on whether ip addresses 54 + are specified or not. 55 + ''; 56 + }; 57 + 48 58 ip4 = mkOption { 49 59 default = [ ]; 50 60 example = [ ··· 203 213 204 214 networking.hostName = mkOption { 205 215 default = "nixos"; 216 + type = types.str; 206 217 description = '' 207 218 The name of the machine. Leave it empty if you want to obtain 208 219 it from a DHCP server (if using DHCP). ··· 225 236 226 237 networking.enableIPv6 = mkOption { 227 238 default = true; 239 + type = types.bool; 228 240 description = '' 229 241 Whether to enable support for IPv6. 230 242 ''; 231 243 }; 232 244 233 245 networking.defaultGateway = mkOption { 234 - default = ""; 246 + default = null; 235 247 example = "131.211.84.1"; 248 + type = types.nullOr types.str; 236 249 description = '' 237 250 The default gateway. It can be left empty if it is auto-detected through DHCP. 238 251 ''; ··· 266 279 }; 267 280 268 281 networking.domain = mkOption { 269 - default = ""; 282 + default = null; 270 283 example = "home"; 284 + type = types.nullOr types.str; 271 285 description = '' 272 286 The domain. It can be left empty if it is auto-detected through DHCP. 273 287 ''; ··· 414 428 }; 415 429 }; 416 430 431 + networking.macvlans = mkOption { 432 + type = types.attrsOf types.optionSet; 433 + default = { }; 434 + example = { 435 + wan = { 436 + interface = "enp2s0"; 437 + mode = "vepa"; 438 + }; 439 + }; 440 + description = '' 441 + This option allows you to define macvlan interfaces which should 442 + be automatically created. 443 + ''; 444 + options = { 445 + 446 + interface = mkOption { 447 + example = "enp4s0"; 448 + type = types.string; 449 + description = "The interface the macvlan will transmit packets through."; 450 + }; 451 + 452 + mode = mkOption { 453 + default = null; 454 + type = types.nullOr types.str; 455 + example = "vepa"; 456 + description = "The mode of the macvlan device."; 457 + }; 458 + 459 + }; 460 + }; 461 + 417 462 networking.sits = mkOption { 418 463 type = types.attrsOf types.optionSet; 419 464 default = { }; ··· 523 568 ''; 524 569 }; 525 570 571 + networking.useNetworkd = mkOption { 572 + default = false; 573 + type = types.bool; 574 + description = '' 575 + Whether we should use networkd as the network configuration backend or 576 + the legacy script based system. Note that this option is experimental, 577 + enable at your own risk. 578 + ''; 579 + }; 580 + 526 581 }; 527 582 528 583 ··· 552 607 # from being created. 553 608 optionalString hasBonds "options bonding max_bonds=0"; 554 609 555 - environment.systemPackages = 556 - [ pkgs.host 557 - pkgs.iproute 558 - pkgs.iputils 559 - pkgs.nettools 560 - pkgs.wirelesstools 561 - pkgs.iw 562 - pkgs.rfkill 563 - pkgs.openresolv 564 - ] 565 - ++ optional (cfg.bridges != {}) pkgs.bridge_utils 566 - ++ optional hasVirtuals pkgs.tunctl 567 - ++ optional cfg.enableIPv6 pkgs.ndisc6; 610 + boot.kernel.sysctl = { 611 + "net.net.ipv4.conf.all.promote_secondaries" = true; 612 + "net.ipv6.conf.all.disable_ipv6" = mkDefault (!cfg.enableIPv6); 613 + "net.ipv6.conf.default.disable_ipv6" = mkDefault (!cfg.enableIPv6); 614 + "net.ipv4.conf.all_forwarding" = mkDefault (any (i: i.proxyARP) interfaces); 615 + "net.ipv6.conf.all.forwarding" = mkDefault (any (i: i.proxyARP) interfaces); 616 + } // listToAttrs (concatLists (flip map (filter (i: i.proxyARP) interfaces) 617 + (i: flip map [ "4" "6" ] (v: nameValuePair "net.ipv${v}.conf.${i.name}.proxy_arp" true)) 618 + )); 568 619 569 620 security.setuidPrograms = [ "ping" "ping6" ]; 570 621 571 - systemd.targets."network-interfaces" = 572 - { description = "All Network Interfaces"; 573 - wantedBy = [ "network.target" ]; 574 - unitConfig.X-StopOnReconfiguration = true; 575 - }; 576 - 577 - systemd.services = 578 - let 579 - 580 - networkSetup = 581 - { description = "Networking Setup"; 582 - 583 - after = [ "network-interfaces.target" ]; 584 - before = [ "network.target" ]; 585 - wantedBy = [ "network.target" ]; 586 - 587 - unitConfig.ConditionCapability = "CAP_NET_ADMIN"; 588 - 589 - path = [ pkgs.iproute ]; 590 - 591 - serviceConfig.Type = "oneshot"; 592 - serviceConfig.RemainAfterExit = true; 593 - 594 - script = 595 - '' 596 - # Set the static DNS configuration, if given. 597 - ${pkgs.openresolv}/sbin/resolvconf -m 1 -a static <<EOF 598 - ${optionalString (cfg.nameservers != [] && cfg.domain != "") '' 599 - domain ${cfg.domain} 600 - ''} 601 - ${optionalString (cfg.search != []) ("search " + concatStringsSep " " cfg.search)} 602 - ${flip concatMapStrings cfg.nameservers (ns: '' 603 - nameserver ${ns} 604 - '')} 605 - EOF 606 - 607 - # Disable or enable IPv6. 608 - ${optionalString (!config.boot.isContainer) '' 609 - if [ -e /proc/sys/net/ipv6/conf/all/disable_ipv6 ]; then 610 - echo ${if cfg.enableIPv6 then "0" else "1"} > /proc/sys/net/ipv6/conf/all/disable_ipv6 611 - fi 612 - ''} 613 - 614 - # Set the default gateway. 615 - ${optionalString (cfg.defaultGateway != "") '' 616 - # FIXME: get rid of "|| true" (necessary to make it idempotent). 617 - ip route add default via "${cfg.defaultGateway}" ${ 618 - optionalString (cfg.defaultGatewayWindowSize != null) 619 - "window ${cfg.defaultGatewayWindowSize}"} || true 620 - ''} 621 - 622 - # Turn on forwarding if any interface has enabled proxy_arp. 623 - ${optionalString (any (i: i.proxyARP) interfaces) '' 624 - echo 1 > /proc/sys/net/ipv4/ip_forward 625 - ''} 626 - 627 - # Run any user-specified commands. 628 - ${cfg.localCommands} 629 - ''; 630 - }; 631 - 632 - # For each interface <foo>, create a job ‘<foo>-cfg.service" 633 - # that performs static configuration. It has a "wants" 634 - # dependency on ‘<foo>.service’, which is supposed to create 635 - # the interface and need not exist (i.e. for hardware 636 - # interfaces). It has a binds-to dependency on the actual 637 - # network device, so it only gets started after the interface 638 - # has appeared, and it's stopped when the interface 639 - # disappears. 640 - configureInterface = i: 641 - let 642 - ips = i.ip4 ++ optionals cfg.enableIPv6 i.ip6 643 - ++ optional (i.ipAddress != null) { 644 - address = i.ipAddress; 645 - prefixLength = i.prefixLength; 646 - } ++ optional (cfg.enableIPv6 && i.ipv6Address != null) { 647 - address = i.ipv6Address; 648 - prefixLength = i.ipv6PrefixLength; 649 - }; 650 - in 651 - nameValuePair "${i.name}-cfg" 652 - { description = "Configuration of ${i.name}"; 653 - wantedBy = [ "network-interfaces.target" ]; 654 - bindsTo = [ (subsystemDevice i.name) ]; 655 - after = [ (subsystemDevice i.name) ]; 656 - serviceConfig.Type = "oneshot"; 657 - serviceConfig.RemainAfterExit = true; 658 - path = [ pkgs.iproute pkgs.gawk ]; 659 - script = 660 - '' 661 - echo "bringing up interface..." 662 - ip link set "${i.name}" up 663 - '' 664 - + optionalString (i.macAddress != null) 665 - '' 666 - echo "setting MAC address to ${i.macAddress}..." 667 - ip link set "${i.name}" address "${i.macAddress}" 668 - '' 669 - + optionalString (i.mtu != null) 670 - '' 671 - echo "setting MTU to ${toString i.mtu}..." 672 - ip link set "${i.name}" mtu "${toString i.mtu}" 673 - '' 674 - 675 - # Ip Setup 676 - + 677 - '' 678 - curIps=$(ip -o a show dev "${i.name}" | awk '{print $4}') 679 - # Only do an add if it's necessary. This is 680 - # useful when the Nix store is accessed via this 681 - # interface (e.g. in a QEMU VM test). 682 - '' 683 - + flip concatMapStrings (ips) (ip: 684 - let 685 - address = "${ip.address}/${toString ip.prefixLength}"; 686 - in 687 - '' 688 - echo "checking ip ${address}..." 689 - if ! echo "$curIps" | grep "${address}" >/dev/null 2>&1; then 690 - if out=$(ip addr add "${address}" dev "${i.name}" 2>&1); then 691 - echo "added ip ${address}..." 692 - restart_network_setup=true 693 - elif ! echo "$out" | grep "File exists" >/dev/null 2>&1; then 694 - echo "failed to add ${address}" 695 - exit 1 696 - fi 697 - fi 698 - '') 699 - + optionalString (ips != [ ]) 700 - '' 701 - if [ restart_network_setup = true ]; then 702 - # Ensure that the default gateway remains set. 703 - # (Flushing this interface may have removed it.) 704 - ${config.systemd.package}/bin/systemctl try-restart --no-block network-setup.service 705 - fi 706 - ${config.systemd.package}/bin/systemctl start ip-up.target 707 - '' 708 - + optionalString i.proxyARP 709 - '' 710 - echo 1 > /proc/sys/net/ipv4/conf/${i.name}/proxy_arp 711 - '' 712 - + optionalString (i.proxyARP && cfg.enableIPv6) 713 - '' 714 - echo 1 > /proc/sys/net/ipv6/conf/${i.name}/proxy_ndp 715 - ''; 716 - preStop = 717 - '' 718 - echo "releasing configured ip's..." 719 - '' 720 - + flip concatMapStrings (ips) (ip: 721 - let 722 - address = "${ip.address}/${toString ip.prefixLength}"; 723 - in 724 - '' 725 - echo -n "Deleting ${address}..." 726 - ip addr del "${address}" dev "${i.name}" >/dev/null 2>&1 || echo -n " Failed" 727 - echo "" 728 - ''); 729 - }; 730 - 731 - createTunDevice = i: nameValuePair "${i.name}-netdev" 732 - { description = "Virtual Network Interface ${i.name}"; 733 - requires = [ "dev-net-tun.device" ]; 734 - after = [ "dev-net-tun.device" ]; 735 - wantedBy = [ "network.target" (subsystemDevice i.name) ]; 736 - path = [ pkgs.iproute ]; 737 - serviceConfig = { 738 - Type = "oneshot"; 739 - RemainAfterExit = true; 740 - }; 741 - script = '' 742 - ip tuntap add dev "${i.name}" \ 743 - ${optionalString (i.virtualType != null) "mode ${i.virtualType}"} \ 744 - user "${i.virtualOwner}" 745 - ''; 746 - postStop = '' 747 - ip link del ${i.name} 748 - ''; 749 - }; 750 - 751 - createBridgeDevice = n: v: nameValuePair "${n}-netdev" 752 - (let 753 - deps = map subsystemDevice v.interfaces; 754 - in 755 - { description = "Bridge Interface ${n}"; 756 - wantedBy = [ "network.target" (subsystemDevice n) ]; 757 - bindsTo = deps; 758 - after = deps; 759 - serviceConfig.Type = "oneshot"; 760 - serviceConfig.RemainAfterExit = true; 761 - path = [ pkgs.bridge_utils pkgs.iproute ]; 762 - script = 763 - '' 764 - # Remove Dead Interfaces 765 - ip link show "${n}" >/dev/null 2>&1 && ip link delete "${n}" 766 - 767 - brctl addbr "${n}" 768 - 769 - # Set bridge's hello time to 0 to avoid startup delays. 770 - brctl setfd "${n}" 0 771 - 772 - ${flip concatMapStrings v.interfaces (i: '' 773 - brctl addif "${n}" "${i}" 774 - ip link set "${i}" up 775 - ip addr flush dev "${i}" 776 - 777 - echo "bringing up network device ${n}..." 778 - ip link set "${n}" up 779 - '')} 780 - 781 - # !!! Should delete (brctl delif) any interfaces that 782 - # no longer belong to the bridge. 783 - ''; 784 - postStop = 785 - '' 786 - ip link set "${n}" down 787 - brctl delbr "${n}" 788 - ''; 789 - }); 790 - 791 - createBondDevice = n: v: nameValuePair "${n}-netdev" 792 - (let 793 - deps = map subsystemDevice v.interfaces; 794 - in 795 - { description = "Bond Interface ${n}"; 796 - wantedBy = [ "network.target" (subsystemDevice n) ]; 797 - bindsTo = deps; 798 - after = deps; 799 - before = [ "${n}-cfg.service" ]; 800 - serviceConfig.Type = "oneshot"; 801 - serviceConfig.RemainAfterExit = true; 802 - path = [ pkgs.ifenslave pkgs.iproute ]; 803 - script = '' 804 - ip link add name "${n}" type bond 805 - 806 - # !!! There must be a better way to wait for the interface 807 - while [ ! -d /sys/class/net/${n} ]; do sleep 0.1; done; 808 - 809 - # Ensure the link is down so that we can set options 810 - ip link set "${n}" down 811 - 812 - # Set the miimon and mode options 813 - ${optionalString (v.miimon != null) 814 - "echo \"${toString v.miimon}\" >/sys/class/net/${n}/bonding/miimon"} 815 - ${optionalString (v.mode != null) 816 - "echo \"${v.mode}\" >/sys/class/net/${n}/bonding/mode"} 817 - ${optionalString (v.lacp_rate != null) 818 - "echo \"${v.lacp_rate}\" >/sys/class/net/${n}/bonding/lacp_rate"} 819 - ${optionalString (v.xmit_hash_policy != null) 820 - "echo \"${v.xmit_hash_policy}\" >/sys/class/net/${n}/bonding/xmit_hash_policy"} 821 - 822 - # Bring up the bond and enslave the specified interfaces 823 - ip link set "${n}" up 824 - ${flip concatMapStrings v.interfaces (i: '' 825 - ifenslave "${n}" "${i}" 826 - '')} 827 - ''; 828 - postStop = '' 829 - ${flip concatMapStrings v.interfaces (i: '' 830 - ifenslave -d "${n}" "${i}" >/dev/null 2>&1 || true 831 - '')} 832 - ip link set "${n}" down >/dev/null 2>&1 || true 833 - ip link del "${n}" >/dev/null 2>&1 || true 834 - ''; 835 - }); 836 - 837 - createSitDevice = n: v: nameValuePair "${n}-netdev" 838 - (let 839 - deps = optional (v.dev != null) (subsystemDevice v.dev); 840 - in 841 - { description = "6-to-4 Tunnel Interface ${n}"; 842 - wantedBy = [ "network.target" (subsystemDevice n) ]; 843 - bindsTo = deps; 844 - after = deps; 845 - serviceConfig.Type = "oneshot"; 846 - serviceConfig.RemainAfterExit = true; 847 - path = [ pkgs.iproute ]; 848 - script = '' 849 - # Remove Dead Interfaces 850 - ip link show "${n}" >/dev/null 2>&1 && ip link delete "${n}" 851 - ip link add name "${n}" type sit \ 852 - ${optionalString (v.remote != null) "remote \"${v.remote}\""} \ 853 - ${optionalString (v.local != null) "local \"${v.local}\""} \ 854 - ${optionalString (v.ttl != null) "ttl ${toString v.ttl}"} \ 855 - ${optionalString (v.dev != null) "dev \"${v.dev}\""} 856 - ip link set "${n}" up 857 - ''; 858 - postStop = '' 859 - ip link delete "${n}" 860 - ''; 861 - }); 862 - 863 - createVlanDevice = n: v: nameValuePair "${n}-netdev" 864 - (let 865 - deps = [ (subsystemDevice v.interface) ]; 866 - in 867 - { description = "Vlan Interface ${n}"; 868 - wantedBy = [ "network.target" (subsystemDevice n) ]; 869 - bindsTo = deps; 870 - after = deps; 871 - serviceConfig.Type = "oneshot"; 872 - serviceConfig.RemainAfterExit = true; 873 - path = [ pkgs.iproute ]; 874 - script = '' 875 - # Remove Dead Interfaces 876 - ip link show "${n}" >/dev/null 2>&1 && ip link delete "${n}" 877 - ip link add link "${v.interface}" name "${n}" type vlan id "${toString v.id}" 878 - ip link set "${n}" up 879 - ''; 880 - postStop = '' 881 - ip link delete "${n}" 882 - ''; 883 - }); 884 - 885 - in listToAttrs ( 886 - map configureInterface interfaces ++ 887 - map createTunDevice (filter (i: i.virtual) interfaces)) 888 - // mapAttrs' createBridgeDevice cfg.bridges 889 - // mapAttrs' createBondDevice cfg.bonds 890 - // mapAttrs' createSitDevice cfg.sits 891 - // mapAttrs' createVlanDevice cfg.vlans 892 - // { "network-setup" = networkSetup; }; 893 - 894 622 # Set the host and domain names in the activation script. Don't 895 623 # clear it if it's not configured in the NixOS configuration, 896 624 # since it may have been set by dhcpcd in the meantime. ··· 899 627 hostname "${cfg.hostName}" 900 628 ''; 901 629 system.activationScripts.domain = 902 - optionalString (cfg.domain != "") '' 630 + optionalString (cfg.domain != null) '' 903 631 domainname "${cfg.domain}" 904 632 ''; 905 633 ··· 918 646 } 919 647 ]; 920 648 921 - services.udev.extraRules = 922 - '' 923 - KERNEL=="tun", TAG+="systemd" 924 - ''; 649 + environment.systemPackages = 650 + [ pkgs.host 651 + pkgs.iproute 652 + pkgs.iputils 653 + pkgs.nettools 654 + pkgs.wirelesstools 655 + pkgs.iw 656 + pkgs.rfkill 657 + pkgs.openresolv 658 + ]; 925 659 660 + systemd.services = { 661 + network-local-commands = { 662 + description = "Extra networking commands."; 663 + before = [ "network.target" ]; 664 + wantedBy = [ "network.target" ]; 665 + unitConfig.ConditionCapability = "CAP_NET_ADMIN"; 666 + path = [ pkgs.iproute ]; 667 + serviceConfig.Type = "oneshot"; 668 + serviceConfig.RemainAfterExit = true; 669 + script = '' 670 + # Run any user-specified commands. 671 + ${cfg.localCommands} 672 + ''; 673 + }; 674 + } // (listToAttrs (flip map interfaces (i: 675 + nameValuePair "network-link-${i.name}" 676 + { description = "Link configuration of ${i.name}"; 677 + wantedBy = [ "network-interfaces.target" ]; 678 + before = [ "network-interfaces.target" ]; 679 + bindsTo = [ (subsystemDevice i.name) ]; 680 + after = [ (subsystemDevice i.name) ]; 681 + path = [ pkgs.iproute ]; 682 + serviceConfig = { 683 + Type = "oneshot"; 684 + RemainAfterExit = true; 685 + }; 686 + script = 687 + '' 688 + echo "Configuring link..." 689 + '' + optionalString (i.macAddress != null) '' 690 + echo "setting MAC address to ${i.macAddress}..." 691 + ip link set "${i.name}" address "${i.macAddress}" 692 + '' + optionalString (i.mtu != null) '' 693 + echo "setting MTU to ${toString i.mtu}..." 694 + ip link set "${i.name}" mtu "${toString i.mtu}" 695 + ''; 696 + }))); 926 697 }; 927 698 928 699 }
+8
nixos/release-combined.nix
··· 66 66 (all nixos.tests.misc) 67 67 (all nixos.tests.nat.firewall) 68 68 (all nixos.tests.nat.standalone) 69 + (all nixos.tests.networking.scripted.static) 70 + (all nixos.tests.networking.scripted.dhcpSimple) 71 + (all nixos.tests.networking.scripted.dhcpOneIf) 72 + (all nixos.tests.networking.scripted.bond) 73 + (all nixos.tests.networking.scripted.bridge) 74 + (all nixos.tests.networking.scripted.macvlan) 75 + (all nixos.tests.networking.scripted.sit) 76 + (all nixos.tests.networking.scripted.vlan) 69 77 (all nixos.tests.nfs3) 70 78 (all nixos.tests.openssh) 71 79 (all nixos.tests.printing)
+16
nixos/release.nix
··· 269 269 tests.mysqlReplication = callTest tests/mysql-replication.nix {}; 270 270 tests.nat.firewall = callTest tests/nat.nix { withFirewall = true; }; 271 271 tests.nat.standalone = callTest tests/nat.nix { withFirewall = false; }; 272 + tests.networking.networkd.static = callTest tests/networking.nix { networkd = true; test = "static"; }; 273 + tests.networking.networkd.dhcpSimple = callTest tests/networking.nix { networkd = true; test = "dhcpSimple"; }; 274 + tests.networking.networkd.dhcpOneIf = callTest tests/networking.nix { networkd = true; test = "dhcpOneIf"; }; 275 + tests.networking.networkd.bond = callTest tests/networking.nix { networkd = true; test = "bond"; }; 276 + tests.networking.networkd.bridge = callTest tests/networking.nix { networkd = true; test = "bridge"; }; 277 + tests.networking.networkd.macvlan = callTest tests/networking.nix { networkd = true; test = "macvlan"; }; 278 + tests.networking.networkd.sit = callTest tests/networking.nix { networkd = true; test = "sit"; }; 279 + tests.networking.networkd.vlan = callTest tests/networking.nix { networkd = true; test = "vlan"; }; 280 + tests.networking.scripted.static = callTest tests/networking.nix { networkd = false; test = "static"; }; 281 + tests.networking.scripted.dhcpSimple = callTest tests/networking.nix { networkd = false; test = "dhcpSimple"; }; 282 + tests.networking.scripted.dhcpOneIf = callTest tests/networking.nix { networkd = false; test = "dhcpOneIf"; }; 283 + tests.networking.scripted.bond = callTest tests/networking.nix { networkd = false; test = "bond"; }; 284 + tests.networking.scripted.bridge = callTest tests/networking.nix { networkd = false; test = "bridge"; }; 285 + tests.networking.scripted.macvlan = callTest tests/networking.nix { networkd = false; test = "macvlan"; }; 286 + tests.networking.scripted.sit = callTest tests/networking.nix { networkd = false; test = "sit"; }; 287 + tests.networking.scripted.vlan = callTest tests/networking.nix { networkd = false; test = "vlan"; }; 272 288 tests.nfs3 = callTest tests/nfs.nix { version = 3; }; 273 289 tests.nsd = callTest tests/nsd.nix {}; 274 290 tests.openssh = callTest tests/openssh.nix {};
+365
nixos/tests/networking.nix
··· 1 + import ./make-test.nix ({ networkd, test, ... }: 2 + let 3 + router = { config, pkgs, ... }: 4 + with pkgs.lib; 5 + let 6 + vlanIfs = range 1 (length config.virtualisation.vlans); 7 + in { 8 + virtualisation.vlans = [ 1 2 3 ]; 9 + networking = { 10 + useDHCP = false; 11 + useNetworkd = networkd; 12 + firewall.allowPing = true; 13 + interfaces = mkOverride 0 (listToAttrs (flip map vlanIfs (n: 14 + nameValuePair "eth${toString n}" { 15 + ipAddress = "192.168.${toString n}.1"; 16 + prefixLength = 24; 17 + }))); 18 + }; 19 + services.dhcpd = { 20 + enable = true; 21 + interfaces = map (n: "eth${toString n}") vlanIfs; 22 + extraConfig = '' 23 + option subnet-mask 255.255.255.0; 24 + '' + flip concatMapStrings vlanIfs (n: '' 25 + subnet 192.168.${toString n}.0 netmask 255.255.255.0 { 26 + option broadcast-address 192.168.${toString n}.255; 27 + option routers 192.168.${toString n}.1; 28 + range 192.168.${toString n}.2 192.168.${toString n}.254; 29 + } 30 + ''); 31 + }; 32 + }; 33 + testCases = { 34 + static = { 35 + name = "Static"; 36 + nodes.router = router; 37 + nodes.client = { config, pkgs, ... }: with pkgs.lib; { 38 + virtualisation.vlans = [ 1 2 ]; 39 + networking = { 40 + useNetworkd = networkd; 41 + firewall.allowPing = true; 42 + useDHCP = false; 43 + defaultGateway = "192.168.1.1"; 44 + interfaces.eth1.ip4 = mkOverride 0 [ 45 + { address = "192.168.1.2"; prefixLength = 24; } 46 + { address = "192.168.1.3"; prefixLength = 32; } 47 + { address = "192.168.1.10"; prefixLength = 32; } 48 + ]; 49 + interfaces.eth2.ip4 = mkOverride 0 [ 50 + { address = "192.168.2.2"; prefixLength = 24; } 51 + ]; 52 + }; 53 + }; 54 + testScript = { nodes, ... }: 55 + '' 56 + startAll; 57 + 58 + $client->waitForUnit("network.target"); 59 + $router->waitForUnit("network.target"); 60 + 61 + # Make sure dhcpcd is not started 62 + $client->fail("systemctl status dhcpcd.service"); 63 + 64 + # Test vlan 1 65 + $client->succeed("ping -c 1 192.168.1.1"); 66 + $client->succeed("ping -c 1 192.168.1.2"); 67 + $client->succeed("ping -c 1 192.168.1.3"); 68 + $client->succeed("ping -c 1 192.168.1.10"); 69 + 70 + $router->succeed("ping -c 1 192.168.1.1"); 71 + $router->succeed("ping -c 1 192.168.1.2"); 72 + $router->succeed("ping -c 1 192.168.1.3"); 73 + $router->succeed("ping -c 1 192.168.1.10"); 74 + 75 + # Test vlan 2 76 + $client->succeed("ping -c 1 192.168.2.1"); 77 + $client->succeed("ping -c 1 192.168.2.2"); 78 + 79 + $router->succeed("ping -c 1 192.168.2.1"); 80 + $router->succeed("ping -c 1 192.168.2.2"); 81 + 82 + # Test default gateway 83 + $router->succeed("ping -c 1 192.168.3.1"); 84 + $client->succeed("ping -c 1 192.168.3.1"); 85 + ''; 86 + }; 87 + dhcpSimple = { 88 + name = "SimpleDHCP"; 89 + nodes.router = router; 90 + nodes.client = { config, pkgs, ... }: with pkgs.lib; { 91 + virtualisation.vlans = [ 1 2 ]; 92 + networking = { 93 + useNetworkd = networkd; 94 + firewall.allowPing = true; 95 + useDHCP = true; 96 + interfaces.eth1.ip4 = mkOverride 0 [ ]; 97 + interfaces.eth2.ip4 = mkOverride 0 [ ]; 98 + }; 99 + }; 100 + testScript = { nodes, ... }: 101 + '' 102 + startAll; 103 + 104 + $client->waitForUnit("network.target"); 105 + $router->waitForUnit("network.target"); 106 + $client->waitForUnit("dhcpcd.service"); 107 + 108 + # Wait until we have an ip address on each interface 109 + $client->succeed("while ! ip addr show dev eth1 | grep '192.168.1'; do true; done"); 110 + $client->succeed("while ! ip addr show dev eth2 | grep '192.168.2'; do true; done"); 111 + 112 + # Test vlan 1 113 + $client->succeed("ping -c 1 192.168.1.1"); 114 + $client->succeed("ping -c 1 192.168.1.2"); 115 + 116 + $router->succeed("ping -c 1 192.168.1.1"); 117 + $router->succeed("ping -c 1 192.168.1.2"); 118 + 119 + # Test vlan 2 120 + $client->succeed("ping -c 1 192.168.2.1"); 121 + $client->succeed("ping -c 1 192.168.2.2"); 122 + 123 + $router->succeed("ping -c 1 192.168.2.1"); 124 + $router->succeed("ping -c 1 192.168.2.2"); 125 + ''; 126 + }; 127 + dhcpOneIf = { 128 + name = "OneInterfaceDHCP"; 129 + nodes.router = router; 130 + nodes.client = { config, pkgs, ... }: with pkgs.lib; { 131 + virtualisation.vlans = [ 1 2 ]; 132 + networking = { 133 + useNetworkd = networkd; 134 + firewall.allowPing = true; 135 + useDHCP = false; 136 + interfaces.eth1 = { 137 + ip4 = mkOverride 0 [ ]; 138 + useDHCP = true; 139 + }; 140 + interfaces.eth2.ip4 = mkOverride 0 [ ]; 141 + }; 142 + }; 143 + testScript = { nodes, ... }: 144 + '' 145 + startAll; 146 + 147 + $client->waitForUnit("network.target"); 148 + $router->waitForUnit("network.target"); 149 + $client->waitForUnit("dhcpcd.service"); 150 + 151 + # Wait until we have an ip address on each interface 152 + $client->succeed("while ! ip addr show dev eth1 | grep '192.168.1'; do true; done"); 153 + 154 + # Test vlan 1 155 + $client->succeed("ping -c 1 192.168.1.1"); 156 + $client->succeed("ping -c 1 192.168.1.2"); 157 + 158 + $router->succeed("ping -c 1 192.168.1.1"); 159 + $router->succeed("ping -c 1 192.168.1.2"); 160 + 161 + # Test vlan 2 162 + $client->succeed("ping -c 1 192.168.2.1"); 163 + $client->fail("ping -c 1 192.168.2.2"); 164 + 165 + $router->succeed("ping -c 1 192.168.2.1"); 166 + $router->fail("ping -c 1 192.168.2.2"); 167 + ''; 168 + }; 169 + bond = let 170 + node = address: { config, pkgs, ... }: with pkgs.lib; { 171 + virtualisation.vlans = [ 1 2 ]; 172 + networking = { 173 + useNetworkd = networkd; 174 + firewall.allowPing = true; 175 + useDHCP = false; 176 + bonds.bond = { 177 + mode = "balance-rr"; 178 + interfaces = [ "eth1" "eth2" ]; 179 + }; 180 + interfaces.bond.ip4 = mkOverride 0 181 + [ { inherit address; prefixLength = 30; } ]; 182 + }; 183 + }; 184 + in { 185 + name = "Bond"; 186 + nodes.client1 = node "192.168.1.1"; 187 + nodes.client2 = node "192.168.1.2"; 188 + testScript = { nodes, ... }: 189 + '' 190 + startAll; 191 + 192 + $client1->waitForUnit("network.target"); 193 + $client2->waitForUnit("network.target"); 194 + 195 + # Test bonding 196 + $client1->succeed("ping -c 2 192.168.1.1"); 197 + $client1->succeed("ping -c 2 192.168.1.2"); 198 + 199 + $client2->succeed("ping -c 2 192.168.1.1"); 200 + $client2->succeed("ping -c 2 192.168.1.2"); 201 + ''; 202 + }; 203 + bridge = let 204 + node = { address, vlan }: { config, pkgs, ... }: with pkgs.lib; { 205 + virtualisation.vlans = [ vlan ]; 206 + networking = { 207 + useNetworkd = networkd; 208 + firewall.allowPing = true; 209 + useDHCP = false; 210 + interfaces.eth1.ip4 = mkOverride 0 211 + [ { inherit address; prefixLength = 24; } ]; 212 + }; 213 + }; 214 + in { 215 + name = "Bridge"; 216 + nodes.client1 = node { address = "192.168.1.2"; vlan = 1; }; 217 + nodes.client2 = node { address = "192.168.1.3"; vlan = 2; }; 218 + nodes.router = { config, pkgs, ... }: with pkgs.lib; { 219 + virtualisation.vlans = [ 1 2 ]; 220 + networking = { 221 + useNetworkd = networkd; 222 + firewall.allowPing = true; 223 + useDHCP = false; 224 + bridges.bridge.interfaces = [ "eth1" "eth2" ]; 225 + interfaces.eth1.ip4 = mkOverride 0 [ ]; 226 + interfaces.eth2.ip4 = mkOverride 0 [ ]; 227 + interfaces.bridge.ip4 = mkOverride 0 228 + [ { address = "192.168.1.1"; prefixLength = 24; } ]; 229 + }; 230 + }; 231 + testScript = { nodes, ... }: 232 + '' 233 + startAll; 234 + 235 + $client1->waitForUnit("network.target"); 236 + $client2->waitForUnit("network.target"); 237 + $router->waitForUnit("network.target"); 238 + 239 + # Test bridging 240 + $client1->succeed("ping -c 1 192.168.1.1"); 241 + $client1->succeed("ping -c 1 192.168.1.2"); 242 + $client1->succeed("ping -c 1 192.168.1.3"); 243 + 244 + $client2->succeed("ping -c 1 192.168.1.1"); 245 + $client2->succeed("ping -c 1 192.168.1.2"); 246 + $client2->succeed("ping -c 1 192.168.1.3"); 247 + 248 + $router->succeed("ping -c 1 192.168.1.1"); 249 + $router->succeed("ping -c 1 192.168.1.2"); 250 + $router->succeed("ping -c 1 192.168.1.3"); 251 + ''; 252 + }; 253 + macvlan = { 254 + name = "MACVLAN"; 255 + nodes.router = router; 256 + nodes.client = { config, pkgs, ... }: with pkgs.lib; { 257 + virtualisation.vlans = [ 1 ]; 258 + networking = { 259 + useNetworkd = networkd; 260 + firewall.allowPing = true; 261 + useDHCP = true; 262 + macvlans.macvlan.interface = "eth1"; 263 + interfaces.eth1.ip4 = mkOverride 0 [ ]; 264 + }; 265 + }; 266 + testScript = { nodes, ... }: 267 + '' 268 + startAll; 269 + 270 + $client->waitForUnit("network.target"); 271 + $router->waitForUnit("network.target"); 272 + $client->waitForUnit("dhcpcd.service"); 273 + 274 + # Wait until we have an ip address on each interface 275 + $client->succeed("while ! ip addr show dev eth1 | grep '192.168.1'; do true; done"); 276 + $client->succeed("while ! ip addr show dev macvlan | grep '192.168.1'; do true; done"); 277 + 278 + # Test macvlan 279 + $client->succeed("ping -c 1 192.168.1.1"); 280 + $client->succeed("ping -c 1 192.168.1.2"); 281 + $client->succeed("ping -c 1 192.168.1.3"); 282 + 283 + $router->succeed("ping -c 1 192.168.1.1"); 284 + $router->succeed("ping -c 1 192.168.1.2"); 285 + $router->succeed("ping -c 1 192.168.1.3"); 286 + ''; 287 + }; 288 + sit = let 289 + node = { address4, remote, address6 }: { config, pkgs, ... }: with pkgs.lib; { 290 + virtualisation.vlans = [ 1 ]; 291 + networking = { 292 + useNetworkd = networkd; 293 + firewall.enable = false; 294 + useDHCP = false; 295 + sits.sit = { 296 + inherit remote; 297 + local = address4; 298 + dev = "eth1"; 299 + }; 300 + interfaces.eth1.ip4 = mkOverride 0 301 + [ { address = address4; prefixLength = 24; } ]; 302 + interfaces.sit.ip6 = mkOverride 0 303 + [ { address = address6; prefixLength = 64; } ]; 304 + }; 305 + }; 306 + in { 307 + name = "Sit"; 308 + nodes.client1 = node { address4 = "192.168.1.1"; remote = "192.168.1.2"; address6 = "fc00::1"; }; 309 + nodes.client2 = node { address4 = "192.168.1.2"; remote = "192.168.1.1"; address6 = "fc00::2"; }; 310 + testScript = { nodes, ... }: 311 + '' 312 + startAll; 313 + 314 + $client1->waitForUnit("network.target"); 315 + $client2->waitForUnit("network.target"); 316 + 317 + $client1->succeed("ip addr >&2"); 318 + $client2->succeed("ip addr >&2"); 319 + 320 + # Test ipv6 321 + $client1->succeed("ping6 -c 1 fc00::1"); 322 + $client1->succeed("ping6 -c 1 fc00::2"); 323 + 324 + $client2->succeed("ping6 -c 1 fc00::1"); 325 + $client2->succeed("ping6 -c 1 fc00::2"); 326 + ''; 327 + }; 328 + vlan = let 329 + node = address: { config, pkgs, ... }: with pkgs.lib; { 330 + #virtualisation.vlans = [ 1 ]; 331 + networking = { 332 + useNetworkd = networkd; 333 + firewall.allowPing = true; 334 + useDHCP = false; 335 + vlans.vlan = { 336 + id = 1; 337 + interface = "eth0"; 338 + }; 339 + interfaces.eth0.ip4 = mkOverride 0 [ ]; 340 + interfaces.eth1.ip4 = mkOverride 0 [ ]; 341 + interfaces.vlan.ip4 = mkOverride 0 342 + [ { inherit address; prefixLength = 24; } ]; 343 + }; 344 + }; 345 + in { 346 + name = "vlan"; 347 + nodes.client1 = node "192.168.1.1"; 348 + nodes.client2 = node "192.168.1.2"; 349 + testScript = { nodes, ... }: 350 + '' 351 + startAll; 352 + 353 + $client1->waitForUnit("network.target"); 354 + $client2->waitForUnit("network.target"); 355 + 356 + # Test vlan is setup 357 + $client1->succeed("ip addr show dev vlan >&2"); 358 + $client2->succeed("ip addr show dev vlan >&2"); 359 + ''; 360 + }; 361 + }; 362 + case = testCases.${test}; 363 + in case // { 364 + name = "${case.name}-Networking-${if networkd then "Networkd" else "Scripted"}"; 365 + })