☰
GRE隧道全解析:原理、配置与排错实战
2026/10/10 12:36:42 网站建设 项目流程

先说个真实的场景:两年前我给一家公司做分支互联方案,A点在江苏、B点在浙江,两边内网都用了 192.168.1.0/24,访问线上系统要走运营商公网。按照常规思路,要么拉专线,要么在每个业务系统上做端口映射,但端口映射只解决具体应用,解决不了“整个网段互通”的需求。后来我选了 GRE 隧道(Generic Routing Encapsulation,通用路由封装),在两家路由器的公网地址之间建立了一条逻辑隧道,把两边私网顺畅地连在一起。

这也是我网络实验笔记里的第 02 篇,主题就是 GRE。写这篇东西不是为了背 RFC 定义,而是把“为什么要用隧道”到“配置怎么下”再到“出问题怎么查”的完整过程讲透。如果你正打算在分支之间建隧道,或者隧道配完发现“物理链路明明通,业务就是不通”,这篇文章应该能帮你省下不少排查时间。

1. 为什么放着现成的IP路由不用,非要再套一层GRE

1.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 点 PC(192.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 头的协议号是 47(GRE),就按 GRE 流程解封装,取出内层 IP 包,再按内层目的地址转发给内网服务器。

整个过程里,中间的网络设备根本不知道内层报文是什么,甚至不知道这是一条隧道。这也是 GRE 能承载任意协议的根本原因:封装的边界是端点设备,不是中间网络。

2.2 GRE头字段拆解:C、K、S位分别管什么

标准 GRE 头是 4 字节,但如果开了校验和、Key、序列号,头部会变长。我做了个简化表:

字段/标志含义使用场景
C(Checksum)带校验和,对 GRE 头和负载做校验强调数据完整性时开启,实际中较少默认开启
K(Key)流标识,相当于隧道 ID多点 GRE、区分不同流时使用,两端 Key 必须一致
S(Sequence)序列号,用于检测丢包、乱序对包序敏感的传输场景
VersionGRE 版本号,必须为 0版本不对会被丢弃
Protocol Type内层协议类型,例如 IPv4 是 0x0800解封装后按该类型交给对应协议栈

我见过不少人在配置里纠结:Key 到底要不要配?如果只有一对一的点对点隧道,Key 可有可无;但在 mGRE(多点 GRE)场景里,没有 Key 就无法区分多个隧道流,这时就必须配置。注意,Key 不是加密密钥,它只是标识,别把它当成 IPsec 的预共享密钥。

2.3 Tunnel接口的虚拟本质:为什么它没有物理链路检测能力

Tunnel 接口是一个虚拟接口,它没有真实的插拔、没有信号强度、没有误码率,设备只能靠 GRE keepalive 或上层路由协议来判断隧道是否活着。这也是很多人第一次配 GRE 时容易困惑的地方:物理接口明明 up,Tunnel 接口怎么就不 up?

记住一句话:Tunnel 接口 up 不代表隧道能通,Tunnel 接口 down 也不一定代表物理线路断了。它只是一个“把报文交给封装引擎”的抽象逻辑点。隧道是不是真的能通,取决于外层物理链路是否正常、封装是否匹配、路由是否回指。排错时如果脑子里有这层“虚拟接口”的概念,就不容易被表象带偏。

3. 从零配置一条能通的GRE隧道:设备参数与周边配套

3.1 拓扑与地址规划

一个最典型的点对点 GRE 组网如下:

  • R1 公网地址:100.64.0.1(连接运营商)
  • R2 公网地址:100.64.0.2(连接运营商)
  • 隧道网段:10.0.1.0/30,R1 侧 10.0.1.1,R2 侧 10.0.1.2
  • 内网网段:A 点 192.168.10.0/24,B 点 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 - 20(IP 头)- 20(TCP 头)= 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。

要让这个切换自动发生,两层配合缺一不可:

  1. keepalive 把 Tunnel 状态打成 down。
  2. 路由或 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 第一件事:先分清“隧道没起来”和“路由不对”

遇到业务不通,我会先做两个 ping:

  • ping 对端公网地址,确认物理链路和运营商路由正常。
  • 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 1500,GRE 封装要吃掉 24 字节,所以内层 IP 包最多 1476,ICMP 负载最多 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 一个完整的排错路径参考

把上面步骤整理成一条执行路径,遇到隧道问题时按顺序走:

  1. 确认外层公网可达:ping 对端公网地址。
  2. 确认隧道建立:ping 对端 Tunnel 地址。
  3. 确认内网路由:在两端分别 ping 对端终端网段网关。
  4. 测 MTU:用 1472 / 1500 字节的 ping 对比。
  5. 看 MSS:检查 TCP 握手协商的 MSS 是否与 Tunnel MTU 匹配。
  6. 抓包分析:重点过滤 GRE 协议,看封装源、目的和分片情况。
  7. 检查递归路由:确认去往 tunnel destination 的路由没有指向 Tunnel0。

按这个链路走下去,90% 的 GRE 问题能在十分钟内定位。剩下的 10%,基本就是设备厂商的特定行为差异或版本 bug——这时把抓包文件和配置贴出来,社区里很容易找到答案。

如果让我给刚接触 GRE 的同行一个建议,我会说:先不要急着配 IPsec 或者上 mGRE,第一步一定是把裸 GRE 隧道配合静态路由跑通,再加上 keepalive、MTU 调整,最后在 Tunnel 上叠加 OSPF 和备份链路。一步一个坎地过,你对这条虚拟通道的理解会比只看文档扎实得多。我自己就是这么一路踩坑过来的,现在只要看到“隧道通了却不通”这类问题,脑子里能立刻冒出一条清晰的排查线,这大概就是经验沉淀的价值吧。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询