summary refs log tree commit diff
diff options
context:
space:
mode:
authorpennae <github@quasiparticle.net>2021-08-11 11:02:47 +0200
committertomberek <tomberek@users.noreply.github.com>2021-10-16 20:48:03 -0400
commitf29ea2d15d833494f7e97e0231b03ca70a8e7db4 (patch)
tree636d34b80a85598e045fb5e0f190c6330a51efdf
parenteebfe7199d9e543acea19de4af15a91ab7774e7c (diff)
downloadnixpkgs-f29ea2d15d833494f7e97e0231b03ca70a8e7db4.tar
nixpkgs-f29ea2d15d833494f7e97e0231b03ca70a8e7db4.tar.gz
nixpkgs-f29ea2d15d833494f7e97e0231b03ca70a8e7db4.tar.bz2
nixpkgs-f29ea2d15d833494f7e97e0231b03ca70a8e7db4.tar.lz
nixpkgs-f29ea2d15d833494f7e97e0231b03ca70a8e7db4.tar.xz
nixpkgs-f29ea2d15d833494f7e97e0231b03ca70a8e7db4.tar.zst
nixpkgs-f29ea2d15d833494f7e97e0231b03ca70a8e7db4.zip
nixos/networking: add foo-over-udp endpoint support
allows configuration of foo-over-udp decapsulation endpoints. sadly networkd
seems to lack the features necessary to support local and peer address
configuration, so those are only supported when using scripted configuration.
-rw-r--r--nixos/doc/manual/from_md/release-notes/rl-2111.section.xml13
-rw-r--r--nixos/doc/manual/release-notes/rl-2111.section.md4
-rw-r--r--nixos/modules/system/boot/networkd.nix26
-rw-r--r--nixos/modules/tasks/network-interfaces-scripted.nix34
-rw-r--r--nixos/modules/tasks/network-interfaces-systemd.nix20
-rw-r--r--nixos/modules/tasks/network-interfaces.nix69
-rw-r--r--nixos/tests/networking.nix46
7 files changed, 211 insertions, 1 deletions
diff --git a/nixos/doc/manual/from_md/release-notes/rl-2111.section.xml b/nixos/doc/manual/from_md/release-notes/rl-2111.section.xml
index 73baf71f5ef..c0f407e11a9 100644
--- a/nixos/doc/manual/from_md/release-notes/rl-2111.section.xml
+++ b/nixos/doc/manual/from_md/release-notes/rl-2111.section.xml
@@ -1535,6 +1535,19 @@ Superuser created successfully.
           release notes</link> for changes and upgrade instructions.
         </para>
       </listitem>
+      <listitem>
+        <para>
+          The <literal>systemd.network</literal> module has gained
+          support for the FooOverUDP link type.
+        </para>
+      </listitem>
+      <listitem>
+        <para>
+          The <literal>networking</literal> module has a new
+          <literal>networking.fooOverUDP</literal> option to configure
+          Foo-over-UDP encapsulations.
+        </para>
+      </listitem>
     </itemizedlist>
   </section>
 </section>
diff --git a/nixos/doc/manual/release-notes/rl-2111.section.md b/nixos/doc/manual/release-notes/rl-2111.section.md
index b7fa2cf0f25..6dadcde3ccc 100644
--- a/nixos/doc/manual/release-notes/rl-2111.section.md
+++ b/nixos/doc/manual/release-notes/rl-2111.section.md
@@ -443,3 +443,7 @@ In addition to numerous new and upgraded packages, this release has the followin
 - Three new options, [xdg.mime.addedAssociations](#opt-xdg.mime.addedAssociations), [xdg.mime.defaultApplications](#opt-xdg.mime.defaultApplications), and [xdg.mime.removedAssociations](#opt-xdg.mime.removedAssociations) have been added to the [xdg.mime](#opt-xdg.mime.enable) module to allow the configuration of `/etc/xdg/mimeapps.list`.
 
 - Kopia was upgraded from 0.8.x to 0.9.x. Please read the [upstream release notes](https://github.com/kopia/kopia/releases/tag/v0.9.0) for changes and upgrade instructions.
+
+- The `systemd.network` module has gained support for the FooOverUDP link type.
+
+- The `networking` module has a new `networking.fooOverUDP` option to configure Foo-over-UDP encapsulations.
diff --git a/nixos/modules/system/boot/networkd.nix b/nixos/modules/system/boot/networkd.nix
index 51e105bf627..662dfe2db98 100644
--- a/nixos/modules/system/boot/networkd.nix
+++ b/nixos/modules/system/boot/networkd.nix
@@ -250,6 +250,16 @@ let
         (assertRange "ERSPANIndex" 1 1048575)
       ];
 
+      sectionFooOverUDP = checkUnitConfig "FooOverUDP" [
+        (assertOnlyFields [
+          "Port"
+          "Encapsulation"
+          "Protocol"
+        ])
+        (assertPort "Port")
+        (assertValueOneOf "Encapsulation" ["FooOverUDP" "GenericUDPEncapsulation"])
+      ];
+
       sectionPeer = checkUnitConfig "Peer" [
         (assertOnlyFields [
           "Name"
@@ -919,6 +929,18 @@ let
       '';
     };
 
+    fooOverUDPConfig = mkOption {
+      default = { };
+      example = { Port = 9001; };
+      type = types.addCheck (types.attrsOf unitOption) check.netdev.sectionFooOverUDP;
+      description = ''
+        Each attribute in this set specifies an option in the
+        <literal>[FooOverUDP]</literal> section of the unit.  See
+        <citerefentry><refentrytitle>systemd.netdev</refentrytitle>
+        <manvolnum>5</manvolnum></citerefentry> for details.
+      '';
+    };
+
     peerConfig = mkOption {
       default = {};
       example = { Name = "veth2"; };
@@ -1449,6 +1471,10 @@ let
           [Tunnel]
           ${attrsToSection def.tunnelConfig}
         ''
+        + optionalString (def.fooOverUDPConfig != { }) ''
+          [FooOverUDP]
+          ${attrsToSection def.fooOverUDPConfig}
+        ''
         + optionalString (def.peerConfig != { }) ''
           [Peer]
           ${attrsToSection def.peerConfig}
diff --git a/nixos/modules/tasks/network-interfaces-scripted.nix b/nixos/modules/tasks/network-interfaces-scripted.nix
index 79624ec7072..055580e3ea2 100644
--- a/nixos/modules/tasks/network-interfaces-scripted.nix
+++ b/nixos/modules/tasks/network-interfaces-scripted.nix
@@ -466,6 +466,39 @@ let
             '';
           });
 
+        createFouEncapsulation = n: v: nameValuePair "${n}-fou-encap"
+          (let
+            # if we have a device to bind to we can wait for its addresses to be
+            # configured, otherwise external sequencing is required.
+            deps = optionals (v.local != null && v.local.dev != null)
+              (deviceDependency v.local.dev ++ [ "network-addresses-${v.local.dev}.service" ]);
+            fouSpec = "port ${toString v.port} ${
+              if v.protocol != null then "ipproto ${toString v.protocol}" else "gue"
+            } ${
+              optionalString (v.local != null) "local ${escapeShellArg v.local.address} ${
+                optionalString (v.local.dev != null) "dev ${escapeShellArg v.local.dev}"
+              }"
+            }";
+          in
+          { description = "FOU endpoint ${n}";
+            wantedBy = [ "network-setup.service" (subsystemDevice n) ];
+            bindsTo = deps;
+            partOf = [ "network-setup.service" ];
+            after = [ "network-pre.target" ] ++ deps;
+            before = [ "network-setup.service" ];
+            serviceConfig.Type = "oneshot";
+            serviceConfig.RemainAfterExit = true;
+            path = [ pkgs.iproute2 ];
+            script = ''
+              # always remove previous incarnation since show can't filter
+              ip fou del ${fouSpec} >/dev/null 2>&1 || true
+              ip fou add ${fouSpec}
+            '';
+            postStop = ''
+              ip fou del ${fouSpec} || true
+            '';
+          });
+
         createSitDevice = n: v: nameValuePair "${n}-netdev"
           (let
             deps = deviceDependency v.dev;
@@ -530,6 +563,7 @@ let
          // mapAttrs' createVswitchDevice cfg.vswitches
          // mapAttrs' createBondDevice cfg.bonds
          // mapAttrs' createMacvlanDevice cfg.macvlans
+         // mapAttrs' createFouEncapsulation cfg.fooOverUDP
          // mapAttrs' createSitDevice cfg.sits
          // mapAttrs' createVlanDevice cfg.vlans
          // {
diff --git a/nixos/modules/tasks/network-interfaces-systemd.nix b/nixos/modules/tasks/network-interfaces-systemd.nix
index 225f9dc67fc..516764b87db 100644
--- a/nixos/modules/tasks/network-interfaces-systemd.nix
+++ b/nixos/modules/tasks/network-interfaces-systemd.nix
@@ -47,6 +47,9 @@ in
     } ] ++ flip mapAttrsToList cfg.bridges (n: { rstp, ... }: {
       assertion = !rstp;
       message = "networking.bridges.${n}.rstp is not supported by networkd.";
+    }) ++ flip mapAttrsToList cfg.fooOverUDP (n: { local, ... }: {
+      assertion = local == null;
+      message = "networking.fooOverUDP.${n}.local is not supported by networkd.";
     });
 
     networking.dhcpcd.enable = mkDefault false;
@@ -194,6 +197,23 @@ in
           macvlan = [ name ];
         } ]);
       })))
+      (mkMerge (flip mapAttrsToList cfg.fooOverUDP (name: fou: {
+        netdevs."40-${name}" = {
+          netdevConfig = {
+            Name = name;
+            Kind = "fou";
+          };
+          # unfortunately networkd cannot encode dependencies of netdevs on addresses/routes,
+          # so we cannot specify Local=, Peer=, PeerPort=. this looks like a missing feature
+          # in networkd.
+          fooOverUDPConfig = {
+            Port = fou.port;
+            Encapsulation = if fou.protocol != null then "FooOverUDP" else "GenericUDPEncapsulation";
+          } // (optionalAttrs (fou.protocol != null) {
+            Protocol = fou.protocol;
+          });
+        };
+      })))
       (mkMerge (flip mapAttrsToList cfg.sits (name: sit: {
         netdevs."40-${name}" = {
           netdevConfig = {
diff --git a/nixos/modules/tasks/network-interfaces.nix b/nixos/modules/tasks/network-interfaces.nix
index 313b0fac7da..a3b41326168 100644
--- a/nixos/modules/tasks/network-interfaces.nix
+++ b/nixos/modules/tasks/network-interfaces.nix
@@ -10,6 +10,7 @@ let
   hasVirtuals = any (i: i.virtual) interfaces;
   hasSits = cfg.sits != { };
   hasBonds = cfg.bonds != { };
+  hasFous = cfg.fooOverUDP != { };
 
   slaves = concatMap (i: i.interfaces) (attrValues cfg.bonds)
     ++ concatMap (i: i.interfaces) (attrValues cfg.bridges)
@@ -823,6 +824,71 @@ in
       });
     };
 
+    networking.fooOverUDP = mkOption {
+      default = { };
+      example =
+        {
+          primary = { port = 9001; local = { address = "192.0.2.1"; dev = "eth0"; }; };
+          backup =  { port = 9002; };
+        };
+      description = ''
+        This option allows you to configure Foo Over UDP and Generic UDP Encapsulation
+        endpoints. See <citerefentry><refentrytitle>ip-fou</refentrytitle>
+        <manvolnum>8</manvolnum></citerefentry> for details.
+      '';
+      type = with types; attrsOf (submodule {
+        options = {
+          port = mkOption {
+            type = port;
+            description = ''
+              Local port of the encapsulation UDP socket.
+            '';
+          };
+
+          protocol = mkOption {
+            type = nullOr (ints.between 1 255);
+            default = null;
+            description = ''
+              Protocol number of the encapsulated packets. Specifying <literal>null</literal>
+              (the default) creates a GUE endpoint, specifying a protocol number will create
+              a FOU endpoint.
+            '';
+          };
+
+          local = mkOption {
+            type = nullOr (submodule {
+              options = {
+                address = mkOption {
+                  type = types.str;
+                  description = ''
+                    Local address to bind to. The address must be available when the FOU
+                    endpoint is created, using the scripted network setup this can be achieved
+                    either by setting <literal>dev</literal> or adding dependency information to
+                    <literal>systemd.services.&lt;name&gt;-fou-encap</literal>; it isn't supported
+                    when using networkd.
+                  '';
+                };
+
+                dev = mkOption {
+                  type = nullOr str;
+                  default = null;
+                  example = "eth0";
+                  description = ''
+                    Network device to bind to.
+                  '';
+                };
+              };
+            });
+            default = null;
+            example = { address = "203.0.113.22"; };
+            description = ''
+              Local address (and optionally device) to bind to using the given port.
+            '';
+          };
+        };
+      });
+    };
+
     networking.sits = mkOption {
       default = { };
       example = literalExpression ''
@@ -1116,7 +1182,8 @@ in
     boot.kernelModules = [ ]
       ++ optional hasVirtuals "tun"
       ++ optional hasSits "sit"
-      ++ optional hasBonds "bonding";
+      ++ optional hasBonds "bonding"
+      ++ optional hasFous "fou";
 
     boot.extraModprobeConfig =
       # This setting is intentional as it prevents default bond devices
diff --git a/nixos/tests/networking.nix b/nixos/tests/networking.nix
index 8b947ddf0cf..fdcf67f1126 100644
--- a/nixos/tests/networking.nix
+++ b/nixos/tests/networking.nix
@@ -380,6 +380,52 @@ let
               router.wait_until_succeeds("ping -c 1 192.168.1.3")
         '';
     };
+    fou = {
+      name = "foo-over-udp";
+      nodes.machine = { ... }: {
+        virtualisation.vlans = [ 1 ];
+        networking = {
+          useNetworkd = networkd;
+          useDHCP = false;
+          interfaces.eth1.ipv4.addresses = mkOverride 0
+            [ { address = "192.168.1.1"; prefixLength = 24; } ];
+          fooOverUDP = {
+            fou1 = { port = 9001; };
+            fou2 = { port = 9002; protocol = 41; };
+            fou3 = mkIf (!networkd)
+              { port = 9003; local.address = "192.168.1.1"; };
+            fou4 = mkIf (!networkd)
+              { port = 9004; local = { address = "192.168.1.1"; dev = "eth1"; }; };
+          };
+        };
+        systemd.services = {
+          fou3-fou-encap.after = optional (!networkd) "network-addresses-eth1.service";
+        };
+      };
+      testScript = { ... }:
+        ''
+          import json
+
+          machine.wait_for_unit("network.target")
+          fous = json.loads(machine.succeed("ip -json fou show"))
+          assert {"port": 9001, "gue": None, "family": "inet"} in fous, "fou1 exists"
+          assert {"port": 9002, "ipproto": 41, "family": "inet"} in fous, "fou2 exists"
+        '' + optionalString (!networkd) ''
+          assert {
+              "port": 9003,
+              "gue": None,
+              "family": "inet",
+              "local": "192.168.1.1",
+          } in fous, "fou3 exists"
+          assert {
+              "port": 9004,
+              "gue": None,
+              "family": "inet",
+              "local": "192.168.1.1",
+              "dev": "eth1",
+          } in fous, "fou4 exists"
+        '';
+    };
     sit = let
       node = { address4, remote, address6 }: { pkgs, ... }: with pkgs.lib; {
         virtualisation.vlans = [ 1 ];