215 lines
6.9 KiB
Nix
215 lines
6.9 KiB
Nix
{ ... }:
|
|
{
|
|
_class = "clan.service";
|
|
manifest.name = "gw-router";
|
|
manifest.description = "A gateway router service to configure most of a router features";
|
|
manifest.readme = "A gateway router service to configure most of a router features";
|
|
manifest.categories = [ "System" ];
|
|
|
|
roles.default = {
|
|
description = "Site gateway router role";
|
|
|
|
interface =
|
|
{ lib, config, ... }:
|
|
let
|
|
vlanModule =
|
|
{ name, config, ... }:
|
|
let
|
|
octet = toString config.id;
|
|
in
|
|
{
|
|
options = {
|
|
id = lib.mkOption {
|
|
type = lib.types.ints.between 1 4094;
|
|
description = "802.1Q VLAN id.";
|
|
};
|
|
address = lib.mkOption {
|
|
type = lib.types.str;
|
|
example = "10.0.10.1";
|
|
description = "Router address on this VLAN.";
|
|
};
|
|
prefixLength = lib.mkOption {
|
|
type = lib.types.ints.between 8 30;
|
|
default = 24;
|
|
};
|
|
subnet = lib.mkOption {
|
|
type = lib.types.str;
|
|
example = "10.0.10.0/24";
|
|
description = "The VLAN's network in CIDR form (must contain `address`).";
|
|
};
|
|
dhcp = {
|
|
enable = lib.mkOption {
|
|
type = lib.types.bool;
|
|
default = true;
|
|
};
|
|
pool = {
|
|
from = lib.mkOption {
|
|
type = lib.types.str;
|
|
example = "10.0.10.100";
|
|
};
|
|
to = lib.mkOption {
|
|
type = lib.types.str;
|
|
example = "10.0.10.199";
|
|
};
|
|
};
|
|
leaseTime = lib.mkOption {
|
|
type = lib.types.ints.positive;
|
|
default = 86400;
|
|
description = ''
|
|
Lease validity in seconds. Lower it for high-churn networks,
|
|
e.g. public-WiFi guest VLANs (3600-7200), so the pool recycles.
|
|
'';
|
|
};
|
|
fixedIPs = lib.mkOption {
|
|
type = lib.types.attrsOf (
|
|
lib.types.submodule {
|
|
options = {
|
|
hwAddress = lib.mkOption {
|
|
type = lib.types.str;
|
|
example = "aa:bb:cc:dd:ee:ff";
|
|
description = "Client MAC address.";
|
|
};
|
|
ipAddress = lib.mkOption {
|
|
type = lib.types.str;
|
|
description = "Fixed address handed to this client (inside the VLAN's subnet, outside the pool).";
|
|
};
|
|
};
|
|
}
|
|
);
|
|
default = { };
|
|
description = "Static DHCP leases; the attribute name becomes the client's hostname.";
|
|
};
|
|
};
|
|
allowWAN = lib.mkOption {
|
|
type = lib.types.bool;
|
|
default = true;
|
|
description = "Whether clients on this VLAN may reach the internet.";
|
|
};
|
|
};
|
|
};
|
|
in
|
|
{
|
|
options = {
|
|
wan = {
|
|
interface = lib.mkOption {
|
|
type = lib.types.str;
|
|
description = "Physical WAN port the PPPoE session runs on.";
|
|
};
|
|
|
|
vlanId = lib.mkOption {
|
|
type = lib.types.nullOr (lib.types.ints.between 1 4094);
|
|
default = null;
|
|
description = ''
|
|
802.1Q tag the ISP requires for the PPPoE session (AIS Thailand: 10);
|
|
null for untagged PPPoE directly on the port. Unrelated to the LAN
|
|
VLANs — this tag exists only on the WAN port.
|
|
'';
|
|
};
|
|
|
|
macAddress = lib.mkOption {
|
|
type = lib.types.nullOr lib.types.str;
|
|
default = null;
|
|
example = "aa:bb:cc:dd:ee:ff";
|
|
description = ''
|
|
Spoofed MAC for the WAN port, e.g. to keep the MAC the ISP has
|
|
pinned (cloned from the old router). null keeps the hardware MAC.
|
|
'';
|
|
};
|
|
|
|
pppInterface = lib.mkOption {
|
|
type = lib.types.str;
|
|
internal = true;
|
|
readOnly = true;
|
|
default = if config.wan.vlanId == null then config.wan.interface else "wan-vlan";
|
|
description = "Interface pppd dials on (the WAN port or its ISP VLAN).";
|
|
};
|
|
};
|
|
|
|
vlans = lib.mkOption {
|
|
type = lib.types.attrsOf (lib.types.submodule vlanModule);
|
|
description = "VLANs setup for this router";
|
|
};
|
|
|
|
trunkPorts = lib.mkOption {
|
|
type = lib.types.listOf lib.types.str;
|
|
description = "LAN ports carrying all VLANs tagged (incl. any 10G SFP+ ports).";
|
|
};
|
|
|
|
accessPorts = lib.mkOption {
|
|
type = lib.types.listOf lib.types.str;
|
|
description = ''
|
|
Ports acting as untagged access ports on a single VLAN (port name ->
|
|
VLAN name). Frames are untagged on the wire; the bridge tags them with
|
|
the VLAN's PVID. Use for an always-available on-site mgmt port.
|
|
'';
|
|
};
|
|
};
|
|
};
|
|
|
|
perInstance =
|
|
{ settings, ... }:
|
|
{
|
|
nixosModule =
|
|
{
|
|
config,
|
|
pkgs,
|
|
lib,
|
|
...
|
|
}:
|
|
let
|
|
vlanIf = name: "vlan=${name}";
|
|
in
|
|
{
|
|
|
|
networking.useNetworkd = true;
|
|
networking.useDHCP = false;
|
|
systemd.network.enable = true;
|
|
|
|
systemd.network.netdevs = {
|
|
"20-br0" = {
|
|
netdevConfig = {
|
|
Name = "br0";
|
|
Kind = "bridge";
|
|
};
|
|
bridgeConfig = {
|
|
VLANFiltering = true;
|
|
};
|
|
};
|
|
}
|
|
// lib.optionalAttrs (settings.wan.vlanId != null) {
|
|
"15-wan-vlan" = {
|
|
netdevConfig = {
|
|
Name = "wan-vlan";
|
|
Kind = "vlan";
|
|
};
|
|
vlanConfig.Id = settings.wan.vlanId;
|
|
};
|
|
}
|
|
// lib.mapAttrs' (
|
|
name: vlan:
|
|
lib.nameValuePair "30-${vlanIf name}" {
|
|
netdevConfig = {
|
|
Name = vlanIf name;
|
|
Kind = "vlan";
|
|
};
|
|
vlanConfig.Id = vlan.id;
|
|
}
|
|
) settings.vlans;
|
|
|
|
environment.systemPackages = with pkgs; [
|
|
tcpdump
|
|
mtr
|
|
ethtool
|
|
conntrack-tools
|
|
knot-dns
|
|
iftop
|
|
librespeed-cli
|
|
];
|
|
|
|
};
|
|
};
|
|
};
|
|
|
|
imports = [ ./pppoe.nix ];
|
|
}
|