ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

6to4隧道实验详解:IPv6过渡技术的地址换算与排错实践

6to4隧道实验详解:IPv6过渡技术的地址换算与排错实践

做IPv6迁移和过渡技术实验,6to4往往是最先接触的一种自动隧道机制,也是最容易让人在配置完成后怀疑人生的一个。理由很简单:它的地址是靠IPv4地址自动算出来的,隧道对端也是自动解析的,凡是"自动"的东西,一旦不符合预期,排查起来就格外费劲。我这次做的6to4实验,目标是在两台仅具备IPv4公网地址的路由设备之间,把两个孤立的IPv6站点通过6to4隧道连起来,再验证双向通信和抓包细节。整个过程不涉及额外服务器或隧道服务,适合在GNS3或真实实验室里快速复现。如果你正在学习IPv6过渡技术,或者要维护一个长期存在的老网段,这篇文章应该能帮你把6to4的来龙去脉和关键坑点一次理清。

1. 6to4隧道原理:不看懂地址生成规则,配置容易白做

1.1 地址是怎么"长"出来的

6to4的核心是RFC 3056定义的一种将IPv4地址内嵌进IPv6地址的编码方式。它规定了一个全局前缀2002::/16,紧接着的16位段和32位段用来承载一个IPv4公网地址,也就是说站点前缀为2002:xxxx:xxxx::/48,其中xxxx:xxxx是IPv4地址按字节转十六进制后的结果。

比如IPv4地址198.51.100.1,四个字节分别转成十六进制:198对应0xC6,51对应0x33,100对应0x64,1对应0x01。拼接得到C633:6401,所以这个站点的6to4前缀就是2002:C633:6401::/48。后面的16位是站点自己可以随意规划的子网ID,再后面64位是接口ID。在实验中我习惯把站点内部第一个子网规划为2002:C633:6401:1::/64,LAN里的主机、路由器接口地址都从这个前缀里取。

这种设计思路的妙处在于:IPv6地址和IPv4地址之间不再需要额外映射表,计算器都不用开,手算就能从对端的IPv6地址反推出它所在的IPv4地址是多少。隧道自动建立的前提就是这种"地址内嵌"机制,配置6to4时需要反复用到这个换算关系,一知半解非常容易翻车。

1.2 转发路径:不用手工指定隧道的另一端

手动6in4隧道要求你为隧道接口指定固定的对端地址,而6to4不要求。发出IPv6报文时,路由器查看目的IPv6地址,如果它落在2002::/16范围内,就把第2段、第3段共32位提取出来,还原成一个IPv4地址,作为外层IPv4封装的目的地址。这就是"自动隧道"的由来。

举个例子:R_A要发一个IPv6包给2002:C633:6402:1::10,它看到2002前缀,提取C633:6402,换算成IPv4就是198.51.100.2。于是路由器构造一个IPv4报文,源地址是自己的WAN口IPv4地址198.51.100.1,目的地址198.51.100.2,内层承载完整的IPv6报文,从物理接口发出。对端收到后剥掉外层IPv4头,把内层IPv6报文交给IPv6协议栈处理,整个过程不需要手工指定隧道对端。

这也解释了6to4站点为什么只要都有可路由的公网IPv4地址,理论上就能自动互通。地址本身就是路由信息,这在隧道协议里是一个非常巧妙的设计,也是后来6rd等衍生技术继续沿用的核心思想。

1.3 中继路由器的角色为什么重要

两个6to4站点之间互相访问,通过2002::/16内的自动解析就够了。但如果某个站点要访问真正的原生IPv6互联网,比如访问一个普通IPv6网站,就需要一台6to4中继路由器。中继路由器一边有公网IPv4地址,一边连着原生IPv6网络,同时宣告6to4路由。6to4站点把默认路由指向中继,中继把从隧道收到的IPv6包剥离外层IPv4头,送进原生IPv6网络;反向数据则从原生IPv6网络进来,封装成IPv4报文送回给6to4站点。

这里的坑在于:老教材普遍教人把默认路由下一跳配成6to4中继的anycast地址192.88.99.1,但该地址对应的192.88.99.0/24前缀在2015年已经被RFC 7526正式废弃。我做实验时按旧文档配置,结果始终无法访问外部IPv6网络,折腾很久才发现是公共中继服务早已不可用。所以现在做实验,要么自己搭一台中继路由器,要么干脆不配默认路由,只验证站点间互通。

2. 实验拓扑与地址规划:先把2002:前缀一次算对

2.1 拓扑设计与设备角色

实验拓扑尽量简单但保留关键要素。我搭建了两个IPv6站点Site A和Site B,各有一台边界路由器R_A、R_B,中间用一台普通交换机把两台路由器的WAN口连起来,模拟一个基本的IPv4网络链路。WAN口网段规划为198.51.100.0/24,R_A的WAN口地址是198.51.100.1,R_B的WAN口地址是198.51.100.2。每个站点内部放一台IPv6测试PC,手工配置静态IPv6地址和默认路由,不做SLAAC和路由通告。

GNS3里给路由器加两个三层接口,一个当WAN口,一个当LAN口,拓扑收敛很快。这样设计的价值在于隔离了无关变量:中间链路不需要跑OSPF之类动态路由协议,IPv4网络只负责转发封装后的数据,方便我们在隧道层集中排查问题。如果你在真实设备上复现,注意两台设备的WAN口必须能通过IPv4互通,这是6to4隧道能够工作的大前提。

2.2 IPv4地址转6to4前缀的三步换算

无论用图形界面还是命令行,地址换算都是同一套流程。以198.51.100.1为例,我习惯分为三步。

第一步,把IPv4地址的四个十进制字节分别转成十六进制:198、51、100、1分别对应C6、33、64、01。第二步,把十六进制两两拼接,得到两个16位段:C633和6401,组合成C633:6401。第三步,套入固定前缀2002::/48,得到2002:C633:6401::/48。

这里有一个极易犯的错误:字节拼接顺序不能颠倒,也不能把单个十六进制数字的补位补错。比如1转成十六进制是01,如果粗心写成1,整个IPv6地址结构就被破坏了,变成2002:C633:6401:1::/48这种错误前缀,语义完全不同。R_B的WAN口是198.51.100.2,同样按三步换算,得到C633:6402,站点前缀就是2002:C633:6402::/48。

2.3 规划结果:一张表搞定全部接口地址

把整个实验的IPv6地址规划成一张表,配置时照着填,能有效避免手误。隧道接口使用站点前缀下的子网0,LAN接口使用子网1。

设备接口相关IPv4地址IPv6地址
R_AWAN口198.51.100.1/24不配置IPv6地址
R_A隧道接口源地址为198.51.100.12002:C633:6401::1/64
R_ALAN口无2002:C633:6401:1::1/64
PC_A网卡无2002:C633:6401:1::10/64
R_BWAN口198.51.100.2/24不配置IPv6地址
R_B隧道接口源地址为198.51.100.22002:C633:6402::1/64
R_BLAN口无2002:C633:6402:1::1/64
PC_B网卡无2002:C633:6402:1::10/64

同时我还会在两边路由器上各加一条IPv6静态路由,目标2002::/16,出接口指向各自的隧道接口。这样任何一个6to4目的地址的数据包都会进入隧道处理,由隧道模块自动解析对应IPv4地址。

3. 在实验平台上一台一台完成配置

3.1 第一台路由器的配置过程

我在GNS3中用一款常见的企业路由器镜像演示配置,命令风格以主流IOS式命令行为主,不同版本可能有细微差异,但核心参数是一致的。R_A的完整配置逻辑分三块:先保证WAN口IPv4地址可用,再创建隧道接口并指定隧道模式,最后给LAN口配IPv6地址和路由。

配置片段大致如下:

interface GigabitEthernet0/1 description IPv4 uplink to middle network ip address 198.51.100.1 255.255.255.0 ! interface Tunnel0 description 6to4 tunnel to Site B ipv6 address 2002:C633:6401::1/64 ipv6 enable tunnel source 198.51.100.1 tunnel mode ipv6ip 6to4 ! interface GigabitEthernet0/0 description LAN Site A ipv6 address 2002:C633:6401:1::1/64 ipv6 enable ! ipv6 route 2002::/16 Tunnel0

这里特别注意隧道模式必须写清楚是6to4,不能是普通的IPv6 over IPv4手动隧道。tunnel source可以写物理接口名也可以写IPv4地址,我倾向于写IPv4地址,因为实验里更容易核对。隧道接口地址、LAN接口地址不能混淆,隧道接口属于子网0,LAN接口属于子网1,两者虽然都在同一个站点前缀下,但路由角色完全不同。

3.2 第二台路由器的镜像配置与差异化调整

R_B的配置几乎是对称的,只有几个关键数字不同:WAN口IPv4地址是198.51.100.2,因此隧道接口地址和LAN前缀里的站点前缀都换成2002:C633:6402。其他命令结构完全一致。

interface GigabitEthernet0/1 description IPv4 uplink to middle network ip address 198.51.100.2 255.255.255.0 ! interface Tunnel0 description 6to4 tunnel to Site A ipv6 address 2002:C633:6402::1/64 ipv6 enable tunnel source 198.51.100.2 tunnel mode ipv6ip 6to4 ! interface GigabitEthernet0/0 description LAN Site B ipv6 address 2002:C633:6402:1::1/64 ipv6 enable ! ipv6 route 2002::/16 Tunnel0

配置时最容易出的问题就是把两边的站点前缀搞混。有些人习惯复制R_A的配置改两处就收工,结果LAN口前缀和隧道前缀对不上,导致Site B的IPv6主机拿了一个不属于自己站点的前缀,路由黑洞立刻出现。所以每配完一台设备,我都会确认接口IPv6地址的2002:C633:640X字段是不是本端WAN口对应的值。

3.3 不同设备命令行风格的核心差异

不同厂商设备在6to4配置上思路完全一致,命令关键词略有差异。我分别试过厂商A和厂商B两种命令行风格,差异主要体现在隧道协议和源地址的写法上。厂商B风格的等价配置大致长这样:

interface Tunnel0/0/1 tunnel-protocol ipv6-ipv4 6to4 source GigabitEthernet0/0/1 ipv6 enable ipv6 address 2002:C633:6401::1/64

相比厂商A把tunnel source直接写IPv4地址,厂商B更推荐引用物理接口名。好处是当接口IPv4地址变化时隧道自动跟随,不会出现配置漂移。缺点是如果物理接口名被误改名或删除,隧道源就变成空值,查起来会比较隐蔽。无论哪种风格,最终都必须满足三个条件:隧道入接口有站点前缀地址、LAN侧有独立子网地址、存在指向2002::/16的静态路由。

4. Linux主机模式下的6to4配置与验证

4.1 用iproute2创建SIT隧道

除了路由器,Linux主机也可以直接充当6to4网关。对学习的人来说,Linux命令行更直观,便于用ip命令观察整个隧道封装细节。以模拟R_A的那台Linux主机为例,配置过程如下:

modprobe sit ip tunnel add tun6to4 mode sit ttl 64 remote any local 198.51.100.1 ip -6 addr add 2002:c633:6401::1/64 dev tun6to4 ip link set tun6to4 up ip -6 route add 2002::/16 dev tun6to4

SIT是IPv6-in-IPv4隧道在Linux里的实现,remote any表示自动模式,即6to4。隧道的IPv6地址我习惯配成2002:c633:6401::1/64,对应站点前缀的子网0。LAN侧接口比如eth1配置2002:c633:6401:1::1/64,然后打开IPv6转发:

sysctl -w net.ipv6.conf.all.forwarding=1

如果PC_A和这台Linux网关在同一局域网,应该把PC_A的默认IPv6路由指向2002:c633:6401:1::1,而不是指向隧道接口地址。隧道接口只承担站点之间的封装转发,站内主机不直接使用它。

4.2 默认路由与中继配置(含192.88.99.1的现状)

如果实验只验证Site A与Site B互通,上面配置已经够了,不需要默认路由。但若要访问原生IPv6网络,比如测试一个IPv6公共网站,就必须配置默认路由指向一台可用的6to4中继。老教材普遍教这样配置:

ip -6 route add default via ::192.88.99.1 dev tun6to4

这个::192.88.99.1是IPv4映射IPv6地址的写法,用来让内核把192.88.99.1当作外层IPv4封装目的地址。但实际环境中,192.88.99.1属于已被废弃的6to4中继anycast前缀,对应服务基本不可用。所以现在做实验要么自己搭一台中继,要么不要照搬这条命令。

自己搭中继的前提是有一台同时具备公网IPv4和原生IPv6连接的路由器,它宣告2002::/16路由并允许把隧道流量转发到原生IPv6网络。对于纯实验场景,我认为站点间互通验证已经足够说明6to4的原理,默认路由配不配都可以。

4.3 连通性测试:ping、traceroute和抓包验证

配置完成后,在PC_A上ping PC_B的IPv6地址:

ping6 2002:c633:6402:1::10

如果能通,基本说明隧道链路正常。进一步在R_A的WAN口抓包,可以看到外层是IPv4头,IP协议号为41,也就是IPv6-in-IPv4封装,内层是完整的IPv6报文:

tcpdump -i eth0 -nn 'ip proto 41'

观察点有两个。外层IPv4源地址应该是198.51.100.1,目的地址应该是198.51.100.2;内层IPv6源地址应该是2002:c633:6401:1::10,目的地址是2002:c633:6402:1::10。如果外层目的地址不是198.51.100.2,说明路由或地址解析逻辑有问题,需要回头检查静态路由是否指到了隧道接口。

用traceroute6也能直观看到路径:

traceroute6 2002:c633:6402:1::10

第一跳通常是PC_A的网关R_A的LAN接口地址,第二跳进入隧道后,有些实现会显示对端隧道接口地址,有些会显示外层IPv4还原出来的IPv6地址,这是正常现象。重点看最终能否到达PC_B,只要最后一跳成功,隧道就工作正常。

5. 排错实录:配置全对却不通的五种典型原因

5.1 底层IPv4先通了吗:被忽略的第一道门槛

6to4的本质是IPv6-in-IPv4隧道,底层IPv4不通,隧道一定不通。很多人把注意力全放在IPv6配置上,忘了先做最基础的IPv4连通性测试。我建议的检查顺序是:先ping 198.51.100.2,确认中间IPv4链路正常;再检查隧道接口状态;最后才看IPv6路由和地址。

如果在GNS3里发现ping不通,常见原因是中间交换机的VLAN划分错了或者路由器WAN口没有起来。真实网络里则可能是中间设备做了ACL过滤。这时候抓包看有没有ICMP回包,能快速判断是物理链路、路由还是策略问题。底层IPv4通了,隧道工作才有基础,这一步别跳过。

5.2 地址换算错误与子网规划不清

6to4配置里最常见的手误就是把IPv4地址转错。典型错误有两类:一类是把198.51.100.1转成C6:33:64:1这种连位数都对不齐的写法,一类是在子网ID上随意发挥。规划时要始终记住,2002:C633:6401::/48是整个站点前缀,后面要留出16位子网ID,隧道接口和LAN接口必须使用同一个站点前缀下的不同子网,不能跨站点使用。

我遇到过一种隐蔽情况:两端配置完成后,Site A能ping通Site B的LAN接口,但ping不通PC_B。原因就是R_B把LAN子网错配成了2002:C633:6401:1::/64,跟对端站点前缀重合,数据包到达R_B后虽然进了隧道,但R_B认为目的地址属于另一个接口直连网段,于是出现了路由环路或被静默丢弃。抓包能看到数据包反复进出隧道,这种情况最容易浪费时间。

5.3 协议41被安全策略静默丢弃

6to4报文使用IPv4协议号41,也就是IPv6-in-IPv4封装。很多防火墙、安全组默认只放行TCP、UDP和ICMP,协议41会被直接丢弃。这个坑非常隐蔽,因为从应用层看ping不通,但路由和地址配置都看不出问题。尤其是GNS3里如果有云连接真实网络,或者实验网络经过虚拟化平台的流量过滤,都可能出现这种状况。

排查方法很简单:在隧道对端的WAN口抓包,看有没有ip proto 41的报文。如果有包到达但没有回包,大概率是回程方向被放行规则拦了;如果连包都到不了,就要检查中间链路的每一跳。部分防火墙规则里把协议41写成IP/IPv6或ipv6-in-ipv4,注意不要选成IPv6 over GRE,两种协议编号不同,放行规则不能混用。

5.4 MTU与分片问题导致小包通大包不通

6to4隧道会引入额外的IPv4报文头开销,隧道接口的MTU如果设置不当,会出现64字节的小ping能通、1500字节的带数据业务不通的现象。原因通常是IPv6最小链路MTU要求1280,隧道封装后还要加上20字节IPv4头,物理链路MTU在1500时,最好把隧道接口的IPv6 MTU配到1280或1480,并确保中间设备不拦截ICMPv6 Packet Too Big消息。

我在实验里遇到过一次,PC_A能ping通PC_B的小包,但用ssh拉取文件时卡死。后来在R_A的隧道接口上把IPv6 MTU调整为1280,问题立刻消失。这个经验对真实网络同样重要,尤其是经过多段隧道或QoS策略的网络,MTU问题很容易被误判为带宽或延迟问题。

5.5 过期anycast中继192.88.99.1的坑

如果实验里照搬了默认路由ip -6 route add default via ::192.88.99.1 dev tun6to4这条命令,再去访问原生IPv6地址,你会发现全网几乎没有设备响应。这不是你的配置错了,而是192.88.99.1所在的192.88.99.0/24作为6to4中继anycast前缀,已经按RFC 7526正式废弃,绝大多数运营商已经停止部署相关服务。

因此我建议实验里用站点间互通来验证6to4,不要依赖外部中继。如果必须访问原生IPv6,就自己在实验网络里搭一台中继路由器,或者改用6in4手动隧道配合隧道服务。理解了这一点,才算真正理解6to4在现网中的定位限制。

6. 6to4与常用替代方案的选型判断

6.1 6to4和6in4的取舍

6to4最大的优势是配置简单、对端自动解析,但有两个致命短板。一是必须使用独立公网IPv4地址,因为2002::/16前缀是从公网IPv4地址推导的,NAT后的私有地址推导出来的前缀没有任何可路由性。二是公共中继基本消失,单独一个6to4站点很难访问原生IPv6网络。

6in4手动隧道虽然要求手动指定隧道对端,配置多一步,但正因为对端明确,稳定性远高于6to4。手动隧道的IPv4地址同样要求公网可达,且对端变化时必须改配置。如果你有一对固定公网IPv4地址,需要长期稳定传输IPv6流量,6in4是比6to4更可靠的选择。两者本质都是SIT隧道,区别只在于对端地址是自动解析还是手工指定。

6.2 6rd思路:可以被救活的运营商版6to4

6rd本质上是运营商对6to4思路的改造,保留IPv4地址嵌入IPv6前缀的自动机制,但摆脱了2002::/16这个全球共享前缀。运营商可以分配自己合法的IPv6前缀,同时允许客户使用私有IPv4地址,在运营商的边界中继设备上完成封装转换。

这是6to4被救活的一个重要方向。家庭宽带场景下,客户终端和中继设备之间自动建立隧道,IPv6流量经过中继进入运营商IPv6骨干,体验比手动配置隧道好得多。缺点是完全依赖运营商部署,个人用户无法自行搭建。学习6to4时理解了地址内嵌机制,再看6rd的配置就能秒懂,这也算做这个实验的额外收益。

6.3 什么时候还值得搭6to4实验

从工程实践看,我不建议在新项目里把6to4作为正式接入方案,但作为学习IPv6过渡技术的实验项目,它依然非常值得做。一是它完整展示了地址与路由信息解耦再耦合的思路,帮你建立隧道协议的整体认知。二是排查6to4问题的方法论可以平移到6rd、过渡网关、手动隧道等更多场景。三是这类自动隧道的排错经验,会让你对协议栈底层交互有更扎实的理解。

如果要在真实网络里做6to4实验,最稳妥的方式是用测试网段和测试设备,不接入生产流量,并提前确认所有中间链路都放行协议41。我在GNS3里反复做过这个实验,每次都会发现新的理解盲区,尤其是中继机制和地址规划部分。最后再说一个实操习惯:做这类自动隧道实验,准备一个十六进制和十进制的换算表在手边,比记任何命令都管用。我一度以为早就滚瓜烂熟,结果一个简单的IP地址转错就排查了半小时。动手敲命令之前,先把IPv4地址的十六进制表示在纸上写一遍,再开始配置,你会省下很多时间。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进