OSPF多路访问网络DR/BDR选举机制与工程排错
上个月被拉去看一个机房的 OSPF 邻居表。六台核心交换机挂在同一对堆叠的二层设备上,跑在同一个广播域里,show ip ospf neighbor一敲,DR 是一位两年前就该退役的老设备,上行链路只有千兆,其他几台都是双万兆捆绑。问了一圈,没人配过优先级,全是默认值 1,谁先上电谁当 DR,这台老机器因为一次计划外断电后最先恢复,位置一坐就是两年。
这个场景里冒出来的两个缩写,就是本文要拆开讲的东西:多路访问网络里的 DR 和 BDR。它们不是一条命令,也不是一个开关,而是 OSPF 在一段共享介质上建立的一套"代言人"机制——谁负责汇总这个网段的拓扑、谁负责把变化转发给所有人、谁在代言人挂掉之后立刻顶上。搞不清楚这套机制,你会在排错时反复怀疑链路、怀疑配置、怀疑设备,唯独怀疑不到选举上。这篇内容面向已经会敲 OSPF 基础配置、但在多路访问环境里踩过坑或者正在做网络设计的人,从选举的判定链条讲到接口优先级怎么钉、网络类型怎么选、邻居卡在 2WAY 到底该不该管,最后落到设计层面该不该人为干预。命令以 Cisco IOS 语法为准,思路通用。
1. 多路访问网络里,为什么非得推一个"发言人"出来
1.1 从 n(n-1)/2 这个数字说起
先算一笔账。假设一段广播网络上有 10 台路由器,如果要求两两之间都建立完整的邻接关系、同步链路状态数据库,那需要维护的邻接关系数量是 10×9/2 = 45 条。每台路由器都要向另外 9 台分别发送 Hello、发送 LSA 更新、等待确认、维护重传队列。20 台就是 190 条,50 台是 1225 条。这个增长曲线不是线性的,是平方级的。
OSPF 的设计者很清楚这件事。链路状态协议的核心开销不在于算路,而在于"把变化可靠地通知到所有该知道的人"。点对点链路上只有两端,天然只有一条邻接关系,没什么可优化的;但以太网、帧中继这类共享介质上,只要设备数量稍微上去一点,泛洪就会变成灾难。所以协议规定:在具备广播能力或者多路访问能力的网络上,先选出一台"指定路由器"(Designated Router,DR)和一台"备份指定路由器"(Backup Designated Router,BDR),所有非 DR/BDR 的路由器(DROTHER)只和这两台建立完全邻接,DROTHER 之间停留在 2WAY 状态。
这一步把邻接关系的数量从 n(n-1)/2 压到了 2n-3 的量级。10 台设备只需要 17 条左右的完全邻接,而不是 45 条。省下来的不只是 CPU 和带宽,还有收敛时的抖动——一个网段里某台接入设备反复起落,只要能保证 DR 和 BDR 不动,整个区域的 SPF 重算范围就被限制住了。
注意:DR/BDR 是接口级的概念,不是设备级。同一台路由器,在 VLAN 10 的接口上可能是 DR,在 VLAN 20 的接口上可能是 DROTHER,在一条串口上根本没有角色。排错时一定要落到具体接口去看,别拿
show ip ospf的全局输出下结论。
1.2 DR、BDR、DROTHER 三种角色的实际分工
角色分工不是名义上的,它直接决定了组播地址的监听行为和 LSA 的产生权。
| 角色 | 监听组播地址 | 与谁建立 FULL 邻接 | 产生 Type 2 LSA | Hello 中的 DR/BDR 字段 |
|---|---|---|---|---|
| DR | 224.0.0.5 与 224.0.0.6 | 所有邻居 | 是 | 填自己的接口地址 |
| BDR | 224.0.0.5 与 224.0.0.6 | 所有邻居 | 否(仅接替后产生) | 填 DR 地址与自己地址 |
| DROTHER | 仅 224.0.0.5 | 仅 DR 与 BDR | 否 | 填已知的 DR 与 BDR 地址 |
224.0.0.5 是 AllSPFRouters,所有运行 OSPF 的接口都监听;224.0.0.6 是 AllDRouters,只有 DR 和 BDR 监听。DROTHER 要把 LSU 送给 DR 和 BDR,就发到 224.0.0.6;DR 要把更新泛洪给所有人,就发到 224.0.0.5。这个分流设计的妙处在于:DROTHER 之间根本不需要互相发送更新,因为它们的状态本来就只有 2WAY,数据库同步的责任全部压在 DR 身上。
BDR 平时看起来"什么都不干",它不产生 Type 2 LSA,也不主动泛洪。但它在做一件很重要的事——全程和 DR 保持完全同步。DR 一挂,BDR 立刻转正,因为数据库里什么都有,不需要重新同步,收敛时间几乎为零。这也是为什么选举时协议坚持"先选 BDR 再选 DR",而不是反过来。
1.3 2类LSA:DR替整个网段"代言"的方式
理解 DR 的价值,绕不开 Type 2 LSA,也就是 Network LSA。在 OSPF 的 SPF 计算里,一段广播网络被抽象成一个"伪节点"(pseudo-node),这个伪节点就是由 DR 代表的。
DR 会产生一条 Type 2 LSA,Link State ID 是自己的接口 IP 地址,里面列出这个网段上所有与之建立完全邻接的路由器(包括 DR 自己)的 Router ID。所有路由器在算 SPF 时,先把自己连到伪节点上,再从伪节点连到其他路由器。这样一来,网段内部的拓扑细节被压缩成"一个节点 + 一组叶子",任何一台 DROTHER 的增减,只会引起 Type 2 LSA 的重新生成和局部重算,不会引发整个区域内所有路由器的 Router LSA 连环变化。
如果没有 DR,每台路由器的 Type 1 LSA 里都要列出网段上所有邻居,任何一台设备抖动,区域内每一台路由器都要重算 SPF。这就是 DR 存在的根本理由——它不是"为了减少邻居数量",而是"为了把拓扑变化的爆炸半径控制住"。
用show ip ospf database network能看到这条 LSA 的全貌,包括 Advertising Router、LS Age、Seq Number,以及下面挂着的 Attached Router 列表。当你怀疑某个网段的路由算错时,先看这条 LSA 是否由预期的设备产生,再看 Attached Router 是否齐全,比盲目查路由表快得多。
2. 选举的判定链条:优先级、Router ID 和那个40秒的等待窗口
2.1 优先级是第一道闸门,0是明确的弃权票
接口下的ip ospf priority取值范围是 0 到 255,默认 1。数值越大越优先。这个参数只影响本接口所在网段的选举,不影响其他接口,也不影响 Router ID 的产生。
值为 0 时含义非常明确:这台路由器在这个网段上永不参选 DR 和 BDR。它发出去的 Hello 包里,DR 和 BDR 字段都填 0.0.0.0,等于举手声明"我不参加"。这个特性在实际工程里用得很多——把边缘接入设备、服务器网关、或者单纯的过路设备设成 0,让它们老老实实当 DROTHER,避免设备重启后意外抢到 DR 位置。
举一个我实际遇到的例子。某园区网的核心层和接入层跑在同一个 OSPF 区域,接入层交换机有十几台,全部默认优先级 1。有一次批量升级固件,十几台设备先后重启,最先起来的那台接入交换机因为接口先 UP,抢到了 DR。结果整个 VLAN 里所有核心之间的流量都要绕到这台接入交换机上同步数据库,它的 CPU 直接被打到 80% 以上,OSPF 邻居反复超时。后来把接入层的接口统一配成ip ospf priority 0,问题再没复现过。
配置本身很简单:
interface GigabitEthernet0/1 ip address 10.10.10.2 255.255.255.0 ip ospf priority 0但要记住,优先级改完不会立即生效,后面 3.3 节会详细说这个时机问题。
2.2 优先级打平之后,比的是 Router ID
优先级相同的情况下,比较 Router ID,大者胜。这条规则听起来简单,坑却不少,因为很多人对"Router ID 是什么"存在误解。
Cisco 设备选择 Router ID 的顺序是:手工配置的router-id优先;没有手工配置时,取所有 Loopback 接口中最高的 IP 地址;连 Loopback 都没有,才取所有活动物理接口中最高的 IP 地址。注意最后一条——是"活动接口",但如果 OSPF 进程启动时某个接口还没 UP,它的地址不会参与比较,等接口后来 UP 了,Router ID 也不会自动改。
这就引出一个非常隐蔽的问题:一台设备重启时,如果它的 Loopback 接口因为某些原因起得比物理接口晚,OSPF 进程可能已经用物理接口地址定下了 Router ID,后面 Loopback 起来也没用。表现就是这台设备在所有邻居眼里的 Router ID 和你预期的完全不一样,选举结果自然也不一样。
更麻烦的是 Router ID 冲突。两台设备如果手工配了相同的router-id,或者在接口地址规划上撞了车,邻居关系会反复建立又断开,日志里刷的是%OSPF-4-DUP_RTRID_NBR。这类问题在虚拟化环境里特别常见——克隆出来的虚拟机模板带着相同的 Loopback 地址就上线了。
我的习惯是:所有参与 OSPF 的设备,无条件手工配置router-id,取值用规划好的 Loopback 地址,绝不放任它自动选。多打一行命令,省掉未来若干个深夜。
router ospf 1 router-id 10.0.0.12.3 先选BDR再选DR,这个顺序不是随便定的
选举的实际过程比大多数人想的要绕。当一个接口刚启用 OSPF,或者网段上原本没有 DR 需要重新选举时,路由器不会立刻投票,而是启动一个 Wait Timer,广播网络上等于 Dead Interval,也就是 40 秒(Hello 10 秒 × 4)。在这 40 秒里,所有参与选举的路由器都在自己的 Hello 包里把 DR 字段填成自己的接口地址,BDR 字段填 0.0.0.0,意思是"我自荐当 DR"。
40 秒一到,每台路由器检查自己收到的所有 Hello 包,按下面的顺序判定:
- 先看有没有人声明自己是 BDR。在所有"没有声明自己是 DR"的路由器里,选优先级最高的当 BDR,优先级相同比 Router ID。
- 再看有没有人声明自己是 DR。同样按优先级、Router ID 排序,胜出者当 DR。
- 如果某一台路由器同时被选为 DR 和 BDR(比如它自荐了 DR 但最终只能当 BDR),则 BDR 位置重新按规则补选。
这个顺序的关键在于:DR 的候选池是"所有没当上 BDR 的路由器",而不是"所有路由器"。所以理论上存在一种看似荒谬但完全合规的结果——优先级最高的那台反而当了 BDR,优先级第二的当了 DR。这种情况在等待窗口内 Hello 包丢失、或者设备加入时间错开时确实会出现。
我在一个跨厂商互联的项目里就见过:某台设备因为接口刚 UP 就发了 Hello,自荐 DR,而另一台优先级更高的设备晚了两秒才开始发 Hello,等它加入时对方的 DR 声明已经被其他设备认可了。最终高优先级设备只能去竞争 BDR。这种结果不是 bug,是协议的"先到先得 + 非抢占"特性共同作用的产物。
2.4 非抢占:后来的高优先级设备只能看着
这是 DR/BDR 选举里最容易被误解的一条:选举结果一旦确定,不会因为新加入一台更高优先级的路由器而改变。
具体表现是:一台优先级 200 的设备接入已经稳定运行的网段,它会在 Hello 里声明自己是 DR,但现有的 DR 不会退位。几个 Hello 周期之后,这台新设备发现网段上已经有一个公认的 DR,于是放弃自荐,退回 DROTHER。如果此时 BDR 位置是空的,它可以去当 BDR;如果 BDR 也已经有人,它就只能是 DROTHER,邻居状态显示为2WAY/DROTHER。
想要强行改变结果,只有三条路:
- 在现有 DR 上执行
clear ip ospf process,让整个网段重新选举; - 把现有 DR 的接口
shutdown再no shutdown,强制它放弃角色; - 把现有 DR 的接口优先级改成 0,然后同样需要重启进程或者等它失效才生效。
第一种最常用,但代价最大——clear ip ospf process会清空整个 OSPF 进程的所有邻居和数据库,不只是这一个接口。在一台承载几十个 VLAN 的核心设备上执行这条命令,等于把整个路由域重启一遍。生产环境里做这件事,务必先确认有没有备用路径、有没有 VRRP/HSRP 之类的网关冗余在兜底。
提示:如果你的目标是"让某台设备成为 DR",正确做法不是"把它的优先级调高然后等着",而是"把所有设备的优先级都规划好,然后在一个维护窗口里统一重启 OSPF 进程"。优先级决定的是选举结果,不是切换动作。
3. 把某台设备钉在DR位置上:配置流程与验证方法
3.1 动手之前先把现场信息摸清楚
直接上去改优先级是最容易出问题的做法。我习惯先做三件事:确认当前角色、确认候选者清单、确认业务影响面。
第一条命令看接口级状态:
show ip ospf interface GigabitEthernet0/1输出里会明确给出Designated Router (ID)和Backup Designated router (ID),注意括号里写的是 ID,也就是 Router ID,不是接口地址。同时还能看到Timer intervals configured, Hello 10, Dead 40, Wait 40, Retransmit 5,这个 Wait 40 就是选举等待窗口,直接印证了前面说的机制。最后一行Neighbor Count is 5, Adjacent neighbor count is 2也很关键——如果邻居数是 5 而邻接数只有 2,说明有三台是 DROTHER,处于 2WAY,这是正常的。
第二条命令看邻居清单:
show ip ospf neighbor Neighbor ID Pri State Dead Time Address Interface 10.0.0.2 1 FULL/DR 00:00:38 10.10.10.2 Gi0/1 10.0.0.3 1 FULL/BDR 00:00:36 10.10.10.3 Gi0/1 10.0.0.4 1 FULL/DROTHER 00:00:39 10.10.10.4 Gi0/1 10.0.0.5 1 2WAY/DROTHER 00:00:37 10.10.10.5 Gi0/1这张表一眼就能看出谁是什么角色。Pri列是邻居的接口优先级,改完之后要确认这一列真的变了,而不是只看本地配置。
第三条是评估影响。如果当前 DR 承载着几十个网段的数据库同步,贸然切换会导致短暂的路由黑洞。稳妥的做法是先确认 BDR 是谁、BDR 的资源和带宽是否够用、切换窗口内有没有关键业务。这些判断没法靠命令完成,得看网络的实际承载情况。
3.2 接口优先级与全局 Router ID 的配置细节
配置分两个层面,别搞混。
接口层面控制的是"这台设备在这个网段上的参选意愿":
interface GigabitEthernet0/1 ip ospf priority 200全局层面控制的是"平局时谁赢":
router ospf 1 router-id 10.0.0.1实际规划中,我通常按这样的原则分配优先级:
| 设备角色 | 接口优先级 | 说明 |
|---|---|---|
| 核心层主设备 | 200 | 期望成为 DR |
| 核心层次设备 | 150 | 期望成为 BDR |
| 汇聚层设备 | 100 | 参与但不主导 |
| 接入层设备 | 0 | 永不参选 |
| 服务器网关/边缘接口 | 0 | 减少无谓泛洪 |
这个梯度留出了足够的余量,未来加设备时不用重新规划。不要用 255 和 254 这种贴边的值,一旦有临时设备需要插队就没空间了。
另外一个容易被忽略的点:ip ospf priority只对广播和 NBMA 网络类型有效。如果接口是 point-to-point 类型,这条命令配了也不起作用,因为点对点链路上根本不选举。有人在串口上配了优先级发现没效果,回头怀疑设备 bug,其实就是网络类型不对。
3.3 改完为什么没生效:重启进程的时机与代价
优先级改完,show running-config里能看到,show ip ospf interface里也能看到新值,但邻居表里的角色纹丝不动。这不是配置没生效,是选举不重来。
前面说过,DR/BDR 是非抢占的。改优先级只是改变了"下一次选举时的投票权重",不会触发一次新的选举。要让它生效,必须让当前 DR 或 BDR 失效。
最直接的做法:
clear ip ospf process系统会弹一个确认提示,输入 yes 之后,OSPF 进程重启,所有邻居断开重来。在一个只有几台设备的实验环境里这没什么,但在生产网关上执行之前,务必想清楚三件事:
- 这台设备上跑着几个 OSPF 进程?
clear ip ospf process不带进程号会清掉所有进程。 - 有没有静态路由或者备用协议能在这几十秒内兜住流量?
- 邻居设备会不会因为同时重连而产生短暂的 CPU 峰值?
更精细一点的做法是指定进程号:
clear ip ospf 1 process只清进程 1,影响范围小一些。如果设备上还跑着第二个 OSPF 实例用于其他业务,这一条能救你一命。
还有一种"温柔"的做法:先在一台非关键设备上把优先级调成 0,等它退出 BDR 竞争,再调另一台,逐步引导。这个过程慢,但每一步的影响都可控,适合对中断极度敏感的场景。缺点是整个过程中间态可能不太符合设计预期,需要有人盯着。
我个人的经验是:优先级规划应该在上线前就做完,而不是运行两年后再来纠正。一旦网络稳定运行,任何一次强制重选都是一次风险事件。真要做,就约在业务低谷,提前通知,准备好回滚配置。
3.4 三条验证命令与它们的预期输出
改完之后怎么确认真的成了?我固定用三条命令交叉验证。
第一条,看接口角色:
show ip ospf interface GigabitEthernet0/1 | include Designated期望输出里,Designated Router (ID)后面跟的应该是本机的 Router ID。
第二条,看邻居状态:
show ip ospf neighbor期望看到其他设备对本机的认知是FULL/DR或FULL/BDR,具体取决于本机角色。注意这里的 State 列是"邻居眼中的本机",不是"本机眼中的邻居"——这个视角很容易搞反,排错时尤其要注意。
第三条,看 Type 2 LSA 是否由本机产生:
show ip ospf database network如果本机是 DR,应该能看到一条 Link ID 等于本接口地址的 Network LSA,Advertising Router 等于本机 Router ID,下面 Attached Router 列表里挂着网段上所有建立完全邻接的设备。
三条都对上,才算真正完成。只改配置不看验证,等于没做。
4. 网络类型才是选举的"舞台":广播、NBMA、点对点该怎么选
4.1 五种网络类型的默认行为对照
OSPF 接口的网络类型决定了三件事:要不要选举、用什么方式发现邻居、Hello 和 Dead 定时器取什么值。Cisco 平台支持五种,默认行为和适用场景差别很大。
| 网络类型 | 是否选举 | 邻居发现 | Hello/Dead | 典型场景 |
|---|---|---|---|---|
| broadcast | 是 | 组播 224.0.0.5 | 10/40 | 以太网、VLAN 接口 |
| non-broadcast | 是 | 单播,需手工 neighbor | 30/120 | 帧中继主接口、X.25 |
| point-to-point | 否 | 组播 224.0.0.5 | 10/40 | 串口、点对点子接口 |
| point-to-multipoint | 否 | 组播 224.0.0.5 | 30/120 | 星型 Hub-Spoke 拓扑 |
| point-to-multipoint non-broadcast | 否 | 单播,需手工 neighbor | 30/120 | 不支持组播的星型拓扑 |
最容易踩的坑是"物理接口类型决定了默认网络类型"。一条 Serial 接口默认是 point-to-point,一个 Frame Relay 主接口默认是 non-broadcast,一个 Ethernet 接口默认是 broadcast。这些默认值在简单拓扑里没问题,一旦拓扑变复杂就会出岔子。
比如一条千兆以太网点对点互联(两台设备直连),默认是 broadcast 类型,会走完整的 DR 选举流程,40 秒等待窗口、Type 2 LSA、伪节点计算,一个不少。而这条链路上明明只有两台设备,根本不存在"多路访问"的语义。把它改成 point-to-point,邻居立刻变成 FULL,没有 DR/BDR,没有 40 秒等待,收敛更快,LSA 更少。
interface GigabitEthernet0/1 ip ospf network point-to-point这个优化在大规模数据中心互联里几乎是标配。我见过的每一套 Spine-Leaf 架构,层间互联全部是 point-to-point 类型,没有人让它在一条两点的链路上跑选举。
4.2 NBMA 环境里 hub 必须坐稳 DR
帧中继、ATM 这类非广播多路访问网络现在虽然少见,但仍有一些专线场景在用,而且它的选举逻辑比广播网络更依赖人工干预。
NBMA 的核心问题是:它没有广播能力,OSPF 无法通过组播发现邻居,必须手工指定neighbor。默认网络类型是 non-broadcast,Hello 30 秒、Dead 120 秒、Wait 120 秒。在中心-分支(Hub-Spoke)拓扑里,所有分支设备都只和 Hub 有 PVC,分支之间没有直连电路。
这种拓扑下,如果让分支设备当了 DR,会发生什么?DR 需要和网段上所有路由器建立完全邻接,但分支之间根本通不了,邻接关系建不起来,整个网段的数据库同步就废了。所以规则很明确:Hub 必须是 DR,而且必须是唯一有资格当 DR 的设备。
配置上分两步。Hub 侧把优先级调到最高:
interface Serial0/0 ip ospf network non-broadcast ip ospf priority 200分支侧把优先级设成 0,彻底放弃参选:
interface Serial0/0 ip ospf network non-broadcast ip ospf priority 0然后 Hub 上把所有分支的地址手工指出来:
router ospf 1 neighbor 10.1.1.2 neighbor 10.1.1.3 neighbor 10.1.1.4分支上只需要指回 Hub 一个地址。这里有个细节:如果分支的优先级是 0,它不会参与 DR 选举,但它仍然需要和 DR 建立完全邻接,所以neighbor指向 Hub 就够了。如果分支之间需要互通,流量必须经过 Hub 转发,这也是 Hub 必须资源充足的原因。
4.3 把链路改成 point-to-point 之后发生了什么
我做过一次对比测试,同一条千兆直连链路,分别用 broadcast 和 point-to-point 类型跑,观察邻居建立时间和数据库规模。
| 指标 | broadcast | point-to-point |
|---|---|---|
| 邻居进入 FULL 的时间 | 约 40 秒(等待窗口) | 约 8 秒 |
| 数据库中的 Type 2 LSA | 1 条 | 0 条 |
| 邻居状态显示 | FULL/DR 或 FULL/BDR | FULL/- |
| 链路抖动时的 SPF 触发 | 网段内所有路由器 | 仅两端 |
40 秒 vs 8 秒的差距,主要来自 Wait Timer。在 broadcast 类型下,接口 UP 后必须等满 40 秒才做选举,选举完成后才开始交换 DD 包。point-to-point 没有这一步,Hello 交换完直接进 ExStart。
Type 2 LSA 的差别看着只有一条,但在一个几百台设备的大区域里,每条 Type 2 LSA 都意味着伪节点参与 SPF 计算,累积起来对 CPU 是有实际影响的。更关键的是,广播类型下如果两端之一重启,另一端会经历"邻居超时—重新选举—重新同步"的完整流程;point-to-point 下只有邻居超时和重新同步,没有选举这一步。
需要提醒的是,改成 point-to-point 之后,接口的 Hello/Dead 定时器仍然是 10/40,但不再有 Wait Timer。两端必须都改,一端改一端不改会导致 Hello 参数不匹配,邻居卡在 INIT 或者直接起不来。这个不对称配置是实际排错中非常高频的问题,尤其是割接只做了一半的时候。
4.4 点对多点与隧道接口的特殊之处
point-to-multipoint 类型是为星型拓扑设计的,它把一条物理链路逻辑上拆成若干条点对点链路,因此不选举 DR/BDR,每对邻居独立建立邻接。它的好处是不需要手工配置neighbor,用组播就能发现,缺点是每条逻辑链路都会产生一条主机路由(/32),路由表会膨胀。
在 GRE 多点隧道(mGRE)场景里,这个特性特别有用。中心节点一个 Tunnel 接口,多个分支节点各自建 tunnel 指向中心,中心侧把 tunnel 接口配成 point-to-multipoint,分支侧也配成 point-to-multipoint,全网就能自动建立邻接,不需要 DR 选举,也不需要手工 neighbor。
但这里有个性能陷阱:point-to-multipoint 的 Hello 是 30 秒、Dead 是 120 秒,收敛速度比 broadcast 慢不少。如果分支数量多、链路质量差,邻居超时误判的概率会上升。我见过一个 40 分支的星型网络,用默认的 120 秒 Dead,每天都有几台分支因为瞬时丢包被判死,日志里全是%OSPF-5-ADJCHG。后来把定时器调成ip ospf hello-interval 10和ip ospf dead-interval 40,两端同步改,抖动才压下去。
提示:任何修改 Hello/Dead 的操作,必须保证同一网段内所有设备的取值完全一致,否则邻居关系根本建立不起来。这不是"建议",是硬性前提。
5. 现场排查链路:从2WAY卡死到DR频繁漂移
5.1 2WAY 到底是正常状态还是故障信号
新手看到邻居表里有2WAY/DROTHER,第一反应往往是"邻居没建起来"。这个判断在多数情况下是错的。
广播网络中,DROTHER 与 DROTHER 之间的邻居状态就是 2WAY,这是设计使然,不是故障。它们已经互相收到了 Hello,确认了对方的存在,只是协议规定它们不需要交换完整的数据库。判断标准很简单:只要网段上有正常的 DR 和 BDR,其他设备显示 2WAY/DROTHER 就是健康状态。
真正需要警惕的是"全部都是 2WAY"。如果所有邻居都停在 2WAY,没有任何一个 FULL/DR 或 FULL/BDR,说明这个网段上根本没有选出 DR。最常见的原因是所有设备的接口优先级都被配成了 0。这种情况在做过"统一把接入层优先级清零"操作之后特别容易发生——如果连核心层也被误配成 0,整个网段就没人当 DR 了,邻居永远停在 2WAY,路由学不到。
排查路径是这样的:
show ip ospf interface GigabitEthernet0/1先看Designated Router (ID)那一行是不是0.0.0.0。如果是,说明没有 DR。再看本接口的优先级是不是 0。然后再逐台确认邻居的优先级。这个链条走完,问题基本就定位了。
还有一种少见但确实存在的情况:网段上只有一台路由器。这时它会成为 DR,但没有任何邻居,show ip ospf neighbor是空的,接口状态里 DR 字段填的是它自己。有人看到这个会以为配置错了,其实完全正常——没有第二台设备,选举结果就是它自己。
5.2 优先级配了却不生效的三类原因
第一类,网络类型不对。前面提过,point-to-point 和 point-to-multipoint 类型下不选举,优先级命令配了也是摆设。用show ip ospf interface看Network Type一栏,如果是 P2P 或者 P2MP,优先级问题根本无从谈起。
第二类,改的是错误的接口。一台设备有十几个接口,ip ospf priority只作用于配置它的那个接口。有人在本地的 Gi0/1 上配了 200,结果发现 Gi0/1 根本没跑 OSPF,或者跑 OSPF 的是另一个子接口。用show ip ospf interface brief列出所有参与 OSPF 的接口,逐个核对。
第三类,也是最隐蔽的一类:优先级生效了,但选举没重来。前面反复说过的非抢占特性。配置本身没问题,show ip ospf interface里优先级确实是 200,但 DR 还是别人。这时候不需要改配置,需要触发一次重新选举。判断方法是看邻居表里的Pri列,如果那一列已经是 200,而 State 还是 DROTHER,那就纯粹是时机问题。
这三类原因排查顺序建议从第三类往前查,因为它的概率最高,而且验证成本最低——只需要看一眼邻居表的 Pri 列。
5.3 MTU 不一致卡在 EXSTART 的完整排查过程
这件事发生在一次跨机房互联割接里。两端设备配置完全对称,优先级、区域、认证、网络类型全部核对过,但邻居就是卡在 EXSTART,一卡就是几个小时,日志里反复刷%OSPF-5-ADJCHG: Nbr ... from EXSTART to DOWN。
排查过程我按这个顺序走的:
第一步,确认基础参数。show ip ospf interface对比两端的 Hello/Dead、Area ID、网络类型、认证方式,全部一致。
第二步,确认二层连通性。两端的接口地址能互 ping 通,ARP 表正常,说明三层可达没问题。
第三步,抓包看 DD 包。这一步是关键。抓下来的包显示,一端发出的 DD 包里 MTU 字段是 1500,另一端回应的 DD 包里 MTU 字段是 9000。EXSTART 阶段双方交换的 DD 包里携带 MTU 信息,如果发现对方 MTU 比自己大,就会拒绝继续,邻居关系卡死。
根因很快找到了:一端是普通千兆接口,MTU 1500;另一端是新上线的设备,接口默认 MTU 配成了 9000(之前为存储网络调的),负责割接的同事只核对了 IP 和 OSPF 参数,没人想到去看 MTU。
修复有两个方向。一是把两端 MTU 对齐,这是正解,但涉及接口重启,有中断。二是临时加忽略命令:
interface GigabitEthernet0/1 ip ospf mtu-ignore这条命令让 OSPF 在 DD 交换时忽略 MTU 差异,邻居能正常建立。但要注意,它只是让协议"装作没看见",实际的 MTU 不一致问题依然存在——如果链路上真的跑 9000 字节的大包,小 MTU 那一端会丢包或者分片。所以mtu-ignore只适合作为临时手段,长期还得把 MTU 统一。
注意:MTU 不匹配的症状是"卡在 EXSTART 或 EXCHANGE,日志反复 DOWN",和认证失败、Router ID 冲突的症状很像。区分方法是看日志里有没有
%OSPF-4-DUP_RTRID_NBR或者%OSPF-5-ADJCHG ... BADSEQ,没有的话优先怀疑 MTU。
5.4 DR 切换瞬间的流量黑洞与规避手段
DR 挂掉,BDR 立刻接替,这个切换本身很快。但"快"不等于"无感"。
切换瞬间会发生几件事:原 DR 产生的 Type 2 LSA 需要重新生成(由新 DR 产生),Age 归零,Seq Number 重新开始;所有 DROTHER 需要和新 DR 重新同步数据库;期间如果有流量正在经过原 DR 转发,会出现短暂中断。
我实测过一个中等规模的广播域(8 台路由器),DR 强制切换造成的中断大约在 3 到 8 秒之间,具体取决于数据库大小和设备的 CPU。这个时间对普通办公网无感,对交易类业务就是事故。
规避手段有三条,按有效性排序:
第一条,不让 DR 挂。给 DR 设备配上冗余电源、冗余上行、双机堆叠,从物理层面减少单点故障。这是最根本的。
第二条,把关键网段的网络类型改成 point-to-point,从设计上消灭 DR 这个角色。两点互联的链路本来就不该选举,改完之后没有 DR 切换这回事,链路断了就是断了,恢复就是恢复,逻辑简单得多。
第三条,接受切换,但把中断窗口控制在业务可容忍范围内。具体做法是提前把 BDR 的资源配足(CPU、内存、上行带宽),确保它接替后不会因为负载过高而二次抖动。同时用 BFD 加速故障检测,把 Dead 时间从 40 秒压到毫秒级,让切换发生得更早、更干脆,而不是让流量在一个已经死掉的 DR 上耗着。
6. 设计层面的取舍:什么时候该人为干预选举
6.1 必须手工干预的几种典型场景
默认优先级 1 加上"谁先起来谁当 DR"的机制,在小型网络里通常不会出问题,因为设备性能差不多,谁当都一样。但下面这几种场景,不干预就是给自己埋雷。
核心与接入混跑同一个广播域。接入层设备数量多、性能弱、重启频繁,如果让它们参与选举,某次批量重启后很可能由一台接入交换机当上 DR。前面 2.1 节那个 CPU 打到 80% 的案例就是这种情况。解决办法是把接入层全部设为优先级 0。
主备核心性能差距明显。主核心是高端框式设备,备核心是几年前的型号,两者带宽和会话数差好几倍。默认选举有可能让备机当上 DR,导致所有跨网段流量都要经过备机同步数据库,长期运行在亚健康状态。这时必须用优先级把主机钉在 DR 位置上。
存在跨厂商设备。不同厂商对 Router ID 的选取习惯不同,自动选举的结果往往不可预测。我参与过一个项目,某厂商设备默认用管理口地址当 Router ID,而这个地址在所有设备上都是同一网段的不同尾数,结果选举结果每次重启都变。最后只能全网手工配router-id加优先级,才算稳定下来。
有明确的流量路径设计要求。比如要求某个网段的所有流量必须经过防火墙,那防火墙直连的那台设备就必须是 DR,否则 DROTHER 之间的 2WAY 关系会让部分流量绕过预期的路径。这种情况不干预就是架构缺陷。
6.2 用优先级做流量的软牵引
DR 除了控制 LSA 泛洪,还有一个副作用:它是这个网段上所有 DROTHER 的数据库同步源,所有 DROTHER 的 LSU 都往它那里送。这意味着 DR 所在设备的接口入向流量会比其他设备高出一截。
利用这个特性,可以做一些"软牵引"。比如在双核心架构里,你希望主核心承担更多的东西向流量,那就让它当 DR,同时把它的上行链路配得更宽。这样即使没有策略路由,流量也会自然地向主核心倾斜一部分。
反过来,如果你希望某台设备尽量少承载流量,就把它的优先级设成 0,让它彻底退出 DR 竞争。它在网段上只做最基本的路由转发,不承担任何数据库汇总的职责。
这种牵引是"软"的,因为它只影响 OSPF 的数据库同步流量,不影响实际的用户数据转发路径。用户数据走哪条路,还是由路由表和底层拓扑决定。所以别指望靠调优先级来解决带宽不均的问题,那是 QoS 和流量工程该干的事。优先级能做的,是把控制面的负载放到你希望的设备上。
6.3 广播域规模与DR负担的平衡
DR 承担的责任和广播域内的路由器数量成正比。一个网段里有 5 台设备,DR 要维护 4 条完全邻接;有 30 台,就是 29 条。每条邻接都意味着独立的 Hello 定时器、重传队列、数据库副本。30 台设备的广播域,DR 的 OSPF 进程内存占用和 CPU 开销会明显高于其他设备。
所以设计上有一条不成文的经验:单个广播域内的 OSPF 路由器数量尽量不要超过 20 台。超过这个量级,考虑用 VLAN 或者子接口做分割,把一个大广播域拆成几个小的。这既是 OSPF 的要求,也是二层网络本身的要求——广播域太大,STP 收敛、ARP 广播、未知单播泛洪都会跟着出问题。
另一个角度是"要不要让服务器接入端口跑 OSPF"。服务器网段通常只有一台路由器(网关),其他都是主机,不参与路由。这种情况下让网关接口跑 OSPF 是可以的,但它会成为 DR(因为它是唯一的 OSPF 路由器),并且周期发送 Hello。如果不希望这些无谓的报文占用带宽,用passive-interface把它关掉:
router ospf 1 passive-interface GigabitEthernet0/2被 passive 的接口仍然会宣告网段,但不发 Hello、不建邻居,自然也就不参与选举。这个操作在收敛优化里非常常见,尤其是那些只有服务器、没有其他路由器的下行接口。
最后提一个容易被忽略的细节:二层交换机上的 IGMP Snooping。OSPF 的 Hello 发往 224.0.0.5 和 224.0.0.6,这些是组播地址,对应固定的组播 MAC。如果接入交换机开了 IGMP Snooping 又没有配置 querier,这些组播报文可能被当成"没人订阅的组播"而丢弃,表现就是部分邻居时通时不通,或者干脆建不起来,但 ping 是通的。这个坑我在两个不同的项目里遇到过,排查时抓包能看到 Hello 从源发出,但对端收不到。解决办法是在 VLAN 上配置 IGMP querier,或者干脆关掉 Snooping,或者静态绑定组播 MAC。这类问题和 OSPF 配置本身毫无关系,但症状和 OSPF 邻居故障一模一样,值得在排查清单里留一个位置。
我个人在实际操作中的体会是:DR/BDR 这套机制,理解了"为什么要有"和"什么时候不会重选"这两点,剩下的大部分问题都能自己推出来。真正难的不是命令,而是在一个已经跑了两年的网络里,判断某次角色异常到底是历史遗留、是设计缺失,还是刚刚发生的一次意外重启造成的。做这类判断,靠的不是记命令,是心里有一张清晰的选举时序图。