📚 第七章 路由协议
📄 一句话概括

五种报文、七个状态,这是 OSPF 的语法。 只有到 Full 才算「邻接」,2-Way 只是「认识」。

📝 本节和前面几节的关系

7.5.1 讲了 OSPF 是什么,这一节讲它怎么说话。 这些术语在排错时天天用,值得记牢。

一、五种报文各管一段

报文 干什么 什么时候发
Hello 打招呼、维持邻居 默认 10 秒一次
DD 报目录:我有哪几条 建邻接时
LSR 点名叫对方发缺的 发现缺的
LSU 把链路状态正文给你 回应 LSR 或泛洪
LSAck 收到了 每次 LSU 之后

看第二行和第三行: OSPF 建邻居时并不急着传内容,先交换「我有哪几条」的目录,再点名叫对方发缺的那几条。 这样两台路由器如果本来就一样,一个字节的正文都不用传。 这个设计让「重启一台路由器」变得很便宜。 第五行的 LSAck 值得单独说:因为 OSPF 不跑在 TCP 上,它没有现成的可靠传输, 所以每收到一个 LSU,必须显式回一个确认。 没收到确认就重传——这就是「自己造轮子」的具体形态。

二、七个状态:从「不认识」到「一致」

状态 含义
Down 还没收到过 Hello
Init 收到了 Hello,但对方没提到我
2-Way 互相在 Hello 里看到对方,邻居建成
ExStart 选出谁先说(Router ID 大的先)
Exchange 交换 DD 目录
Loading 按目录要缺的 LSA
Full 数据库一致,可以一起算路了

第二行和第三行的区别很关键:Init 是「我听见你了,但你没听见我」——单向能通。 2-Way 才是「互相听见」。 所以「邻居卡在 Init」几乎一定是单向故障:对面没收到我的 Hello,或者收到了但被过滤掉了。 这个判断比「重启试试」有用得多。 第四行的「Router ID 大的先说话」是为了避免协商死锁:如果两边都说「你先说」,就会僵住。 所以规定一个确定的规则,谁大谁先说。 这种「用固定规则打破对称」的做法,在协议里很常见——DR 选举也是同一个思路。

三、为什么有 2-Way 和 Full 两种

广播网段上不是所有人都要互相同步。 OSPF 会选一个 DR(指定路由器)和一个 BDR(备份), 所有路由器只跟 DR 同步,DR 再把结果告诉大家。 所以在一个有五台路由器的广播网段上: DR 和每台之间是 Full(四对),非 DR 之间停在 2-Way(六对)。 这样同步次数从「十对」降到「四对」——从 n 乘 (n 减 1) 除 2 降到 n 减 1。 为什么需要这个优化:因为每建一对 Full 邻接,就要交换一次完整的 LSDB。 以太网这种广播网段上可能有几十台路由器(比如一个大的数据中心),两两 Full 就是几百对, 同步风暴会把设备压垮。 所以「看到一堆 2-Way 不是故障」是一条重要的排错经验——它说明 DR 选举正常工作。 反过来,如果「所有邻居都是 Full」,那可能是「这个网段被当成了点对点」,配置有问题。

四、Router ID 和几个默认值

Router ID 是一个 32 位的数,格式跟 IP 地址一样, 但它不是接口地址,只是「这台路由器的名字」。 选举规则是「手工配的优先,否则取最大的环回口地址,再否则取最大的物理口地址」。 几个必须记住的默认值:

参数 默认值 说明
Hello 间隔 10 秒 广播网、点对点
死亡间隔 40 秒 4 倍 Hello,四个周期没听到就判死
参考带宽 100 Mbps 算代价用
多播地址 224.0.0.5 / .6 .5 给所有人,.6 给 DR

第二行的「4 倍」是设计出来的,不是巧合:它保证「偶尔丢三个 Hello 也不会误判死亡」。 如果设成 2 倍,网络一抖就断邻居,然后重新收敛,反而更乱。 这个「容忍几个周期的丢失」的思路,和 RIP 的「6 个周期才超时」是一样的。 注意「两端必须一致」:Hello 间隔和死亡间隔都写在 Hello 报文里, 两端不一致就建不成邻居。 这是「配了但邻居起不来」最常见的原因之一——不是链路问题,是参数不匹配。

五、动手 Demo:OSPF 靠五种报文干活

把五种报文和七个状态列出来,再算 Hello 的开销。 重点看「什么时候发」这一列。

pkts = [
    ('Hello',  '打招呼、维持邻居',   '默认 10 秒'),
    ('DD',     '报目录:我有哪几条', '建邻接时'),
    ('LSR',    '点名叫对方发缺的',   '发现缺的'),
    ('LSU',    '把正文给你',         '回应或泛洪'),
    ('LSAck',  '收到了',             '每次 LSU 之后'),
]
for p in pkts:
    print('%-8s %-22s %s' % p)

pkt = 44 + 24 + 20 + 18     # 正文 + OSPF 头 + IP + 以太网
print('Hello 报文 %d 字节,每 10 秒一次,约 %.0f bps'
      % (pkt, pkt * 8 / 10.0))

实跑输出:

--------------------------------------------------------------------
OSPF 靠五种报文干活
--------------------------------------------------------------------
**OSPF 不传「路由表」,它传「链路状态」。**
**要把这件事做成,需要五种报文配合。**

  报文          干什么                         什么时候发                  备注
  ------------------------------------------------------------------------------------------------
  **Hello**     **打招呼、维持邻居**           **默认 10 秒**              **死了 40 秒才判死**
  **DD**        **先报个目录:我有哪几条**     **建邻接时**                **只报摘要,不传内容**
  **LSR**       **我要第 3、第 7 条**          **发现缺的**                **点名要**
  **LSU**       **把整条链路状态给你**         **回应 LSR 或主动泛洪**     **真正的内容在这里**
  **LSAck**     **收到了**                     **每次 LSU 之后**           **可靠传输靠它**

**看第二行和第三行**:
**OSPF 建邻居时并不急着传内容,先交换「我有哪几条」的目录,再点名叫对方发缺的那几条。**
**这样两台路由器如果本来就一样,一个字节的正文都不用传。**
**这个设计让「重启一台路由器」变得很便宜。**

**邻居关系要走过七个状态**:

  状态         含义
  -----------------------------------------------
  Down         还没收到过 Hello
  Init         收到了 Hello,但对方没提到我
  2-Way        互相在 Hello 里看到对方,邻居建成
  ExStart      选出谁先说话(Router ID 大的先)
  Exchange     交换 DD 目录
  Loading      按目录要缺的 LSA
  Full         两边数据库一致,可以一起算路了

**只有到 Full 才算「邻接」**,**2-Way 只是「认识」**。
**这个区分很实用**:
**广播网里,非 DR 的路由器之间就停在 2-Way**——**它们不需要两两同步,只要都跟 DR 同步就够了。**
**所以看到一堆 2-Way 不是故障,是设计如此。**

**Hello 的开销很小**:

**一个 Hello 报文大约 106 字节**(OSPF 头 24、IP 头 20、以太网 18,再加 44 字节正文)。
**每 10 秒一次,占用约 85 bps。**
**和 RIP 每 30 秒发整张表比,这点开销可以忽略**——**代价是要维护邻居状态机,实现复杂度高得多。**

看第二行和第三行: OSPF 建邻居时并不急着传内容,先交换「我有哪几条」的目录,再点名叫对方发缺的那几条。 这样两台路由器如果本来就一样,一个字节的正文都不用传。 这个设计让「重启一台路由器」变得很便宜。 只有到 Full 才算「邻接」,2-Way 只是「认识」。 广播网里,非 DR 的路由器之间就停在 2-Way——它们不需要两两同步,只要都跟 DR 同步就够了。 所以看到一堆 2-Way 不是故障,是设计如此。 Hello 的开销很小(约 85 bps), 和 RIP 每 30 秒发整张表比,这点开销可以忽略——代价是要维护邻居状态机,实现复杂度高得多。

30 秒实验

  1. 算一下:Hello 10 秒一次、死亡 40 秒,能容忍丢几个 Hello?
  2. 想一下:「邻居卡在 Init」最可能是什么故障?
  3. 想一下:五台路由器的广播网段上,有几对 Full、几对 2-Way?

常见误区

  1. 以为「2-Way 是故障」。 它是正常状态,非 DR 之间本来就停在 2-Way。
  2. 以为「Hello 间隔可以两端不同」。 必须一致,不一致直接建不成邻居。
  3. 以为「Router ID 是接口地址」。 它只是个 32 位的名字,可以手工指定,不必等于任何接口。
自测三问(点开对答案) 1. 五种报文分别是什么? —— **Hello、DD、LSR、LSU、LSAck。** 2. 为什么要有 DR? —— **把同步次数从 n 平方降到 n**,避免广播网段上的同步风暴。 3. 「邻居卡在 Init」说明什么? —— **单向能通**:我听见你,你没听见我。

下一节看这些报文在时间上是怎么排的。

相关笔记