at master 6.9 kB view raw
1# This test verifies that we can ping an IPv4-only server from an IPv6-only 2# client via a NAT64 router using CLAT on the client. The hosts and networks 3# are configured as follows: 4# 5# +------ 6# Client | clat Address: 192.0.0.1/32 (configured via clatd) 7# | Route: default 8# | 9# | eth1 Address: Assigned via SLAAC within 2001:db8::/64 10# | | Route: default via IPv6LL address 11# +--|--- 12# | VLAN 3 13# +--|--- 14# | eth2 Address: 2001:db8::1/64 15# Router | 16# | nat64 Address: 64:ff9b::1/128 17# | Route: 64:ff9b::/96 18# | Address: 192.0.2.0/32 19# | Route: 192.0.2.0/24 20# | 21# | eth1 Address: 100.64.0.1/24 22# +--|--- 23# | VLAN 2 24# +--|--- 25# Server | eth1 Address: 100.64.0.2/24 26# | Route: 192.0.2.0/24 via 100.64.0.1 27# +------ 28 29{ lib, ... }: 30 31{ 32 name = "clatd"; 33 34 meta.maintainers = with lib.maintainers; [ 35 hax404 36 jmbaur 37 ]; 38 39 nodes = { 40 # The server is configured with static IPv4 addresses. RFC 6052 Section 3.1 41 # disallows the mapping of non-global IPv4 addresses like RFC 1918 into the 42 # Well-Known Prefix 64:ff9b::/96. TAYGA also does not allow the mapping of 43 # documentation space (RFC 5737). To circumvent this, 100.64.0.2/24 from 44 # RFC 6589 (Carrier Grade NAT) is used here. 45 # To reach the IPv4 address pool of the NAT64 gateway, there is a static 46 # route configured. In normal cases, where the router would also source NAT 47 # the pool addresses to one IPv4 addresses, this would not be needed. 48 server = { 49 virtualisation.vlans = [ 50 2 # towards router 51 ]; 52 networking = { 53 useDHCP = false; 54 interfaces.eth1 = lib.mkForce { }; 55 }; 56 systemd.network = { 57 enable = true; 58 networks."vlan1" = { 59 matchConfig.Name = "eth1"; 60 address = [ 61 "100.64.0.2/24" 62 ]; 63 routes = [ 64 { 65 Destination = "192.0.2.0/24"; 66 Gateway = "100.64.0.1"; 67 } 68 ]; 69 }; 70 }; 71 }; 72 73 # The router is configured with static IPv4 addresses towards the server 74 # and IPv6 addresses towards the client. DNS64 is exposed towards the 75 # client so clatd is able to auto-discover the PLAT prefix. For NAT64, the 76 # Well-Known prefix 64:ff9b::/96 is used. NAT64 is done with TAYGA which 77 # provides the tun-interface nat64 and does the translation over it. The 78 # IPv6 packets are sent to this interfaces and received as IPv4 packets and 79 # vice versa. As TAYGA only translates IPv6 addresses to dedicated IPv4 80 # addresses, it needs a pool of IPv4 addresses which must be at least as 81 # big as the expected amount of clients. In this test, the packets from the 82 # pool are directly routed towards the client. In normal cases, there would 83 # be a second source NAT44 to map all clients behind one IPv4 address. 84 router = { 85 boot.kernel.sysctl = { 86 "net.ipv4.conf.all.forwarding" = 1; 87 "net.ipv6.conf.all.forwarding" = 1; 88 }; 89 90 virtualisation.vlans = [ 91 2 # towards server 92 3 # towards client 93 ]; 94 95 networking = { 96 useDHCP = false; 97 useNetworkd = true; 98 firewall.enable = false; 99 interfaces.eth1 = lib.mkForce { 100 ipv4 = { 101 addresses = [ 102 { 103 address = "100.64.0.1"; 104 prefixLength = 24; 105 } 106 ]; 107 }; 108 }; 109 interfaces.eth2 = lib.mkForce { 110 ipv6 = { 111 addresses = [ 112 { 113 address = "2001:db8::1"; 114 prefixLength = 64; 115 } 116 ]; 117 }; 118 }; 119 }; 120 121 systemd.network.networks."40-eth2" = { 122 networkConfig.IPv6SendRA = true; 123 ipv6Prefixes = [ { Prefix = "2001:db8::/64"; } ]; 124 ipv6PREF64Prefixes = [ { Prefix = "64:ff9b::/96"; } ]; 125 ipv6SendRAConfig = { 126 EmitDNS = true; 127 DNS = "_link_local"; 128 }; 129 }; 130 131 services.resolved.extraConfig = '' 132 DNSStubListener=no 133 ''; 134 135 networking.extraHosts = '' 136 192.0.0.171 ipv4only.arpa 137 192.0.0.170 ipv4only.arpa 138 ''; 139 140 services.coredns = { 141 enable = true; 142 config = '' 143 .:53 { 144 bind :: 145 hosts /etc/hosts 146 dns64 64:ff9b::/96 147 } 148 ''; 149 }; 150 151 services.tayga = { 152 enable = true; 153 ipv4 = { 154 address = "192.0.2.0"; 155 router = { 156 address = "192.0.2.1"; 157 }; 158 pool = { 159 address = "192.0.2.0"; 160 prefixLength = 24; 161 }; 162 }; 163 ipv6 = { 164 address = "2001:db8::1"; 165 router = { 166 address = "64:ff9b::1"; 167 }; 168 pool = { 169 address = "64:ff9b::"; 170 prefixLength = 96; 171 }; 172 }; 173 }; 174 }; 175 176 # The client uses SLAAC to assign IPv6 addresses. To reach the IPv4-only 177 # server, the client starts the clat daemon which starts and configures the 178 # local IPv4 -> IPv6 translation via Tayga after discovering the PLAT 179 # prefix via DNS64. 180 client = 181 { pkgs, ... }: 182 { 183 virtualisation.vlans = [ 184 3 # towards router 185 ]; 186 187 networking = { 188 useDHCP = false; 189 interfaces.eth1 = lib.mkForce { }; 190 }; 191 192 systemd.network = { 193 enable = true; 194 networks."vlan1" = { 195 matchConfig.Name = "eth1"; 196 ipv6AcceptRAConfig.UsePREF64 = true; 197 }; 198 }; 199 200 services.clatd.enable = true; 201 202 environment.systemPackages = [ pkgs.mtr ]; 203 }; 204 }; 205 206 testScript = '' 207 import json 208 209 start_all() 210 211 # wait for all machines to start up 212 for machine in client, router, server: 213 machine.wait_for_unit("network.target") 214 215 with subtest("Wait for tayga and clatd"): 216 router.wait_for_unit("tayga.service") 217 client.wait_for_unit("clatd.service") 218 # clatd checks if this system has IPv4 connectivity for 10 seconds 219 client.wait_until_succeeds( 220 'journalctl -u clatd -e | grep -q "Starting up TAYGA, using config file"' 221 ) 222 223 with subtest("networkd exports PREF64 prefix"): 224 assert json.loads(client.succeed("networkctl status eth1 --json=short"))[ 225 "NDisc" 226 ]["PREF64"][0]["Prefix"] == [0x0, 0x64, 0xFF, 0x9B] + ([0] * 12) 227 228 with subtest("Test ICMP"): 229 client.wait_until_succeeds("ping -c3 100.64.0.2 >&2") 230 231 with subtest("Test ICMP and show a traceroute"): 232 client.wait_until_succeeds("mtr --show-ips --report-wide 100.64.0.2 >&2") 233 234 client.log(client.execute("systemd-analyze security clatd.service")[1]) 235 ''; 236}