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 dnsmasq_conf=/etc/dnsmasq-conf.conf 85 dnsmasq_resolv=/etc/dnsmasq-resolv.conf 86 ''; 87 - }; 88 89 # The ‘ip-up’ target is started when we have IP connectivity. So 90 # services that depend on IP connectivity (like ntpd) should be 91 # pulled in by this target. 92 systemd.targets.ip-up.description = "Services Requiring IP Connectivity"; 93 94 }; 95
··· 84 dnsmasq_conf=/etc/dnsmasq-conf.conf 85 dnsmasq_resolv=/etc/dnsmasq-resolv.conf 86 ''; 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 + )); 95 96 # The ‘ip-up’ target is started when we have IP connectivity. So 97 # services that depend on IP connectivity (like ntpd) should be 98 # pulled in by this target. 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 + ''; 120 121 }; 122
+2
nixos/modules/module-list.nix
··· 388 ./tasks/kbd.nix 389 ./tasks/lvm.nix 390 ./tasks/network-interfaces.nix 391 ./tasks/scsi-link-power-management.nix 392 ./tasks/swraid.nix 393 ./tasks/trackpoint.nix
··· 388 ./tasks/kbd.nix 389 ./tasks/lvm.nix 390 ./tasks/network-interfaces.nix 391 + ./tasks/network-interfaces-systemd.nix 392 + ./tasks/network-interfaces-scripted.nix 393 ./tasks/scsi-link-power-management.nix 394 ./tasks/swraid.nix 395 ./tasks/trackpoint.nix
+2 -2
nixos/modules/services/networking/chrony.nix
··· 100 jobs.chronyd = 101 { description = "chrony daemon"; 102 103 - wantedBy = [ "ip-up.target" ]; 104 - partOf = [ "ip-up.target" ]; 105 106 path = [ chrony ]; 107
··· 100 jobs.chronyd = 101 { description = "chrony daemon"; 102 103 + wantedBy = [ "multi-user.target" ]; 104 + after = [ "network.target" ]; 105 106 path = [ chrony ]; 107
+17 -3
nixos/modules/services/networking/dhcpcd.nix
··· 8 9 cfg = config.networking.dhcpcd; 10 11 # Don't start dhcpcd on explicitly configured interfaces or on 12 # interfaces that are part of a bridge, bond or sit device. 13 ignoredInterfaces = 14 - map (i: i.name) (filter (i: i.ip4 != [ ] || i.ipAddress != null) (attrValues config.networking.interfaces)) 15 ++ mapAttrsToList (i: _: i) config.networking.sits 16 ++ concatLists (attrValues (mapAttrs (n: v: v.interfaces) config.networking.bridges)) 17 ++ concatLists (attrValues (mapAttrs (n: v: v.interfaces) config.networking.bonds)) 18 ++ config.networking.dhcpcd.denyInterfaces; 19 20 # Config file adapted from the one that ships with dhcpcd. 21 dhcpcdConf = pkgs.writeText "dhcpcd.conf" ··· 41 denyinterfaces ${toString ignoredInterfaces} lo peth* vif* tap* tun* virbr* vnet* vboxnet* sit* 42 43 # Use the list of allowed interfaces if specified 44 - ${optionalString (cfg.allowInterfaces != null) "allowinterfaces ${toString cfg.allowInterfaces}"} 45 46 ${cfg.extraConfig} 47 ''; ··· 132 133 ###### implementation 134 135 - config = mkIf config.networking.useDHCP { 136 137 systemd.services.dhcpcd = 138 { description = "DHCP Client";
··· 8 9 cfg = config.networking.dhcpcd; 10 11 + interfaces = attrValues config.networking.interfaces; 12 + 13 + enableDHCP = config.networking.useDHCP || any (i: i.useDHCP == true) interfaces; 14 + 15 # Don't start dhcpcd on explicitly configured interfaces or on 16 # interfaces that are part of a bridge, bond or sit device. 17 ignoredInterfaces = 18 + map (i: i.name) (filter (i: if i.useDHCP != null then !i.useDHCP else i.ip4 != [ ] || i.ipAddress != null) interfaces) 19 ++ mapAttrsToList (i: _: i) config.networking.sits 20 ++ concatLists (attrValues (mapAttrs (n: v: v.interfaces) config.networking.bridges)) 21 ++ concatLists (attrValues (mapAttrs (n: v: v.interfaces) config.networking.bonds)) 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); 33 34 # Config file adapted from the one that ships with dhcpcd. 35 dhcpcdConf = pkgs.writeText "dhcpcd.conf" ··· 55 denyinterfaces ${toString ignoredInterfaces} lo peth* vif* tap* tun* virbr* vnet* vboxnet* sit* 56 57 # Use the list of allowed interfaces if specified 58 + ${optionalString (allowInterfaces != null) "allowinterfaces ${toString allowInterfaces}"} 59 60 ${cfg.extraConfig} 61 ''; ··· 146 147 ###### implementation 148 149 + config = mkIf enableDHCP { 150 151 systemd.services.dhcpcd = 152 { description = "DHCP Client";
+1 -1
nixos/modules/services/networking/dnsmasq.nix
··· 82 83 systemd.services.dnsmasq = { 84 description = "dnsmasq daemon"; 85 - after = [ "network.target" ]; 86 wantedBy = [ "multi-user.target" ]; 87 path = [ dnsmasq ]; 88 preStart = ''
··· 82 83 systemd.services.dnsmasq = { 84 description = "dnsmasq daemon"; 85 + after = [ "network.target" "systemd-resolved.conf" ]; 86 wantedBy = [ "multi-user.target" ]; 87 path = [ dnsmasq ]; 88 preStart = ''
+1 -2
nixos/modules/services/networking/gogoclient.nix
··· 76 exec ${pkgs.gogoclient}/bin/gogoc -y -f /var/lib/gogoc/gogoc.conf 77 ''; 78 } // optionalAttrs cfg.autorun { 79 - wantedBy = [ "ip-up.target" ]; 80 - partOf = [ "ip-up.target" ]; 81 }; 82 83 };
··· 76 exec ${pkgs.gogoclient}/bin/gogoc -y -f /var/lib/gogoc/gogoc.conf 77 ''; 78 } // optionalAttrs cfg.autorun { 79 + wantedBy = [ "multi-user.target" ]; 80 }; 81 82 };
+1
nixos/modules/services/networking/networkmanager.nix
··· 52 #!/bin/sh 53 if test "$2" = "up"; then 54 ${config.systemd.package}/bin/systemctl start ip-up.target 55 fi 56 ''; 57
··· 52 #!/bin/sh 53 if test "$2" = "up"; then 54 ${config.systemd.package}/bin/systemctl start ip-up.target 55 + ${config.systemd.package}/bin/systemctl start network-online.target 56 fi 57 ''; 58
+1 -2
nixos/modules/services/networking/ntpd.nix
··· 77 jobs.ntpd = 78 { description = "NTP Daemon"; 79 80 - wantedBy = [ "ip-up.target" ]; 81 - partOf = [ "ip-up.target" ]; 82 83 path = [ ntp ]; 84
··· 77 jobs.ntpd = 78 { description = "NTP Daemon"; 79 80 + wantedBy = [ "multi-user.target" ]; 81 82 path = [ ntp ]; 83
+1 -2
nixos/modules/services/networking/openntpd.nix
··· 41 42 systemd.services.openntpd = { 43 description = "OpenNTP Server"; 44 - wantedBy = [ "ip-up.target" ]; 45 - partOf = [ "ip-up.target" ]; 46 serviceConfig.ExecStart = "${package}/sbin/ntpd -d -f ${cfgFile}"; 47 }; 48 };
··· 41 42 systemd.services.openntpd = { 43 description = "OpenNTP Server"; 44 + wantedBy = [ "multi-user.target" ]; 45 serviceConfig.ExecStart = "${package}/sbin/ntpd -d -f ${cfgFile}"; 46 }; 47 };
-2
nixos/modules/system/boot/stage-2-init.sh
··· 141 # Use /etc/resolv.conf supplied by systemd-nspawn, if applicable. 142 if [ -n "@useHostResolvConf@" -a -e /etc/resolv.conf ]; then 143 cat /etc/resolv.conf | resolvconf -m 1000 -a host 144 - else 145 - touch /etc/resolv.conf 146 fi 147 148
··· 141 # Use /etc/resolv.conf supplied by systemd-nspawn, if applicable. 142 if [ -n "@useHostResolvConf@" -a -e /etc/resolv.conf ]; then 143 cat /etc/resolv.conf | resolvconf -m 1000 -a host 144 fi 145 146
+528 -9
nixos/modules/system/boot/systemd-unit-options.nix
··· 4 5 let 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; 16 17 unitOption = mkOptionType { 18 name = "systemd option"; ··· 137 description = '' 138 If the specified units are started, then this unit is stopped 139 and vice versa. 140 ''; 141 }; 142 ··· 440 }; 441 442 targetOptions = commonUnitOptions; 443 444 }
··· 4 5 let 6 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 + ]; 185 186 unitOption = mkOptionType { 187 name = "systemd option"; ··· 306 description = '' 307 If the specified units are started, then this unit is stopped 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. 318 ''; 319 }; 320 ··· 618 }; 619 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 + }; 962 963 }
+250 -4
nixos/modules/system/boot/systemd.nix
··· 96 "systemd-modules-load.service" 97 "kmod-static-nodes.service" 98 99 # Filesystems. 100 "systemd-fsck@.service" 101 "systemd-fsck-root.service" ··· 212 { PartOf = toString config.partOf; } 213 // optionalAttrs (config.conflicts != []) 214 { Conflicts = toString config.conflicts; } 215 // optionalAttrs (config.restartTriggers != []) 216 { X-Restart-Triggers = toString config.restartTriggers; } 217 // optionalAttrs (config.description != "") { ··· 292 }; 293 }; 294 295 toOption = x: 296 if x == true then "true" 297 else if x == false then "false" ··· 384 ''; 385 }; 386 387 generateUnits = type: units: upstreamUnits: upstreamWants: 388 pkgs.runCommand "${type}-units" { preferLocalBuild = true; } '' 389 mkdir -p $out ··· 468 mkdir -p $out/getty.target.wants/ 469 ln -s ../autovt@tty1.service $out/getty.target.wants/ 470 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/ 473 ''} 474 ''; # */ 475 ··· 562 ''; 563 }; 564 565 systemd.defaultUnit = mkOption { 566 default = "multi-user.target"; 567 type = types.str; ··· 645 ''; 646 }; 647 648 systemd.tmpfiles.rules = mkOption { 649 type = types.listOf types.str; 650 default = []; ··· 701 702 ###### implementation 703 704 - config = { 705 706 warnings = concatLists (mapAttrsToList (name: service: 707 optional (service.serviceConfig.Type or "" == "oneshot" && service.serviceConfig.Restart or "no" != "no") ··· 714 environment.etc."systemd/system".source = 715 generateUnits "system" cfg.units upstreamSystemUnits upstreamSystemWants; 716 717 environment.etc."systemd/user".source = 718 generateUnits "user" cfg.user.units upstreamUserUnits []; 719 ··· 766 unitConfig.X-StopOnReconfiguration = true; 767 }; 768 769 systemd.units = 770 mapAttrs' (n: v: nameValuePair "${n}.target" (targetToUnit n v)) cfg.targets 771 // mapAttrs' (n: v: nameValuePair "${n}.service" (serviceToUnit n v)) cfg.services ··· 779 (v: let n = escapeSystemdPath v.where; 780 in nameValuePair "${n}.automount" (automountToUnit n v)) cfg.automounts); 781 782 systemd.user.units = 783 mapAttrs' (n: v: nameValuePair "${n}.service" (serviceToUnit n v)) cfg.user.services 784 // mapAttrs' (n: v: nameValuePair "${n}.socket" (socketToUnit n v)) cfg.user.sockets; ··· 833 systemd.services.systemd-remount-fs.restartIfChanged = false; 834 systemd.services.systemd-journal-flush.restartIfChanged = false; 835 836 - }; 837 }
··· 96 "systemd-modules-load.service" 97 "kmod-static-nodes.service" 98 99 + # Networking 100 + "systemd-networkd.service" 101 + "systemd-networkd-wait-online.service" 102 + "systemd-resolved.service" 103 + "systemd-timesyncd.service" 104 + 105 # Filesystems. 106 "systemd-fsck@.service" 107 "systemd-fsck-root.service" ··· 218 { PartOf = toString config.partOf; } 219 // optionalAttrs (config.conflicts != []) 220 { Conflicts = toString config.conflicts; } 221 + // optionalAttrs (config.requisite != []) 222 + { Requisite = toString config.requisite; } 223 // optionalAttrs (config.restartTriggers != []) 224 { X-Restart-Triggers = toString config.restartTriggers; } 225 // optionalAttrs (config.description != "") { ··· 300 }; 301 }; 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 + 316 toOption = x: 317 if x == true then "true" 318 else if x == false then "false" ··· 405 ''; 406 }; 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 + 505 generateUnits = type: units: upstreamUnits: upstreamWants: 506 pkgs.runCommand "${type}-units" { preferLocalBuild = true; } '' 507 mkdir -p $out ··· 586 mkdir -p $out/getty.target.wants/ 587 ln -s ../autovt@tty1.service $out/getty.target.wants/ 588 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/ 592 ''} 593 ''; # */ 594 ··· 681 ''; 682 }; 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 + 725 systemd.defaultUnit = mkOption { 726 default = "multi-user.target"; 727 type = types.str; ··· 805 ''; 806 }; 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 + 824 systemd.tmpfiles.rules = mkOption { 825 type = types.listOf types.str; 826 default = []; ··· 877 878 ###### implementation 879 880 + config = mkMerge [ { 881 882 warnings = concatLists (mapAttrsToList (name: service: 883 optional (service.serviceConfig.Type or "" == "oneshot" && service.serviceConfig.Restart or "no" != "no") ··· 890 environment.etc."systemd/system".source = 891 generateUnits "system" cfg.units upstreamSystemUnits upstreamSystemWants; 892 893 + environment.etc."systemd/network".source = 894 + generateUnits "network" cfg.network.units [] []; 895 + 896 environment.etc."systemd/user".source = 897 generateUnits "user" cfg.user.units upstreamUserUnits []; 898 ··· 945 unitConfig.X-StopOnReconfiguration = true; 946 }; 947 948 + systemd.targets.network-online.after = [ "ip-up.target" ]; 949 + 950 systemd.units = 951 mapAttrs' (n: v: nameValuePair "${n}.target" (targetToUnit n v)) cfg.targets 952 // mapAttrs' (n: v: nameValuePair "${n}.service" (serviceToUnit n v)) cfg.services ··· 960 (v: let n = escapeSystemdPath v.where; 961 in nameValuePair "${n}.automount" (automountToUnit n v)) cfg.automounts); 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 + 968 systemd.user.units = 969 mapAttrs' (n: v: nameValuePair "${n}.service" (serviceToUnit n v)) cfg.user.services 970 // mapAttrs' (n: v: nameValuePair "${n}.socket" (socketToUnit n v)) cfg.user.sockets; ··· 1019 systemd.services.systemd-remount-fs.restartIfChanged = false; 1020 systemd.services.systemd-journal-flush.restartIfChanged = false; 1021 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 + ]; 1083 }
+2 -4
nixos/modules/tasks/filesystems/nfs.nix
··· 73 74 path = [ pkgs.nfsUtils pkgs.sysvtools pkgs.utillinux ]; 75 76 - wantedBy = [ "network-online.target" "multi-user.target" ]; 77 - before = [ "network-online.target" ]; 78 requires = [ "basic.target" "rpcbind.service" ]; 79 after = [ "basic.target" "rpcbind.service" "network.target" ]; 80 ··· 100 101 path = [ pkgs.sysvtools pkgs.utillinux ]; 102 103 - wantedBy = [ "network-online.target" "multi-user.target" ]; 104 - before = [ "network-online.target" ]; 105 requires = [ "rpcbind.service" ]; 106 after = [ "rpcbind.service" ]; 107
··· 73 74 path = [ pkgs.nfsUtils pkgs.sysvtools pkgs.utillinux ]; 75 76 + wantedBy = [ "multi-user.target" ]; 77 requires = [ "basic.target" "rpcbind.service" ]; 78 after = [ "basic.target" "rpcbind.service" "network.target" ]; 79 ··· 99 100 path = [ pkgs.sysvtools pkgs.utillinux ]; 101 102 + wantedBy = [ "multi-user.target" ]; 103 requires = [ "rpcbind.service" ]; 104 after = [ "rpcbind.service" ]; 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 description = "Name of the interface."; 46 }; 47 48 ip4 = mkOption { 49 default = [ ]; 50 example = [ ··· 203 204 networking.hostName = mkOption { 205 default = "nixos"; 206 description = '' 207 The name of the machine. Leave it empty if you want to obtain 208 it from a DHCP server (if using DHCP). ··· 225 226 networking.enableIPv6 = mkOption { 227 default = true; 228 description = '' 229 Whether to enable support for IPv6. 230 ''; 231 }; 232 233 networking.defaultGateway = mkOption { 234 - default = ""; 235 example = "131.211.84.1"; 236 description = '' 237 The default gateway. It can be left empty if it is auto-detected through DHCP. 238 ''; ··· 266 }; 267 268 networking.domain = mkOption { 269 - default = ""; 270 example = "home"; 271 description = '' 272 The domain. It can be left empty if it is auto-detected through DHCP. 273 ''; ··· 414 }; 415 }; 416 417 networking.sits = mkOption { 418 type = types.attrsOf types.optionSet; 419 default = { }; ··· 523 ''; 524 }; 525 526 }; 527 528 ··· 552 # from being created. 553 optionalString hasBonds "options bonding max_bonds=0"; 554 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; 568 569 security.setuidPrograms = [ "ping" "ping6" ]; 570 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 # Set the host and domain names in the activation script. Don't 895 # clear it if it's not configured in the NixOS configuration, 896 # since it may have been set by dhcpcd in the meantime. ··· 899 hostname "${cfg.hostName}" 900 ''; 901 system.activationScripts.domain = 902 - optionalString (cfg.domain != "") '' 903 domainname "${cfg.domain}" 904 ''; 905 ··· 918 } 919 ]; 920 921 - services.udev.extraRules = 922 - '' 923 - KERNEL=="tun", TAG+="systemd" 924 - ''; 925 926 }; 927 928 }
··· 45 description = "Name of the interface."; 46 }; 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 + 58 ip4 = mkOption { 59 default = [ ]; 60 example = [ ··· 213 214 networking.hostName = mkOption { 215 default = "nixos"; 216 + type = types.str; 217 description = '' 218 The name of the machine. Leave it empty if you want to obtain 219 it from a DHCP server (if using DHCP). ··· 236 237 networking.enableIPv6 = mkOption { 238 default = true; 239 + type = types.bool; 240 description = '' 241 Whether to enable support for IPv6. 242 ''; 243 }; 244 245 networking.defaultGateway = mkOption { 246 + default = null; 247 example = "131.211.84.1"; 248 + type = types.nullOr types.str; 249 description = '' 250 The default gateway. It can be left empty if it is auto-detected through DHCP. 251 ''; ··· 279 }; 280 281 networking.domain = mkOption { 282 + default = null; 283 example = "home"; 284 + type = types.nullOr types.str; 285 description = '' 286 The domain. It can be left empty if it is auto-detected through DHCP. 287 ''; ··· 428 }; 429 }; 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 + 462 networking.sits = mkOption { 463 type = types.attrsOf types.optionSet; 464 default = { }; ··· 568 ''; 569 }; 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 + 581 }; 582 583 ··· 607 # from being created. 608 optionalString hasBonds "options bonding max_bonds=0"; 609 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 + )); 619 620 security.setuidPrograms = [ "ping" "ping6" ]; 621 622 # Set the host and domain names in the activation script. Don't 623 # clear it if it's not configured in the NixOS configuration, 624 # since it may have been set by dhcpcd in the meantime. ··· 627 hostname "${cfg.hostName}" 628 ''; 629 system.activationScripts.domain = 630 + optionalString (cfg.domain != null) '' 631 domainname "${cfg.domain}" 632 ''; 633 ··· 646 } 647 ]; 648 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 + ]; 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 + }))); 697 }; 698 699 }
+8
nixos/release-combined.nix
··· 66 (all nixos.tests.misc) 67 (all nixos.tests.nat.firewall) 68 (all nixos.tests.nat.standalone) 69 (all nixos.tests.nfs3) 70 (all nixos.tests.openssh) 71 (all nixos.tests.printing)
··· 66 (all nixos.tests.misc) 67 (all nixos.tests.nat.firewall) 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) 77 (all nixos.tests.nfs3) 78 (all nixos.tests.openssh) 79 (all nixos.tests.printing)
+16
nixos/release.nix
··· 269 tests.mysqlReplication = callTest tests/mysql-replication.nix {}; 270 tests.nat.firewall = callTest tests/nat.nix { withFirewall = true; }; 271 tests.nat.standalone = callTest tests/nat.nix { withFirewall = false; }; 272 tests.nfs3 = callTest tests/nfs.nix { version = 3; }; 273 tests.nsd = callTest tests/nsd.nix {}; 274 tests.openssh = callTest tests/openssh.nix {};
··· 269 tests.mysqlReplication = callTest tests/mysql-replication.nix {}; 270 tests.nat.firewall = callTest tests/nat.nix { withFirewall = true; }; 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"; }; 288 tests.nfs3 = callTest tests/nfs.nix { version = 3; }; 289 tests.nsd = callTest tests/nsd.nix {}; 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 + })