{ config, lib, pkgs, ... }: with lib; let cfg = config.clerie.uplink-selector; startScript = pkgs.writeScriptBin "uplink-selector-start" '' #! ${pkgs.runtimeShell} -e ip46() { ip "$@" ip -6 "$@" } ip46 rule flush || true # Route everything except default route first ip46 rule add lookup main suppress_prefixlength 0 prio 10000 # Decide which uplink to use ${concatStrings (mapAttrsToList (iface: ifacecfg: '' ip46 rule add iif ${iface} lookup ${cfg.uplinks.${ifacecfg.uplink}.table} prio 20000 '') cfg.interfaces)} # Fallback to the main default table ip46 rule add lookup main prio 32000 ''; stopScript = pkgs.writeScriptBin "uplink-selector-stop" '' #! ${pkgs.runtimeShell} -e ip rule flush || true ip -6 rule flush || true ''; in { options = { clerie.uplink-selector = { enable = mkOption { type = types.bool; default = false; description = '' Select a default gateway for each interface manually ''; }; uplinks = mkOption { default = { }; type = with types; attrsOf (submodule { options = { table = mkOption { type = types.str; example = "5001"; description = "Route table containing the gateway route of this uplink"; }; }; }); description = '' Uplink interface name ''; }; interfaces = mkOption { default = { }; type = with types; attrsOf (submodule { options = { uplink = mkOption { type = types.nullOr types.str; example = "uplink-a"; description = "Name of the uplink that should used as a default gateway by this interface"; }; }; }); description = '' Interface ''; }; }; }; config = mkIf cfg.enable { environment.systemPackages = [ pkgs.iproute2 ]; systemd.services.uplink-selector = { description = "Uplink Selector"; before = [ "network.target" ]; wantedBy = [ "network.target" ]; after = [ "network-pre.target" ]; path = [ pkgs.iproute2 ]; unitConfig.ConditionCapability = "CAP_NET_ADMIN"; serviceConfig = { Type = "oneshot"; RemainAfterExit = true; ExecStart = "@${startScript}/bin/uplink-selector-start uplink-selector-start"; ExecStop = "@${stopScript}/bin/uplink-selector-stop uplink-selector-stop"; }; }; }; }