388 lines
16 KiB
Nix
388 lines
16 KiB
Nix
|
{ lib
|
||
|
, config
|
||
|
, pkgs
|
||
|
, ... }:
|
||
|
|
||
|
let
|
||
|
cfg = config.router;
|
||
|
in {
|
||
|
imports = [
|
||
|
/*./avahi.nix*/
|
||
|
./hostapd.nix
|
||
|
./kea.nix
|
||
|
./radvd.nix
|
||
|
./corerad.nix
|
||
|
];
|
||
|
|
||
|
options.router = {
|
||
|
enable = lib.mkEnableOption "router config";
|
||
|
interfaces = lib.mkOption {
|
||
|
default = { };
|
||
|
description = "All interfaces managed by the router";
|
||
|
type = lib.types.attrsOf (lib.types.submodule {
|
||
|
options.matchUdevAttrs = lib.mkOption {
|
||
|
default = { };
|
||
|
description = lib.mdDoc ''
|
||
|
When a device with those attrs is detected by udev, the device is automatically renamed to this interface name.
|
||
|
|
||
|
See [kernel docs](https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/Documentation/ABI/testing/sysfs-class-net?h=linux-6.3.y) for the list of attrs available.
|
||
|
'';
|
||
|
example = lib.literalExpression { address = "11:22:33:44:55:66"; };
|
||
|
type = lib.types.attrs;
|
||
|
};
|
||
|
options.bridge = lib.mkOption {
|
||
|
description = "Add this device to this bridge";
|
||
|
default = null;
|
||
|
type = with lib.types; nullOr str;
|
||
|
};
|
||
|
options.macAddress = lib.mkOption {
|
||
|
description = "Change this device's mac address to this";
|
||
|
default = null;
|
||
|
type = with lib.types; nullOr str;
|
||
|
};
|
||
|
options.hostapd = lib.mkOption {
|
||
|
description = "hostapd options";
|
||
|
default = { };
|
||
|
type = lib.types.submodule {
|
||
|
options.enable = lib.mkEnableOption "hostapd";
|
||
|
options.settings = lib.mkOption {
|
||
|
description = "hostapd config";
|
||
|
default = { };
|
||
|
type = lib.types.attrs;
|
||
|
};
|
||
|
};
|
||
|
};
|
||
|
options.dhcpcd = lib.mkOption {
|
||
|
description = "dhcpcd options";
|
||
|
default = { };
|
||
|
type = lib.types.submodule {
|
||
|
options.enable = lib.mkEnableOption "dhcpcd";
|
||
|
options.extraConfig = lib.mkOption {
|
||
|
description = "dhcpcd text config";
|
||
|
default = "";
|
||
|
type = lib.types.lines;
|
||
|
};
|
||
|
};
|
||
|
};
|
||
|
options.ipv4 = lib.mkOption {
|
||
|
description = "IPv4 config";
|
||
|
default = { };
|
||
|
type = lib.types.submodule {
|
||
|
options.addresses = lib.mkOption {
|
||
|
description = "Device's IPv4 addresses";
|
||
|
default = [ ];
|
||
|
type = lib.types.listOf (lib.types.submodule {
|
||
|
options.address = lib.mkOption {
|
||
|
description = "IPv4 address";
|
||
|
type = lib.types.str;
|
||
|
};
|
||
|
options.prefixLength = lib.mkOption {
|
||
|
description = "IPv4 prefix length";
|
||
|
type = lib.types.int;
|
||
|
};
|
||
|
options.assign = lib.mkOption {
|
||
|
description = "Whether to assign this address to the device. Default: no if the first hextet is zero, yes otherwise.";
|
||
|
type = with lib.types; nullOr bool;
|
||
|
default = null;
|
||
|
};
|
||
|
options.gateways = lib.mkOption {
|
||
|
description = "IPv4 gateway addresses (optional)";
|
||
|
default = [ ];
|
||
|
type = with lib.types; listOf str;
|
||
|
};
|
||
|
options.dns = lib.mkOption {
|
||
|
description = "IPv4 DNS servers associated with this device";
|
||
|
type = with lib.types; listOf str;
|
||
|
default = [ ];
|
||
|
};
|
||
|
options.keaSettings = lib.mkOption {
|
||
|
default = { };
|
||
|
type = (pkgs.formats.json {}).type;
|
||
|
example = lib.literalExpression {
|
||
|
pools = [ { pool = "192.168.1.15 - 192.168.1.200"; } ];
|
||
|
option-data = [ {
|
||
|
name = "domain-name-servers";
|
||
|
code = 6;
|
||
|
csv-format = true;
|
||
|
space = "dhcp4";
|
||
|
data = "8.8.8.8, 8.8.4.4";
|
||
|
} ];
|
||
|
};
|
||
|
description = "Kea IPv4 prefix-specific settings";
|
||
|
};
|
||
|
});
|
||
|
};
|
||
|
options.kea = lib.mkOption {
|
||
|
description = "Kea options";
|
||
|
default = { };
|
||
|
type = lib.types.submodule {
|
||
|
options.enable = lib.mkOption {
|
||
|
type = lib.types.bool;
|
||
|
description = "Enable Kea for IPv4";
|
||
|
default = true;
|
||
|
};
|
||
|
options.extraArgs = lib.mkOption {
|
||
|
type = with lib.types; listOf str;
|
||
|
default = [ ];
|
||
|
description = "List of additional arguments to pass to the daemon.";
|
||
|
};
|
||
|
options.configFile = lib.mkOption {
|
||
|
type = with lib.types; nullOr path;
|
||
|
default = null;
|
||
|
description = "Kea config file (takes precedence over settings)";
|
||
|
};
|
||
|
options.settings = lib.mkOption {
|
||
|
default = { };
|
||
|
type = (pkgs.formats.json {}).type;
|
||
|
description = "Kea settings";
|
||
|
};
|
||
|
};
|
||
|
};
|
||
|
};
|
||
|
};
|
||
|
options.ipv6 = lib.mkOption {
|
||
|
description = "IPv6 config";
|
||
|
default = { };
|
||
|
type = lib.types.submodule {
|
||
|
options.addresses = lib.mkOption {
|
||
|
description = "Device's IPv6 addresses";
|
||
|
default = [ ];
|
||
|
type = lib.types.listOf (lib.types.submodule {
|
||
|
options.address = lib.mkOption {
|
||
|
description = "IPv6 address";
|
||
|
type = lib.types.str;
|
||
|
};
|
||
|
options.prefixLength = lib.mkOption {
|
||
|
description = "IPv6 prefix length";
|
||
|
type = lib.types.int;
|
||
|
};
|
||
|
options.assign = lib.mkOption {
|
||
|
description = "Whether to assign this address to the device. Default: no if the first hextet is zero, yes otherwise";
|
||
|
type = with lib.types; nullOr bool;
|
||
|
default = null;
|
||
|
};
|
||
|
options.gateways = lib.mkOption {
|
||
|
description = "IPv6 gateways information (optional)";
|
||
|
default = [ ];
|
||
|
type = with lib.types; listOf (either str (submodule {
|
||
|
options.address = lib.mkOption {
|
||
|
description = "Gateway's IPv6 address";
|
||
|
type = str;
|
||
|
};
|
||
|
options.prefixLength = lib.mkOption {
|
||
|
description = "Gateway's IPv6 prefix length (defaults to interface address's prefix length)";
|
||
|
type = nullOr int;
|
||
|
default = null;
|
||
|
};
|
||
|
options.radvdSettings = lib.mkOption {
|
||
|
default = { };
|
||
|
type = attrsOf (oneOf [ bool str int ]);
|
||
|
example = lib.literalExpression {
|
||
|
AdvRoutePreference = "high";
|
||
|
};
|
||
|
description = "radvd prefix-specific route settings";
|
||
|
};
|
||
|
options.coreradSettings = lib.mkOption {
|
||
|
default = { };
|
||
|
type = (pkgs.formats.toml {}).type;
|
||
|
example = lib.literalExpression {
|
||
|
preference = "high";
|
||
|
};
|
||
|
description = "CoreRAD prefix-specific route settings";
|
||
|
};
|
||
|
}));
|
||
|
};
|
||
|
options.dns = lib.mkOption {
|
||
|
description = "IPv6 DNS servers associated with this device";
|
||
|
type = with lib.types; listOf (either str (submodule {
|
||
|
options.address = lib.mkOption {
|
||
|
description = "DNS server's address";
|
||
|
type = lib.types.str;
|
||
|
};
|
||
|
options.radvdSettings = lib.mkOption {
|
||
|
default = { };
|
||
|
type = attrsOf (oneOf [ bool str int ]);
|
||
|
example = lib.literalExpression { FlushRDNSS = false; };
|
||
|
description = "radvd prefix-specific RDNSS settings";
|
||
|
};
|
||
|
options.coreradSettings = lib.mkOption {
|
||
|
default = { };
|
||
|
type = (pkgs.formats.toml {}).type;
|
||
|
example = lib.literalExpression { lifetime = "auto"; };
|
||
|
description = "CoreRAD prefix-specific RDNSS settings";
|
||
|
};
|
||
|
}));
|
||
|
default = [ ];
|
||
|
};
|
||
|
options.keaSettings = lib.mkOption {
|
||
|
default = { };
|
||
|
type = (pkgs.formats.json {}).type;
|
||
|
example = lib.literalExpression {
|
||
|
pools = [ {
|
||
|
pool = "192.168.1.15 - 192.168.1.200";
|
||
|
} ];
|
||
|
option-data = [ {
|
||
|
name = "dns-servers";
|
||
|
code = 23;
|
||
|
csv-format = true;
|
||
|
space = "dhcp6";
|
||
|
data = "aaaa::, bbbb::";
|
||
|
} ];
|
||
|
};
|
||
|
description = "Kea prefix-specific settings";
|
||
|
};
|
||
|
options.radvdSettings = lib.mkOption {
|
||
|
default = { };
|
||
|
type = with lib.types; attrsOf (oneOf [ bool str int ]);
|
||
|
example = lib.literalExpression {
|
||
|
AdvOnLink = true;
|
||
|
AdvAutonomous = true;
|
||
|
Base6to4Interface = "ppp0";
|
||
|
};
|
||
|
description = "radvd prefix-specific settings";
|
||
|
};
|
||
|
options.coreradSettings = lib.mkOption {
|
||
|
default = { };
|
||
|
type = (pkgs.formats.toml {}).type;
|
||
|
example = lib.literalExpression {
|
||
|
on_link = true;
|
||
|
autonomous = true;
|
||
|
};
|
||
|
description = "CoreRAD prefix-specific settings";
|
||
|
};
|
||
|
});
|
||
|
};
|
||
|
options.kea = lib.mkOption {
|
||
|
description = "Kea options";
|
||
|
default = { };
|
||
|
type = lib.types.submodule {
|
||
|
options.enable = lib.mkEnableOption "Kea for IPv6";
|
||
|
options.extraArgs = lib.mkOption {
|
||
|
type = with lib.types; listOf str;
|
||
|
default = [ ];
|
||
|
description = "List of additional arguments to pass to the daemon.";
|
||
|
};
|
||
|
options.configFile = lib.mkOption {
|
||
|
type = with lib.types; nullOr path;
|
||
|
default = null;
|
||
|
description = "Kea config file (takes precedence over settings)";
|
||
|
};
|
||
|
options.settings = lib.mkOption {
|
||
|
default = { };
|
||
|
type = (pkgs.formats.json {}).type;
|
||
|
description = "Kea settings";
|
||
|
};
|
||
|
};
|
||
|
};
|
||
|
options.radvd = lib.mkOption {
|
||
|
description = "radvd options";
|
||
|
default = { };
|
||
|
type = lib.types.submodule {
|
||
|
options.enable = lib.mkOption {
|
||
|
type = lib.types.bool;
|
||
|
description = "Enable radvd";
|
||
|
default = true;
|
||
|
};
|
||
|
options.interfaceSettings = lib.mkOption {
|
||
|
default = { };
|
||
|
type = with lib.types; attrsOf (oneOf [ bool str int ]);
|
||
|
example = lib.literalExpression {
|
||
|
UnicastOnly = true;
|
||
|
};
|
||
|
description = "radvd interface-specific settings";
|
||
|
};
|
||
|
};
|
||
|
};
|
||
|
options.corerad = lib.mkOption {
|
||
|
description = "CoreRAD options";
|
||
|
default = { };
|
||
|
type = lib.types.submodule {
|
||
|
options.enable = lib.mkEnableOption "CoreRAD (don't forget to disable radvd)";
|
||
|
options.configFile = lib.mkOption {
|
||
|
type = with lib.types; nullOr path;
|
||
|
default = null;
|
||
|
description = "CoreRAD config file (takes precedence over settings)";
|
||
|
};
|
||
|
options.interfaceSettings = lib.mkOption {
|
||
|
default = { };
|
||
|
type = (pkgs.formats.toml {}).type;
|
||
|
description = "CoreRAD interface-specific settings";
|
||
|
};
|
||
|
options.settings = lib.mkOption {
|
||
|
default = { };
|
||
|
type = (pkgs.formats.toml {}).type;
|
||
|
example = lib.literalExpression {
|
||
|
debug.address = "localhost:9430";
|
||
|
debug.prometheus = true;
|
||
|
};
|
||
|
description = "General CoreRAD settings";
|
||
|
};
|
||
|
};
|
||
|
};
|
||
|
};
|
||
|
};
|
||
|
});
|
||
|
};
|
||
|
};
|
||
|
|
||
|
config = lib.mkIf cfg.enable {
|
||
|
environment.systemPackages = with pkgs; [
|
||
|
dig.dnsutils
|
||
|
ethtool
|
||
|
tcpdump
|
||
|
];
|
||
|
|
||
|
# performance tweaks
|
||
|
powerManagement.cpuFreqGovernor = lib.mkDefault "ondemand";
|
||
|
services.irqbalance.enable = lib.mkDefault true;
|
||
|
boot.kernelPackages = lib.mkDefault pkgs.linuxPackages_xanmod;
|
||
|
|
||
|
boot.kernel.sysctl = {
|
||
|
"net.netfilter.nf_log_all_netns" = true;
|
||
|
"net.ipv4.conf.all.forwarding" = true;
|
||
|
"net.ipv4.conf.default.forwarding" = true;
|
||
|
"net.ipv6.conf.all.forwarding" = config.networking.enableIPv6;
|
||
|
"net.ipv6.conf.default.forwarding" = config.networking.enableIPv6;
|
||
|
};
|
||
|
|
||
|
networking.enableIPv6 = lib.mkDefault true;
|
||
|
networking.usePredictableInterfaceNames = true;
|
||
|
networking.firewall.allowPing = lib.mkDefault true;
|
||
|
networking.firewall.rejectPackets = lib.mkDefault false; # drop rather than reject
|
||
|
services.udev.extraRules =
|
||
|
let
|
||
|
devs = lib.filterAttrs (k: v: (v.matchUdevAttrs or { }) != { }) cfg.interfaces;
|
||
|
in lib.mkIf (devs != { })
|
||
|
(builtins.concatStringsSep "\n" (lib.mapAttrsToList (k: v:
|
||
|
let
|
||
|
attrs = lib.mapAttrsToList (k: v: "ATTR{${k}}==${builtins.toJSON (toString v)}") v.matchUdevAttrs;
|
||
|
in ''
|
||
|
SUBSYSTEM=="net", ACTION=="add", ${builtins.concatStringsSep ", " attrs}, NAME="${k}"
|
||
|
'') devs));
|
||
|
networking.interfaces = builtins.mapAttrs (interface: icfg: {
|
||
|
ipv4.addresses = map
|
||
|
({ address, prefixLength, ... }: { inherit address prefixLength; })
|
||
|
(builtins.filter
|
||
|
(x: x.assign == true || (x.assign == null && (lib.hasPrefix "0." x.address)))
|
||
|
icfg.ipv4.addresses);
|
||
|
ipv6.addresses = map
|
||
|
({ address, prefixLength, ... }: { inherit address prefixLength; })
|
||
|
(builtins.filter
|
||
|
(x: x.assign == true || (x.assign == null && (lib.hasPrefix ":" x.address || lib.hasPrefix "0:" x.address)))
|
||
|
icfg.ipv6.addresses);
|
||
|
} // lib.optionalAttrs (icfg.macAddress != null) {
|
||
|
inherit (icfg) macAddress;
|
||
|
}) cfg.interfaces;
|
||
|
networking.bridges =
|
||
|
builtins.zipAttrsWith
|
||
|
(k: vs: { interfaces = vs; })
|
||
|
(lib.mapAttrsToList
|
||
|
(interface: icfg:
|
||
|
if icfg.bridge != null && !icfg.hostapd.enable then {
|
||
|
${icfg.bridge} = interface;
|
||
|
} else {})
|
||
|
cfg.interfaces);
|
||
|
networking.useDHCP = lib.mkIf (builtins.any (x: x.dhcpcd.enable) (builtins.attrValues cfg.interfaces)) false;
|
||
|
};
|
||
|
}
|