Chapter: 6 (NAT + IPv6 Transition)
Duration: 90 minutes
Tools: Containerlab, tayga, BIND 9, tcpdump, Wireshark
Points: 10
Objectives
- Deploy a Containerlab topology with an IPv6-only client and an IPv4-only server
- Configure NAT64 (tayga) and DNS64 (BIND 9) on the gateway
- Demonstrate IPv6-only client reaching an IPv4-only server via NAT64
- Capture NAT64 translation in Wireshark
- Verify Happy Eyeballs behavior on a dual-stack client
Topology
IPv6-only client Gateway (NAT64 + DNS64) IPv4-only server
2001:db8:1::10 --[IPv6]--> 2001:db8:1::1 / 10.100.0.1 --[IPv4]--> 10.100.0.100
NAT64 prefix: 64:ff9b::/96
Setup
# topo-nat64.clab.yml
name: nat64
topology:
nodes:
client6:
kind: linux
image: alpine:latest
gw:
kind: linux
image: ubuntu:22.04
server4:
kind: linux
image: alpine:latest
client46:
kind: linux
image: alpine:latest
links:
- endpoints: ["client6:eth1", "gw:eth1"] # IPv6-only segment
- endpoints: ["gw:eth2", "server4:eth1"] # IPv4 segment
- endpoints: ["client46:eth1", "gw:eth3"] # dual-stack segment
containerlab deploy -t topo-nat64.clab.yml
Configure addresses:
# IPv6-only client
docker exec clab-nat64-client6 ip addr add 2001:db8:1::10/64 dev eth1
docker exec clab-nat64-client6 ip -6 route add default via 2001:db8:1::1
# Gateway eth1 (IPv6 side)
docker exec clab-nat64-gw ip addr add 2001:db8:1::1/64 dev eth1
# Gateway eth2 (IPv4 side)
docker exec clab-nat64-gw ip addr add 10.100.0.1/24 dev eth2
# IPv4-only server
docker exec clab-nat64-server4 ip addr add 10.100.0.100/24 dev eth1
docker exec clab-nat64-server4 ip route add default via 10.100.0.1
# Dual-stack client
docker exec clab-nat64-client46 ip addr add 10.100.1.10/24 dev eth1
docker exec clab-nat64-client46 ip addr add 2001:db8:2::10/64 dev eth1
docker exec clab-nat64-client46 ip route add default via 10.100.1.1
Part 1: Configure NAT64 (tayga) (25 min)
# Install tayga on gateway
docker exec clab-nat64-gw apt-get update -q && \
docker exec clab-nat64-gw apt-get install -y tayga bind9
# Create tayga configuration
docker exec clab-nat64-gw bash -c "cat > /etc/tayga.conf << 'EOF'
tun-device nat64
ipv4-addr 192.168.255.1
ipv6-addr 2001:db8:1::1
prefix 64:ff9b::/96
dynamic-pool 192.168.255.0/24
data-dir /var/db/tayga
EOF"
# Enable IP forwarding
docker exec clab-nat64-gw sysctl -w net.ipv4.ip_forward=1
docker exec clab-nat64-gw sysctl -w net.ipv6.conf.all.forwarding=1
# Start tayga
docker exec clab-nat64-gw tayga --mktun
docker exec clab-nat64-gw ip link set nat64 up
docker exec clab-nat64-gw ip route add 192.168.255.0/24 dev nat64
docker exec clab-nat64-gw ip -6 route add 64:ff9b::/96 dev nat64
docker exec clab-nat64-gw tayga --daemon
Test NAT64 manually (without DNS64):
# Compute the NAT64 address for 10.100.0.100
# 64:ff9b:: + IPv4 = 64:ff9b::0a64:0064 (10.100.0.100 in hex = 0a 64 00 64)
# Python: hex(10) + hex(100) + hex(0) + hex(100) = 0a640064
docker exec clab-nat64-client6 ping6 -c 3 64:ff9b::a64:64
# Expected: ping reaches 10.100.0.100 via NAT64
Record:
- Did the ping succeed? What path did it take?
- From the gateway, run
cat /var/db/tayga/dynamic.map-- what translation entry was created?
Part 2: Configure DNS64 (20 min)
# Configure BIND 9 DNS64 on gateway
docker exec clab-nat64-gw bash -c "cat >> /etc/bind/named.conf.options << 'EOF'
// DNS64 configuration
dns64 64:ff9b::/96 {
clients { any; };
exclude { 64:ff9b::/96; };
};
EOF"
docker exec clab-nat64-gw service named restart
# Add a test A record for server4 that DNS64 will synthesize
docker exec clab-nat64-gw bash -c "cat > /etc/bind/db.virtuslab.test << 'EOF'
\$ORIGIN virtuslab.test.
\$TTL 60
@ IN SOA ns1 admin ( 2026052901 3600 900 604800 60 )
@ IN NS ns1
ns1 IN A 10.100.0.1
ipv4only IN A 10.100.0.100
EOF"
# Add zone to named.conf
echo 'zone "virtuslab.test" { type master; file "/etc/bind/db.virtuslab.test"; };' | \
docker exec -i clab-nat64-gw tee -a /etc/bind/named.conf.local
docker exec clab-nat64-gw service named reload
Test DNS64 synthesis:
# Configure IPv6 client to use gateway as DNS resolver
docker exec clab-nat64-gw ip addr add 2001:db8:1::53/64 dev eth1 # DNS resolver IP
# Query DNS64 - should synthesize AAAA from A
docker exec clab-nat64-client6 nslookup ipv4only.virtuslab.test 2001:db8:1::1
# Expected: returns 64:ff9b::a64:64 (NAT64 prefix + IPv4 address)
Record:
- What AAAA record did DNS64 synthesize for ipv4only.virtuslab.test?
- Is the synthesized AAAA record in the 64:ff9b::/96 prefix?
- Ping the hostname from the IPv6-only client: does it reach the IPv4-only server?
docker exec clab-nat64-client6 ping6 -c 3 ipv4only.virtuslab.test
Part 3: Wireshark NAT64 Translation Capture (15 min)
# Capture on the gateway's both interfaces simultaneously
docker exec clab-nat64-gw tcpdump -i eth1 -n -w /tmp/ipv6_side.pcap &
docker exec clab-nat64-gw tcpdump -i eth2 -n -w /tmp/ipv4_side.pcap &
docker exec clab-nat64-client6 ping6 -c 5 64:ff9b::a64:64
docker exec clab-nat64-gw killall tcpdump 2>/dev/null
docker cp clab-nat64-gw:/tmp/ipv6_side.pcap /tmp/
docker cp clab-nat64-gw:/tmp/ipv4_side.pcap /tmp/
Open both PCAPs in Wireshark.
Record:
- On the IPv6 side (eth1): what is the source and destination IPv6 address of the ICMPv6 echo?
- On the IPv4 side (eth2): what is the source and destination IPv4 address of the translated ICMP echo?
- How did tayga map 64:ff9b::a64:64 to 10.100.0.100? Show the bit-level mapping.
Part 4: Happy Eyeballs on Dual-Stack Client (15 min)
# Give the dual-stack client both IPv4 and IPv6 routes to a host
# Create a server with both IPv4 and IPv6 addresses
docker exec clab-nat64-gw ip addr add 2001:db8:2::1/64 dev eth3
docker exec clab-nat64-client46 ip -6 route add default via 2001:db8:2::1
# Use curl with verbose output to observe Happy Eyeballs
docker exec clab-nat64-client46 curl -v --max-time 5 \
https://1.1.1.1/ 2>&1 | grep -E "(Connected|IPv[46]|Trying)"
Record:
- Does curl attempt IPv6 first or IPv4 first?
- Which connection actually succeeds?
- What is the Happy Eyeballs specification for which protocol to try first, and by how long?
Lab Report
- Explain the difference between NAT44 and NAT64. What type of addresses does each translate between?
- DNS64 synthesizes AAAA records. What happens when a DNSSEC-validating resolver receives a DNS64-synthesized AAAA? Why does this cause a problem?
- Draw (ASCII art acceptable) the packet flow when an IPv6-only client connects to an IPv4-only server via NAT64/DNS64. Label each segment with the IP version used.
Cleanup
containerlab destroy -t topo-nat64.clab.yml
Grading (10 points)
| Item | Points |
|---|---|
| Manual NAT64 ping: IPv6 client reaches IPv4 server; translation map shown | 2 |
| DNS64 synthesis: AAAA record correctly synthesized and verified | 2 |
| Wireshark capture: IPv6 and IPv4 headers correctly identified on each side | 3 |
| Lab report: NAT44 vs NAT64 distinction; DNSSEC interaction; packet flow diagram | 3 |