{ config, pkgs, lib, ... }: with lib; let cfg = config.sixnix; in { options.sixnix = { enable = mkEnableOption "sixnix networking configuration"; hostname = mkOption { type = types.str; description = "Server hostname"; }; domain = mkOption { type = types.str; default = "notwork.in"; description = "Domain name"; }; interface = mkOption { type = types.str; default = "enp0s3"; description = "Network interface name"; }; dns = mkOption { type = types.listOf types.str; default = [ "9.9.9.9" "2620:fe::fe" ]; description = "DNS resolvers for systemd-networkd"; }; ipv4 = { address = mkOption { type = types.nullOr types.str; default = null; description = "IPv4 address with CIDR (documentation only - using DHCP)"; }; gateway = mkOption { type = types.nullOr types.str; default = null; description = "IPv4 gateway (documentation only - using DHCP)"; }; }; ipv6 = { address = mkOption { type = types.nullOr types.str; default = null; description = "IPv6 address with CIDR (documentation only - using SLAAC)"; }; gateway = mkOption { type = types.str; default = "fe80::1"; description = "IPv6 gateway (documentation only - using SLAAC)"; }; }; bgp = { enable = mkOption { type = types.bool; default = false; description = "Enable BGP/FRR for shared IP failover"; }; peers = mkOption { type = types.listOf types.str; default = []; example = [ "2001:db8::1" "2001:db8::2" "2001:db8::3" "2001:db8::4" ]; description = "List of BGP peer addresses (e.g., Linode route servers)"; }; advertisedSubnets = mkOption { type = types.listOf types.str; default = []; example = [ "2001:db8:1234::/56" ]; description = "List of IPv6 subnets to advertise via BGP"; }; routeMap = mkOption { type = types.enum [ "primary" "secondary" ]; description = "BGP route map type (primary/secondary for HA)"; }; }; wireguard = { enable = mkOption { type = types.bool; default = false; description = "Enable WireGuard configuration"; }; }; }; imports = [ ./wireguard.nix ]; config = mkIf cfg.enable { networking.hostName = cfg.hostname; networking.domain = cfg.domain; networking.nftables.enable = true; # Enable systemd-networkd systemd.network.enable = true; networking.useNetworkd = true; # Tools for monitoring/key generation environment.systemPackages = with pkgs; [ wireguard-tools tcpdump ]; # Configure network interface - pure SLAAC/DHCP systemd.network.networks."10-egress" = { matchConfig.Name = cfg.interface; networkConfig = { DHCP = "yes"; # Enable both IPv4 and IPv6 DHCP IPv6AcceptRA = "yes"; IPv6PrivacyExtensions = "no"; IPv6Forwarding = "yes"; }; dns = cfg.dns; }; # Add blackhole routes for advertised subnets on loopback systemd.network.networks."10-loopback" = mkIf cfg.bgp.enable { matchConfig.Name = "lo"; routes = map (subnet: { Destination = subnet; Type = "blackhole"; }) cfg.bgp.advertisedSubnets; }; # Create dummy interface for shared WireGuard endpoint IP (HA setup) systemd.network.netdevs."20-shared-dummy" = mkIf cfg.bgp.enable { netdevConfig = { Kind = "dummy"; Name = "shared0"; }; }; systemd.network.networks."20-shared-dummy" = mkIf cfg.bgp.enable { matchConfig.Name = "shared0"; address = [ "2600:3c08:e002:6cff::1/128" ]; }; boot.kernel.sysctl = { "net.ipv6.conf.all.forwarding" = 1; "net.ipv6.conf.${cfg.interface}.accept_ra" = 2; # Accept prefix info "net.ipv6.conf.${cfg.interface}.accept_ra_pinfo" = 1; # Accept default router "net.ipv6.conf.${cfg.interface}.accept_ra_defrtr" = 1; }; # BGP configuration # Reference: https://techdocs.akamai.com/cloud-computing/docs/configuring-ip-failover-over-bgp-using-frr-advanced services.frr.bgpd.enable = cfg.bgp.enable; services.frr.config = mkIf cfg.bgp.enable (let peerConfig = concatMapStringsSep "\n" (peer: " neighbor ${peer} peer-group RS") cfg.bgp.peers; networkConfig = concatMapStringsSep "\n" (subnet: " network ${subnet} route-map ${cfg.bgp.routeMap}") cfg.bgp.advertisedSubnets; in '' hostname ${cfg.hostname} router bgp 65001 no bgp ebgp-requires-policy coalesce-time 1000 bgp bestpath as-path multipath-relax neighbor RS peer-group neighbor RS remote-as external neighbor RS ebgp-multihop 10 neighbor RS capability extended-nexthop ${peerConfig} address-family ipv6 unicast ${networkConfig} redistribute static neighbor RS activate exit-address-family route-map primary permit 10 set community 65000:1 route-map secondary permit 10 set community 65000:2 ipv6 nht resolve-via-default ''); }; }