IPFW How to properly configure NPTv6?

I've created an instance on Vultr, both wireguard and ipfw (IPv4 NAT) are enabled successfully and work perfectly.
Now I want to use NPTv6 on this server, how can I get a correct sample of NPTv6's configuration?

FreeBSD version: 13.2-RELEASE

This is my configuration of rc.conf and IPFW:

rc.conf:
Code:
hostname="testnptv6"
sshd_enable="YES"
ntpd_enable="YES"
static_routes="linklocal"
route_linklocal="-net 169.254.0.0/16 -interface vtnet0"
sendmail_enable="NONE"
ifconfig_vtnet0="DHCP -rxcsum -tso"
ifconfig_vtnet0_ipv6="inet6 accept_rtadv -rxcsum6 -tso6"
ipv6_activate_all_interfaces="YES"
rtsold_enable="YES"
rtsold_flags="-aF"
# All above were generated by Vultr
firewall_enable="YES"
firewall_type="open"
firewall_nptv6_enable="YES"
firewall_script="/etc/ipfw.rules"
gateway_enable="YES"
firewall_nat_enable="YES"
firewall_nat_interface="vtnet0"
ipv6_gateway_enable="YES"
ipv6_defaultrouter="-iface vtnet0"
ipv6_cpe_wanif="vtnet0"
wireguard_interfaces="wg0"
wireguard_enable="YES"

ipfw.rules:
Code:
#!/bin/sh
ipfw -q -f flush
cmd="ipfw -q add "
ipfw disable one_pass
ipfw nptv6 NPT create int_prefix fdc9:281f:4d7:9ee9:: ext_if vtnet0 prefixlen 64
$cmd allow ip6 from any to any via vtnet0
$cmd nptv6 NPT ip6 from any to any
ipfw -q nat 1 config if vtnet0 same_ports unreg_only reset
$cmd nat 1 ip4 from any to any via vtnet0
$cmd allow all from any to any
$cmd check-state

When I ping6 www.freebsd.org from client, all packets are lost. No response from endpoint.

But If I run ping6 www.freebsd.org on server directly, I could get response from 2610:1c1:1:606c::50:25

If I run ipfw nptv6 NPT stats on server, I could get this result:
Code:
root@testnptv6:~ # ipfw nptv6 NPT stats
nptv6 NPT
        286 packets translated (internal to external)
        8 packets translated (external to internal)
        0 packets dropped due to some error
root@testnptv6:~ #

This is my wireguard's setting of server side:
Code:
[Interface]
Address = 10.96.100.1/24, fdc9:281f:04d7:9ee9::1/64
ListenPort = 51820
PrivateKey = eORvmY8Vl5oklG9Jtm9faPuy4DD50Qkji9Au0/5/blI=

[Peer]
AllowedIPs = 10.96.100.2/32, fdc9:281f:04d7:9ee9::2/128
PreSharedKey = o5VNilHlznaoiEzj64cwjVOn+6n+Jmod0U6qNib4puE=
PublicKey = nL663YAZB1Q0Bt5VeNZywz1XVNfHbh9meQJdxLSiBiM=

Wireguard's setting of client side:
Code:
[Interface]
PrivateKey = kNCDuKCyFsrtZ7cPUDM+e5jTLVqao9AWXf7iWPy1P3s=
Address = 10.96.100.2/32, fdc9:281f:04d7:9ee9::2/64

[Peer]
PublicKey = 5h8xu54hrWX7pL85z3/LcrrTgiA4iXRPvJdbvOmQZHI=
PreSharedKey = o5VNilHlznaoiEzj64cwjVOn+6n+Jmod0U6qNib4puE=
#AllowedIPs = 10.96.100.1/32, fdc9:281f:04d7:9ee9::1/128
AllowedIPs = 0.0.0.0/0, ::/0
Endpoint = 1xx.xxx.xx.xxx:51820

IPv6 settings:
Code:
root@testnptv6:~ # sysctl net.inet6.ip6.fw.enable
net.inet6.ip6.fw.enable: 1
root@testnptv6:~ # sysctl net.inet6.ip6.forwarding
net.inet6.ip6.forwarding: 1
root@testnptv6:~ # sysctl net.inet6.ip6.redirect
net.inet6.ip6.redirect: 1

May I know which step went wrong? What is the correct way to do it?

Thanks in advance for any help!
 
Issue Solved by the help of Bug 280390, tested on FreeBSD 15.

If the ISP or cloud provider only assigns a single /64 subnet to WAN interface, the IPv6 routing issue can be resolved by using ndproxy with the following configuration:

Step 1: Install ndproxy

sh:
pkg install -y ndproxy

Step 2: Find the upstream router's IPv6 and MAC addresses

Run the following command:

sh:
ndp -a

The output may looks like this:

Code:
Neighbor                             Linklayer Address  Netif Expire    S Flags
fe80::fc00:6ff:fe2c:2725%vtnet0      fe:00:06:2c:27:25 vtnet0 23h51m33s S R
2001:19f0:ac00:39d2:5400:6ff:fe2c:2725 56:00:06:2c:27:25 vtnet0 permanent R
fe80::5400:6ff:fe2c:2725%vtnet0      56:00:06:2c:27:25 vtnet0 permanent R

The upstream router is typically the entry that does not have permanent flag.

Step 3: Modify /etc/rc.conf

Add the ndproxy configurations to existing setup. Here is an example:

Code:
# --- Existing configuration (adjust based on your needs) ---
firewall_enable="YES"
firewall_type="open"
firewall_nptv6_enable="YES"
firewall_script="/etc/ipfw.rules"
gateway_enable="YES"
firewall_nat_enable="YES"
firewall_nat_interface="vtnet0"
ipv6_gateway_enable="YES"
ipv6_defaultrouter="-iface vtnet0"
ipv6_cpe_wanif="vtnet0"
wireguard_interfaces="wg0"
wireguard_enable="YES"

# --- New configuration for ndproxy ---
ndproxy_enable="YES"
ndproxy_uplink_interface="vtnet0"
# Copy the MAC and IPv6 addresses of the upstream router from the 'ndp -a' output
ndproxy_downlink_mac_address="fe:00:06:2c:27:25"
ndproxy_uplink_ipv6_addresses="fe80::fc00:6ff:fe2c:2725"

Step 4: Update IPFW rules

Add the following two rules to IPFW script (e.g., /etc/ipfw.rules) to allow IPv6 traffic to and from the host:

Code:
$cmd allow ip6 from me6 to any
$cmd allow ip6 from any to me6

before $cmd nptv6 NPT ip6 from any to any

Here is an example:
sh:
#!/bin/sh
ipfw -q -f flush
cmd="ipfw -q add "
ipfw disable one_pass
ipfw nptv6 NPT create int_prefix fdc9:281f:4d7:9ee9:: ext_if vtnet0 prefixlen 64
$cmd allow ip6 from me6 to any
$cmd allow ip6 from any to me6
$cmd nptv6 NPT ip6 from any to any
ipfw -q nat 1 config if vtnet0 same_ports unreg_only reset
$cmd nat 1 ip4 from any to any via vtnet0
$cmd allow all from any to any
$cmd check-state

If the rule allow ip6 from any to any is required, just put it after nptv6 NPT ip6 from any to any

Finally, reboot the system for all changes to take effect. Hope this helps anyone facing the same issue.
 
Back
Top