Self-Hosted NAT64: Native IPv6 Access to IPv4
Preparations
You will need:
- A dual-stack VPS (with both IPv4 and IPv6)
- Root access
- Knowledge of systemd/openrc
- A Linux system installed (examples for Alpine, Debian/Ubuntu, and RHEL/CentOS are below)
- Find your IPv6 subnet
ip -6 route
You will see something like this: 2001:db8:1234:5678::/64 dev eth0 proto kernel metric 256 pref medium
This shows your /64 subnet and network interface. Take note of it, you'll need it later!
- Choose a /96 prefix for IPv4 mapping
Choose a 'lucky' /96 prefix from your subnet to be used for mapping IPv4 addresses. If your subnet is 2001:db8:1234:5678::/64, you could use 2001:db8:1234:5678:ff9b::/96. This format uses 96 bits as the IPv6 prefix and 32 bits to embed the IPv4 address. - Add a local route
ip -6 route add local 2001:db8:1234:5678:ff9b::/96 dev lo
- Set up NDP proxy
The purpose of an NDP proxy is to allow your server to respond to NDP and RA requests for the NAT64 prefix.
Alpine:
# Install npd6
apk add npd6
# Configure npd6
cat > /etc/npd6.conf <<EOF
prefix=2001:db8:1234:5678:ff9b:: # Your chosen prefix
interface=eth0
ralogging=off
listtype=none
listlogging=off
collectTargets=100
linkOption=false
ignoreLocal=true
routerNA=true
maxHops=255
pollErrorLimit=20
EOF
# Start the service
rc-update add npd6
service npd6 start
Debian/Ubuntu:
# Install ndppd
apt install ndppd
# Configure ndppd
cat > /etc/ndppd.conf <<EOF
proxy eth0 {
rule 2001:db8:1234:5678:ff9b::/96 {
static
}
}
EOF
# Start the service
systemctl enable ndppd
systemctl start ndppd
RHEL/CentOS:
# Install ndppd
dnf install ndppd
# Configuration is the same as Debian
# Then start the service
systemctl enable ndppd
systemctl start ndppd
Remember to replace 2001:db8:1234:5678:ff9b:: and eth0 with your own actual values!
- Test the local route settings
ping -6 2001:db8:1234:5678:ff9b::1.1.1.1
If you run into issues, check that your firewall is on and ICMPv6 is allowed.
Note: Some cloud providers have traffic restrictions. For example, Oracle Cloud filters traffic for IPv6 addresses not bound to a VNIC. Oracle Cloud sucks.
- Download the NAT64 server:
curl -fsSL -o nat64 https://github.com/ysshz-ns/nat64/releases/download/v1.0.0/nat64
chmod +x nat64
# If on Alpine, run apk add libc6-compat
If accessing from inside the (G-F-W) to the outside, you might need to enable tcp-brutal acceleration. My github has more detailed instructions.
Then configure iptables to redirect traffic:
ip6tables -t mangle -A PREROUTING -d 2001:db8:1234:5678:ff9b::/96 -p tcp -j TPROXY --on-port=8888 --on-ip=::1
Copy
Run the server:
./nat64
- Set up DNS64
You can choose two DNS64 deployment modes
Server-side DNS64: You can run DNS64 and NAT64 on the dual-stack server. Not recommended, as it's slower.
Client-side DNS64: Run DNS64 on your IPv6-only client machine, and only run NAT64 on the dual-stack server. This method is generally better because it provides:
- Faster DNS resolution (local DNS queries)
- Full control over traffic that goes through NAT64
- The ability to selectively send only certain domains through NAT64
You need to install and configure Unbound on the v6 VPS, pointing to your NAT64 server's prefix.
# Debian/Ubuntu
apt install unbound
# Alpine
apk add unbound
# RHEL/CentOS
dnf install unbound
Configure DNS64:
cat > /etc/unbound/unbound.conf.d/dns64.conf <<EOF
server:
module-config: "dns64 iterator"
dns64-prefix: 2001:db8:1234:5678:ff9b::/96
do-not-query-localhost: yes
forward-zone:
name: "."
forward-addr: 1.1.1.1@53
forward-addr: 8.8.8.8@53
EOF
Replace 2001:db8:1234:5678:ff9b::/96 with your actual NAT64 prefix, and adjust forward-addr to your DNS servers.
Restart Unbound and configure DNS:
systemctl restart unbound
echo "nameserver ::1" > /etc/resolv.conf
- Run NAT64 as a service
For systemd systems:
cat > /etc/systemd/system/nat64.service <<EOF
[Unit]
Description=NAT64 Server
After=network.target
[Service]
ExecStart=/path/to/nat64 -p 8888
Restart=always
[Install]
WantedBy=multi-user.target
EOF
systemctl enable nat64
systemctl start nat64
For Alpine using OpenRC:
cat > /etc/init.d/nat64 <<EOF
#!/sbin/openrc-run
command=/path/to/nat64
command_args="-p 8888"
pidfile=/run/nat64.pid
depend() {
need net
after network
}
start() {
ebegin "Starting NAT64"
start-stop-daemon --start --make-pidfile --pidfile ${pidfile} --background --exec ${command} -- ${command_args}
eend $?
}
EOF
chmod +x /etc/init.d/nat64
rc-update add nat64 default
service nat64 start
- Create a startup script so your settings persist after a reboot
Alpine:
cat > /etc/local.d/nat64-setup.start <<EOF
#!/bin/sh
ip -6 route add local 2001:db8:1234:5678:ff9b::/96 dev lo
ip6tables -t mangle -A PREROUTING -d 2001:db8:1234:5678:ff9b::/96 -p tcp -j TPROXY --on-port=8888 --on-ip=::1
EOF
chmod +x /etc/local.d/nat64-setup.start
Debian/Ubuntu:
cat > /etc/network/if-up.d/nat64-setup <<EOF
#!/bin/sh
ip -6 route add local 2001:db8:1234:5678:ff9b::/96 dev lo
ip6tables -t mangle -A PREROUTING -d 2001:db8:1234:5678:ff9b::/96 -p tcp -j TPROXY --on-port=8888 --on-ip=::1
EOF
chmod +x /etc/network/if-up.d/nat64-setup
Test
Visit http://test.ustc.edu.cn, if you can access it, your NAT64 setup is successful
Comments