Files
nixos/modules/vlans.nix
Joakim Repomaa 13e119a6c3
Some checks failed
Build Images / build (push) Failing after 1m21s
Check / check (push) Successful in 3m36s
apu: setup dynamic dns record based on DHCP leases
2026-02-21 13:05:22 +02:00

218 lines
4.4 KiB
Nix

{ lib, config, ... }:
let
types = {
portList = lib.types.listOf lib.types.int;
service = lib.types.submodule {
options = {
tcp = lib.mkOption {
type = types.portList;
default = [ ];
};
udp = lib.mkOption {
type = types.portList;
default = [ ];
};
};
};
vlan = lib.types.submodule {
options = {
id = lib.mkOption {
type = lib.types.int;
};
staticLeases = lib.mkOption {
type = lib.types.attrsOf lib.types.str;
default = { };
};
ipv6 = lib.mkEnableOption "ipv6";
domain = lib.mkOption {
type = lib.types.str;
default = "home.arpa";
description = "Domain name to advertise via DHCP";
};
};
};
bridge = lib.types.submodule {
options = {
enable = lib.mkEnableOption "Enable bridge";
pvid = lib.mkOption {
type = lib.types.int;
};
bindNetwork = lib.mkOption {
type = lib.types.str;
};
network = lib.mkOption {
type = lib.types.str;
};
netdev = lib.mkOption {
type = lib.types.str;
};
};
};
};
cfg = config.modules.vlans;
vlans = lib.mapAttrsToList (
attrName:
{
name ? attrName,
...
}@vlan:
({ inherit name; } // vlan)
) cfg.networks;
vlanAttrs = lib.listToAttrs (
lib.map (
{ name, ... }@value:
{
inherit name value;
}
) vlans
);
buildNetdev =
name:
{ id, ... }:
{
name = "20-${name}";
value = {
netdevConfig = {
Name = name;
Kind = "vlan";
MACAddress = "00:0d:b9:49:d2:${toString id}";
};
vlanConfig = {
Id = id;
};
};
};
buildStaticLease = macAddress: address: ''
[DHCPServerStaticLease]
MACAddress=${macAddress}
Address=${address}
'';
buildNetwork =
name:
{
id,
ipv6,
staticLeases,
domain,
...
}:
{
name = "30-${name}";
value = {
matchConfig = {
Name = name;
};
networkConfig = {
Address = "10.${toString id}.0.1/23";
IPMasquerade = "ipv4";
DHCPServer = true;
IPv6AcceptRA = false;
IPv6SendRA = ipv6;
DHCPPrefixDelegation = ipv6;
};
dhcpServerConfig = {
PoolOffset = 255;
DNS = "10.${toString id}.0.1";
EmitDNS = true;
};
extraConfig = lib.concatLines (
(lib.mapAttrsToList buildStaticLease staticLeases)
++ [
''
[DHCPServer]
SendOption=15:string:${domain}
''
]
);
};
};
vlanIds = lib.mapAttrsToList (_: { id, ... }: id) vlanAttrs;
vlanRange = {
min = lib.foldr lib.min 999 vlanIds;
max = lib.foldr lib.max 0 vlanIds;
};
bridgeVLANConfig = ''
[BridgeVLAN]
VLAN=${toString vlanRange.min}-${toString vlanRange.max}
EgressUntagged=${toString cfg.bridge.pvid}
PVID=${toString cfg.bridge.pvid}
'';
bridgeNetDev =
if (cfg.bridge.enable) then
{
"${cfg.bridge.netdev}".bridgeConfig = {
VLANFiltering = true;
DefaultPVID = cfg.bridge.pvid;
};
}
else
{ };
bridgeBindNetwork =
if (cfg.bridge.enable) then
{
${cfg.bridge.bindNetwork}.extraConfig = bridgeVLANConfig;
}
else
{ };
bridgeNetwork =
if (cfg.bridge.enable) then
{
"${cfg.bridge.network}" = {
vlan = lib.map ({ name, ... }: name) vlans;
bridgeVLANs = lib.map (
{ id, ... }:
{
VLAN = id;
}
) vlans;
};
}
else
{ };
netdevs = lib.mergeAttrsList [
(lib.mapAttrs' buildNetdev vlanAttrs)
bridgeNetDev
];
networks = lib.mergeAttrsList [
(lib.mapAttrs' buildNetwork vlanAttrs)
bridgeBindNetwork
bridgeNetwork
];
in
{
options.modules.vlans = {
enable = lib.mkEnableOption "Enable VLANs";
networks = lib.mkOption {
type = lib.types.attrsOf types.vlan;
};
bridge = lib.mkOption {
type = types.bridge;
};
};
config = lib.mkIf cfg.enable {
systemd.network = {
enable = lib.mkDefault true;
inherit netdevs networks;
};
};
}