diff options
-rw-r--r-- | nixos/doc/manual/release-notes/rl-1903.xml | 8 | ||||
-rw-r--r-- | nixos/modules/services/networking/ndppd.nix | 170 | ||||
-rw-r--r-- | nixos/tests/all-tests.nix | 1 | ||||
-rw-r--r-- | nixos/tests/ndppd.nix | 61 | ||||
-rw-r--r-- | pkgs/applications/networking/ndppd/default.nix | 14 |
5 files changed, 217 insertions, 37 deletions
diff --git a/nixos/doc/manual/release-notes/rl-1903.xml b/nixos/doc/manual/release-notes/rl-1903.xml index 6c45301b91e..eca280afdf1 100644 --- a/nixos/doc/manual/release-notes/rl-1903.xml +++ b/nixos/doc/manual/release-notes/rl-1903.xml @@ -476,6 +476,14 @@ </para> </note> </listitem> + <listitem> + <para> + The <link xlink:href="https://github.com/DanielAdolfsson/ndppd"><literal>ndppd</literal></link> module + now supports <link linkend="opt-services.ndppd.enable">all config options</link> provided by the current + upstream version as service options. Additionally the <literal>ndppd</literal> package doesn't contain + the systemd unit configuration from upstream anymore, the unit is completely configured by the NixOS module now. + </para> + </listitem> </itemizedlist> </section> </section> diff --git a/nixos/modules/services/networking/ndppd.nix b/nixos/modules/services/networking/ndppd.nix index 1d6c48dd8d3..ba17f1ba825 100644 --- a/nixos/modules/services/networking/ndppd.nix +++ b/nixos/modules/services/networking/ndppd.nix @@ -5,43 +5,163 @@ with lib; let cfg = config.services.ndppd; - configFile = pkgs.runCommand "ndppd.conf" {} '' - substitute ${pkgs.ndppd}/etc/ndppd.conf $out \ - --replace eth0 ${cfg.interface} \ - --replace 1111:: ${cfg.network} - ''; -in { - options = { - services.ndppd = { - enable = mkEnableOption "daemon that proxies NDP (Neighbor Discovery Protocol) messages between interfaces"; + render = s: f: concatStringsSep "\n" (mapAttrsToList f s); + prefer = a: b: if a != null then a else b; + + ndppdConf = prefer cfg.configFile (pkgs.writeText "ndppd.conf" '' + route-ttl ${toString cfg.routeTTL} + ${render cfg.proxies (proxyInterfaceName: proxy: '' + proxy ${prefer proxy.interface proxyInterfaceName} { + router ${boolToString proxy.router} + timeout ${toString proxy.timeout} + ttl ${toString proxy.ttl} + ${render proxy.rules (ruleNetworkName: rule: '' + rule ${prefer rule.network ruleNetworkName} { + ${rule.method}${if rule.method == "iface" then " ${rule.interface}" else ""} + }'')} + }'')} + ''); + + proxy = types.submodule { + options = { interface = mkOption { - type = types.string; - default = "eth0"; - example = "ens3"; - description = "Interface which is on link-level with router."; + type = types.nullOr types.str; + description = '' + Listen for any Neighbor Solicitation messages on this interface, + and respond to them according to a set of rules. + Defaults to the name of the attrset. + ''; + default = null; + }; + router = mkOption { + type = types.bool; + description = '' + Turns on or off the router flag for Neighbor Advertisement Messages. + ''; + default = true; + }; + timeout = mkOption { + type = types.int; + description = '' + Controls how long to wait for a Neighbor Advertisment Message before + invalidating the entry, in milliseconds. + ''; + default = 500; + }; + ttl = mkOption { + type = types.int; + description = '' + Controls how long a valid or invalid entry remains in the cache, in + milliseconds. + ''; + default = 30000; }; + rules = mkOption { + type = types.attrsOf rule; + description = '' + This is a rule that the target address is to match against. If no netmask + is provided, /128 is assumed. You may have several rule sections, and the + addresses may or may not overlap. + ''; + default = {}; + }; + }; + }; + + rule = types.submodule { + options = { network = mkOption { - type = types.string; - default = "1111::"; - example = "2001:DB8::/32"; - description = "Network that we proxy."; + type = types.nullOr types.str; + description = '' + This is the target address is to match against. If no netmask + is provided, /128 is assumed. The addresses of serveral rules + may or may not overlap. + Defaults to the name of the attrset. + ''; + default = null; + }; + method = mkOption { + type = types.enum [ "static" "iface" "auto" ]; + description = '' + static: Immediately answer any Neighbor Solicitation Messages + (if they match the IP rule). + iface: Forward the Neighbor Solicitation Message through the specified + interface and only respond if a matching Neighbor Advertisement + Message is received. + auto: Same as iface, but instead of manually specifying the outgoing + interface, check for a matching route in /proc/net/ipv6_route. + ''; + default = "auto"; }; - configFile = mkOption { - type = types.nullOr types.path; + interface = mkOption { + type = types.nullOr types.str; + description = "Interface to use when method is iface."; default = null; - description = "Path to configuration file."; }; }; }; +in { + options.services.ndppd = { + enable = mkEnableOption "daemon that proxies NDP (Neighbor Discovery Protocol) messages between interfaces"; + interface = mkOption { + type = types.nullOr types.str; + description = '' + Interface which is on link-level with router. + (Legacy option, use services.ndppd.proxies.<interface>.rules.<network> instead) + ''; + default = null; + example = "eth0"; + }; + network = mkOption { + type = types.nullOr types.str; + description = '' + Network that we proxy. + (Legacy option, use services.ndppd.proxies.<interface>.rules.<network> instead) + ''; + default = null; + example = "1111::/64"; + }; + configFile = mkOption { + type = types.nullOr types.path; + description = "Path to configuration file."; + default = null; + }; + routeTTL = mkOption { + type = types.int; + description = '' + This tells 'ndppd' how often to reload the route file /proc/net/ipv6_route, + in milliseconds. + ''; + default = 30000; + }; + proxies = mkOption { + type = types.attrsOf proxy; + description = '' + This sets up a listener, that will listen for any Neighbor Solicitation + messages, and respond to them according to a set of rules. + ''; + default = {}; + example = { "eth0".rules."1111::/64" = {}; }; + }; + }; + config = mkIf cfg.enable { - systemd.packages = [ pkgs.ndppd ]; - environment.etc."ndppd.conf".source = if (cfg.configFile != null) then cfg.configFile else configFile; + warnings = mkIf (cfg.interface != null && cfg.network != null) [ '' + The options services.ndppd.interface and services.ndppd.network will probably be removed soon, + please use services.ndppd.proxies.<interface>.rules.<network> instead. + '' ]; + + services.ndppd.proxies = mkIf (cfg.interface != null && cfg.network != null) { + "${cfg.interface}".rules."${cfg.network}" = {}; + }; + systemd.services.ndppd = { - serviceConfig.RuntimeDirectory = [ "ndppd" ]; + description = "NDP Proxy Daemon"; + documentation = [ "man:ndppd(1)" "man:ndppd.conf(5)" ]; + after = [ "network-pre.target" ]; wantedBy = [ "multi-user.target" ]; + serviceConfig.ExecStart = "${pkgs.ndppd}/bin/ndppd -c ${ndppdConf}"; }; }; - - meta.maintainers = with maintainers; [ gnidorah ]; } diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index 0c8284eb08d..96b34755d6f 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -142,6 +142,7 @@ in nat.firewall = handleTest ./nat.nix { withFirewall = true; }; nat.firewall-conntrack = handleTest ./nat.nix { withFirewall = true; withConntrackHelpers = true; }; nat.standalone = handleTest ./nat.nix { withFirewall = false; }; + ndppd = handleTest ./ndppd.nix {}; neo4j = handleTest ./neo4j.nix {}; netdata = handleTest ./netdata.nix {}; networking.networkd = handleTest ./networking.nix { networkd = true; }; diff --git a/nixos/tests/ndppd.nix b/nixos/tests/ndppd.nix new file mode 100644 index 00000000000..9f24eb6d9d4 --- /dev/null +++ b/nixos/tests/ndppd.nix @@ -0,0 +1,61 @@ +import ./make-test.nix ({ pkgs, lib, ...} : { + name = "ndppd"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ fpletz ]; + }; + + nodes = { + upstream = { pkgs, ... }: { + environment.systemPackages = [ pkgs.tcpdump ]; + networking.useDHCP = false; + networking.interfaces = { + eth1 = { + ipv6.addresses = [ + { address = "fd23::1"; prefixLength = 112; } + ]; + ipv6.routes = [ + { address = "fd42::"; + prefixLength = 112; + } + ]; + }; + }; + }; + server = { pkgs, ... }: { + boot.kernel.sysctl = { + "net.ipv6.conf.all.forwarding" = "1"; + "net.ipv6.conf.default.forwarding" = "1"; + }; + environment.systemPackages = [ pkgs.tcpdump ]; + networking.useDHCP = false; + networking.interfaces = { + eth1 = { + ipv6.addresses = [ + { address = "fd23::2"; prefixLength = 112; } + ]; + }; + }; + services.ndppd = { + enable = true; + interface = "eth1"; + network = "fd42::/112"; + }; + containers.client = { + autoStart = true; + privateNetwork = true; + hostAddress = "192.168.255.1"; + localAddress = "192.168.255.2"; + hostAddress6 = "fd42::1"; + localAddress6 = "fd42::2"; + config = {}; + }; + }; + }; + + testScript = '' + startAll; + $server->waitForUnit("multi-user.target"); + $upstream->waitForUnit("multi-user.target"); + $upstream->waitUntilSucceeds("ping -c5 fd42::2"); + ''; +}) diff --git a/pkgs/applications/networking/ndppd/default.nix b/pkgs/applications/networking/ndppd/default.nix index a5eb9021048..6e6315ced7d 100644 --- a/pkgs/applications/networking/ndppd/default.nix +++ b/pkgs/applications/networking/ndppd/default.nix @@ -1,11 +1,6 @@ -{ stdenv, fetchFromGitHub, fetchurl, gzip, ... }: +{ stdenv, fetchFromGitHub, fetchurl, gzip }: -let - serviceFile = fetchurl { - url = "https://raw.githubusercontent.com/DanielAdolfsson/ndppd/f37e8eb33dc68b3385ecba9b36a5efd92755580f/ndppd.service"; - sha256 = "1zf54pzjfj9j9gr48075njqrgad4myd3dqmhvzxmjy4gjy9ixmyh"; - }; -in stdenv.mkDerivation rec { +stdenv.mkDerivation rec { name = "ndppd-${version}"; version = "0.2.5"; @@ -27,11 +22,6 @@ in stdenv.mkDerivation rec { postInstall = '' mkdir -p $out/etc cp ndppd.conf-dist $out/etc/ndppd.conf - - mkdir -p $out/lib/systemd/system - # service file needed for our module is not in release yet - substitute ${serviceFile} $out/lib/systemd/system/ndppd.service \ - --replace /usr/sbin/ndppd $out/sbin/ndppd ''; meta = { |