1# This test verifies that we can ping an IPv4-only server from an IPv6-only
2# client via a NAT64 router. The hosts and networks are configured as follows:
3#
4# +------
5# Client | eth1 Address: 2001:db8::2/64
6# | | Route: 64:ff9b::/96 via 2001:db8::1
7# +--|---
8# | VLAN 3
9# +--|---
10# | eth2 Address: 2001:db8::1/64
11# Router |
12# | nat64 Address: 64:ff9b::1/128
13# | Route: 64:ff9b::/96
14# | Address: 192.0.2.0/32
15# | Route: 192.0.2.0/24
16# |
17# | eth1 Address: 100.64.0.1/24
18# +--|---
19# | VLAN 2
20# +--|---
21# Server | eth1 Address: 100.64.0.2/24
22# | Route: 192.0.2.0/24 via 100.64.0.1
23# +------
24
25{ pkgs, lib, ... }:
26
27{
28 name = "tayga";
29 meta = with pkgs.lib.maintainers; {
30 maintainers = [ hax404 ];
31 };
32
33 nodes = {
34 # The server is configured with static IPv4 addresses. We have to disable the
35 # well-known prefix restrictions (as required by RFC 6052 Section 3.1) because
36 # we're using private space (TAYGA also considers documentation space non-global,
37 # unfortunately).
38 # To reach the IPv4 address pool of the NAT64 gateway, there is a static
39 # route configured. In normal cases, where the router would also source NAT
40 # the pool addresses to one IPv4 addresses, this would not be needed.
41 server = {
42 virtualisation.vlans = [
43 2 # towards router
44 ];
45 networking = {
46 useDHCP = false;
47 interfaces.eth1 = lib.mkForce { };
48 };
49 systemd.network = {
50 enable = true;
51 networks."vlan1" = {
52 matchConfig.Name = "eth1";
53 address = [
54 "100.64.0.2/24"
55 ];
56 routes = [
57 {
58 Destination = "192.0.2.0/24";
59 Gateway = "100.64.0.1";
60 }
61 ];
62 };
63 };
64 programs.mtr.enable = true;
65 environment.systemPackages = [ pkgs.tcpdump ];
66 };
67
68 # The router is configured with static IPv4 addresses towards the server
69 # and IPv6 addresses towards the client. For NAT64, the Well-Known prefix
70 # 64:ff9b::/96 is used. NAT64 is done with TAYGA which provides the
71 # tun-interface nat64 and does the translation over it. The IPv6 packets
72 # are sent to this interfaces and received as IPv4 packets and vice versa.
73 # As TAYGA only translates IPv6 addresses to dedicated IPv4 addresses, it
74 # needs a pool of IPv4 addresses which must be at least as big as the
75 # expected amount of clients. In this test, the packets from the pool are
76 # directly routed towards the client. In normal cases, there would be a
77 # second source NAT44 to map all clients behind one IPv4 address.
78 router_systemd = {
79 boot.kernel.sysctl = {
80 "net.ipv4.ip_forward" = 1;
81 "net.ipv6.conf.all.forwarding" = 1;
82 };
83
84 virtualisation.vlans = [
85 2 # towards server
86 3 # towards client
87 ];
88
89 networking = {
90 hostName = "router-systemd";
91 useDHCP = false;
92 useNetworkd = true;
93 firewall.enable = false;
94 interfaces.eth1 = lib.mkForce {
95 ipv4 = {
96 addresses = [
97 {
98 address = "100.64.0.1";
99 prefixLength = 24;
100 }
101 ];
102 };
103 };
104 interfaces.eth2 = lib.mkForce {
105 ipv6 = {
106 addresses = [
107 {
108 address = "2001:db8::1";
109 prefixLength = 64;
110 }
111 ];
112 };
113 };
114 };
115
116 services.tayga = {
117 enable = true;
118 ipv4 = {
119 address = "192.0.2.0";
120 router = {
121 address = "192.0.2.1";
122 };
123 pool = {
124 address = "192.0.2.0";
125 prefixLength = 24;
126 };
127 };
128 ipv6 = {
129 address = "2001:db8::1";
130 router = {
131 address = "64:ff9b::1";
132 };
133 pool = {
134 address = "64:ff9b::";
135 prefixLength = 96;
136 };
137 };
138 mappings = {
139 "192.0.2.42" = "2001:db8::2";
140 };
141 log = [
142 "drop"
143 "reject"
144 "icmp"
145 "self"
146 ];
147 wkpfStrict = false;
148 };
149 environment.systemPackages = [ pkgs.tcpdump ];
150 };
151
152 router_nixos = {
153 boot.kernel.sysctl = {
154 "net.ipv4.ip_forward" = 1;
155 "net.ipv6.conf.all.forwarding" = 1;
156 };
157
158 virtualisation.vlans = [
159 2 # towards server
160 3 # towards client
161 ];
162
163 networking = {
164 hostName = "router-nixos";
165 useDHCP = false;
166 firewall.enable = false;
167 interfaces.eth1 = lib.mkForce {
168 ipv4 = {
169 addresses = [
170 {
171 address = "100.64.0.1";
172 prefixLength = 24;
173 }
174 ];
175 };
176 };
177 interfaces.eth2 = lib.mkForce {
178 ipv6 = {
179 addresses = [
180 {
181 address = "2001:db8::1";
182 prefixLength = 64;
183 }
184 ];
185 };
186 };
187 };
188
189 services.tayga = {
190 enable = true;
191 ipv4 = {
192 address = "192.0.2.0";
193 router = {
194 address = "192.0.2.1";
195 };
196 pool = {
197 address = "192.0.2.0";
198 prefixLength = 24;
199 };
200 };
201 ipv6 = {
202 address = "2001:db8::1";
203 router = {
204 address = "64:ff9b::1";
205 };
206 pool = {
207 address = "64:ff9b::";
208 prefixLength = 96;
209 };
210 };
211 mappings = {
212 "192.0.2.42" = "2001:db8::2";
213 };
214 log = [
215 "drop"
216 "reject"
217 "icmp"
218 "self"
219 ];
220 wkpfStrict = false;
221 };
222 environment.systemPackages = [ pkgs.tcpdump ];
223 };
224
225 # The client is configured with static IPv6 addresses. It has also a static
226 # route for the NAT64 IP space where the IPv4 addresses are mapped in. In
227 # normal cases, there would be only a default route.
228 client = {
229 virtualisation.vlans = [
230 3 # towards router
231 ];
232
233 networking = {
234 useDHCP = false;
235 interfaces.eth1 = lib.mkForce { };
236 };
237
238 systemd.network = {
239 enable = true;
240 networks."vlan1" = {
241 matchConfig.Name = "eth1";
242 address = [
243 "2001:db8::2/64"
244 ];
245 routes = [
246 {
247 Destination = "64:ff9b::/96";
248 Gateway = "2001:db8::1";
249 }
250 ];
251 };
252 };
253 programs.mtr.enable = true;
254 environment.systemPackages = [ pkgs.tcpdump ];
255 };
256 };
257
258 testScript = ''
259 # start client and server
260 for machine in client, server:
261 machine.systemctl("start network-online.target")
262 machine.wait_for_unit("network-online.target")
263 machine.log(machine.execute("ip addr")[1])
264 machine.log(machine.execute("ip route")[1])
265 machine.log(machine.execute("ip -6 route")[1])
266
267 # test systemd-networkd and nixos-scripts based router
268 for router in router_systemd, router_nixos:
269 router.start()
270 router.systemctl("start network-online.target")
271 router.wait_for_unit("network-online.target")
272 router.wait_for_unit("tayga.service")
273 router.log(machine.execute("ip addr")[1])
274 router.log(machine.execute("ip route")[1])
275 router.log(machine.execute("ip -6 route")[1])
276
277 with subtest("Wait for tayga"):
278 router.wait_for_unit("tayga.service")
279
280 with subtest("Test ICMP server -> client"):
281 server.wait_until_succeeds("ping -c 3 192.0.2.42 >&2")
282
283 with subtest("Test ICMP and show a traceroute server -> client"):
284 server.wait_until_succeeds("mtr --show-ips --report-wide 192.0.2.42 >&2")
285
286 with subtest("Test ICMP client -> server"):
287 client.wait_until_succeeds("ping -c 3 64:ff9b::100.64.0.2 >&2")
288
289 with subtest("Test ICMP and show a traceroute client -> server"):
290 client.wait_until_succeeds("mtr --show-ips --report-wide 64:ff9b::100.64.0.2 >&2")
291
292 router.log(router.execute("systemd-analyze security tayga.service")[1])
293 router.shutdown()
294 '';
295}