{ pkgs, lib, config, options, ... }:
with lib;
let
  cfg = config.services.netns;
  inherit (config) networking;
  # Escape as required by: https://www.freedesktop.org/software/systemd/man/systemd.unit.html
  escapeUnitName = name:
    lib.concatMapStrings (s: if lib.isList s then "-" else s)
    (builtins.split "[^a-zA-Z0-9_.\\-]+" name);
  systemd = import (pkgs.path + "/nixos/modules/system/boot/systemd-unit-options.nix") { inherit config lib; };
in
{
options.services.netns = {
  namespaces = mkOption {
    description = "netns namespaces to create";
    type = types.attrsOf (types.submodule {
      options = {
        nftables = mkOption {
          type = types.lines;
          default = networking.nftables.ruleset;
          description = ''
            Nftables ruleset.
          '';
        };
        sysctl = options.boot.kernel.sysctl // {
          default = config.boot.kernel.sysctl;
        };
        service = mkOption {
          # Avoid error: The option `services.netns.namespaces.${netns}.service.startLimitIntervalSec' is used but not defined.
          #type = types.submodule [ { options = builtins.removeAttrs systemd.serviceOptions ["startLimitIntervalSec"]; } ];
          type = types.attrs;
          default = {};
          description = ''
            Systemd configuration specific to this netns service.
          '';
        };
      };
    });
    default = {};
  };
};
config = {
  systemd.services = mapAttrs' (name: c:
    nameValuePair "netns-${escapeUnitName name}" (mkMerge [
      { description = mkForce "${name} network namespace";
        before = [ "network.target" ];
        serviceConfig = {
          Type = mkForce "oneshot";
          RemainAfterExit = true;
          PrivateNetwork = true;
          ExecStart = mkForce (pkgs.writeShellScript "netns-start" ''
            test -e /var/run/netns/${escapeShellArg name} ||
            ${pkgs.iproute}/bin/ip netns add ${escapeShellArg name}
          '');
          ExecStartPost =
            # Use --ignore because some keys may no longer exist in that new namespace,
            # like net.ipv6.conf.eth0.addr_gen_mode or net.core.rmem_max
            [''${pkgs.iproute}/bin/ip netns exec ${escapeShellArg name} ${pkgs.procps}/bin/sysctl --ignore -p ${pkgs.writeScript "sysctl"
                (concatStrings (mapAttrsToList (n: v: optionalString (v != null) "${n}=${if v == false then "0" else toString v}\n") c.sysctl))}
            ''] ++
            optional networking.nftables.enable ''
              ${pkgs.iproute}/bin/ip netns exec ${escapeShellArg name} ${pkgs.writeScript "nftables-ruleset" ''
                #!${pkgs.nftables}/bin/nft -f
                flush ruleset
                ${c.nftables}
              ''}
            '';
          ExecStop = mkForce "${pkgs.iproute}/bin/ip netns del ${escapeShellArg name}";
        };
      }
      c.service
    ]
    )) cfg.namespaces;
  meta.maintainers = with lib.maintainers; [ julm ];
};
}