OSPF 状态机详解:七种邻居状态与卡在 ExStart 的排查
OSPF 状态机描述两台路由器从互不认识到数据库完全同步的过程,一共七种状态:Down、Init、2-Way、ExStart、Exchange、Loading、Full(NBMA 网络另有一个 Attempt)。前三种是发现邻居,后四种是同步链路状态数据库;邻居停在哪个状态,就说明卡在了哪一步。
OSPF 状态机,指的是 OSPF 路由器为每一个邻居维护的一组状态,以及状态之间的迁移规则,定义在 RFC 2328 第 10 章。常说的“OSPF 七种状态”是 Down、Init、2-Way、ExStart、Exchange、Loading、Full,只用于 NBMA 网络的 Attempt 通常不计在内。前三种状态完成“发现邻居、确认双向通信”,后四种完成“同步链路状态数据库”;两台路由器走到 Full,才算建立了完全邻接,之后才能据此计算路由。排障时最有用的一点是:邻居停在哪个状态,就说明卡在了哪一步,每个状态对应的故障范围都很小。
OSPF 七种邻居状态一览
| 状态 | 进入条件 | 这一阶段收发的报文 | 正常情况下 |
|---|---|---|---|
| Down | 初始状态;或在 Dead 时间内没收到对方的 Hello | 本端照常发送 Hello | 邻居表里看不到对方,或对方刚断开 |
| Init | 收到对方的 Hello,但其中的邻居列表里还没有自己的 Router ID | Hello | 一闪而过,通常不超过一个 Hello 间隔 |
| 2-Way | 在对方的 Hello 里看到了自己的 Router ID,确认双向通信 | Hello;广播网络在这一阶段选举 DR、BDR | 点到点链路上一闪而过;广播网络里 DROther 之间长期停在这里是正常的 |
| ExStart | 决定建立邻接,协商主从关系和 DD 序列号 | 不带 LSA 摘要的空 DD 报文,I、M、MS 三个标志位置 1 | 一两秒 |
| Exchange | 主从确定,交换数据库摘要 | 携带 LSA 头部的 DD 报文;发现缺少的条目时可以开始发 LSR | 数据库越大越久,通常几秒 |
| Loading | 摘要交换完毕,请求自己缺少或较旧的 LSA | LSR、LSU、LSAck | 几秒 |
| Full | 请求列表清空,双方数据库一致 | 周期性的 Hello;拓扑变化时的 LSU、LSAck | 长期稳定在这里 |
Attempt 只出现在 NBMA 网络:手工指定了邻居地址,本端正在向它单播 Hello,但还没收到回应。
邻居(neighbor)和邻接(adjacency)是两个概念:到 2-Way 就算邻居,从 ExStart 开始建立邻接,到 Full 才是完全邻接。只有建立邻接的路由器之间才同步数据库。
从 Down 到 Full:OSPF 邻居建立过程
以两台核心交换机为例:Core-A 的 Router ID 是 1.1.1.1,Core-B 是 2.2.2.2,用一条点到点链路互联,都在区域 0。
发现邻居:Down、Init、2-Way
- Core-A 的接口启用 OSPF 后,每 10 秒向组播地址 224.0.0.5 发送 Hello。Hello 里带着区域号、Hello 与 Dead 间隔、网络掩码、认证信息、优先级,以及“我已经见过的邻居”列表,这时列表是空的。
- Core-B 收到这个 Hello,先核对参数(见下文),一致就把 Core-A 加入邻居表,状态记为 Init,并在自己的下一个 Hello 里把 1.1.1.1 写进邻居列表。
- Core-A 收到 Core-B 的 Hello,在列表里看到了自己,确认双向可达,Core-B 在它这里直接进入 2-Way;Core-A 随后发出的 Hello 里也带上了 2.2.2.2,Core-B 收到后同样进入 2-Way。
- 点到点网络上双方总要建立邻接,2-Way 立即转入 ExStart。广播网络上,这一步要先等 DR、BDR 选举出结果,只有和 DR、BDR 之间才继续往下走。接口刚启用时,路由器会先等待一个 Dead 间隔(默认 40 秒)收集 Hello,再进行选举,所以广播网络上刚上线的邻居在 2-Way 停留几十秒是正常的。
同步数据库:ExStart、Exchange、Loading、Full
- ExStart:双方各发一个空的 DD 报文,I(Init)、M(More)、MS(Master)三个标志位都置 1,都宣称自己是主。比较 Router ID,较大的一方为主(Master)。这里 2.2.2.2 较大,Core-A 承认 Core-B 为主,并在回应中采用 Core-B 的序列号,双方进入 Exchange。主从关系与 DR 选举无关,只决定由谁控制 DD 报文的序列号。
- Exchange:双方用 DD 报文发送自己数据库里每一条 LSA 的头部,包括类型、链路状态 ID、通告路由器、序列号、校验和、老化时间。主方每发一个 DD,从方用相同的序列号回一个作为确认,一问一答。收到对方的摘要后,各自把“我没有的”和“我这份比较旧的”LSA 记入请求列表。
- Loading:用 LSR 请求列表中的 LSA,对方用 LSU 发出完整内容,收到后回 LSAck 确认。请求列表清空,进入 Full。如果 Exchange 结束时请求列表本来就是空的,就跳过 Loading,直接进入 Full。
- Full:双方数据库一致,各自更新 Router LSA,把这条邻接写进去并泛洪出去,然后运行 SPF 计算路由。
在点到点链路、数据库不大的情况下,从 Init 到 Full 一般只需要几秒钟。
OSPF 状态机怎么迁移:事件与状态对照
状态之间的跳转由事件触发。除了上面一路向前的过程,下表里的几种“回退”事件是排障时最常遇到的:
| 当前状态 | 事件 | 迁移到 | 说明 |
|---|---|---|---|
| Down | HelloReceived | Init | 收到对方的 Hello |
| Down(仅 NBMA) | Start | Attempt | 开始向手工指定的邻居单播 Hello |
| Init | 2-WayReceived | 2-Way 或 ExStart | 看到了自己的 Router ID;需要建立邻接的直接进入 ExStart |
| 2-Way | AdjOK? | ExStart | DR、BDR 选出后,发现需要和对方建立邻接 |
| ExStart | NegotiationDone | Exchange | 主从关系与初始序列号协商完成 |
| Exchange | ExchangeDone | Loading 或 Full | 请求列表非空进入 Loading,为空直接 Full |
| Loading | LoadingDone | Full | 请求的 LSA 全部收到 |
| Exchange 及以上 | SeqNumberMismatch、BadLSReq | ExStart | DD 序列号或标志位异常,或请求了对方没有的 LSA,从头协商 |
| ExStart 及以上 | AdjOK?(不再需要邻接) | 2-Way | 例如 DR 变化后,双方都成了 DROther |
| 2-Way 及以上 | 1-WayReceived | Init | 对方的 Hello 里不再有自己 |
| 任意状态 | InactivityTimer | Down | Dead 时间内没收到 Hello |
| 任意状态 | KillNbr、LLDown | Down | 接口关闭、链路断开、OSPF 进程重启 |
“回到 ExStart”的两个事件,是邻居在 ExStart、Exchange、Loading 之间来回跳的原因;InactivityTimer 则是 Full 掉回 Down 最常见的原因。
用 display ospf peer 看邻居状态
华为、H3C 用 display ospf peer,加 brief 只看概要:
<Core-A> display ospf peer brief
OSPF Process 1 with Router ID 1.1.1.1
Peer Statistic Information
----------------------------------------------------------------------------
Area Id Interface Neighbor id State
0.0.0.0 Vlanif10 2.2.2.2 Full
0.0.0.0 Vlanif20 3.3.3.3 ExStart
----------------------------------------------------------------------------
不加 brief 时,每个邻居会显示 State、主从角色(Mode)、优先级、DR 和 BDR 地址、MTU、距离 Dead 超时还剩几秒、邻居已建立多久,排障时这些字段都用得上。输出格式随设备版本略有差异。
思科用 show ip ospf neighbor:
Router# show ip ospf neighbor
Neighbor ID Pri State Dead Time Address Interface
2.2.2.2 1 FULL/DR 00:00:35 10.0.10.2 GigabitEthernet0/0
4.4.4.4 1 2WAY/DROTHER 00:00:33 10.0.10.4 GigabitEthernet0/0
5.5.5.5 0 FULL/ - 00:00:38 10.0.12.2 GigabitEthernet0/1
State 列斜杠前是邻居状态,斜杠后是对方在这个网段上的角色:FULL/DR 表示与 DR 已完全邻接;2WAY/DROTHER 表示双方都是 DROther,正常;FULL/ - 出现在点到点网络上,没有 DR 的概念。Dead Time 是距离判定邻居失效还剩的时间,默认从 40 秒开始倒数,每收到一个 Hello 就重置一次,所以正常情况下在 30 到 40 秒之间循环;如果经常看到它明显低于 30 秒才被重置,说明有 Hello 丢失或晚到。
卡在哪个状态,对应什么故障
| 现象 | 最可能的原因 | 先查什么 |
|---|---|---|
| 邻居表里根本没有对方 | Hello 收不到,或收到后因参数不一致被丢弃 | 接口是否启用 OSPF、是否被设为静默接口;Hello 参数与认证;ACL 是否拦截了协议号 89 |
| 停在 Init | 单向通信:我收到了对方的 Hello,对方没收到或没接受我的 | 链路单向丢包、中间设备单向过滤组播、ACL 方向、对端的错误计数 |
| 停在 2-Way | 广播网络上双方都是 DROther(正常);或网段里所有路由器优先级都为 0 | DR、BDR 字段与各台的优先级 |
| 停在 ExStart,或在 ExStart 与 Exchange 间反复 | 两端 MTU 不一致;单播的 DD 报文不通 | 两端接口 MTU;ACL 是否只放行了组播 |
| 停在 Loading | 发出 LSR 后收不到 LSU,常见是大报文在中途被丢弃 | 两端之间大包能否通过;设备日志 |
| 已经 Full,却没有路由 | 两端网络类型不一致、路由被过滤、被其他路由覆盖 | 接口的网络类型、路由表 |
邻居不出现或停在 Init:Hello 参数与认证
收到 Hello 后,下面这些字段必须与本端接口一致,否则这个 Hello 直接丢弃:
- 区域 ID;
- 认证类型与密钥:不认证、明文、MD5 等,两端必须完全一致;
- Hello 间隔与 Dead 间隔:广播和点到点网络默认 10 秒、40 秒,NBMA 和点到多点默认 30 秒、120 秒,两端网络类型默认值不同时最容易出错;
- 网络掩码:只在广播和 NBMA 网络上检查;
- 区域类型标志:一端把区域配成了 Stub 或 NSSA,另一端没有;
- 另外,Router ID 不能重复。
注意,参数不一致或认证失败时,对方的 Hello 被丢弃,邻居表里根本不会出现对方,并不会停在 Init。真正停在 Init,意味着“我收到了你的 Hello,你却没收到我的”:链路一个方向丢包(比如一芯光纤衰减过大)、中间的二层设备或虚拟交换机过滤了一个方向的组播、ACL 只在一侧放行。少数检查不对称的情况也会造成单边 Init,例如一端是广播类型、另一端是点到点且掩码不同,只有广播一端会检查并丢弃对方的 Hello。
华为上先看本端接口参数,再看错误计数:
display ospf interface Vlanif10
display ospf error
display ospf error 隔几秒执行两次,看哪个计数在增长:Bad area id(区域号)、Bad authentication type 和 Bad authentication key(认证)、Netmask mismatch(掩码)、Hello timer mismatch 和 Dead timer mismatch(计时器)、Extern option mismatch(区域类型)、Router id confusion(Router ID 重复)。字段名以设备版本为准。
思科可以临时打开调试,R 是收到的值,C 是本端配置的值:
show ip ospf interface GigabitEthernet0/0
debug ip ospf hello
OSPF: Mismatched hello parameters from 10.0.10.2
OSPF: Dead R 120 C 40, Hello R 30 C 10 Mask R 255.255.255.0 C 255.255.255.0
debug ip ospf adj
OSPF: Rcv pkt from 10.0.10.2, GigabitEthernet0/0 : Mismatch Authentication type. Input packet specified type 0, we use type 2
认证类型 0 是不认证,1 是明文,2 是 MD5。生产设备上调试完要立即执行 undebug all。修改时两端一起改,华为在接口下用 ospf timer hello 10、ospf timer dead 40、ospf authentication-mode md5 1 cipher 密钥,思科用 ip ospf hello-interval 10、ip ospf dead-interval 40、ip ospf authentication message-digest 加 ip ospf message-digest-key 1 md5 密钥。
停在 2-Way:先确认是不是正常现象
广播网络上,一个网段有 N 台路由器,两两建立邻接需要 N(N−1)/2 条;选出 DR、BDR 后,其他路由器只和这两台建立邻接。6 台路由器两两相连要 15 条邻接,有了 DR、BDR 只要 9 条。所以 DROther 之间停在 2-Way 是设计如此。
不正常的情况有两种:一是网段里所有路由器的优先级都被设成了 0,没有 DR 可选,所有邻居都停在 2-Way,这个网段上建立不了任何邻接,经过它的路由也就学不到,至少要让一台的优先级大于 0;二是应当与 DR 建立邻接的路由器也停在 2-Way,要按 ExStart 的思路继续查。
机房里两台设备之间的以太网互联,建议两端都改为点到点类型(华为 ospf network-type p2p,思科 ip ospf network point-to-point),省去选举和等待,收敛更快。两端必须同时改,否则就成了后文的网络类型不一致。
卡在 ExStart 或 Exchange:多半是 MTU 不一致
DD 报文里带有发送接口的 MTU。按 RFC 2328,收到的 DD 里的 MTU 大于本端接口 MTU 时,本端认为自己收不下对方的大报文,拒绝这个 DD。结果是 MTU 小的一端停在 ExStart,MTU 大的一端常显示为 Exchange,双方不断重传。机房里最常见的场景,是一端为存储或虚拟化网络开了 9000 字节的巨型帧,另一端仍是 1500。
两家厂商的默认行为不同:
- 思科默认检查 MTU,
debug ip ospf adj会提示Nbr 10.0.10.2 has larger interface MTU。正确的做法是把两端 MTU 改成一致;ip ospf mtu-ignore只是跳过检查,大于对端 MTU 的报文照样可能在对端被丢弃,不建议长期使用。 - 华为默认在 DD 报文里把 MTU 填为 0、也不检查,所以两台华为设备 MTU 不同照样能走到 Full,
display ospf peer里看到的 MTU 为 0 就是这个原因。接口下配置ospf mtu-enable后才填写并检查。不检查不等于没有问题:数据库变大、LSU 报文超过对端 MTU 时,可能表现为停在 Loading。
查看接口 MTU,华为用 display interface Vlanif10,思科用 show ip interface GigabitEthernet0/0 | include MTU。
ExStart 的另一个原因是 DD 报文不通。在广播网络上,Hello 走组播,DD 和 LSR 却是发往对方接口地址的单播。中间的 ACL 或防火墙如果只放行了 224.0.0.5、224.0.0.6,Hello 正常、2-Way 正常,到 ExStart 就过不去了。
停在 Loading,或已经 Full 却没有路由
停在 Loading,说明 LSR 发出去了,对应的 LSU 迟迟收不到;如果对方收到的 LSR 请求的是它数据库里没有的 LSA,会触发 BadLSReq 事件,邻居退回 ExStart 重新协商,周而复始。先用不分片的大包测两端之间能否通过:华为 ping -f -s 1472 10.0.10.2,思科 ping 10.0.10.2 size 1500 df-bit。大包不通,回到 MTU 的思路;大包正常,看设备日志里有没有 LSA 校验相关的报错。
已经 Full 却缺路由,最典型的是两端网络类型不一致:一端广播、一端点到点,计时器默认值相同,邻居可以走到 Full,但两端对这条链路的描述方式不同,SPF 无法确认链路双向可用,路由算不出来。用 display ospf interface 或 show ip ospf interface 核对两端的网络类型。其次是路由被过滤,或同一前缀存在优先级更高的路由:华为上静态路由(优先级 60)会压过 OSPF 外部路由(150),思科上静态路由(管理距离 1)会压过 OSPF(110)。用 display ip routing-table protocol ospf 或 show ip route ospf 看 OSPF 实际装进路由表的条目,再用 display ospf lsdb 确认 LSA 是否已经收到。
在管理系统里怎么落地
OSPF 排障的大部分时间花在登录两端设备、执行 display 命令、对比配置上;而修改 MTU、计时器、网络类型这类操作,只改了一端就会把原本正常的邻居打断。Toplink DCIM 的 交换机管理 可以在浏览器里直接打开交换机的 SSH 或 Telnet 终端,每次会话全程录像并记录命令日志,事后可以回看每次会话执行过哪些命令;邻居异常时,对照会话时间就能找到是哪一次操作改动了 OSPF 参数。交换机的端口占用与上联关系也在系统里统一登记,查一条互联链路时,先在系统里确认两端的设备和端口,再分别登录核对两端的配置。
常见问题
OSPF 的 Router ID 是怎么确定的?改了为什么不生效?
手工配置的优先;没有配置时,思科从处于 up 状态的 Loopback 接口中选最大的 IP,没有 Loopback 再从物理接口里选,华为的自动选取规则以版本手册为准。Router ID 在进程启动时就确定了,修改后要重启 OSPF 进程才生效(华为 reset ospf 进程号 process,思科 clear ip ospf process),重启会断开这个进程的所有邻居。
邻居失效后要多久才能发现?能缩短吗?
接口物理断开会立即感知;对端设备故障、或中间隔着二层设备时,要等 Dead 间隔到期,广播和点到点网络默认 40 秒,这段时间里流量仍会发往已失效的邻居。可以两端一起缩短 Hello 与 Dead 间隔,或为 OSPF 启用 BFD,把检测时间缩短到秒级以内,BFD 同样需要两端都配置。
邻居在 Full 和 Down 之间反复切换是什么原因?
多数是 Hello 报文在 Dead 时间内连续丢失:链路丢包或拥塞、设备 CPU 过高来不及处理协议报文、二层环路。思科日志里的 Neighbor Down: Dead timer expired 就是这种情况,先查接口错误计数、CPU 和链路利用率。