{ config, lib, pkgs, ... }: with lib; let cfg = config.clerie.gre-tunnel; generateInterfaceUnit = isIPv6: (name: tunnel: nameValuePair "gre-tunnel-${name}" { description = "GRE Tunnel - ${name}"; requires = [ "network-online.target" ]; after = [ "network.target" "network-online.target" ]; wantedBy = [ "multi-user.target" ]; environment.DEVICE = name; path = with pkgs; [ iproute ]; serviceConfig = { Type = "oneshot"; RemainAfterExit = true; }; script = '' ${tunnel.preSetup} ip${optionalString isIPv6 " -6"} tunnel add ${name} mode ${optionalString isIPv6 "ip6"}gre remote ${tunnel.remote} local ${tunnel.local} ip link set ${name} up ip${optionalString isIPv6 " -6"} a add ${tunnel.address} dev ${name} ${tunnel.postSetup} ''; postStop = '' ip link set ${name} down ip tunnel del ${name} ${tunnel.postShutdown} ''; }); checkOpts = { config, ... }@moduleAttrs: { options = { remote = mkOption { type = types.str; description = "Address of reciever."; }; local = mkOption { type = types.str; description = "Address our packets originate from."; }; address = mkOption { type = types.str; description = "Our address in this tunnel."; }; preSetup = mkOption { type = types.str; default = ""; description = "Commands called at the start of the interface setup."; }; postSetup = mkOption { type = types.str; default = ""; description = "Commands called at the end of the interface setup."; }; postShutdown = mkOption { type = types.str; default = ""; description = "Commands called after shutting down the interface."; }; }; }; in { options = { clerie.gre-tunnel = { enable = mkEnableOption "Declarative Policy-Routing"; ipv6 = mkOption { type = with types; attrsOf (submodule checkOpts); default = {}; }; ipv4 = mkOption { type = with types; attrsOf (submodule checkOpts); default = {}; }; }; }; config = mkIf cfg.enable { systemd.services = (mapAttrs' (generateInterfaceUnit false) cfg.ipv4) // (mapAttrs' (generateInterfaceUnit true) cfg.ipv6); }; }