Merge pull request #21882 from abbradar/dhcp6

DHCPv6 improvements
This commit is contained in:
Nikolay Amiantov 2017-01-15 19:53:33 +03:00 committed by GitHub
commit 70a6628848
5 changed files with 222 additions and 120 deletions

View File

@ -164,6 +164,9 @@ with lib;
else { addr = value inetAddr; port = value inetPort; } else { addr = value inetAddr; port = value inetPort; }
)) ))
# dhcpd
(mkRenamedOptionModule [ "services" "dhcpd" ] [ "services" "dhcpd4" ])
# Options that are obsolete and have no replacement. # Options that are obsolete and have no replacement.
(mkRemovedOptionModule [ "boot" "initrd" "luks" "enable" ] "") (mkRemovedOptionModule [ "boot" "initrd" "luks" "enable" ] "")
(mkRemovedOptionModule [ "programs" "bash" "enable" ] "") (mkRemovedOptionModule [ "programs" "bash" "enable" ] "")

View File

@ -4,11 +4,10 @@ with lib;
let 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. writeConfig = cfg: pkgs.writeText "dhcpd.conf"
configFile = if cfg.configFile != null then cfg.configFile else pkgs.writeText "dhcpd.conf"
'' ''
default-lease-time 600; default-lease-time 600;
max-lease-time 7200; max-lease-time 7200;
@ -29,21 +28,85 @@ let
} }
''; '';
in 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
'';
###### interface 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;
options = { in {
ExecStart = concatMapStringsSep " " escapeShellArg args;
Type = "forking";
Restart = "always";
RuntimeDirectory = [ "dhcpd${postfix}" ];
PIDFile = "/run/dhcpd${postfix}/dhcpd.pid";
};
};
};
services.dhcpd = { machineOpts = {...}: {
config = {
hostName = mkOption {
type = types.str;
example = "foo";
description = ''
Hostname which is assigned statically to the machine.
'';
};
ethernetAddress = mkOption {
type = types.str;
example = "00:16:76:9a:32:1d";
description = ''
MAC address of the machine.
'';
};
ipAddress = mkOption {
type = types.str;
example = "192.168.1.10";
description = ''
IP address of the machine.
'';
};
};
};
dhcpConfig = postfix: {
enable = mkOption { enable = mkOption {
type = types.bool;
default = false; default = false;
description = " description = ''
Whether to enable the DHCP server. 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 { extraConfig = mkOption {
@ -59,38 +122,41 @@ in
range 192.168.1.100 192.168.1.200; range 192.168.1.100 192.168.1.200;
} }
''; '';
description = " description = ''
Extra text to be appended to the DHCP server configuration Extra text to be appended to the DHCP server configuration
file. Currently, you almost certainly need to specify file. Currently, you almost certainly need to specify something
something here, such as the options specifying the subnet there, such as the options specifying the subnet mask, DNS servers,
mask, DNS servers, etc. etc.
"; '';
}; };
extraFlags = mkOption { extraFlags = mkOption {
default = ""; type = types.listOf types.str;
example = "-6"; default = [];
description = " description = ''
Additional command line flags to be passed to the dhcpd daemon. Additional command line flags to be passed to the dhcpd daemon.
"; '';
}; };
configFile = mkOption { configFile = mkOption {
type = types.nullOr types.path;
default = null; default = null;
description = " description = ''
The path of the DHCP server configuration file. If no file The path of the DHCP server configuration file. If no file
is specified, a file is generated using the other options. is specified, a file is generated using the other options.
"; '';
}; };
interfaces = mkOption { interfaces = mkOption {
type = types.listOf types.str;
default = ["eth0"]; default = ["eth0"];
description = " description = ''
The interfaces on which the DHCP server should listen. The interfaces on which the DHCP server should listen.
"; '';
}; };
machines = mkOption { machines = mkOption {
type = types.listOf (types.submodule machineOpts);
default = []; default = [];
example = [ example = [
{ hostName = "foo"; { hostName = "foo";
@ -102,20 +168,31 @@ in
ipAddress = "192.168.1.11"; ipAddress = "192.168.1.11";
} }
]; ];
description = " description = ''
A list mapping ethernet addresses to IP addresses for the A list mapping Ethernet addresses to IPv${postfix} addresses for the
DHCP server. DHCP server.
"; '';
}; };
}; };
in
{
###### interface
options = {
services.dhcpd4 = dhcpConfig "4";
services.dhcpd6 = dhcpConfig "6";
}; };
###### implementation ###### implementation
config = mkIf config.services.dhcpd.enable { config = mkIf (cfg4.enable || cfg6.enable) {
users = { users = {
extraUsers.dhcpd = { extraUsers.dhcpd = {
@ -124,36 +201,7 @@ in
}; };
}; };
systemd.services.dhcpd = systemd.services = dhcpdService "4" cfg4 // dhcpdService "6" cfg6;
{ description = "DHCP server";
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];
path = [ pkgs.dhcp ];
preStart =
''
mkdir -m 755 -p ${stateDir}
touch ${stateDir}/dhcpd.leases
mkdir -m 755 -p /run/dhcpd
chown dhcpd /run/dhcpd
'';
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";
};
};
}; };

View File

@ -172,13 +172,16 @@ let
}-j nixos-fw-accept }-j nixos-fw-accept
''} ''}
${optionalString config.networking.enableIPv6 ''
# Accept all ICMPv6 messages except redirects and node # Accept all ICMPv6 messages except redirects and node
# information queries (type 139). See RFC 4890, section # information queries (type 139). See RFC 4890, section
# 4.4. # 4.4.
${optionalString config.networking.enableIPv6 ''
ip6tables -A nixos-fw -p icmpv6 --icmpv6-type redirect -j DROP 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 --icmpv6-type 139 -j DROP
ip6tables -A nixos-fw -p icmpv6 -j nixos-fw-accept 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} ${cfg.extraCommands}

View File

@ -10,29 +10,61 @@ let
vlanIfs = range 1 (length config.virtualisation.vlans); vlanIfs = range 1 (length config.virtualisation.vlans);
in { in {
virtualisation.vlans = [ 1 2 3 ]; virtualisation.vlans = [ 1 2 3 ];
boot.kernel.sysctl."net.ipv6.conf.all.forwarding" = true;
networking = { networking = {
useDHCP = false; useDHCP = false;
useNetworkd = networkd; useNetworkd = networkd;
firewall.allowPing = true; firewall.allowPing = true;
firewall.checkReversePath = true;
firewall.allowedUDPPorts = [ 547 ];
interfaces = mkOverride 0 (listToAttrs (flip map vlanIfs (n: interfaces = mkOverride 0 (listToAttrs (flip map vlanIfs (n:
nameValuePair "eth${toString n}" { nameValuePair "eth${toString n}" {
ipAddress = "192.168.${toString n}.1"; ipAddress = "192.168.${toString n}.1";
prefixLength = 24; prefixLength = 24;
ipv6Address = "fd00:1234:5678:${toString n}::1";
ipv6PrefixLength = 64;
}))); })));
}; };
services.dhcpd = { services.dhcpd4 = {
enable = true; enable = true;
interfaces = map (n: "eth${toString n}") vlanIfs; interfaces = map (n: "eth${toString n}") vlanIfs;
extraConfig = '' extraConfig = ''
option subnet-mask 255.255.255.0; authoritative;
'' + flip concatMapStrings vlanIfs (n: '' '' + flip concatMapStrings vlanIfs (n: ''
subnet 192.168.${toString n}.0 netmask 255.255.255.0 { 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; 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; 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 = { testCases = {
@ -108,8 +140,14 @@ let
useNetworkd = networkd; useNetworkd = networkd;
firewall.allowPing = true; firewall.allowPing = true;
useDHCP = true; useDHCP = true;
interfaces.eth1.ip4 = mkOverride 0 [ ]; interfaces.eth1 = {
interfaces.eth2.ip4 = mkOverride 0 [ ]; ip4 = mkOverride 0 [ ];
ip6 = mkOverride 0 [ ];
};
interfaces.eth2 = {
ip4 = mkOverride 0 [ ];
ip6 = mkOverride 0 [ ];
};
}; };
}; };
testScript = { nodes, ... }: testScript = { nodes, ... }:
@ -121,21 +159,31 @@ let
# Wait until we have an ip address on each interface # 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 '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 '192.168.2'");
$client->waitUntilSucceeds("ip addr show dev eth2 | grep -q 'fd00:1234:5678:2:'");
# Test vlan 1 # Test vlan 1
$client->waitUntilSucceeds("ping -c 1 192.168.1.1"); $client->waitUntilSucceeds("ping -c 1 192.168.1.1");
$client->waitUntilSucceeds("ping -c 1 192.168.1.2"); $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.1");
$router->waitUntilSucceeds("ping -c 1 192.168.1.2"); $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 # Test vlan 2
$client->waitUntilSucceeds("ping -c 1 192.168.2.1"); $client->waitUntilSucceeds("ping -c 1 192.168.2.1");
$client->waitUntilSucceeds("ping -c 1 192.168.2.2"); $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.1");
$router->waitUntilSucceeds("ping -c 1 192.168.2.2"); $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 = { dhcpOneIf = {

View File

@ -1,16 +1,16 @@
{ stdenv, fetchurl, pkgconfig, libdaemon, bison, flex, check }: { stdenv, fetchurl, pkgconfig, libdaemon, bison, flex, check }:
stdenv.mkDerivation rec { stdenv.mkDerivation rec {
name = "radvd-2.13"; name = "radvd-${version}";
version = "2.15";
src = fetchurl { src = fetchurl {
url = "http://www.litech.org/radvd/dist/${name}.tar.xz"; url = "http://www.litech.org/radvd/dist/${name}.tar.xz";
sha256 = "1lzgg6zpizcldb78n5gkykjnpr7sqm4r1xy9bm4ig0chbrink4ka"; sha256 = "09spyj4f05rjx21v8vmyqmmj0fz1wx810s63md1vf05hyzd0v8dk";
}; };
buildInputs = [ pkgconfig libdaemon bison flex check ]; nativeBuildInputs = [ pkgconfig bison flex check ];
buildInputs = [ libdaemon ];
hardeningEnable = [ "pie" ];
meta = with stdenv.lib; { meta = with stdenv.lib; {
homepage = http://www.litech.org/radvd/; homepage = http://www.litech.org/radvd/;