summary refs log tree commit diff
path: root/nixos
diff options
context:
space:
mode:
authorMaximilian Bosch <maximilian@mbosch.me>2019-02-04 21:51:00 +0100
committerGitHub <noreply@github.com>2019-02-04 21:51:00 +0100
commit5a3a54307841397bd3cb84924c3df989e9b6dc96 (patch)
treeb69c5ec81b738c6ccd180cbf58b501e0a54da4d1 /nixos
parent73ad11dd70fde3c5e8c5d5304091de91c4f6930a (diff)
parent722af384ea4979c3245161f25abf8463d405f507 (diff)
downloadnixpkgs-5a3a54307841397bd3cb84924c3df989e9b6dc96.tar
nixpkgs-5a3a54307841397bd3cb84924c3df989e9b6dc96.tar.gz
nixpkgs-5a3a54307841397bd3cb84924c3df989e9b6dc96.tar.bz2
nixpkgs-5a3a54307841397bd3cb84924c3df989e9b6dc96.tar.lz
nixpkgs-5a3a54307841397bd3cb84924c3df989e9b6dc96.tar.xz
nixpkgs-5a3a54307841397bd3cb84924c3df989e9b6dc96.tar.zst
nixpkgs-5a3a54307841397bd3cb84924c3df989e9b6dc96.zip
Merge pull request #55122 from elseym/ndppd-module
ndppd module: refactor and fix
Diffstat (limited to 'nixos')
-rw-r--r--nixos/doc/manual/release-notes/rl-1903.xml8
-rw-r--r--nixos/modules/services/networking/ndppd.nix170
-rw-r--r--nixos/tests/all-tests.nix1
-rw-r--r--nixos/tests/ndppd.nix61
4 files changed, 215 insertions, 25 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.&lt;interface&gt;.rules.&lt;network&gt; instead)
+      '';
+      default = null;
+      example = "eth0";
+    };
+    network = mkOption {
+      type = types.nullOr types.str;
+      description = ''
+        Network that we proxy.
+        (Legacy option, use services.ndppd.proxies.&lt;interface&gt;.rules.&lt;network&gt; 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");
+  '';
+})