diff options
author | Nikolay Amiantov <ab@fmap.me> | 2017-01-15 19:53:33 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-01-15 19:53:33 +0300 |
commit | 70a662884895a079672a7daa57200d2848b80550 (patch) | |
tree | 4894daca3015ccf334d5dacad1abf70180a51f1d /nixos | |
parent | 12b283044612c5336638fd3a825e15799c7394aa (diff) | |
parent | 86755d923b201e2e450cf08af0ec8260d00b39a9 (diff) |
Merge pull request #21882 from abbradar/dhcp6
DHCPv6 improvements
Diffstat (limited to 'nixos')
-rw-r--r-- | nixos/modules/rename.nix | 3 | ||||
-rw-r--r-- | nixos/modules/services/networking/dhcpd.nix | 250 | ||||
-rw-r--r-- | nixos/modules/services/networking/firewall.nix | 9 | ||||
-rw-r--r-- | nixos/tests/networking.nix | 58 |
4 files changed, 211 insertions, 109 deletions
diff --git a/nixos/modules/rename.nix b/nixos/modules/rename.nix index 758f229d59d78..ad1ba86980d5e 100644 --- a/nixos/modules/rename.nix +++ b/nixos/modules/rename.nix @@ -164,6 +164,9 @@ with lib; else { addr = value inetAddr; port = value inetPort; } )) + # dhcpd + (mkRenamedOptionModule [ "services" "dhcpd" ] [ "services" "dhcpd4" ]) + # Options that are obsolete and have no replacement. (mkRemovedOptionModule [ "boot" "initrd" "luks" "enable" ] "") (mkRemovedOptionModule [ "programs" "bash" "enable" ] "") diff --git a/nixos/modules/services/networking/dhcpd.nix b/nixos/modules/services/networking/dhcpd.nix index d2cd00e74a1f0..86bcaa96f3454 100644 --- a/nixos/modules/services/networking/dhcpd.nix +++ b/nixos/modules/services/networking/dhcpd.nix @@ -4,11 +4,10 @@ with lib; let - cfg = config.services.dhcpd; + cfg4 = config.services.dhcpd4; + cfg6 = config.services.dhcpd6; - stateDir = "/var/lib/dhcp"; # Don't use /var/state/dhcp; not FHS-compliant. - - configFile = if cfg.configFile != null then cfg.configFile else pkgs.writeText "dhcpd.conf" + writeConfig = cfg: pkgs.writeText "dhcpd.conf" '' default-lease-time 600; max-lease-time 7200; @@ -29,131 +28,180 @@ let } ''; -in - -{ - - ###### interface - - options = { + dhcpdService = postfix: cfg: optionalAttrs cfg.enable { + "dhcpd${postfix}" = { + description = "DHCPv${postfix} server"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + + preStart = '' + mkdir -m 755 -p ${cfg.stateDir} + touch ${cfg.stateDir}/dhcpd.leases + ''; + + serviceConfig = + let + configFile = if cfg.configFile != null then cfg.configFile else writeConfig cfg; + args = [ "@${pkgs.dhcp}/sbin/dhcpd" "dhcpd${postfix}" "-${postfix}" + "-pf" "/run/dhcpd${postfix}/dhcpd.pid" + "-cf" "${configFile}" + "-lf" "${cfg.stateDir}/dhcpd.leases" + "-user" "dhcpd" "-group" "nogroup" + ] ++ cfg.extraFlags + ++ cfg.interfaces; + + in { + ExecStart = concatMapStringsSep " " escapeShellArg args; + Type = "forking"; + Restart = "always"; + RuntimeDirectory = [ "dhcpd${postfix}" ]; + PIDFile = "/run/dhcpd${postfix}/dhcpd.pid"; + }; + }; + }; - services.dhcpd = { + machineOpts = {...}: { + config = { - enable = mkOption { - default = false; - description = " - Whether to enable the DHCP server. - "; + hostName = mkOption { + type = types.str; + example = "foo"; + description = '' + Hostname which is assigned statically to the machine. + ''; }; - extraConfig = mkOption { - type = types.lines; - default = ""; - example = '' - option subnet-mask 255.255.255.0; - option broadcast-address 192.168.1.255; - option routers 192.168.1.5; - option domain-name-servers 130.161.158.4, 130.161.33.17, 130.161.180.1; - option domain-name "example.org"; - subnet 192.168.1.0 netmask 255.255.255.0 { - range 192.168.1.100 192.168.1.200; - } + ethernetAddress = mkOption { + type = types.str; + example = "00:16:76:9a:32:1d"; + description = '' + MAC address of the machine. ''; - description = " - Extra text to be appended to the DHCP server configuration - file. Currently, you almost certainly need to specify - something here, such as the options specifying the subnet - mask, DNS servers, etc. - "; }; - extraFlags = mkOption { - default = ""; - example = "-6"; - description = " - Additional command line flags to be passed to the dhcpd daemon. - "; + ipAddress = mkOption { + type = types.str; + example = "192.168.1.10"; + description = '' + IP address of the machine. + ''; }; - configFile = mkOption { - default = null; - description = " - The path of the DHCP server configuration file. If no file - is specified, a file is generated using the other options. - "; - }; + }; + }; - interfaces = mkOption { - default = ["eth0"]; - description = " - The interfaces on which the DHCP server should listen. - "; - }; + dhcpConfig = postfix: { - machines = mkOption { - default = []; - example = [ - { hostName = "foo"; - ethernetAddress = "00:16:76:9a:32:1d"; - ipAddress = "192.168.1.10"; - } - { hostName = "bar"; - ethernetAddress = "00:19:d1:1d:c4:9a"; - ipAddress = "192.168.1.11"; - } - ]; - description = " - A list mapping ethernet addresses to IP addresses for the - DHCP server. - "; - }; + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to enable the DHCPv${postfix} server. + ''; + }; + stateDir = mkOption { + type = types.path; + # We use /var/lib/dhcp for DHCPv4 to save backwards compatibility. + default = "/var/lib/dhcp${if postfix == "4" then "" else postfix}"; + description = '' + State directory for the DHCP server. + ''; }; - }; + extraConfig = mkOption { + type = types.lines; + default = ""; + example = '' + option subnet-mask 255.255.255.0; + option broadcast-address 192.168.1.255; + option routers 192.168.1.5; + option domain-name-servers 130.161.158.4, 130.161.33.17, 130.161.180.1; + option domain-name "example.org"; + subnet 192.168.1.0 netmask 255.255.255.0 { + range 192.168.1.100 192.168.1.200; + } + ''; + description = '' + Extra text to be appended to the DHCP server configuration + file. Currently, you almost certainly need to specify something + there, such as the options specifying the subnet mask, DNS servers, + etc. + ''; + }; + extraFlags = mkOption { + type = types.listOf types.str; + default = []; + description = '' + Additional command line flags to be passed to the dhcpd daemon. + ''; + }; - ###### implementation + configFile = mkOption { + type = types.nullOr types.path; + default = null; + description = '' + The path of the DHCP server configuration file. If no file + is specified, a file is generated using the other options. + ''; + }; - config = mkIf config.services.dhcpd.enable { + interfaces = mkOption { + type = types.listOf types.str; + default = ["eth0"]; + description = '' + The interfaces on which the DHCP server should listen. + ''; + }; - users = { - extraUsers.dhcpd = { - uid = config.ids.uids.dhcpd; - description = "DHCP daemon user"; - }; + machines = mkOption { + type = types.listOf (types.submodule machineOpts); + default = []; + example = [ + { hostName = "foo"; + ethernetAddress = "00:16:76:9a:32:1d"; + ipAddress = "192.168.1.10"; + } + { hostName = "bar"; + ethernetAddress = "00:19:d1:1d:c4:9a"; + ipAddress = "192.168.1.11"; + } + ]; + description = '' + A list mapping Ethernet addresses to IPv${postfix} addresses for the + DHCP server. + ''; }; - systemd.services.dhcpd = - { description = "DHCP server"; + }; + +in + +{ + + ###### interface - wantedBy = [ "multi-user.target" ]; + options = { - after = [ "network.target" ]; + services.dhcpd4 = dhcpConfig "4"; + services.dhcpd6 = dhcpConfig "6"; - path = [ pkgs.dhcp ]; + }; - preStart = - '' - mkdir -m 755 -p ${stateDir} - touch ${stateDir}/dhcpd.leases + ###### implementation - mkdir -m 755 -p /run/dhcpd - chown dhcpd /run/dhcpd - ''; + config = mkIf (cfg4.enable || cfg6.enable) { - serviceConfig = - { ExecStart = "@${pkgs.dhcp}/sbin/dhcpd dhcpd" - + " -pf /run/dhcpd/dhcpd.pid -cf ${configFile}" - + " -lf ${stateDir}/dhcpd.leases -user dhcpd -group nogroup" - + " ${cfg.extraFlags}" - + " ${toString cfg.interfaces}"; - Restart = "always"; - Type = "forking"; - PIDFile = "/run/dhcpd/dhcpd.pid"; - }; + users = { + extraUsers.dhcpd = { + uid = config.ids.uids.dhcpd; + description = "DHCP daemon user"; }; + }; + + systemd.services = dhcpdService "4" cfg4 // dhcpdService "6" cfg6; }; diff --git a/nixos/modules/services/networking/firewall.nix b/nixos/modules/services/networking/firewall.nix index 1c0ea5034df3d..ea406864fd3f7 100644 --- a/nixos/modules/services/networking/firewall.nix +++ b/nixos/modules/services/networking/firewall.nix @@ -172,13 +172,16 @@ let }-j nixos-fw-accept ''} - # Accept all ICMPv6 messages except redirects and node - # information queries (type 139). See RFC 4890, section - # 4.4. ${optionalString config.networking.enableIPv6 '' + # Accept all ICMPv6 messages except redirects and node + # information queries (type 139). See RFC 4890, section + # 4.4. ip6tables -A nixos-fw -p icmpv6 --icmpv6-type redirect -j DROP ip6tables -A nixos-fw -p icmpv6 --icmpv6-type 139 -j DROP ip6tables -A nixos-fw -p icmpv6 -j nixos-fw-accept + + # Allow this host to act as a DHCPv6 client + ip6tables -A nixos-fw -d fe80::/64 -p udp --dport 546 -j nixos-fw-accept ''} ${cfg.extraCommands} diff --git a/nixos/tests/networking.nix b/nixos/tests/networking.nix index 17d4a878d3a4f..83103f35d4821 100644 --- a/nixos/tests/networking.nix +++ b/nixos/tests/networking.nix @@ -10,29 +10,61 @@ let vlanIfs = range 1 (length config.virtualisation.vlans); in { virtualisation.vlans = [ 1 2 3 ]; + boot.kernel.sysctl."net.ipv6.conf.all.forwarding" = true; networking = { useDHCP = false; useNetworkd = networkd; firewall.allowPing = true; + firewall.checkReversePath = true; + firewall.allowedUDPPorts = [ 547 ]; interfaces = mkOverride 0 (listToAttrs (flip map vlanIfs (n: nameValuePair "eth${toString n}" { ipAddress = "192.168.${toString n}.1"; prefixLength = 24; + ipv6Address = "fd00:1234:5678:${toString n}::1"; + ipv6PrefixLength = 64; }))); }; - services.dhcpd = { + services.dhcpd4 = { enable = true; interfaces = map (n: "eth${toString n}") vlanIfs; extraConfig = '' - option subnet-mask 255.255.255.0; + authoritative; '' + flip concatMapStrings vlanIfs (n: '' subnet 192.168.${toString n}.0 netmask 255.255.255.0 { - option broadcast-address 192.168.${toString n}.255; option routers 192.168.${toString n}.1; + # XXX: technically it's _not guaranteed_ that IP addresses will be + # issued from the first item in range onwards! We assume that in + # our tests however. range 192.168.${toString n}.2 192.168.${toString n}.254; } ''); }; + services.radvd = { + enable = true; + config = flip concatMapStrings vlanIfs (n: '' + interface eth${toString n} { + AdvSendAdvert on; + AdvManagedFlag on; + AdvOtherConfigFlag on; + + prefix fd00:1234:5678:${toString n}::/64 { + AdvAutonomous off; + }; + }; + ''); + }; + services.dhcpd6 = { + enable = true; + interfaces = map (n: "eth${toString n}") vlanIfs; + extraConfig = '' + authoritative; + '' + flip concatMapStrings vlanIfs (n: '' + subnet6 fd00:1234:5678:${toString n}::/64 { + range6 fd00:1234:5678:${toString n}::2 fd00:1234:5678:${toString n}::2; + } + ''); + }; }; testCases = { @@ -108,8 +140,14 @@ let useNetworkd = networkd; firewall.allowPing = true; useDHCP = true; - interfaces.eth1.ip4 = mkOverride 0 [ ]; - interfaces.eth2.ip4 = mkOverride 0 [ ]; + interfaces.eth1 = { + ip4 = mkOverride 0 [ ]; + ip6 = mkOverride 0 [ ]; + }; + interfaces.eth2 = { + ip4 = mkOverride 0 [ ]; + ip6 = mkOverride 0 [ ]; + }; }; }; testScript = { nodes, ... }: @@ -121,21 +159,31 @@ let # Wait until we have an ip address on each interface $client->waitUntilSucceeds("ip addr show dev eth1 | grep -q '192.168.1'"); + $client->waitUntilSucceeds("ip addr show dev eth1 | grep -q 'fd00:1234:5678:1:'"); $client->waitUntilSucceeds("ip addr show dev eth2 | grep -q '192.168.2'"); + $client->waitUntilSucceeds("ip addr show dev eth2 | grep -q 'fd00:1234:5678:2:'"); # Test vlan 1 $client->waitUntilSucceeds("ping -c 1 192.168.1.1"); $client->waitUntilSucceeds("ping -c 1 192.168.1.2"); + $client->waitUntilSucceeds("ping6 -c 1 fd00:1234:5678:1::1"); + $client->waitUntilSucceeds("ping6 -c 1 fd00:1234:5678:1::2"); $router->waitUntilSucceeds("ping -c 1 192.168.1.1"); $router->waitUntilSucceeds("ping -c 1 192.168.1.2"); + $router->waitUntilSucceeds("ping6 -c 1 fd00:1234:5678:1::1"); + $router->waitUntilSucceeds("ping6 -c 1 fd00:1234:5678:1::2"); # Test vlan 2 $client->waitUntilSucceeds("ping -c 1 192.168.2.1"); $client->waitUntilSucceeds("ping -c 1 192.168.2.2"); + $client->waitUntilSucceeds("ping6 -c 1 fd00:1234:5678:2::1"); + $client->waitUntilSucceeds("ping6 -c 1 fd00:1234:5678:2::2"); $router->waitUntilSucceeds("ping -c 1 192.168.2.1"); $router->waitUntilSucceeds("ping -c 1 192.168.2.2"); + $router->waitUntilSucceeds("ping6 -c 1 fd00:1234:5678:2::1"); + $router->waitUntilSucceeds("ping6 -c 1 fd00:1234:5678:2::2"); ''; }; dhcpOneIf = { |