Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions tests/nat64-pref64/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# NAT64 with PREF64

This configuration extends the [nat64-dns64](../nat64-dns64/) test by replacing DNS64
functionality with PREF64. Instead of using a DNS64-enabled DNS server, the NAT64 gateway
now uses radvd (Router Advertisement Daemon) to advertise IPv6 prefixes that embed IPv4 addresses,
allowing the client to generate IPv6 addresses for IPv4 destinations automatically using clatd.

```mermaid
flowchart LR
server["**Server**
nginx HTTP server
bind DNS server"]

nat64gw["**NAT64 Gateway**
jool NAT64
dnsmasq DNS server
radvd"]

client["**Client**
clatd"]

server <-- ipv4 only --> nat64gw <-- ipv6 only --> client
```
32 changes: 32 additions & 0 deletions tests/nat64-pref64/client.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{ lib, pkgs, ... }:
{
virtualisation.interfaces.eth1 = {
vlan = 2;
assignIP = false;
};

networking = {
interfaces.eth1 = { };
nameservers = [ "2001:db8::1" ];
};

services.clatd = {
enable = true;
settings = {
plat-prefix = "64:ff9b::/96";
debug = 1;
};
};

systemd.services.clatd.preStart =
let
ip = lib.getExe' pkgs.iproute2 "ip";
jq = lib.getExe pkgs.jq;
in
''
while [ $(${ip} -j -6 address show eth1 | ${jq} '.[] | .addr_info | map(select((.scope == "global") and (.tentative == true))) | length') -ne 0 ]
do
sleep 0.1
done
'';
}
114 changes: 114 additions & 0 deletions tests/nat64-pref64/default.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
{
lib,
pkgs,
...
}:
{
name = "nat64-pref64";

defaults = {
networking = {
useDHCP = false;
firewall.enable = false;
};
};

nodes = {
server = import ./server.nix { inherit pkgs; };
nat64gw = import ./nat64gw.nix;
client = import ./client.nix { inherit lib pkgs; };
};

interactive.nodes = lib.listToAttrs (
map
(name: {
inherit name;
value.environment.systemPackages = with pkgs; [
curl
dig
tcpdump
pwru
];
})
[
"server"
"client"
"nat64gw"
]
);

testScript =
let
curl = lib.getExe pkgs.curl;
dig = lib.getExe pkgs.dig;
jq = lib.getExe pkgs.jq;
in
''
start_all()

for m in [server, nat64gw, client]:
m.wait_for_unit("network.target")

server.wait_for_unit("nginx.service")
server.wait_for_unit("bind.service")
nat64gw.wait_for_unit("dnsmasq.service")
nat64gw.wait_for_unit("jool-nat64-eth2.service")
nat64gw.wait_for_unit("radvd.service")

with subtest('ensure "public" dns server is working properly'):
assert server.succeed("${dig} +short A example.com").strip() == "192.0.2.2"
assert server.succeed("${dig} +short AAAA example.com").strip() == ""

with subtest('ensure http server is online'):
server.succeed("${curl} -sI http://example.com")

with subtest('ensure ipv4 network between server and gateway is working'):
server.succeed("ping -c 1 192.0.2.1")
nat64gw.succeed("ping -c 1 192.0.2.2")

# first dig fails for whatever reason
nat64gw.succeed("${dig} A example.com @2001:db8::1")

with subtest('ensure dns server on nat64gw works as expected'):
assert nat64gw.succeed("${dig} +short A example.com @2001:db8::1").strip() == "192.0.2.2"
assert nat64gw.succeed("${dig} +short AAAA example.com @2001:db8::1").strip() == ""

with subtest('ensure ipv6 network between gateway and client is working'):
client.wait_until_succeeds("""
ip -j -6 a sh eth1 | \
${jq} -r '.[] | .addr_info | .[] | select((.family == "inet6") and .dynamic == true) | .local' | \
grep 2001:db8
""")
client.succeed("ping -6 -c 1 2001:db8::1")

with subtest('ensure dns works as expected from client, and there is no aaaa record'):
assert client.succeed("${dig} +short A example.com @2001:db8::1").strip() == "192.0.2.2"
assert client.succeed("${dig} +short AAAA example.com @2001:db8::1").strip() == ""

with subtest('ensure nat64 works as expected'):
client.wait_until_succeeds("ping -6 -c 1 64:ff9b::192.0.2.2")
client.wait_until_succeeds("${curl} -sI http://[64:ff9b::192.0.2.2] | grep 'HTTP/1.1 200 OK'")

# clat needs to be started after slaac, restarting it now should be sufficient
client.succeed("systemctl restart clatd")
client.wait_for_unit("clatd.service")
client.wait_until_succeeds("ip link show clat")

with subtest('ensure clat uses correct address from ipv4 service continuity prefix'):
client.wait_until_succeeds("""
ip -j -4 address sh clat | ${jq} -e -r '.[].addr_info.[].local == "192.0.0.1"'
""")

with subtest('ensure clat created a ipv4 default route to attract ipv4 traffic'):
client.wait_until_succeeds("""
ip -4 -j route show default | ${jq} -e -r '.[].dev == "clat"'
""")

# for debugging purpose:
print(client.succeed("${curl} -v http://example.com"))
# connect to 192.0.2.2 port 80 from 192.0.0.1 port 37636 failed: No route to host

with subtest('ensure clat works as expected'):
client.succeed("${curl} -sI http://example.com | grep 'HTTP/1.1 200 OK'")
'';
}
59 changes: 59 additions & 0 deletions tests/nat64-pref64/nat64gw.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
{
virtualisation.interfaces.eth1 = {
vlan = 1;
assignIP = false;
};
virtualisation.interfaces.eth2 = {
vlan = 2;
assignIP = false;
};

networking.interfaces.eth1 = {
ipv4.addresses = [
{
address = "192.0.2.1";
prefixLength = 24;
}
];
};

networking.interfaces.eth2 = {
ipv6.addresses = [
{
address = "2001:db8::1";
prefixLength = 64;
}
];
};

# nat64
boot.kernelModules = [ "jool" ];
networking.jool = {
enable = true;
nat64.eth2.framework = "netfilter";
};

services.dnsmasq = {
enable = true;
alwaysKeepRunning = true;
settings = {
listen-address = "2001:db8::1";
no-hosts = true;
no-resolv = true;
server = [
"192.0.2.2"
];
};
};

services.radvd = {
enable = true;
config = ''
interface eth2 {
AdvSendAdvert on;
prefix 2001:db8::/64 { };
nat64prefix 64:ff9b::/96 { };
};
'';
};
}
50 changes: 50 additions & 0 deletions tests/nat64-pref64/server.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
{ pkgs, ... }:
{
virtualisation.interfaces.eth1 = {
vlan = 1;
assignIP = false;
};
networking.interfaces.eth1 = {
ipv4.addresses = [
{
address = "192.0.2.2";
prefixLength = 24;
}
];
};
services.nginx = {
enable = true;
virtualHosts."example.com".locations."/".return = "200";
};

# public dns server
services.resolved.enable = false;
services.bind = {
enable = true;
cacheNetworks = [ "0.0.0.0/0" ];
# first line (comment) is to align config
extraOptions = ''
#
recursion yes;
auth-nxdomain no;
dnssec-validation no;
'';
zones."example.com" = {
master = true;
file = pkgs.writeText "zone-example.com.conf" ''
$TTL 1
@ IN SOA example.com. zonemaster.example.com. (
2023013100 ; serial number
86400 ; refresh: 1d
900 ; update retry: 15m
604800 ; expiry: 1w
3600 ) ; negative caching 1h

@ IN NS example.com.
@ IN A 192.0.2.2

'';
};
};
networking.nameservers = [ "127.0.0.1" ];
}
Loading