9 cfg = config.networking.jool;
11 jool = config.boot.kernelPackages.jool;
12 jool-cli = pkgs.jool-cli;
15 # Run as unprivileged user
20 # Restrict filesystem to only read the jool module
21 TemporaryFileSystem = [ "/" ];
24 "/run/booted-system/kernel-modules"
27 # Give capabilities to load the module and configure it
28 AmbientCapabilities = [
32 RestrictAddressFamilies = [ "AF_NETLINK" ];
35 RestrictNamespaces = [ "net" ];
40 CapabilityBoundingSet = [
46 configFormat = pkgs.formats.json { };
48 # Generate the config file of instance `name`
50 name: configFormat.generate "jool-nat64-${name}.conf" (cfg.nat64.${name} // { instance = name; });
52 name: configFormat.generate "jool-siit-${name}.conf" (cfg.siit.${name} // { instance = name; });
55 nat64Options = lib.types.submodule {
56 # The format is plain JSON
57 freeformType = configFormat.type;
58 # Some options with a default value
59 options.framework = lib.mkOption {
60 type = lib.types.enum [
64 default = "netfilter";
66 The framework to use for attaching Jool's translation to the exist
67 kernel packet processing rules. See the
68 [documentation](https://nicmx.github.io/Jool/en/intro-jool.html#design)
69 for the differences between the two options.
72 options.global.pool6 = lib.mkOption {
73 type = lib.types.strMatching "[[:xdigit:]:]+/[[:digit:]]+" // {
74 description = "Network prefix in CIDR notation";
76 default = "64:ff9b::/96";
78 The prefix used for embedding IPv4 into IPv6 addresses.
79 Defaults to the well-known NAT64 prefix, defined by
80 [RFC 6052](https://datatracker.ietf.org/doc/html/rfc6052).
86 siitOptions = lib.types.submodule {
87 # The format is, again, plain JSON
88 freeformType = configFormat.type;
89 # Some options with a default value
90 options = { inherit (nat64Options.getSubOptions [ ]) framework; };
93 makeNat64Unit = name: opts: {
94 "jool-nat64-${name}" = {
95 description = "Jool, NAT64 setup of instance ${name}";
96 documentation = [ "https://nicmx.github.io/Jool/en/documentation.html" ];
97 after = [ "network.target" ];
98 wantedBy = [ "multi-user.target" ];
101 RemainAfterExit = true;
102 ExecStartPre = "${pkgs.kmod}/bin/modprobe jool";
103 ExecStart = "${jool-cli}/bin/jool file handle ${nat64Conf name}";
104 ExecStop = "${jool-cli}/bin/jool -f ${nat64Conf name} instance remove";
109 makeSiitUnit = name: opts: {
110 "jool-siit-${name}" = {
111 description = "Jool, SIIT setup of instance ${name}";
112 documentation = [ "https://nicmx.github.io/Jool/en/documentation.html" ];
113 after = [ "network.target" ];
114 wantedBy = [ "multi-user.target" ];
117 RemainAfterExit = true;
118 ExecStartPre = "${pkgs.kmod}/bin/modprobe jool_siit";
119 ExecStart = "${jool-cli}/bin/jool_siit file handle ${siitConf name}";
120 ExecStop = "${jool-cli}/bin/jool_siit -f ${siitConf name} instance remove";
125 checkNat64 = name: _: ''
126 printf 'Validating Jool configuration for NAT64 instance "${name}"... '
127 jool file check ${nat64Conf name}
128 printf 'Ok.\n'; touch "$out"
131 checkSiit = name: _: ''
132 printf 'Validating Jool configuration for SIIT instance "${name}"... '
133 jool_siit file check ${siitConf name}
134 printf 'Ok.\n'; touch "$out"
141 networking.jool.enable = lib.mkOption {
142 type = lib.types.bool;
149 Whether to enable Jool, an Open Source implementation of IPv4/IPv6
150 translation on Linux.
152 Jool can perform stateless IP/ICMP translation (SIIT) or stateful
153 NAT64, analogous to the IPv4 NAPT. Refer to the upstream
154 [documentation](https://nicmx.github.io/Jool/en/intro-xlat.html) for
155 the supported modes of translation and how to configure them.
157 Enabling this option will install the Jool kernel module and the
158 command line tools for controlling it.
162 networking.jool.nat64 = lib.mkOption {
163 type = lib.types.attrsOf nat64Options;
165 example = lib.literalExpression ''
168 # custom NAT64 prefix
169 global.pool6 = "2001:db8:64::/96";
173 { # SSH 192.0.2.16 → 2001:db8:a::1
175 "ipv4 address" = "192.0.2.16#22";
176 "ipv6 address" = "2001:db8:a::1#22";
178 { # DNS (TCP) 192.0.2.16 → 2001:db8:a::2
180 "ipv4 address" = "192.0.2.16#53";
181 "ipv6 address" = "2001:db8:a::2#53";
183 { # DNS (UDP) 192.0.2.16 → 2001:db8:a::2
185 "ipv4 address" = "192.0.2.16#53";
186 "ipv6 address" = "2001:db8:a::2#53";
191 # Port ranges for dynamic translation
192 { protocol = "TCP"; prefix = "192.0.2.16/32"; "port range" = "40001-65535"; }
193 { protocol = "UDP"; prefix = "192.0.2.16/32"; "port range" = "40001-65535"; }
194 { protocol = "ICMP"; prefix = "192.0.2.16/32"; "port range" = "40001-65535"; }
196 # Ports for static BIB entries
197 { protocol = "TCP"; prefix = "192.0.2.16/32"; "port range" = "22"; }
198 { protocol = "UDP"; prefix = "192.0.2.16/32"; "port range" = "53"; }
204 Definitions of NAT64 instances of Jool.
206 [documentation](https://nicmx.github.io/Jool/en/config-atomic.html) for
207 the available options. Also check out the
208 [tutorial](https://nicmx.github.io/Jool/en/run-nat64.html) for an
209 introduction to NAT64 and how to troubleshoot the setup.
211 The attribute name defines the name of the instance, with the main one
212 being `default`: this can be accessed from the command line without
213 specifying the name with `-i`.
216 Instances created imperatively from the command line will not interfere
217 with the NixOS instances, provided the respective `pool4` addresses and
218 port ranges are not overlapping.
222 Changes to an instance performed via `jool -i <name>` are applied
223 correctly but will be lost after restarting the respective
224 `jool-nat64-<name>.service`.
229 networking.jool.siit = lib.mkOption {
230 type = lib.types.attrsOf siitOptions;
232 example = lib.literalExpression ''
235 # Maps any IPv4 address x.y.z.t to 2001:db8::x.y.z.t and v.v.
236 global.pool6 = "2001:db8::/96";
238 # Explicit address mappings
240 # 2001:db8:1:: ←→ 192.0.2.0
241 { "ipv6 prefix" = "2001:db8:1::/128"; "ipv4 prefix" = "192.0.2.0"; }
242 # 2001:db8:1::x ←→ 198.51.100.x
243 { "ipv6 prefix" = "2001:db8:2::/120"; "ipv4 prefix" = "198.51.100.0/24"; }
249 Definitions of SIIT instances of Jool.
251 [documentation](https://nicmx.github.io/Jool/en/config-atomic.html) for
252 the available options. Also check out the
253 [tutorial](https://nicmx.github.io/Jool/en/run-vanilla.html) for an
254 introduction to SIIT and how to troubleshoot the setup.
256 The attribute name defines the name of the instance, with the main one
257 being `default`: this can be accessed from the command line without
258 specifying the name with `-i`.
261 Instances created imperatively from the command line will not interfere
262 with the NixOS instances, provided the respective EAMT addresses and
263 port ranges are not overlapping.
267 Changes to an instance performed via `jool -i <name>` are applied
268 correctly but will be lost after restarting the respective
269 `jool-siit-<name>.service`.
276 config = lib.mkIf cfg.enable {
277 # Install kernel module and cli tools
278 boot.extraModulePackages = [ jool ];
279 environment.systemPackages = [ jool-cli ];
281 # Install services for each instance
282 systemd.services = lib.mkMerge (
283 lib.mapAttrsToList makeNat64Unit cfg.nat64 ++ lib.mapAttrsToList makeSiitUnit cfg.siit
286 # Check the configuration of each instance
287 system.checks = lib.optional (cfg.nat64 != { } || cfg.siit != { }) (
288 pkgs.runCommand "jool-validated"
290 nativeBuildInputs = with pkgs.buildPackages; [ jool-cli ];
291 preferLocalBuild = true;
294 lib.concatStrings (lib.mapAttrsToList checkNat64 cfg.nat64 ++ lib.mapAttrsToList checkSiit cfg.siit)
299 meta.maintainers = with lib.maintainers; [ rnhmjoj ];