先说个真实的场景两年前我给一家公司做分支互联方案A点在江苏、B点在浙江两边内网都用了 192.168.1.0/24访问线上系统要走运营商公网。按照常规思路要么拉专线要么在每个业务系统上做端口映射但端口映射只解决具体应用解决不了“整个网段互通”的需求。后来我选了 GRE 隧道Generic Routing Encapsulation通用路由封装在两家路由器的公网地址之间建立了一条逻辑隧道把两边私网顺畅地连在一起。这也是我网络实验笔记里的第 02 篇主题就是 GRE。写这篇东西不是为了背 RFC 定义而是把“为什么要用隧道”到“配置怎么下”再到“出问题怎么查”的完整过程讲透。如果你正打算在分支之间建隧道或者隧道配完发现“物理链路明明通业务就是不通”这篇文章应该能帮你省下不少排查时间。1. 为什么放着现成的IP路由不用非要再套一层GRE1.1 私网跨公网互访的天然矛盾很多人刚接触组网时会有个疑问两边内网既然都是 IP 网络为什么不能直接写一条静态路由指到对端的公网地址上这背后的矛盾在于运营商公网设备根本不会学习你内网的路由。你可以把自己的私网段写进静态路由但中间那十多台运营商路由器不认识 192.168.x.x它们只会按照公网 IP 转发。就算你把私网路由强推给运营商运营商的设备也会因为路由条目过多、安全性差等原因直接丢弃。所以想让私网数据穿过公网唯一的办法就是把私网报文“藏”在一个公网能识别的外壳里。这个“外壳”的思路其实很好理解你有一封信要寄给一个只有门牌号、没有邮路直达的地方你不能直接让邮递员把这封信送到屋内但你可以把它装进一个只写到“转发站”的快递袋里由转发站拆开再送到目的地。GRE 干的正是这件事——把原始 IP 报文整个封装进一个新的外层 IP 报文里中间网络只关心外层地址。1.2 GRE的定位不加密但解决可达性GRE 在设计之初就没有考虑加密它的核心诉求只有一个在一个网络之上再架一层网络。这个设计带来几个直接好处内层可以是 IPv4、IPv6、组播甚至以太网帧外网根本不关心里面是什么。隧道两端可以直接运行 OSPF、BGP 等动态路由协议让路由收敛跟着隧道状态走。配置开销小一条 Tunnel 接口命令就能起一个隧道不像某些方案需要复杂的协商过程。因为“通用”二字GRE 能承载很多非 IP 协议这也是它在很多高层方案里被当作基础通道的原因。很多工程场景下你不会单独看到一个裸 GRE而是看到“GRE over IPsec”“mGRE NHRP”这类复合方案但万变不离其宗底层的承载逻辑都是 GRE。1.3 与专线、VXLAN等方案相比GRE的取舍在哪里做网络方案时我常用下面这张表来对比 GRE 和应用层/虚拟化方案方案成本部署复杂度组播支持适合场景运营商专线/MPLS高低开通等工期支持取决于运营商对 SLA 要求高、预算充足GRE 隧道低中配置简单支持分支互通、临时链路、动态路由实验VXLAN中高需要 Underlay 网络支持需配置数据中心大二层、虚拟化网络如果一个场景只是偶尔跨网段访问个网页那 GRE 未必是最佳选择但如果你要在两个站点之间跑组播、跑 OSPF、还要让故障时路由自动收敛GRE 就是成本最低的方案。专线当然更稳可很多时候需求只是一条临时链路申请专线周期长、费用高GRE 点到点隧道十分钟就能拉通这在实际工作中非常实用。2. GRE报文拆开看外层IP头与内层协议之间的关系2.1 一次GRE封装的完整旅程假设 A 点 PC192.168.10.10要访问 B 点服务器192.168.20.10数据包进入 R1 后R1 查路由表发现目的网段指向 Tunnel 接口于是开始封装。封装顺序是外层IP头源100.64.0.1目的100.64.0.2 | GRE头4字节可带选项 | 内层IP报文原始包封装完成后R1 把新的报文交给物理接口发出运营商设备看到的是一个普通公网单播包按外层目的 IP 转发到 R2。R2 收到后发现外层 IP 头的协议号是 47GRE就按 GRE 流程解封装取出内层 IP 包再按内层目的地址转发给内网服务器。整个过程里中间的网络设备根本不知道内层报文是什么甚至不知道这是一条隧道。这也是 GRE 能承载任意协议的根本原因封装的边界是端点设备不是中间网络。2.2 GRE头字段拆解C、K、S位分别管什么标准 GRE 头是 4 字节但如果开了校验和、Key、序列号头部会变长。我做了个简化表字段/标志含义使用场景CChecksum带校验和对 GRE 头和负载做校验强调数据完整性时开启实际中较少默认开启KKey流标识相当于隧道 ID多点 GRE、区分不同流时使用两端 Key 必须一致SSequence序列号用于检测丢包、乱序对包序敏感的传输场景VersionGRE 版本号必须为 0版本不对会被丢弃Protocol Type内层协议类型例如 IPv4 是 0x0800解封装后按该类型交给对应协议栈我见过不少人在配置里纠结Key 到底要不要配如果只有一对一的点对点隧道Key 可有可无但在 mGRE多点 GRE场景里没有 Key 就无法区分多个隧道流这时就必须配置。注意Key 不是加密密钥它只是标识别把它当成 IPsec 的预共享密钥。2.3 Tunnel接口的虚拟本质为什么它没有物理链路检测能力Tunnel 接口是一个虚拟接口它没有真实的插拔、没有信号强度、没有误码率设备只能靠 GRE keepalive 或上层路由协议来判断隧道是否活着。这也是很多人第一次配 GRE 时容易困惑的地方物理接口明明 upTunnel 接口怎么就不 up记住一句话Tunnel 接口 up 不代表隧道能通Tunnel 接口 down 也不一定代表物理线路断了。它只是一个“把报文交给封装引擎”的抽象逻辑点。隧道是不是真的能通取决于外层物理链路是否正常、封装是否匹配、路由是否回指。排错时如果脑子里有这层“虚拟接口”的概念就不容易被表象带偏。3. 从零配置一条能通的GRE隧道设备参数与周边配套3.1 拓扑与地址规划一个最典型的点对点 GRE 组网如下R1 公网地址100.64.0.1连接运营商R2 公网地址100.64.0.2连接运营商隧道网段10.0.1.0/30R1 侧 10.0.1.1R2 侧 10.0.1.2内网网段A 点 192.168.10.0/24B 点 192.168.20.0/24隧道地址选 /30 通常就够了因为点对点隧道只需要两个可用地址。有人喜欢用 /24也能通但没必要。3.2 核心配置与逐行注释下面以 H3C 风格为例Cisco、华为的命令行大同小异我会在括号里注明差异# R1 配置 interface LoopBack0 ip address 10.0.0.1 255.255.255.255 interface GigabitEthernet0/1 ip address 100.64.0.1 255.255.255.255.0 interface Tunnel0 description To-R2 ip address 10.0.1.1 255.255.255.252 tunnel source 100.64.0.1 tunnel destination 100.64.0.2 keepalive 5 3 mtu 1400 ip tcp adjust-mss 1360 ip route-static 192.168.20.0 255.255.255.0 Tunnel 0# R2 配置对称 interface GigabitEthernet0/1 ip address 100.64.0.2 255.255.255.255.0 interface Tunnel0 description To-R1 ip address 10.0.1.2 255.255.255.252 tunnel source 100.64.0.2 tunnel destination 100.64.0.1 keepalive 5 3 mtu 1400 ip tcp adjust-mss 1360 ip route-static 192.168.10.0 255.255.255.0 Tunnel 0简单解释几个关键点tunnel source和tunnel destination决定外层 IP 头的源和目的必须写成公网可达地址。Cisco 允许直接写物理接口名H3C 也支持引用接口但我建议写 IP因为更直观、也避免接口状态变化时影响隧道。华为的命令需要加一条tunnel-protocol gre源和目的用source 100.64.0.1、destination 100.64.0.2其余思路一样。隧道两端的内网路由必须双向写好只写一端肯定不通。3.3 容易被默认值坑的参数keepalive、MTU、MSS我实际配置时从来不会只配 IP 和 source/destination因为我踩过太多次默认值的坑。先说 MTU。GRE 封装会在原有 IP 报文外面加 20 字节外层 IP 头 4 字节 GRE 头一共 24 字节开销。如果物理接口 MTU 是 1500那么隧道内层 IP 包最大只能设 1476否则就会出现超过物理链路 MTU 导致分片的情况。我习惯把 Tunnel 口 MTU 设为 1400留一点余量给额外的 IP 选项或将来叠加 IPsec 的 ESP 头。很多设备默认 Tunnel MTU 是 1476 或 1500如果默认值太高大包很容易丢。然后是 TCP MSS。TCP 握手时双方会协商 MSS默认参考的是本地出口接口 MTU。如果隧道 MTU 降到 1400而 TCP 还按 1500 协商 MSS业务数据包就会超过隧道承载能力被丢弃或分片表现为网页打开慢、文件传一半卡住。所以我在 Tunnel 接口上会配ip tcp adjust-mss 1360这个值的计算是 1400 - 20IP 头- 20TCP 头 1360。还有 keepalive。GRE keepalive 是两个隧道端点之间的问候机制一端周期性发送探测对端回应如果连续多次收不到回应Tunnel 状态就 down。keepalive 5 3表示每 5 秒发一次连续 3 次无回应判定失效。有了它静态路由才能感知隧道故障并联动备份链路切换。两端 keepalive 参数建议保持一致不然会出现一端认为隧道 down、另一端认为 up 的不对称状态。3.4 路由打通的两条路静态路由还是OSPF隧道建好后内网路由可以走静态也可以在隧道上跑 OSPF。静态路由最简单就是我在配置里写的ip route-static 192.168.20.0 ... Tunnel 0但它的缺点是一旦隧道 down 但没有联动检测路由会一直存在数据包被扔进 Tunnel 后丢掉用户体验就是“时好时坏”。配合 keepalive 把它变成 down 状态后还需要静态路由关心 Tunnel down 的情况——这时可以用接口路由的自动关联或用 NQA/Track 联动。如果站点比较多我更推荐在 Tunnel 上直接跑 OSPF# R1 上启用 OSPF宣告隧道网段和终端网段 ospf 1 area 0.0.0.0 network 10.0.1.0 0.0.0.3 network 192.168.10.0 0.0.0.255隧道本质是点对点网络OSPF 建邻居非常快隧道 down 时 OSPF 能秒级撤销路由收敛速度远比静态路由可靠。第一次做 GRE 实验时建议先跑通静态再加 OSPF这样能清楚体会到两种方式的差异。4. GRE不只是点对点组播、keepalive与复合隧道怎么用4.1 组播和动态路由协议为什么依赖GRE运营商公网通常不传递组播流量但 OSPF 邻接关系的 Hello 包、组播应用的数据流都需要组播支持。GRE 把组播报文作为内层负载封装进单播外层 IP 头中间的运营商设备只看外层单播头自然就能转发组播业务。这是 GRE 一个非常关键的价值它把“组播不能跨公网”这个问题变成了“只要公网能转发单播就行”。举一个我实际遇到的场景两个站点要跑一套依赖组播的运维监控系统视频流从 A 点发给 B 点中间跨运营商。直接裸跑肯定不行我在两端各放一台路由器中间建一条 GRE 隧道把组播源和接收端分别接入隧道两侧内层组播包在源端封装、接收端解封装业务正常跑起来。如果没有 GRE这类需求就得拉组播专线成本完全不是一个量级。4.2 keepalive联动备份链路故障切换的完整逻辑GRE 本身没有物理链路检测所以要用协议机制补上。我把一条场景拆开说主路径R1 与 R2 之间的 GRE 隧道。备份路径R1 与 备用节点 R3 之间的专线。平时流量走 Tunnel一旦 Tunnel down立刻切换到 R3。要让这个切换自动发生两层配合缺一不可keepalive 把 Tunnel 状态打成 down。路由或 Track 模块感知 Tunnel down启动备份路径。以 H3C 为例NQA 检测隧道对端打通后再联动静态路由nqa entry admin gre-monitor type icmp-echo destination ip 10.0.1.2 frequency 5 probe count 3 next-hop ip 100.64.0.2 return track 1 nqa entry admin gre-monitor reaction 1 ip route-static 192.168.20.0 255.255.255.0 10.0.1.2 track 1 ip route-static 192.168.20.0 255.255.255.0 100.80.0.2 preference 100这段配置的意思是NQA 每 5 秒向隧道对端内网地址打一次 ICMP连续探测失败后让第一条静态路由失效流量自动落到优先级更低的备份路由上。整个过程 15 秒左右完成业务中断窗口很短。比起纯静态路由靠天吃饭这已经算廉价的可靠性方案了。4.3 从GRE到GRE over IPsec再到DMVPN裸 GRE 有个问题数据明文传输。业务安全性要求高的时候我会在 GRE 外层再叠加 IPsec 加密这就是常说的 GRE over IPsec。外层 IP 协议号从 47 变成 ESP 的 50抓包时能看到明文 GRE 头但负载已经是加密内容。到了多分部场景点到点 GRE 隧道数量会呈组合数增长。比如 5 个站点全互联需要建立 10 条隧道管理成本让人头疼。mGRE多点 GRE NHRP下一跳解析协议的方案能解决这个问题分支只需要知道中心 Hub 的地址通过 NHRP 协议动态学习其他分支的公网地址按需建立隧道。Hub 负责控制面Spoke 之间流量可以直连也可以走 Hub。这种模式后来也演变成了不少 SD-WAN 方案的基础设计。如果你从裸 GRE 起步逐步叠加 IPsec 加密、mGRE 多点再到 NHRP就能把网络从“两点专线”扩展成“全网状动态互联”整个演进路径非常清晰。5. 隧道通了却不通的排错链路从MTU黑洞到路由黑洞5.1 第一件事先分清“隧道没起来”和“路由不对”遇到业务不通我会先做两个 pingping 对端公网地址确认物理链路和运营商路由正常。ping 对端 Tunnel 接口地址10.0.1.2确认隧道封装和解封装是否正常。如果公网能 ping 通、Tunnel 地址 ping 不通问题大概率出在封装相关配置tunnel source/destination 写错、keepalive 参数不对称、设备没有放行外层 IP 协议 47。如果 Tunnel 地址能 ping 通但 PCs 访问不通说明隧道本身是好的问题在内网路由、NAT 策略或防火墙策略上。这一步先分清定位能省下一大半排查时间。我见过太多人一上来就抓包看业务端口绕了很大一圈发现隧道根本就没建立。5.2 MTU黑洞ping小包通、大包不通MTU 问题最典型的表象是小包能通、大包不能通网页会打开但图片加载不出来或者 SSH 连上后一敲一堆输出的命令就卡死。标准的验证方法是用 ping 限制包大小并禁止分片。在 Linux 上# 发送长度为1472字节的ping包20字节IP头8字节ICMP头1500 ping -s 1472 -M do 10.0.1.2 # 再发1500字节如果1472通而1500不通基本可断定MTU受限 ping -s 1500 -M do 10.0.1.2这里的计算逻辑是物理接口 MTU 1500GRE 封装要吃掉 24 字节所以内层 IP 包最多 1476ICMP 负载最多 1472。1472 能通说明隧道封装没问题1500 不通说明超大包要么在物理口被分片要么因为 DF 位被直接丢弃。解决方式就是我在配置里强调过的两板斧把 Tunnel MTU 降到 1400同时把 TCP MSS 调整到 1360。不要只改一头MTU 改了但 MSS 没改TCP 还是按旧值协商大包照样丢。5.3 递归路由最隐蔽的路由黑洞这个坑我踩过一次印象特别深。当时隧道配置看着全对Tunnel 地址也能 ping 通但是业务时通时不通排查到半夜才发现是路由递归。错误配置长这样ip route-static 100.64.0.0 255.255.255.0 Tunnel 0问题出在R1 去往隧道对端公网地址 100.64.0.2 的路由被写进了 Tunnel0。当设备封装一个目的地址为 100.64.0.2 的外层包时查路由发现这个地址的出口也是 Tunnel0于是又进行第二次 GRE 封装形成无限递归。最终结果是外层 IP 包永远到不了对端Tunnel 地址 even 能 ping 通因为 ICMP 请求进入 Tunnel 后又被解封装成一个新的请求但真实业务全丢。排查这种问题看路由表很有用如果去往 tunnel destination 的下一跳是 Tunnel 口基本就是递归路由。解决方法是把去往对端公网地址的路由改为指向物理接口或默认路由保证外层封装的源、目的 IP 都走物理链路。5.4 抓包验证Wireshark里的关键信息如果上面几步都查不出问题就该抓包了。在外层物理链路上抓包重点看两个东西过滤gre或ip.proto 47确认 GRE 报文是否存在。如果只有请求没有应答说明内层数据在到达对端后被丢弃重点查对端的解封装和路由。如果抓到大量分片碎片只有首片带 GRE 头后序分片没有说明 MTU 问题仍然存在。Wireshark 打开 GRE 报文后直接看外层 IP 头里的源和目的是不是你预期的公网地址再看内层 IP 头的源和目的是不是私网地址。这两段地址关系一目了然大多数封装错误都能当场看出来。提示GRE 报文的分片逻辑有一个容易误判的细节——只有第一个分片会携带 GRE 头后序分片是纯 IP 分片。所以抓包时不要因为“看到的不是 GRE 头”就断定封装异常去外层 IP 头的 Fragment Offset 字段确认是否分片更靠谱。5.5 一个完整的排错路径参考把上面步骤整理成一条执行路径遇到隧道问题时按顺序走确认外层公网可达ping 对端公网地址。确认隧道建立ping 对端 Tunnel 地址。确认内网路由在两端分别 ping 对端终端网段网关。测 MTU用 1472 / 1500 字节的 ping 对比。看 MSS检查 TCP 握手协商的 MSS 是否与 Tunnel MTU 匹配。抓包分析重点过滤 GRE 协议看封装源、目的和分片情况。检查递归路由确认去往 tunnel destination 的路由没有指向 Tunnel0。按这个链路走下去90% 的 GRE 问题能在十分钟内定位。剩下的 10%基本就是设备厂商的特定行为差异或版本 bug——这时把抓包文件和配置贴出来社区里很容易找到答案。如果让我给刚接触 GRE 的同行一个建议我会说先不要急着配 IPsec 或者上 mGRE第一步一定是把裸 GRE 隧道配合静态路由跑通再加上 keepalive、MTU 调整最后在 Tunnel 上叠加 OSPF 和备份链路。一步一个坎地过你对这条虚拟通道的理解会比只看文档扎实得多。我自己就是这么一路踩坑过来的现在只要看到“隧道通了却不通”这类问题脑子里能立刻冒出一条清晰的排查线这大概就是经验沉淀的价值吧。