搞网络的人应该都有体会,IPv4地址池见底这件事,运营商比我们着急得多。现在很多宽带接入网已经逐步转向IPv6-only,但用户还要打开IPv4时代的网站和应用,于是就有了各种过渡技术。DS-Lite是目前运营商用得最多的一套方案,核心就是AFTR和B4这两个角色。我这次要在Ubuntu上把AFTR和B4整套环境搭起来,从原理到配置、从排查到验证,一次说清楚。这篇文章适合网络工程师、ISP运维,以及所有想在实验室里复现IPv6过渡网络的朋友。
1. AFTR和B4到底是什么
1.1 先从DS-Lite的名字说起
DS-Lite全称Dual-Stack Lite,直译过来是“轻量级双栈”。名字里带Lite,是因为它和传统双栈不同:用户侧不再分配公网IPv4地址,只分配IPv6地址,IPv4流量通过隧道送给运营商侧统一处理。
这套架构里有两个角色:
- B4(Basic Bridging Broadband Element):部署在用户CPE侧,也就是家庭网关或小型路由器里。它负责把用户内网的IPv4报文封装进IPv6隧道,发给AFTR。
- AFTR(Address Family Transition Router):部署在运营商网络边缘,是隧道的终点,同时承担NAT44功能,把用户的私有IPv4流量转换成公网IPv4后送去IPv4互联网。
打个比方,B4像一个快递揽收点,用户家中的IPv4报文在这里打包,贴上IPv6地址标签,然后走IPv6骨干网送到AFTR这个分拣中心。AFTR拆包、改地址、重新投递到IPv4互联网,回程反过来再走一遍。
1.2 两边的职责可不能搞混
很多初学者容易把B4当成简单的IPv4路由器,其实B4的关键能力是“IPv4 over IPv6封装”。它要能在自己的LAN口收到普通IPv4报文之后,把这些报文塞进外层的IPv6头里,发往AFTR的IPv6地址。
AFTR这边的职责更重:
- 终结来自所有B4设备的隧道连接
- 解封装IPv6报文,还原出原始的IPv4报文
- 对IPv4报文做NAT44,实现私网到公网的源地址转换
- 维护连接跟踪,保证回程流量能够原路返回
在实际运营商网络中,一个AFTR会同时承载成千上万个B4隧道,所以性能要求比普通路由器高得多。但在实验室里,用Ubuntu加Linux内核自带的隧道能力,就能完全模拟这套流程。
1.3 为什么不用NAT64或者6rd
这里顺便说说DS-Lite和其他过渡技术的区别。NAT64方案要求运营商的DNS服务器做DNS64改造,还要在IPv6-only网络中引入NAT64网关,整个链路的应用层可能会因为MTU或者协议问题踩坑,而且纯IPv4的老客户端根本用不了。6rd则是IPv6 over IPv4的隧道方案,方向正好相反,它解决的是“没有IPv6接入但有IPv4接入”的场景。DS-Lite的目标场景很明确:接入网是IPv6-only,用户终端还是IPv4。它不需要改动用户终端,也不需要在用户侧做DNS适配,只要CPE支持B4就能无缝接入。
2. 实验环境准备与地址规划
2.1 拓扑设计与硬件准备
搭建这套环境不一定要真实硬件,两台Ubuntu虚拟机就够了。我在实验室里用的是Ubuntu 22.04 LTS,内核版本5.15,系统自带的iproute2、netplan、iptables都是现成的。如果你用24.04 LTS也没问题,配置方式完全一样。
拓扑上需要三台设备:
- 一台模拟用户内网客户端的Linux机器,可以放进B4的LAN侧
- 一台B4设备,充当CPE角色
- 一台AFTR设备,充当运营商边缘路由器
实际测试时,客户端这台机器也可以用B4设备上的虚拟网卡或者命名空间来替代。最简单的拓扑是:客户端 --B4-- IPv6网络 --AFTR-- IPv4公网出口。
这里有个前提要确认:AFTR设备上需要准备两个公网出口,一个是IPv6地址(接收B4隧道),一个是IPv4地址(访问IPv4互联网)。在纯实验室环境里,IPv4互联网用一台普通公网IP的服务器甚至另一台Ubuntu模拟都可以。
2.2 地址规划表
我习惯把地址规划写清楚再动手,避免配置过程中搞混。
| 设备 | 接口 | IPv6地址 | 隧道IPv4地址 | 说明 |
|---|---|---|---|---|
| AFTR | eth0-up | 2001:db8:1::1/64 | - | 面向IPv6接入网,隧道源地址 |
| AFTR | eth0-v4 | 192.0.2.200/24 | - | 模拟公网IPv4出口 |
| AFTR | tun4over6 | - | 192.0.2.1/29 | 隧道虚拟接口 |
| B4 | eth0 | 2001:db8:1::2/64 | - | 面向AFTR的IPv6地址 |
| B4 | eth1 | - | - | 面向LAN,地址192.168.100.1/24 |
| B4 | tun4over6 | - | 192.0.2.2/29 | 隧道虚拟接口 |
| Client | eth0 | - | - | 192.168.100.10/24,网关指向B4 |
这里隧道地址段用的是192.0.2.0/29,这是文档保留地址段,适合实验。生产环境中DS-Lite常用RFC 6333建议的192.0.0.0/29局部保留段,不过原理一样。
2.3 Ubuntu基础环境准备
在开始前,先把两台设备的apt源换一下,国内镜像能省很多等待时间,这点不用多说。安装必要的工具包:
sudo apt update sudo apt install -y net-tools iproute2 iptables tcpdump还要确认内核模块是否加载。IPv4 in IPv6隧道依赖ip6_tunnel模块:
sudo modprobe ip6_tunnel lsmod | grep ip6_tunnel如果模块没加载,会报隧道创建失败的错误。对于IPv6隧道模式,Linux内核默认支持,一般不会有大问题。
3. 在Ubuntu上配置AFTR
3.1 用netplan创建IPv4-in-IPv6隧道
Ubuntu从18.04开始默认使用netplan管理网络,配置文件在/etc/netplan/目录下。AFTR侧的隧道配置如下:
network: version: 2 ethernets: eth0: accept-ra: false addresses: - 2001:db8:1::1/64 eth0-v4: addresses: - 192.0.2.200/24 tunnels: tun4over6: mode: ipip6 local: 2001:db8:1::1 remote: 2001:db8:1::2 addresses: - 192.0.2.1/29 routes: - to: 192.0.2.0/29 via: 192.0.2.1这里mode填ipip6,表示IPv4-in-IPv6隧道,也就是RFC 2473定义的IPv6隧道封装。local是本端IPv6地址,remote是B4的IPv6地址。隧道虚拟接口分配192.0.2.1/29,并添加去往隧道网段的路由。
如果你更喜欢用ip命令临时创建隧道做验证,可以这么写:
sudo ip -6 tunnel add tun4over6 mode ipip6 local 2001:db8:1::1 remote 2001:db8:1::2 sudo ip addr add 192.0.2.1/29 dev tun4over6 sudo ip link set tun4over6 upnetplan配置完成后,执行sudo netplan apply生效。用ip addr show tun4over6确认隧道接口状态是UP。
3.2 开启IPv4/IPv6转发与NAT44
隧道建立后,AFTR要做两件事:开启转发、配置NAT。
编辑/etc/sysctl.conf:
net.ipv4.ip_forward=1 net.ipv6.conf.all.forwarding=1执行sysctl -p生效。然后配置iptables:
sudo iptables -t nat -A POSTROUTING -o eth0-v4 -j MASQUERADE sudo iptables -A FORWARD -i tun4over6 -o eth0-v4 -j ACCEPT sudo iptables -A FORWARD -i eth0-v4 -o tun4over6 -m state --state RELATED,ESTABLISHED -j ACCEPT第一条规则把从隧道进来的内部私网地址,转换成eth0-v4接口的公网地址发出去。这里我直接MASQUERADE,好处是不用关心出口IP具体是多少,适合动态出口地址。如果网络规划要求固定公网IP,换成SNAT也行:
sudo iptables -t nat -A POSTROUTING -o eth0-v4 -s 192.168.0.0/16 -j SNAT --to-source 192.0.2.200MASQUERADE和SNAT的核心区别在于:MASQUERADE在每次连接建立时自动读取出口网卡当前IP,适合PPPoE拨号这类动态地址场景;SNAT则固定转换到指定IP,适合固定公网地址场景。测试环境用MASQUERADE省心。
注意,iptables规则默认不持久化,重启后会丢失。Ubuntu上可以用iptables-persistent保存:
sudo apt install -y iptables-persistent sudo netfilter-persistent save3.3 可选:配置DHCPv6下发AFTR地址
实验环境手动配置隧道地址就够了,但如果你模拟真实运营商场景,B4设备上线后应该自动获取AFTR地址,而不是手工指定。DS-Lite定义了DHCPv6选项64(OPTION_S46_BR),用于向B4下发AFTR的IPv6地址。
在Ubuntu上可以用KEA DHCPv6服务器来做。安装后配置option-def,然后在下发子网中携带该选项。这种做法生产环境很常见,因为运营商不可能给每个用户手工配置AFTR地址,必须要让CPE通过DHCPv6自动发现AFTR。实验室里如果没有那么多设备,先把静态配置跑通就行。
4. 在Ubuntu上配置B4
4.1 B4侧隧道与路由配置
B4设备的IPv6上行接口是eth0,IPv4 LAN接口是eth1。netplan配置如下:
network: version: 2 ethernets: eth0: accept-ra: false addresses: - 2001:db8:1::2/64 eth1: addresses: - 192.168.100.1/24 tunnels: tun4over6: mode: ipip6 local: 2001:db8:1::2 remote: 2001:db8:1::1 addresses: - 192.0.2.2/29 routes: - to: 192.0.2.0/29 via: 192.0.2.1 - to: 0.0.0.0/0 via: 192.0.2.1 dev: tun4over6关键点在最后一条默认路由,它把所有IPv4互联网流量引导到tun4over6隧道接口。这样客户端无论访问哪个IPv4地址,都会被转发到AFTR。
同时需要确认eth0上有一个IPv6默认路由指向AFTR。因为B4和AFTR在同一网段,内联链路可达;如果是跨网络部署,需要保证外层IPv6路由可达,比如:
sudo ip -6 route add default via 2001:db8:1::1 dev eth0B4同样需要开启IPv4转发,因为要转发LAN侧客户端的流量:
sudo sysctl -w net.ipv4.ip_forward=1 sudo sysctl -w net.ipv6.conf.all.forwarding=14.2 B4要不要做NAT
这是新手最容易纠结的问题。标准DS-Lite架构里,NAT44统一由AFTR做,B4只是桥接转发,不做地址转换。这样运营商可以集中管理地址池,用户私网地址在AFTR侧统一转换。
但实际CPE产品中,为了隔离用户侧流量或者限制单用户并发连接数,很多B4设备会先做一次NAT到隧道地址段,然后AFTR再做一次NAT,形成双层NAT。这种方式并不违反DS-Lite规范,只是增加了AFTR连接跟踪表的压力。
实验环境我推荐严格按标准来:B4不做NAT,AFTR统一做。这样便于观察DS-Lite的原始转发路径,排查问题时也更清晰。客户端流量路径是:
client 192.168.100.10 -> B4 eth1 -> B4 tun4over6 -> IPv6隧道 -> AFTR tun4over6 -> NAT44 -> 公网IPv4
如果B4想要限速或者做用户隔离,可以在转发链上额外加流量控制规则,但地址转换留给AFTR。
4.3 LAN侧客户端配置
客户端机器很简单,给它一个192.168.100.10/24的地址,网关指向192.168.100.1。在真实的CPE场景中,B4通常还会内置一个小型DHCP服务器给LAN设备分配地址,实验里手动配置就可以了。
sudo ip addr add 192.168.100.10/24 dev eth0 sudo ip route add default via 192.168.100.1如果客户端上还需要配置DNS,我在测试时一般临时用公共DNS,反正最终还是要走到IPv4互联网去解析。
5. 联通性测试与抓包验证
5.1 基础连通性检查
配置完成后,从B4上先ping通AFTR的隧道地址:
ping 192.0.2.1 -c 3如果通了,说明隧道链路没问题。然后再从客户端ping一个公网IPv4地址,比如:
ping 8.8.8.8 -c 3第一次测试很可能不通,这时不要慌。先检查AFTR的NAT和转发规则是不是生效了:
sudo iptables -t nat -L -n -v sudo iptables -L -n -v再看B4上能不能ping通AFTR的隧道地址。一层层排查下来,问题基本都出在转发开关或者防火墙规则上。
5.2 用tcpdump抓包确认隧道封装
链路通了之后,最有意思的就是抓包看隧道封装。在AFTR的eth0上执行:
sudo tcpdump -i eth0 -nn 'ip6 proto 4'然后从客户端ping一个IPv4目标。你会看到外层IPv6头的next header是4,说明里面封装的是IPv4协议。源地址是B4的IPv6地址,目的地址是AFTR的IPv6地址。在AFTR的tun4over6接口上再抓,看到的就是解封装后的原始IPv4报文,源地址变成了客户端的192.168.100.10。
sudo tcpdump -i tun4over6 -nn icmp这两个抓包对照起来看,整个DS-Lite转发路径就非常清晰了。在B4侧也能做同样的抓包验证,逻辑完全对称。这个步骤我很推荐每一步操作之前都做一下,能明显减少后面的玄学问题。
5.3 验证NAT后的回程路径
在AFTR的eth0-v4接口上抓包:
sudo tcpdump -i eth0-v4 -nn icmp抓包结果里,ICMP请求的源地址是192.0.2.200,也就是AFTR的NAT出口地址,而不是客户端的192.168.100.10。这就是NAT44生效的直接证据。回程的ICMP回复会回到AFTR,然后AFTR查连接跟踪表,找到这个连接对应的原始源地址和隧道信息,再封装进IPv6隧道送回B4。整个过程依赖Linux内核的conntrack机制,在/proproc/net/nf_conntrack里能看到当时的连接记录。
6. 常见问题与避坑记录
6.1 隧道接口起不来或者状态异常
创建隧道时报“Operation not permitted”,第一反应检查两个东西:内核模块ip6_tunnel是否加载,以及当前用户是否有创建网络接口的权限。root下一般都能解决。还有一个坑是netplan中tunnel的mode写错了,比如写成ipip或者gre,Linux不会报错,但隧道就是不通。ipip是IPv4 over IPv4,ipip6才是IPv4 over IPv6,一字之差,行为完全不同。
如果netplan apply后tun4over6接口状态是DOWN,可以手动执行ip link set tun4over6 up,或者用networkctl status tun4over6看systemd-networkd日志。也有可能是IPv6地址没配上,隧道local地址缺失会导致起不来,检查eth0是否有对应的IPv6地址。
6.2 能ping通隧道地址,但客户端访问外网不通
这个问题我踩过好几回,最常见原因是AFTR的iptables FORWARD链策略是DROP。Ubuntu默认没有自带防火墙规则,但如果安装过ufw或者之前跑过其他防火墙管理工具,FORWARD链策略可能变成DROP。先检查再放行:
sudo iptables -L FORWARD -n -v确认策略后,加上前面提到的两条FORWARD ACCEPT规则。还有另一个原因,B4设备上如果之前做过NAT测试,会在POSTROUTING链留下规则,导致客户端流量源地址变成B4的隧道地址,AFTR解封装后看到的源地址和预期不符,影响NAT连接跟踪。建议排查时把B4的NAT规则先清掉:
sudo iptables -t nat -F6.3 MTU问题导致网页打不开、图片加载不全
IPv4-in-IPv6封装会带来40字节的外层IPv6头开销,如果内网链路MTU设置不对,大包传输就会失败。现象很典型:小包通的,大包超时,打开网页能显示文字但加载不出图片。
解决办法是在隧道接口上调整MTU。B4和AFTR的tun4over6接口都设为1440:
sudo ip link set tun4over6 mtu 14401440这个值怎么来的?外层IPv6 MTU按1500计算,减去40字节IPv6头,再减去20字节内层IPv4头,就是1440。实际使用时还可以留点余量,比如设成1420,避免IPv6网络上还有额外封装(比如PPPoE会再减8字节)。用ping可以验证:
ping -M do -s 1400 192.0.2.1 -c 3-M do表示禁止分片,如果1400字节能通,说明当前MTU配置合理。如果1440字节的包不通,可以试试1420、1400,找到合适的值。生产环境还需要关注PMTUD(路径MTU发现)是否正常工作,ICMPv6错误消息能不能顺利返回B4侧。
6.4 NAT表满或者性能不足
实验环境不会遇到这个问题,但我实际参与过的DS-Lite项目中,AFTR最大的瓶颈往往是连接跟踪表和NAT44的转发性能。几万用户同时在线时,Linux默认的conntrack哈希表很容易被打满。调整方法:
sudo sysctl -w net.netfilter.nf_conntrack_max=1048576 sudo sysctl -w net.netfilter.nf_conntrack_buckets=262144第二个参数要求在加载nf_conntrack模块之前设置才能生效,生产环境可以考虑写进/etc/sysctl.conf。另外,多队列网卡和RSS(Receive Side Scaling)配置也能显著提升隧道吞吐,参数调整和内核配置相关。
6.5 B4设备重启后配置丢失
netplan写的配置重启后是持久的,但如果你用ip命令临时创建的隧道,重启后会消失。如果遇到重启后隧道起不来,优先检查netplan配置里local/remote的IPv6地址是否和实际接口匹配。另外要注意eth0如果启用了DHCPv6或者RA,它获取到的IPv6地址可能会变化,导致隧道源地址失效。实验环境建议关闭accept-ra,或者给eth0配置静态IPv6地址。
还有一个容易忽略的点:如果AFTR设备的IPv6地址变了,所有B4设备的remote地址都要跟着改。这就是为什么生产环境要用DHCPv6选项64动态下发AFTR地址,静态配置虽然简单,但可维护性太差。
6.6 关于双栈和464XLAT的扩展思考
DS-Lite解决了IPv4互联网访问的问题,但它没有解决双栈IPv4路径上的真双栈问题。如果用户终端本身是IPv6-only,需要访问IPv4-only服务,可以考虑464XLAT方案,它的CLAT端和B4类似,但需要额外做一次NAT46转换。DS-Lite和464XLAT在实际网络中经常并存,AFTR同时承担PLAT角色。在Linux上做464XLAT改造,只需要在B4侧加入NAT46规则,AFTR侧增加对应的NAT64逻辑即可。
搭建这套环境如果只是为了应付考试或者面试,静态配置跑通DS-Lite就足够。但如果你真的想搞懂运营商级IPv6过渡网络,强烈建议把DHCPv6选项64、动态隧道建立、NAT44性能压测都做一遍。我在实际调试过程中的体会是,DS-Lite最难的部分不是配置命令,而是理解流量到底在哪一层被封装、在哪一层被转换、回程流量如何被正确处理。把这套逻辑想透了,后续遇到任何隧道类问题都能很快定位。