{ pkgs, runTest }:
let
inherit (pkgs) lib;
ipv6Only = {
networking.useDHCP = false;
networking.interfaces.eth1.ipv4.addresses = lib.mkVMOverride [ ];
};
ipv4Only = {
networking.interfaces.eth1.ipv6.addresses = lib.mkVMOverride [ ];
webserver = ip: msg: {
systemd.services.webserver = {
description = "Mock webserver";
wants = [ "network-online.target" ];
wantedBy = [ "multi-user.target" ];
script = ''
while true; do
{
printf 'HTTP/1.0 200 OK\n'
printf 'Content-Length: ${toString (1 + builtins.stringLength msg)}\n'
printf '\n${msg}\n\n'
} | ${pkgs.libressl.nc}/bin/nc -${toString ip}nvl 80
done
'';
networking.firewall.allowedTCPPorts = [ 80 ];
in
siit = runTest {
# This test simulates the setup described in [1] with two IPv6 and
# IPv4-only devices on different subnets communicating through a border
# relay running Jool in SIIT mode.
# [1]: https://nicmx.github.io/Jool/en/run-vanilla.html
name = "jool-siit";
meta.maintainers = with lib.maintainers; [ rnhmjoj ];
# Border relay
nodes.relay = {
virtualisation.vlans = [
1
2
];
# Enable packet routing
boot.kernel.sysctl = {
"net.ipv6.conf.all.forwarding" = 1;
"net.ipv4.conf.all.forwarding" = 1;
networking.interfaces = lib.mkVMOverride {
eth1.ipv6.addresses = [
address = "fd::198.51.100.1";
prefixLength = 120;
}
eth2.ipv4.addresses = [
address = "192.0.2.1";
prefixLength = 24;
networking.jool.enable = true;
networking.jool.siit.default.global.pool6 = "fd::/96";
# IPv6 only node
nodes.alice = {
imports = [
ipv6Only
(webserver 6 "Hello, Bob!")
virtualisation.vlans = [ 1 ];
networking.interfaces.eth1.ipv6 = {
addresses = [
address = "fd::198.51.100.8";
routes = [
address = "fd::192.0.2.0";
via = "fd::198.51.100.1";
# IPv4 only node
nodes.bob = {
ipv4Only
(webserver 4 "Hello, Alice!")
virtualisation.vlans = [ 2 ];
networking.interfaces.eth1.ipv4 = {
address = "192.0.2.16";
address = "198.51.100.0";
via = "192.0.2.1";
testScript = ''
start_all()
relay.wait_for_unit("jool-siit-default.service")
alice.wait_for_unit("network-addresses-eth1.service")
bob.wait_for_unit("network-addresses-eth1.service")
with subtest("Alice and Bob can't ping each other"):
relay.systemctl("stop jool-siit-default.service")
alice.fail("ping -c1 fd::192.0.2.16")
bob.fail("ping -c1 198.51.100.8")
with subtest("Alice and Bob can ping using the relay"):
relay.systemctl("start jool-siit-default.service")
alice.wait_until_succeeds("ping -c1 fd::192.0.2.16")
bob.wait_until_succeeds("ping -c1 198.51.100.8")
with subtest("Alice can connect to Bob's webserver"):
bob.wait_for_open_port(80)
alice.succeed("curl -vvv http://[fd::192.0.2.16] >&2")
alice.succeed("curl --fail -s http://[fd::192.0.2.16] | grep -q Alice")
with subtest("Bob can connect to Alices's webserver"):
alice.wait_for_open_port(80)
bob.succeed("curl --fail -s http://198.51.100.8 | grep -q Bob")
nat64 = runTest {
# This test simulates the setup described in [1] with two IPv6-only nodes
# (a client and a homeserver) on the LAN subnet and an IPv4 node on the WAN.
# The router runs Jool in stateful NAT64 mode, masquarading the LAN and
# forwarding ports using static BIB entries.
# [1]: https://nicmx.github.io/Jool/en/run-nat64.html
name = "jool-nat64";
# Router
nodes.router = {
address = "2001:db8::1";
prefixLength = 96;
address = "203.0.113.1";
networking.jool.nat64.default = {
bib = [
# forward HTTP 203.0.113.1 (router) → 2001:db8::9 (homeserver)
"protocol" = "TCP";
"ipv4 address" = "203.0.113.1#80";
"ipv6 address" = "2001:db8::9#80";
pool4 = [
# Ports for dynamic translation
protocol = "TCP";
prefix = "203.0.113.1/32";
"port range" = "40001-65535";
protocol = "UDP";
protocol = "ICMP";
# Ports for static BIB entries
"port range" = "80";
# LAN client (IPv6 only)
nodes.client = {
imports = [ ipv6Only ];
addresses = lib.mkForce [
address = "2001:db8::8";
routes = lib.mkForce [
address = "64:ff9b::";
via = "2001:db8::1";
# LAN server (IPv6 only)
nodes.homeserver = {
(webserver 6 "Hello from IPv6!")
address = "2001:db8::9";
# WAN server (IPv4 only)
nodes.server = {
(webserver 4 "Hello from IPv4!")
networking.interfaces.eth1.ipv4.addresses = [
address = "203.0.113.16";
for node in [client, homeserver, server]:
node.wait_for_unit("network-addresses-eth1.service")
with subtest("Client can ping the WAN server"):
router.wait_for_unit("jool-nat64-default.service")
client.succeed("ping -c1 64:ff9b::203.0.113.16")
with subtest("Client can connect to the WAN webserver"):
server.wait_for_open_port(80)
client.succeed("curl --fail -s http://[64:ff9b::203.0.113.16] | grep -q IPv4!")
with subtest("Router BIB entries are correctly populated"):
router.succeed("jool bib display | grep -q 'Dynamic TCP.*2001:db8::8'")
router.succeed("jool bib display | grep -q 'Static TCP.*2001:db8::9'")
with subtest("WAN server can reach the LAN server"):
homeserver.wait_for_open_port(80)
server.succeed("curl --fail -s http://203.0.113.1 | grep -q IPv6!")