{ hostName, config, lib, ... }: let hostConf = config.site.hosts.${hostName}; upstreamInterfaces = lib.filterAttrs (_: { upstream, ... }: upstream != null) hostConf.interfaces; firstUpstreamInterface = if builtins.length (builtins.attrNames upstreamInterfaces) > 0 then builtins.head ( builtins.attrNames upstreamInterfaces ) else null; enabled = firstUpstreamInterface != null; in { systemd.network.networks = { core = { # systemd-networkd only requests Prefix Delegation via DHCPv6 on # the upstream interface if another interface is configured for it. # without this, the static ipv6 subnet won't be routed to us. networkConfig.DHCPv6PrefixDelegation = true; dhcpV6PrefixDelegationConfig = { SubnetId = "81"; # because we have static addresses, we don't actually use this Assign = false; }; }; } // builtins.mapAttrs (_: { upstream, ... }: { DHCP = "yes"; networkConfig.IPv6AcceptRA = true; dhcpV6Config.PrefixDelegationHint = "::/56"; # Traffic Shaping extraConfig = '' [CAKE] Parent = root ${lib.optionalString (upstream.provider == "vodafone") '' # DOCSIS overhead OverheadBytes = 18 ''} ${lib.optionalString (upstream.provider == "dsi") '' # PPPoE overhead OverheadBytes = 18 ''} ${lib.optionalString (upstream.upBandwidth != null) '' Bandwidth = ${toString upstream.upBandwidth}K ''} ''; }) upstreamInterfaces; networking.nat = lib.optionalAttrs enabled { enable = true; internalInterfaces = [ "core" ]; externalInterface = firstUpstreamInterface; externalIP = upstreamInterfaces.${firstUpstreamInterface}.upstream.staticIpv4Address; extraCommands = '' # Prohibit SMTP except for servers iptables -N fwd_smtp iptables -A fwd_smtp --source ${config.site.net.serv.subnet4} -j RETURN iptables -A fwd_smtp -j REJECT iptables -I FORWARD -p tcp --dport 25 -j fwd_smtp ip6tables -N fwd_smtp ${lib.concatMapStrings (subnet6: '' ip6tables -A fwd_smtp --source ${subnet6} -j RETURN ip6tables -A fwd_smtp --dest ${subnet6} -j RETURN '') (builtins.attrValues config.site.net.serv.subnets6)} ip6tables -A fwd_smtp -j REJECT ip6tables -I FORWARD -p tcp --dport 25 -j fwd_smtp # Provide IPv6 upstream for everyone, using NAT66 when not from # our static prefixes ${lib.concatMapStringsSep "\n" (net: '' ip6tables -t nat -N ${net}_nat || \ ip6tables -t nat -F ${net}_nat ${lib.concatMapStringsSep "\n" (subnet: '' ip6tables -t nat -A ${net}_nat \ -s ${subnet} \ -j RETURN '') upstreamInterfaces.${net}.upstream.noNat.subnets6} ip6tables -t nat -A ${net}_nat -j MASQUERADE ip6tables -t nat -A POSTROUTING \ -o ${net} \ -j ${net}_nat '') (builtins.attrNames upstreamInterfaces)} ''; extraStopCommands = '' iptables -F FORWARD 2>/dev/null || true ip6tables -F FORWARD 2>/dev/null || true ip6tables -t nat -F POSTROUTING 2>/dev/null || true ${lib.concatMapStringsSep "\n" (net: '' ip6tables -t nat -F ${net}_nat 2>/dev/null || true ip6tables -t nat -X ${net}_nat 2>/dev/null || true '') (builtins.attrNames upstreamInterfaces)} ''; forwardPorts = map ({ destination, sourcePort, reflect, ... }@forwardedPort: removeAttrs forwardedPort ["reflect"] // { destination = if builtins.match ".*:.*" destination != null then destination else "${destination}:${toString sourcePort}"; loopbackIPs = if reflect then builtins.filter (ip: ip != null) ( map (net: upstreamInterfaces.${net}.upstream.staticIpv4Address ) (builtins.attrNames upstreamInterfaces) ) else []; } ) hostConf.forwardPorts; }; }