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 和鏈路利用率。