0%

基于OpenWrt+NAT64构建IPV6-mostly网络

本文介绍基于OpenWrt 23.05,构建一个IPv6-mostly家庭网络。

目前国内三大运营商的家庭宽带基本实现了IPv6全覆盖。那么在2025年的今天,我们可以抛弃IPv4,只使用ipv6了吗?基于这个问题我在家里搞了一个纯ipv6的试验网。

所谓IPv6-mostly,个人理解就是在一个双栈的网络下。对于支持IPv6-Only的终端只使用IPv6协议栈,不使用IPv4协议栈,在访问IPv4网络时通过NAT64进行协议栈转换。对于仍然需要使用IPv4的终端,仍然维持双栈配置,从而实现平滑过渡。

这里还涉及到一系列的过渡技术,包括NAT64、DNS64、PREF64、DHCP Option 108、464XLT等。

终端支持情况

问了AI,现代的操作系统对IPv6-Only的支持情况已经很好了。基本上手机的操作系统都能很好适应IPv6-Only,因为app版本迭代快,并且有相应的审核机制在这里。如果一个app不支持IPv6,那么他很难上架到应用商店。

Windows 这边,系统层面是没问题了。但是为了兼容要老旧程序,IPv6-Only的支持的情况实际上还是需要看具体的软件。

以下是AI给出的答案,仅供参考,生成时间为2025年2月。

总结对比

平台 系统版本要求 核心技术 开发者适配重点 局限性
Android 5.0+(部分功能需更高版本) 464XLAT、SLAAC 避免IPv4硬编码,替换底层Socket API 设备碎片化,DHCPv6支持不足
iOS 9.0+ DNS64/NAT64、CLAT集成 使用高层API,禁用IP地址硬编码 低版本iOS(<9.0)不兼容IPv6-only
Windows Windows 10 1703+ 464XLAT、NAT64 适配双栈API,优化企业级网络配置 老旧版本需手动配置IPv6

建立IPv6-Only 的vlan

为了不影响原有使用的网络和方便调试,这里先新建一个vlan只配置IPv6地址。

我这里运营商给到的DHCPv6-PD前缀是一段/60的,可以划分出2个/61,或者4个/62,或者8个/63,或者16个/64子网。

在主网关上新建一个接口命名为lan8,使用静态地址协议,关联到eth0.8,表示eth0打上vlan tag 8,不配置IPv4,IPv6分配长度/61,表示这个接口会申请一段/61的地址,IPv6 分配提示8,表示会从DHCPv6-PD前缀的第8个ID开始申请地址。

例如运营商给我的DHCPv6-PD是2001:db8:d20::/60,那这个接口分配的地址将会是2001:db8:d20:8::/61。

/etc/config/network配置如下。

1
2
3
4
5
config interface 'lan8'
option proto 'static'
option device 'eth0.8'
option ip6assign '61'
option ip6hint '8'

lan8接口的DHCP服务器页面勾选”忽略此接口”,表示关闭DHCPv4服务器。RA 服务和DHCPv6 服务均配置为服务器模式,勾选启用 SLAAC,IPv6 RA设置RA 标记增加M标记。/etc/config/dhcp配置如下

1
2
3
4
5
6
7
8
9
10
config dhcp 'lan8'
option interface 'lan8'
option start '100'
option limit '150'
option leasetime '12h'
option ra 'server'
option dhcpv6 'server'
option ignore '1'
list ra_flags 'managed-config'
list ra_flags 'other-config'

配置好后,把电脑接入这个vlan,会发现只能自动获取到IPv6地址,访问ipv6.test-ipv6.com,他会告诉我:你只接入了 IPv6 互联网,没有接入 IPv4。你可真勇敢!

访问了一些比较常见的网站,例如淘宝、京东、百度贴吧、B站大部分都可以正常使用,不是只有首页能打开那种,点进去图片和视频都能正常加载,和几年前比确实进步很大。

也有不能用的,例如:百度新闻、百度地图、高德地图、bing搜索、deepseek、豆瓣、github、csdn等。

配置NAT64

为了解决转换到IPv6-only后还需要访问IPv4的问题,我这里在旁路增加一台OpenWrt设备,负责实现NAT64和DNS64。

新增的OpenWrt设备,名字就叫NAT64吧,Wan口接入vlan2,也就是原来在用的双栈vlan,配置静态IPv4地址。Lan口接入vlan8获取自动获取IPv6地址。

NAT64和NAT44道理是差不多的,首先所有的IPv4地址全部映射到一个NAT64的前缀上,例如64:ff9b::/96,这是一个NAT64的通用前缀,当然也可以跟据实际需要自定义其他前缀。

当vlan8的终端想去访问192.0.2.1的时候,通过DNS64域名解析让他实际去访问64:ff9b::192.0.2.1,流量会走终端的默认路由到达主网关,在主网关上把64:ff9b::/96这个IP段路由到NAT64设备,NAT64设备进行地址转换后以wan口vlan2 的IPv4作为源地址,192.0.2.1作为目标地址走NAT64设备的默认路由到达主网关,主网关再进行NAT44出互联网。

在OpenWrt里,使用jool可以实现NAT64转换,安装jool。

1
2
opkg update
opkg install kmod-jool-netfilter jool-tools-netfilter

安装完成后在/etc/jool/jool-nat64.conf.json已经有很多默认的参数,使用默认参数基本上也可以满足要求,在日后遇到瓶颈时候再来深入微调。

但是有几个地方是需要改的,pool4是NAT64转换后使用的IPv4地址池。我这里只是用1个地址,改成NAT64设备的Wan口IPv4地址。也可以分配一个地址块,这和IPv4 NAT地址池的概念是类似的。如果分配一个地址块,需要在主网关上把地址块路由过来。

其次就是端口范围,由于我使用的是接口地址,所以端口范围最好在sysctl net.ipv4.ip_local_port_range的范围内。默认的范围是

1
net.ipv4.ip_local_port_range = 32768    60999

/etc/sysctl.conf,加入以下一段,把端口范围改大一点,然后sysctl -p应用

1
net.ipv4.ip_local_port_range = 32768    65535

附上/etc/jool/jool-nat64.conf.json的完整配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
{
"comment": {
"description": "Sample full NAT64 configuration.",
"notes": [
"192.0.2/24 and 2001:db8::/32 are documentation blocks",
"(RFC 5737 and RFC 3849), and you WILL need to change or",
"remove them for your setup."
],
"last update": "2022-02-09"
},

"instance": "default",
"framework": "netfilter",

"global": {
"manually-enabled": true,
"pool6": "64:ff9b::/96",
"lowest-ipv6-mtu": 1280,
"logging-debug": false,
"zeroize-traffic-class": false,
"override-tos": false,
"tos": 0,
"mtu-plateaus": [
65535, 32000, 17914, 8166,
4352, 2002, 1492, 1006,
508, 296, 68
],
"address-dependent-filtering": false,
"drop-externally-initiated-tcp": false,
"drop-icmpv6-info": false,
"source-icmpv6-errors-better": true,
"f-args": 11,
"handle-rst-during-fin-rcv": false,
"tcp-est-timeout": "2:00:00",
"tcp-trans-timeout": "0:04:00",
"udp-timeout": "0:05:00",
"icmp-timeout": "0:01:00",
"logging-bib": false,
"logging-session": false,
"maximum-simultaneous-opens": 10,
"ss-enabled": false,
"ss-flush-asap": true,
"ss-flush-deadline": 2000,
"ss-capacity": 512,
"ss-max-payload": 1452
},

"pool4": [
{
"comment": "mark, port range and max-iterations are optional.",
"mark": 0,
"protocol": "TCP",
"prefix": "192.168.2.236/32",
"port range": "32768-65535"
}, {
"mark": 0,
"protocol": "UDP",
"prefix": "192.168.2.236/32",
"port range": "32768-65535",
"max-iterations": 1500
}, {
"mark": 0,
"protocol": "ICMP",
"prefix": "192.168.2.236/32",
"port range": "1000-2000"
}
]

}

别忘了启用jool

1
2
3
4
uci set jool.general.enabled="1"
uci set jool.nat64.enabled="1"
uci commit jool
/etc/init.d/jool restart

在主网关上添加64:ff9b::/96静态路由指向NAT64设备,主网关设备的/etc/config/network加入

1
2
3
4
config route6
option interface 'lan8'
option target '64:ff9b::/96'
option gateway 'fd20:d20:0:8::cd7'

其中fd20:d20:0:8::cd7是NAT64设备lan口(vlan8)的ULA前缀地址,ULA地址由主网关通过DHCPv6分配,基本上属于固定地址了,实在不放心可以在DHCPv6的静态租约里面固定后缀。当然也可以使用FE80开头的本地链路地址。

找一台vlan8的终端,ping测一下64:ff9b::223.5.5.5,如无意外应该是可以ping通的。

配置DNS64、PREF64

有了NAT64后还需要DNS64来配合把域名的A记录转换为AAAA记录。配置DNS64后当一个域名有AAAA记录,DNS64服务器直接返回该域名的AAAA记录,如果域名没有AAAA记录只有A记录,那么DNS64服务器将通过NAT64前缀合成一个IPv6地址返回,引导终端访问NAT64服务。

在OpneWrt里用unbound可以实现DNS64这个功能。

1
2
opkg update
opkg install luci-app-unbound

OpneWrt的dns服务默认由dnsmasq提供,所以dnsmasq会占用53端口。我这里把他修改成别的端口,然后启用unbound。

/etc/config/dhcp 配置文件修改dnsmasq的dns端口

1
2
config dnsmasq
option port '54'

/etc/config/unbound配置文件开启DNS64,配置NAT64前缀,并把根域名转发至上游的DNS服务器,相当于把unbound配置成一个DNS递归服务器。

1
2
3
4
5
6
7
8
9
10
config unbound 'ub_main'
option dns64 '1'
option dns64_prefix '64:ff9b::/96'
config zone
option fallback '0'
option enabled '1'
option zone_type 'forward_zone'
list server '2400:3200::1'
option dns_assist 'none'
list zone_name '.'

在主网关的/etc/config/dhcp,通过RA下发DNS服务器地址。默认是下发主网关本身作为DNS服务器,我把ubound部署在NAT64设备(也就是旁网关)上,所以需要修改下发DNS地址为旁网关的地址。

1
2
config dhcp 'lan8'
list dns 'fd20:d20:0:8::cd7'

配置完成后通过nslookup一个仅有A记录的域名,可以看到有NAT64前缀的AAAA记录返回。

1
2
3
4
5
6
7
8
nslookup ipv4.3322.net
服务器: UnKnown
Address: fd20:d20:0:8::cd7

非权威应答:
名称: ipv4.3322.net
Addresses: 64:ff9b::76b8:a920
118.184.169.32

PREF64是通过路由器公告(RA)或DHCPv6,向客户端动态传递NAT64前缀信息,在主网关的/etc/config/dhcp加入配置或者在luci界面均可进行配置。

1
2
config dhcp 'lan8'
option ra_pref64 '64:ff9b::/96'

在vlan8的终端上抓包确认PREF64是否已经通告RA宣告tcpdump -i eth0.8 -nnv icmp6

1
2
3
4
5
6
7
8
9
10
11
01:36:43.670678 IP6 (flowlabel 0x7e65c, hlim 255, next-header ICMPv6 (58) payload length: 192) fe80::xxxx:xxxx:xxxx:xxxx > ff02::1: [icmp6 sum ok] ICMP6, router advertisement, length 192
hop limit 64, Flags [managed, other stateful], pref medium, router lifetime 1800s, reachable time 0ms, retrans timer 0ms
source link-address option (1), length 8 (1): xx:xx:xx:xx:xx:xx
mtu option (5), length 8 (1): 1492
prefix info option (3), length 32 (4): 2409:xxxx:xxxx:xxx8::/64, Flags [onlink, auto], valid time 5400s, pref. time 2700s
prefix info option (3), length 32 (4): fd20:d20:0:8::/64, Flags [onlink, auto], valid time 5400s, pref. time 2700s
route info option (24), length 24 (3): 2409:xxxx:xxxx:xxx0::/60, pref=medium, lifetime=1800s
route info option (24), length 24 (3): fd20:d20::/48, pref=medium, lifetime=1800s
rdnss option (25), length 24 (3): lifetime 1800s, addr: fd20:d20:0:8::cd7
unknown option (38), length 16 (2):
0x0000: 0708 0064 ff9b 0000 0000 0000 0000

可以看到option (38) 包含了NAT64前缀,说明成功了。tcpdump version 4.99.4还识别不出来option (38) 所以显示为unknown option。

不过暂时还不知道有什么直观的办法去验证PREF64的具体作用,开没开感觉区别不大,猜想可能在大型网络中,有多个NAT64设备的时候,通过PREF64同时通告多个NAT64前缀实现可以负载分担的效果。

也有可能我手上的终端还不支持PREF64,或者系统的CLAT在没有PREF64的情况下自动转换成了公共NAT64前缀64:ff9b::/96

以下是AI给出的PREF64支持情况(2025年2月)。

各平台支持情况

平台 DNS64支持 PREF64支持
Android 支持(需Android 5.0+) 部分支持(Android 12+)
iOS 完全支持(iOS 9.0+) 支持(iOS 14+)
Windows 支持(Windows 10 1703+) 需手动配置或依赖第三方工具

为原有的双栈vlan开启DHCP Option 108

开启办法,在主网关的DHCPv4里面开启即可/etc/config/dhcp或LUCI页面配置均可,其中0:0:7:8=0x78(16进制)= 1800秒 = 30分钟。

1
2
config dhcp 'lan2'
list dhcp_option '108,0:0:7:8'

开启后,在当前网络支持IPv6-only的前提下(包括但不限于已经具备NAT64、DNS64、PREF64、464XLT等能力),支持DHCP Option 108的终端将会在获取到IPv4地址30分钟后,释放IPv4地址租约,取消所在接口的IPv4地址,并运行在IPv6-only模式。

我暂时还没开启这个选项,一方面还不清楚我的终端是否支持DHCP Option 108。截止2025年2月,AI也不认识DHCP Option 108,没法给出支持情况。另一方面,我还需要长期运行并观察NAT64/DNS64的稳定性和可用性,待坑都踩完只再考虑把在主vlan开启这个选项。

总结

以上所有操作所用的OpenWrt版本均为官网23.05,部署完成后我将会持续使用观察。

另外,浏览器安装IPvFoo这个插件可以方便查看当前网站有多少域名使用了多少IPv6。

Enjoy~~

参考:

https://blog.lingxh.com/post/464xlat/
https://ripe87.ripe.net/wp-content/uploads/presentations/8-IPv6-mostly_on_OpenWRT.pdf
https://openwrt.org/docs/guide-user/network/ipv6/nat64
https://datatracker.ietf.org/doc/html/rfc8925