做HCIP实验做到MGRE这一段,不少人是又兴奋又头疼。兴奋的是终于要做多点隧道了,头疼的是明明照着视频敲完了命令,分支之间却怎么也ping不通,一查OSPF邻居才发现始终停在2-Way/DROTHER。这个实验我前前后后做了三遍,第一遍把所有坑踩了个遍,后面每做一遍对NHRP的理解就深一层。这篇就把MGRE实验从原理到配置再到排错完整捋一遍,适合正在备考HCIP的兄弟,也适合企业网络里想搞分支互联但不想手工维护一堆GRE隧道的人参考。
1. 先搞清楚MGRE到底解决什么问题
1.1 传统GRE的痛点
GRE是最常用的隧道封装协议之一,它能把私网报文直接封装进公网IP报文里,让两个隔了公网的站点像在一个二层网络里一样通信。但传统GRE是点到点模型,一条隧道只能在两台设备之间建立,隧道两端都要手工指定对端的真实公网地址。
假设企业有一个总部和三十个分支,如果每个分支都要和总部建立GRE隧道,总部路由器上要手动创建三十个Tunnel接口,每一个接口都要写上对端公网IP和隧道IP。更麻烦的是,分支之间如果也想直接通信,就得两两建隧道,三十个分支就是435条隧道,这个配置量基本不可维护。就算只让分支通过总部中转,总部也成了性能瓶颈,而且所有流量都绕路,时延和带宽都被白白浪费。
实际场景里还有一个隐藏问题:很多分支的公网地址是动态获取的,今天这个IP明天就变了。传统GRE隧道配置的是静态IP,对端地址变了隧道就断,运维人员只能一遍遍改配置。MGRE(Multipoint GRE,多点GRE)就是为了解决这些问题出现的。
1.2 MGRE加NHRP:把"手工配邻居"变成"动态学邻居"
MGRE不是一个全新的隧道协议,它本质上还是GRE封装,只是把隧道接口的工作模式改成了multipoint,也就是一个Tunnel接口可以同时对接多个对端。这样一来,总部路由器只需要一个Tunnel接口,就能和所有分支建立隧道,不需要为每个分支创建单独的Tunnel。
但问题来了:GRE隧道是点到点封装,封装报文时总要写清楚目的公网地址。MGRE一个Tunnel接口面对多个对端,怎么知道某个私网地址对应的公网地址是谁?这就是NHRP(Next Hop Resolution Protocol,下一跳解析协议)的职责。
可以这样理解:MGRE负责"把隧道接口变成多点模式",NHRP负责"告诉路由器某个隧道IP对应的真实公网IP是什么"。两者配合之后,分支设备启动时先向中心节点登记自己的公网地址,分支之间想直接通信时再通过中心节点查询对方的公网地址,查到后就自己建立一个直连隧道。
这就是MGRE的核心价值:隧道接口可以复用,对端地址是动态学习和注册的,不再依赖手工静态配置。分支IP变了也没关系,重新注册一次就行。
1.3 实验场景设定与学习路线
本次实验我建议用三台路由器模拟一个最简单的中心到分支组网:R1作为总部中心节点,R2和R3作为两个分支节点。三台路由器之间用模拟公网的网段互联,每个节点后面再接一个私网网段模拟真实业务网。这个拓扑虽然简单,但把MGRE和NHRP的核心机制全都能体现出来。
学这个实验有一个正确的顺序:先理解NHRP的注册和解析流程,再动手配置隧道,然后再跑OSPF,否则直接照着配置敲很容易被各种诡异现象带偏。下面就从这三步依次展开。
2. 实验环境与基础配置:模拟公网互通的细节
2.1 拓扑规划与IP编址
实验拓扑不需要太复杂,三台路由器加三个模拟终端即可。公网部分用一个文档保留网段来模拟运营商网络,每个节点各自的私网网段代表真实业务网。规划如下:
| 设备 | 公网接口地址 | Tunnel接口地址 | 业务私网网段 |
|---|---|---|---|
| R1(中心) | 203.0.113.1/24 | 10.0.0.1/24 | 192.168.1.0/24 |
| R2(分支A) | 203.0.113.2/24 | 10.0.0.2/24 | 192.168.2.0/24 |
| R3(分支B) | 203.0.113.3/24 | 10.0.0.3/24 | 192.168.3.0/24 |
公网地址之间通过一台模拟运营商设备或者直接用交换机互联都可以,关键是保证三个公网接口在同一个广播域里。注意这些地址都是实验保留地址,实际生产环境中的公网地址规划要遵循运营商分配方案,不能随意使用。
2.2 公网三层互通验证:别急着建隧道
很多人在这个实验上翻车,不是因为MGRE配置错,而是因为模拟公网的底层三层都没通就开始建隧道。隧道跑在公网之上,底层不通,上层隧道就是空中楼阁。
先把每台路由器的物理接口地址配上,然后给每台路由器写一条默认路由指向模拟公网的网关。我习惯这么写:
# R1 interface GigabitEthernet0/0/0 ip address 203.0.113.1 255.255.255.0 # ip route-static 0.0.0.0 0.0.0.0 203.0.113.254 # R2 interface GigabitEthernet0/0/0 ip address 203.0.113.2 255.255.255.0 # ip route-static 0.0.0.0 0.0.0.0 203.0.113.254 # R3 interface GigabitEthernet0/0/0 ip address 203.0.113.3 255.255.255.0 # ip route-static 0.0.0.0 0.0.0.0 203.0.113.254配置完成后,先做一轮Ping验证:R1能Ping通203.0.113.2和203.0.113.3,R2和R3能Ping通203.0.113.1。这个验证必须通过再继续,如果底层都不通,后面排查NHRP和OSPF的问题时会把原因搞混。我甚至可以断言,MGRE实验里至少有一半的"隧道起不来"问题,根源都在模拟公网这一段,而不是NHRP本身。
2.3 Tunnel接口的MGRE模式配置清单
底层互通后就到了核心环节:配置Tunnel接口。中心节点R1的配置如下:
# R1 interface Tunnel0/0/1 ip address 10.0.0.1 255.255.255.0 tunnel-protocol gre multipoint source 203.0.113.1 nhrp network-id 100 nhrp authentication mgre-lab分支节点R2的配置如下:
# R2 interface Tunnel0/0/1 ip address 10.0.0.2 255.255.255.0 tunnel-protocol gre multipoint source 203.0.113.2 nhrp network-id 100 nhrp authentication mgre-lab nhrp nhs 10.0.0.1 nbma 203.0.113.1R3的配置和R2完全类似,把地址换成10.0.0.3和203.0.113.3即可。
这里有几个细节必须讲清楚。第一,tunnel-protocol必须明确指定为gre multipoint,如果写成普通的gre,隧道接口就是点到点模式,NHRP相关的命令根本没法生效。第二,source参数既可以写具体IP,也可以写接口名,生产环境中建议写接口名,这样当物理接口IP变化时隧道源地址会自动跟随,不会因为IP变更导致隧道失效。本次实验为了便于理解,直接写了具体IP。第三,nhrp network-id相当于划分NHRP域的标识,只有相同network-id的设备才能互相注册和解析。第四,nhrp authentication是可选的认证密钥,但强烈建议配置,否则同一网络里任何设备都能向你注册,存在安全隐患。
分支节点相比中心节点多了一条nhrp nhs命令。nhs全称是Next Hop Server,下一跳服务器。在这个场景里,中心节点R1就是NHRP服务器,负责接收分支的注册请求和查询请求,所以分支上要用这条命令指明"我的NHRP服务器是谁"。命令后面的10.0.0.1是R1的隧道地址,nbma 203.0.113.1是R1的公网地址。nbma这个术语容易劝退新手,它其实就是Non-Broadcast Multi-Access,非广播多路访问,指的就是对端设备的真实公网地址。
中心节点初始可以不用配置nhrp nhs指向任何分支,因为分支会主动向中心注册,中心通过注册消息就能学到分支的隧道地址到公网地址的映射。这一点和OSPF里DR/BDR的选举逻辑有点像,中心天然处于被注册的位置。
配置完成后,在中心R1上执行display nhrp peer all,正常情况下能看到R2和R3的NHRP邻居条目。如果没有看到,优先检查三台设备的nhrp network-id和认证密钥是否一致,这是最常见的低级错误。
3. NHRP注册与解析:MGRE的灵魂机制
3.1 注册流程:Spoke到Hub的登记动作
在MGRE的术语体系里,中心节点叫Hub,分支节点叫Spoke。Spoke上线后做的第一件事就是向Hub注册,这个动作由NHRP协议自动完成,不需要人工干预。
具体过程可以这样理解:R2的Tunnel接口up之后,NHRP进程发现自己配置了nhs 10.0.0.1,于是构造一个NHRP Registration Request报文,源地址是R2的隧道地址10.0.0.2,目的地址是R2的NHRP服务器地址10.0.0.1。由于这个报文要发往10.0.0.1,而10.0.0.0/24网段在Tunnel接口上是直连的,路由器会尝试通过Tunnel接口发送,GRE封装时目的公网地址取NHRP映射表里10.0.0.1对应的nbma地址,也就是203.0.113.1。
R1收到注册请求后,把自己学到的信息加入NHRP映射表:10.0.0.2对应的公网地址是203.0.113.2,然后回一个Registration Reply确认。至此,R1的NHRP表里就有了R2的条目,R2也确认自己注册成功。
这个注册动作的好处是,即使R2的公网IP是动态变化的,只要它每次重新拨号或重新上线后Tunnel接口重新UP,就会自动向R1重新注册,R1表中的映射条目会自动更新。生产环境中很多分支使用PPPoE拨号或DHCP获取公网地址,地址经常变化,MGRE加NHRP的动态注册机制在这种场景下优势非常明显。
3.2 解析流程:分支之间怎么"互相找到"
注册只是第一步,分支之间的直接通信才是MGRE真正体现价值的地方。
假设R2要访问R3的私网网段192.168.3.0/24。报文到达R2的Tunnel接口后,下一跳指向10.0.0.3。R2查找NHRP映射表,发现没有10.0.0.3对应的公网地址条目。这时NHRP会触发一个解析流程:R2向自己的NHS(也就是R1)发送一个NHRP Resolution Request,询问"10.0.0.3的公网地址是多少"。
R1收到解析请求后查自己的NHRP表,发现10.0.0.3对应203.0.113.3,就把这个信息封装成Resolution Reply返回给R2。R2收到回复后,在自己的NHRP表中增加一条10.0.0.3到203.0.113.3的映射,然后直接向203.0.113.3发起GRE封装,R2与R3之间的直连隧道就这样动态建立起来了。
用一个生活化的类比:中心节点像公司前台的登记簿,每个新来的同事都要到前台登记自己的工位和电话。当你要找某个不认识的同事时,先问前台,前台告诉你他在哪个工位,然后你直接去找他,不用每次联系都让前台传话。NHRP就是这套登记和查询机制。
这个设计最大的好处是流量路径最优。R2访问R3的流量从R2直接到R3,不需要绕到R1再转发,时延和带宽消耗都更低。如果分支数量很多,这个优势会成倍放大。
3.3 常见误解:为什么Hub上还要配静态NHRP映射
一个非常常见的困惑是:既然分支会主动注册,中心节点上是不是什么都不用配,只要开了MGRE和NHRP就能自动学习到所有分支?
这个理解不完全对。中心节点确实可以通过注册消息学到分支的动态映射,但在有些场景下,中心节点需要主动向分支发起通信,或者需要依赖静态NHRP映射来配合路由协议的工作。比如在本次实验中,如果OSPF的Hello报文需要由中心节点主动发往分支,而中心节点的NHRP表中暂时没有分支条目,就无法封装。
所以在正式配置中,我通常会在中心节点上也配置针对分支的静态NHRP映射,类似这样:
# R1 interface Tunnel0/0/1 nhrp map 10.0.0.2 203.0.113.2 nhrp map 10.0.0.3 203.0.113.3这样做的目的不是"必须",而是为了消除不确定性。静态映射和动态注册并不冲突,动态注册条目会刷新静态条目的状态或覆盖临时信息,static条目始终作为兜底存在。很多工程实践里,中心节点上加静态NHRP映射能有效避免"OSPF邻居建立一半不成功"这类问题。
4. 在MGRE上运行动态路由:OSPF的p2mp陷阱与解法
4.1 为什么默认网络类型跑不出Full状态
隧道接口建好后,如果只是手工写静态路由,两边业务也能通,但分支数量多了之后静态路由根本维护不过来,所以实验里一般都会在隧道接口上跑OSPF。
问题马上就来了。OSPF在不同接口上有不同的网络类型,Tunnel接口默认的网络类型通常是广播(broadcast)或者根据接口状态动态决定。在广播网络类型下,OSPF会认为当前链路上所有路由器都在同一个二层域里,需要选举DR和BDR,然后用组播地址发送Hello报文。
但MGRE隧道本质上是一个NBMA网络,也就是非广播多路访问网络。虽然NHRP能动态解析对端地址,但OSPF不知道NHRP的存在,它只会机械地按照广播网络的行为去发送组播Hello。而GRE隧道本身不支持组播封装,这就导致了根本性的矛盾。
结果是,OSPF邻居关系建立过程卡在2-Way状态,路由器显示邻居状态为DROTHER,永远无法进入Full状态,路由表里看不到对端私网网段。我在第一次做实验时,看到三台路由器都在同一个网段里,却怎么都建立不起邻居,一度怀疑自己的配置有问题,后来才发现是网络类型在作怪。
具体还有一个容易被忽略的点:在广播网络类型下,DR和BDR的选举要求所有路由器之间两两互通。但MGRE隧道在分支之间建立直连隧道是NHRP解析触发的,是动态的,如果某个分支还没有解析到另一个分支的地址,它们的OSPF邻居就根本无法建立。即使DR选出来了,也只能和DR/BDR建立Full邻居,DROTHER之间停留在2-Way。这不符合广播网络对全互联的要求。
4.2 正确的做法:把网络类型改成p2mp
解决这个问题的方法很直接:把Tunnel接口的OSPF网络类型改成点对多点(point-to-multipoint),命令如下:
# 三台路由器Tunnel接口下都执行 interface Tunnel0/0/1 ospf network-type p2mp改成p2mp之后,OSPF不在这个接口上选举DR/BDR,把每条邻居关系都当作点对点来处理。更关键的是,在p2mp网络类型下,OSPF会以单播方式向所有已知邻居发送Hello报文,不需要组播能力。而邻居是怎么"已知"的呢?一部分来自手工指定的邻居,另一部分通过NHRP解析动态学习。
在实际配置时,还需要在OSPF进程里把Tunnel接口宣告进对应区域。比如:
# R1 ospf 1 area 0 network 10.0.0.0 0.0.0.255 network 192.168.1.0 0.0.0.255但要注意一个细节:在p2mp网络类型下,默认情况下OSPF是按接口来建立邻居的,分支R2能否学到分支R3,取决于R2是否能通过NHRP解析到R3的公网地址。如果解析不了,OSPF邻居就建立不起来。这里就需要中心节点R1配合。一般建议在中心节点上开启针对NHRP的伪广播功能,这样中心节点发送的组播或广播报文会通过NHRP映射逐一向各分支单播复制。
# R1 interface Tunnel0/0/1 nhrp entry multicast dynamic加上这条命令后,R1上所有动态学习到的NHRP条目都会成为伪广播的目标。当OSPF需要发送Hello时,R1会向所有注册过的分支分别单播一份。这样一来,R2通过R1的Hello知道了R3的存在,会主动触发NHRP解析流程去获取R3的公网地址,之后两台分支设备之间就能直接建立OSPF邻居关系。
4.3 路由学习与水平分割的联动问题
p2mp模式下还有一个容易踩的坑,就是水平分割问题。
传统GRE隧道接口默认不会转发从同一接口收到的路由更新,这是为了避免路由环路的水平分割机制。但在MGRE场景下,分支R2的路由需要通过中心R1转发给分支R3,如果R1在Tunnel接口上开启了水平分割,R2宣告的私网路由就无法被R3学到。
实际上在很多设备上,GRE Tunnel接口在p2mp网络类型下默认会关闭水平分割,或者OSPF本身在p2mp类型下会特殊处理,但不同设备型号和软件版本行为并不完全一致。最稳妥的做法是显式检查并关闭Tunnel接口的水平分割:
# R1 interface Tunnel0/0/1 undo ip split-horizon配置完成后,再次查看OSPF邻居状态,正常情况下三台路由器之间都是Full状态。进一步查看路由表,R1上除了自己的直连路由外,应该能看到192.168.2.0/24和192.168.3.0/24,R2和R3上也能彼此看到对方的私网路由。这一步跑通,整个MGRE加OSPF的实验才算真正闭环。
4.4 业务流量路径验证
路由表学全之后,最后一个重要验证是业务流量的实际路径。这步很多人会跳过,但恰恰是区分"实验通了"和"真正理解了"的关键。
在R2上发起对192.168.3.1的Ping,然后在R2的Tunnel接口抓包,可以看到报文目的MAC和公网目的IP指向203.0.113.3,也就是直接发往R3,中间没有经过R1转发。如果你的抓包结果显示目的公网地址始终是R1,说明分支之间没有建立直连隧道,流量在走中心转发,这就不符合MGRE的设计意图。
要验证流量路径,还可以通过display ip routing-table查看目的网段的下一跳:
display ip routing-table 192.168.3.0如果下一跳显示10.0.0.3,说明路由学习正确;如果下一跳显示10.0.0.1,说明R2到R3的路由是通过R1学到的,虽然业务仍然能通,但没有走最优路径。这种情况下需要检查NHRP解析是否成功,以及在R2上是否配置了正确的nhrp nhs。
5. 抓包与排错:MGRE实验中的翻车现场
5.1 排错的第一条命令链
做MGRE实验最容易遇到的问题就是分支之间建立不起隧道或者邻居关系,这时候按顺序执行一套命令基本能定位大半问题。
第一,检查Tunnel接口状态:
display tunnel-info all重点看Tunnel接口的状态是否UP。如果接口状态是Down,说明隧道封装配置有问题,检查tunnel-protocol是否为gre multipoint、source地址是否有效。
第二,查看NHRP表:
display nhrp peer all这条命令应该能看到对端设备的隧道地址到公网地址的映射条目。如果分支R2上查不到R3的NHRP条目,先想办法触发解析,比如用ping 10.0.0.3命令发起一次流量。如果怎么触发都查不到,基本可以确认R1没有正确转发解析请求,检查R1上是否配置了nhrp entry multicast dynamic。
第三,查看OSPF邻居:
display ospf peer如果邻居状态是Init或者2-Way,排查方向是网络类型和Hello报文交互;如果是ExStart或Exchange卡住,排查方向是MTU不一致;如果是Full,说明邻居关系正常。
第四,查看OSPF路由表:
display ospf routing table确认私网路由是否学习完整。路由缺失时,重点检查水平分割接口配置和区域宣告是否正确。
这套命令链的执行顺序是有讲究的,从隧道到NHRP再到OSPF,一层层往下查。很多人一上来就看OSPF邻居,邻居起不来就在OSPF里折腾半天,其实问题可能出在底层NHRP根本没有解析成功。按照从底层到上层的顺序排查,能少走很多弯路。
5.2 抓包看NHRP协议交互
如果命令排查看不出明显问题,就需要通过抓包来看NHRP协议的真实交互过程。这个实验在模拟器里也能抓包,非常直观。
在R2上发起访问R3业务的流量时,Wireshark里过滤gre或者直接过滤nhrp字段,能看到一个完整的协议交互:
- R2发出NHRP Resolution Request,目的IP是10.0.0.1(也就是R1的隧道地址),GRE封装后公网目的地址是203.0.113.1。
- R1回复NHRP Resolution Reply,报文中携带的信息包括:请求方10.0.0.2、被请求方10.0.0.3、以及10.0.0.3对应的公网地址203.0.113.3。
- R2收到Reply后,立即向203.0.113.3发送GRE封装的OSPF Hello报文或业务报文。
如果抓包只看到Request没有Reply,说明R1没有在NHRP表中找到10.0.0.3对应的条目,需要回到R1上检查NHRP邻居表里是否已经有R3的注册信息。如果Reply已经返回但后续到R3的流量还是发向R1,说明R2的NHRP表没有正确更新,检查是不是配置了错误的静态nhrp map,导致动态学习条目被覆盖。
实际环境中有一个比较隐蔽的抓包细节:NHRP报文使用的协议类型字段是0x2001,在Wireshark里直接过滤protocol 0x2001就能看到。GRE封装本身协议类型字段是0x6558,数据报文则是0x0800。分清这几个字段,看抓包时就不会一头雾水。
5.3 排错清单与ACL/NAT处理
最后整理一张我在实验中总结的排错清单,覆盖了最常见的几类问题:
| 现象 | 排查方向 | 常用处理 |
|---|---|---|
| NHRP邻居表为空 | network-id不一致、认证密钥不一致、底层公网不通 | 核对nhrp参数,Ping对端公网地址 |
| OSPF邻居卡在2-Way | 隧道接口网络类型为广播 | 改为ospf network-type p2mp |
| 分支学不到对端私网路由 | 中心接口水平分割未关闭 | undo ip split-horizon |
| 分支无法触发直连隧道 | 中心没有伪广播能力 | 配置nhrp entry multicast dynamic |
| 业务流量仍绕行中心 | NHRP解析不完整或OSPF下一跳不对 | 检查display nhrp peer all,触发解析 |
除了协议层面的问题,生产环境中还要考虑ACL和NAT的干扰。GRE报文对应的IP协议号是47,如果分支出口有ACL过滤或者防火墙策略,必须放行协议47。另外如果分支在出口做了NAT,GRE封装后的报文不能被地址转换破坏,常见做法是把隧道源地址到中心公网地址的流量做NAT豁免,或者使用NAT的地址池转换外部接口地址而不能改变内部GRE封装。这些问题是模拟器实验里不会出现、但真实组网必然遇到的,提前了解能避免上线后抓瞎。
我在做实验时还遇到过一种情况:分支路由器的Tunnel接口源地址写的是GigabitEthernet0/0/0,但路由器有多个出口,源地址选择不唯一导致GRE报文被发送到了错误的接口。解决方法是直接指定具体的源IP,或者确保默认路由的下一跳和源接口匹配,这也是排查时需要多留个心眼的地方。
5.4 从配置习惯上减少故障的几个经验
MGRE实验做完之后,我再回头看自己的配置过程,发现整个实验里最省时间的不是排错命令,而是几个从一开始就养成的配置习惯。第一个习惯是每台设备的Tunnel接口编号保持一致,R1、R2、R3全部使用Tunnel0/0/1,排查时不用反复确认接口号。第二个习惯是所有NHRP配置集中在Tunnel接口视图下,用注释标明谁是Hub谁是Spoke,配置黏贴到文档里以后自己也能一眼看懂。第三个习惯是先把基础连通性验证做完再进入路由协议配置,绝不跳过。
另外一个经验是,配置OSPF网络类型时不光要在所有节点上改成p2mp,还要确认OSPF进程里宣告的网段与Tunnel接口的网段完全一致。如果Tunnel接口是10.0.0.0/24,宣告时写成10.0.0.0/16,OSPF会认为接口不在区域里,邻居照样起不来。这类问题靠抓包很难一眼定位,最好在配置完就检查display ospf interface,确认Tunnel接口已经加入到正确的区域。
最后就是多利用模拟器的快照功能。每次在做可能导致大面积配置回滚的操作之前,保存一个快照。MGRE实验排错时经常需要反复修改NHRP映射、切换OSPF网络类型,如果没有快照,一次错误配置可能要把整个实验推倒重来。我在第三次做实验时,就靠快照在不同网络类型的配置间来回对比,才彻底理解了广播网络和p2mp网络在邻居建立上的行为差异。