6 cfg = config.networking.nat;
11 else "snat ${externalIP}";
12 dest = mkDest cfg.externalIP;
13 destIPv6 = mkDest cfg.externalIPv6;
15 toNftSet = list: concatStringsSep ", " list;
16 toNftRange = ports: replaceStrings [ ":" ] [ "-" ] (toString ports);
18 ifaceSet = toNftSet (map (x: ''"${x}"'') cfg.internalInterfaces);
19 ipSet = toNftSet cfg.internalIPs;
20 ipv6Set = toNftSet cfg.internalIPv6s;
21 oifExpr = optionalString (cfg.externalInterface != null) ''oifname "${cfg.externalInterface}"'';
23 # Whether given IP (plus optional port) is an IPv6.
24 isIPv6 = ip: length (lib.splitString ":" ip) > 2;
26 splitIPPorts = IPPorts:
28 matchIP = if isIPv6 IPPorts then "[[]([0-9a-fA-F:]+)[]]" else "([0-9.]+)";
29 m = builtins.match "${matchIP}:([0-9-]+)" IPPorts;
32 IP = if m == null then throw "bad ip:ports `${IPPorts}'" else elemAt m 0;
33 ports = if m == null then throw "bad ip:ports `${IPPorts}'" else elemAt m 1;
36 mkTable = { ipVer, dest, ipSet, forwardPorts, dmzHost }:
38 # nftables maps for port forward
39 # l4proto . dport : addr . port
40 fwdMap = toNftSet (map
42 with (splitIPPorts fwd.destination);
43 "${fwd.proto} . ${toNftRange fwd.sourcePort} : ${IP} . ${ports}"
47 # nftables maps for port forward loopback dnat
48 # daddr . l4proto . dport : addr . port
49 fwdLoopDnatMap = toNftSet (concatMap
52 with (splitIPPorts fwd.destination);
53 "${loopbackip} . ${fwd.proto} . ${toNftRange fwd.sourcePort} : ${IP} . ${ports}"
58 # nftables set for port forward loopback snat
59 # daddr . l4proto . dport
60 fwdLoopSnatSet = toNftSet (map
62 with (splitIPPorts fwd.destination);
63 "${IP} . ${fwd.proto} . ${ports}"
69 type nat hook prerouting priority dstnat;
71 ${optionalString (fwdMap != "") ''
72 iifname "${cfg.externalInterface}" meta l4proto { tcp, udp } dnat meta l4proto . th dport map { ${fwdMap} } comment "port forward"
75 ${optionalString (fwdLoopDnatMap != "") ''
76 meta l4proto { tcp, udp } dnat ${ipVer} daddr . meta l4proto . th dport map { ${fwdLoopDnatMap} } comment "port forward loopback from other hosts behind NAT"
79 ${optionalString (dmzHost != null) ''
80 iifname "${cfg.externalInterface}" dnat ${dmzHost} comment "dmz"
85 type nat hook postrouting priority srcnat;
87 ${optionalString (ifaceSet != "") ''
88 iifname { ${ifaceSet} } ${oifExpr} ${dest} comment "from internal interfaces"
90 ${optionalString (ipSet != "") ''
91 ${ipVer} saddr { ${ipSet} } ${oifExpr} ${dest} comment "from internal IPs"
94 ${optionalString (fwdLoopSnatSet != "") ''
95 iifname != "${cfg.externalInterface}" ${ipVer} daddr . meta l4proto . th dport { ${fwdLoopSnatSet} } masquerade comment "port forward loopback snat"
100 type nat hook output priority mangle;
102 ${optionalString (fwdLoopDnatMap != "") ''
103 meta l4proto { tcp, udp } dnat ${ipVer} daddr . meta l4proto . th dport map { ${fwdLoopDnatMap} } comment "port forward loopback from the host itself"
112 config = mkIf (config.networking.nftables.enable && cfg.enable) {
116 assertion = cfg.extraCommands == "";
117 message = "extraCommands is incompatible with the nftables based nat module: ${cfg.extraCommands}";
120 assertion = cfg.extraStopCommands == "";
121 message = "extraStopCommands is incompatible with the nftables based nat module: ${cfg.extraStopCommands}";
124 assertion = config.networking.nftables.rulesetFile == null;
125 message = "networking.nftables.rulesetFile conflicts with the nat module";
129 networking.nftables.tables = {
135 forwardPorts = filter (x: !(isIPv6 x.destination)) cfg.forwardPorts;
136 inherit (cfg) dmzHost;
139 "nixos-nat6" = mkIf cfg.enableIPv6 {
146 forwardPorts = filter (x: isIPv6 x.destination) cfg.forwardPorts;
152 networking.firewall.extraForwardRules = optionalString config.networking.firewall.filterForward ''
153 ${optionalString (ifaceSet != "") ''
154 iifname { ${ifaceSet} } ${oifExpr} accept comment "from internal interfaces"
156 ${optionalString (ipSet != "") ''
157 ip saddr { ${ipSet} } ${oifExpr} accept comment "from internal IPs"
159 ${optionalString (ipv6Set != "") ''
160 ip6 saddr { ${ipv6Set} } ${oifExpr} accept comment "from internal IPv6s"