3-3 RIP — 距離ベクトル型の本質と限界
距離ベクトル型プロトコル RIP の伝播モデル、ホップ数メトリックの限界、Split Horizon と Route Poisoning による収束防衛を整理する。CSR1000v 3 台の実機 RIB と debug ip rip で確認する。
1. 前節の振り返りと本節の内容
3-2 では複数の経路情報源から 1 つを選ぶ枠組み (Longest Match → AD 比較) と、同一ソース内で経路を絞るためのメトリックを整理しました。検証ラボの最後で、静的を消すと OSPF (AD=110) が、OSPF も止めると RIP (AD=120) が席を取り、それぞれが自分のメトリックで R1↔R3 直結を選ぶ様子も観察しました。
本節はこのとき動かしっぱなしにしていた RIP (Routing Information Protocol) に焦点を当てます。RIP は 1988 年の RFC 1058 以来、動的ルーティングプロトコルの始祖として扱われてきた 距離ベクトル型 (Distance Vector) の代表格です。現代のキャリア網や企業基幹で RIP が選ばれることはほぼ無くなりましたが、距離ベクトル型の動作原理は EIGRP の DUAL 理解にそのまま効くため、教科書としての価値は今も大きいといえます。
本節では距離ベクトル型の伝播モデル、ホップ数というメトリックの限界、4 つのタイマー (Update / Invalid / Holddown / Flush)、Split Horizon と Route Poisoning によるループ防止、そして count-to-infinity が IOS-XE で本当に再現するかまでを、CSR1000v 3 台の debug ip rip を撮りながら確認します。
2. 距離ベクトル型の伝播モデル
RIP の動作を 1 行で書くと「隣接が言ったメトリックに +1 して、自分の database に取り込み、また別の隣接へ再放送する」になります。これが 距離ベクトル (Distance Vector) という名前の中身で、各ルータが持つ情報は「宛先プレフィックス + そこに行くまでの距離 (= ホップ数) + 次のルータ (= 隣接の IP)」の 3 点だけです。
距離ベクトル型で重要なのは 「経路の中身」を知らない という点です。R1 は「10.30.3.0/24 へは R2 (10.12.0.1) 経由で metric 2」という事実だけを持ち、その先 R2 が誰経由で届けるかは関知しません。R2 が嘘をついても (たとえば本当は届かない宛先を metric 1 で広告しても) R1 はそれを信じます。距離ベクトル型は「噂で伝わるルーティング」と呼ばれることがありますが、噂の出所を遡れない設計上の制約がループ防止の難しさに直結します。
対比として、3-5 で扱う リンクステート型 (Link State) の OSPF は逆の発想で動きます。各ルータがネットワーク全体のトポロジを LSA で集めて手元で再構築し、Dijkstra で最短経路を自分で計算します。経路の中身 (どのリンクが生きていてどのリンクが死んでいるか) を全員が共有するため、噂の伝言ゲームに頼りません。距離ベクトル型は計算が軽く設定も単純ですが、トポロジ全体の整合性は隣接同士の地道な情報交換でしか保てません。
3. RIPv1 と RIPv2 の差分
RIP には 1988 年策定の v1 (RFC 1058) と 1998 年改訂の v2 (RFC 2453) があり、教科書で RIP と書けば普通 v2 を指します。両者の差は以下のとおりです。
| 観点 | RIPv1 | RIPv2 |
|---|---|---|
| アドレッシング | classful (サブネットマスクを広告しない) | classless (VLSM / CIDR 対応) |
| 配信方式 | broadcast (255.255.255.255) | multicast (224.0.0.9) |
| 認証 | 無し | plain text / MD5 |
| Auto-summary | 強制 (classful 集約) | 既定有効だが no auto-summary で無効化できる |
| ホップ数上限 | 15 | 15 (同じ) |
| 経路タグ | 無し | あり (再配送識別用) |
VLSM (Variable Length Subnet Mask) が使えないという点だけで v1 は現代ネットワークでは事実上採用できず、以降の解説では RIPv2 だけを扱います。設定の冒頭で version 2 を明示し、no auto-summary で classful 集約を切るのが最低限の作法となっています。
router rip
version 2
no auto-summary
network 10.0.0.0network 10.0.0.0 は 3-2 §11 で触れたとおり「広告するプレフィックス」ではなく「RIP を起動する IF を選ぶセレクタ」です。10.x.x.x に該当する IP を持つ IF (本ラボでは Gi2 / Gi3 / Lo0 / Lo10 など) で RIP が起動し、結果としてそれらの IF のサブネットが広告されます。
4. メトリック: ホップ数だけの世界
RIP のメトリックは 経由ルータ数 (ホップ数) だけです。1 Gbps のリンクも 56 kbps のリンクも同じ「1 ホップ」と数え、ホップ数最小の経路が無条件に勝ちます。3-2 §6 の表で並べた他プロトコル (帯域ベースの OSPF、複合の EIGRP) とは思想が異なります。
設計上の上限は 15 ホップ で、16 を到達不能 (infinity) として扱います。16 ホップを超える経路を作りたければ最初から RIP は選べません。これは実装の都合ではなく仕様で、後述する count-to-infinity の自然な上界として 16 が選ばれた経緯があります。
本ラボでは R1 / R2 / R3 の三角構成にそれぞれ Loopback を追加し、RIP で広告して経路がどう伝わるかを観察します。アドレス計画は 3-2 を流用し、各ルータに「サービス側 Loopback」を 1 つずつ追加します。
| 用途 | アドレス | 備考 |
|---|---|---|
| R1 Lo10 | 10.10.1.0/24 | R1 が RIP で広告するサービス網 |
| R2 Lo20 | 10.20.2.0/24 | R2 のサービス網 |
| R3 Lo30 | 10.30.3.0/24 | R3 のサービス網 (後段でこれを落とす) |
その他 (R1/R2/R3 Lo0, P2P /31 リンク 3 本) は 3-2 と同一です。
3 台に同じ router rip / version 2 / no auto-summary / network 10.0.0.0 を投入し、60 秒待ってから R1 視点で RIP database を確認します。以下のとおりです。
R1#show ip rip database
10.0.0.0/8 auto-summary
10.0.1.1/32 directly connected, Loopback0
10.0.2.1/32
[1] via 10.12.0.1, 00:00:22, GigabitEthernet2
10.0.3.1/32
[1] via 10.13.0.1, 00:00:07, GigabitEthernet3
10.10.1.0/24 directly connected, Loopback10
10.12.0.0/31 directly connected, GigabitEthernet2
10.13.0.0/31 directly connected, GigabitEthernet3
10.20.2.0/24
[1] via 10.12.0.1, 00:00:22, GigabitEthernet2
10.23.0.0/31
[1] via 10.13.0.1, 00:00:07, GigabitEthernet3
[1] via 10.12.0.1, 00:00:22, GigabitEthernet2
10.30.3.0/24
[1] via 10.13.0.1, 00:00:07, GigabitEthernet3R3 の 10.30.3.0/24 を R1 は metric 1 で学習しています。これは R1↔R3 直結 (Gi3) 経由で 1 ホップという意味で、R2 経由 (2 ホップ) よりホップ数が小さいため直結が勝ちました。3-2 で OSPF が同じ宛先を直結経由で取った構図 (帯域ベースのコスト計算でも結局直結が短かった) と一致します。
ルーティングテーブルにも以下のとおり載ります。
R1#show ip route rip
10.0.0.0/8 is variably subnetted, 12 subnets, 3 masks
R 10.0.2.1/32 [120/1] via 10.12.0.1, 00:00:00, GigabitEthernet2
R 10.0.3.1/32 [120/1] via 10.13.0.1, 00:00:12, GigabitEthernet3
R 10.20.2.0/24 [120/1] via 10.12.0.1, 00:00:00, GigabitEthernet2
R 10.23.0.0/31 [120/1] via 10.13.0.1, 00:00:12, GigabitEthernet3
[120/1] via 10.12.0.1, 00:00:00, GigabitEthernet2
R 10.30.3.0/24 [120/1] via 10.13.0.1, 00:00:12, GigabitEthernet3[120/1] は AD=120 / メトリック=1 を意味し、3-2 で見た [1/0] (static) や [110/2] (OSPF) と同じ並び順です。10.23.0.0/31 が R1↔R3 経由と R1↔R2↔R3 経由の両方で metric 1 として登録されているのは、両経路がホップ数で並んだため ECMP (Equal-Cost Multi-Path) として両方 RIB に載せたものです (maximum-paths 4 が既定値)。
ここで RIP のホップ数だけメトリックの限界が見えます。仮に R1↔R2 リンクが 1 Gbps、R1↔R3 リンクが 56 kbps だったとしても、RIP は両方を「1 ホップ」と数えて区別しません。実速度を見ない設計の限界として広く知られている話で、1990 年代後半に企業網が高速化していくにつれ RIP が OSPF / EIGRP に置き換えられていった最大の理由がここにあります。
5. RIP の 4 つのタイマー
距離ベクトル型は隣接同士の周期通信で世界を保ちます。RIP は 4 つのタイマーでこの保ち方を統制しており、以下のとおりです。
| タイマー | 既定値 | 役割 |
|---|---|---|
| Update | 30 秒 | 全 RIP IF に database 全体を周期広告 |
| Invalid | 180 秒 | Update が途切れた経路を metric=16 に昇格 (到達不能扱い) |
| Holddown | 180 秒 | Invalid 化した経路について、他経路からの「もっと悪い」広告を一定期間拒否 |
| Flush | 240 秒 | 経路を database から完全に削除 |
show ip protocols で実際の設定値が確認できます。
R1#show ip protocols
Routing Protocol is "rip"
Sending updates every 30 seconds, next due in 9 seconds
Invalid after 180 seconds, hold down 180, flushed after 240タイマー設計の意図は 「定期的に再確認することで世界を保つ」 と 「不安定な広告に振り回されない」 の両立にあります。Update の 30 秒は隣接の生存確認も兼ねており、180 秒間届かなければ隣接が消えたか経路が消えたかと判断します。Holddown は「直前まで使っていた経路が消えた」と分かった直後に、別経路からの広告 (それが本当に正しいかまだ怪しい) で安易に席を埋めないための猶予期間です。
このタイマー設計は穏当に見えて、収束 (再計算が安定するまでの時間) を分単位に押し上げる主因でもあります。180 秒 (= 3 分) の Invalid 待ちは、現代の LAN ではあまりに遅いといえます。EIGRP の Hold Time が既定 15 秒、OSPF の Dead Interval が既定 40 秒であるのと比べると、RIP の収束の遅さが際立ちます。
6. debug ip rip で Update の中身を見る
タイマーの周期動作を直接観察するため、R1 で debug ip rip を 60 秒回します。出力は 30 秒ごとに 2 周分の Update が見えます。
*May 20 12:20:51.622: RIP: sending v2 update to 224.0.0.9 via GigabitEthernet3 (10.13.0.0)
*May 20 12:20:51.622: RIP: build update entries
*May 20 12:20:51.622: 10.0.1.1/32 via 0.0.0.0, metric 1, tag 0
*May 20 12:20:51.622: 10.0.2.1/32 via 0.0.0.0, metric 2, tag 0
*May 20 12:20:51.623: 10.10.1.0/24 via 0.0.0.0, metric 1, tag 0
*May 20 12:20:51.623: 10.12.0.0/31 via 0.0.0.0, metric 1, tag 0
*May 20 12:20:51.623: 10.20.2.0/24 via 0.0.0.0, metric 2, tag 0
*May 20 12:20:51.853: RIP: received v2 update from 10.13.0.1 on GigabitEthernet3
*May 20 12:20:51.853: 10.0.2.1/32 via 0.0.0.0 in 2 hops
*May 20 12:20:51.854: 10.0.3.1/32 via 0.0.0.0 in 1 hops
*May 20 12:20:51.854: 10.20.2.0/24 via 0.0.0.0 in 2 hops
*May 20 12:20:51.854: 10.23.0.0/31 via 0.0.0.0 in 1 hops
*May 20 12:20:51.854: 10.30.3.0/24 via 0.0.0.0 in 1 hops宛先マルチキャスト 224.0.0.9 が RIPv2 標準で、Gi3 (R3 向け) と Gi2 (R2 向け) の両方から同じタイミングで Update が飛んでいます。Gi3 向け Update には R3 から学んだ経路 (10.0.3.1/32, 10.23.0.0/31, 10.30.3.0/24) が含まれていない 点が重要で、これが §7 の Split Horizon の動作です。
各エントリの形式は <prefix> via <next-hop>, metric <N>, tag <T> で、via 0.0.0.0 は「next-hop の指定なし = 受信者から見た送信者の IP を next-hop と解釈せよ」を意味します。多くの距離ベクトル型でこの省略形が標準です。
7. Split Horizon: 同じ IF からは広告し返さない
Split Horizon は 「ある IF から学んだ経路は、同じ IF からは広告しない」 という単純なルールで、距離ベクトル型のループ防止策のうち最も古典的かつ強力なものです。
R2 視点で show ip interface | include Split horizon を見ると、両 P2P IF で有効になっています。
R2#show ip interface GigabitEthernet2 | include Split horizon
Split horizon is enabled
R2#show ip interface GigabitEthernet3 | include Split horizon
Split horizon is enabledR2 の RIP database は 10.10.1.0/24 を R1 から学んでいます。
R2#show ip rip database 10.10.1.0 255.255.255.0
10.10.1.0/24
[1] via 10.12.0.0, 00:00:09, GigabitEthernet2R2 は Gi2 (R1 向け) からこの経路を学んだので、Split Horizon により Gi2 からは 広告し返しません。R1 側の debug ip rip で R2 → R1 向け Update を観察すると、Gi2 から受信する Update に 10.10.1.0/24 が含まれないことが確認できます。
*May 20 12:22:28.866: RIP: received v2 update from 10.12.0.1 on GigabitEthernet2
*May 20 12:22:28.866: 10.0.2.1/32 via 0.0.0.0 in 1 hops
*May 20 12:22:28.867: 10.0.3.1/32 via 0.0.0.0 in 2 hops
*May 20 12:22:28.867: 10.20.2.0/24 via 0.0.0.0 in 1 hops
*May 20 12:22:28.867: 10.23.0.0/31 via 0.0.0.0 in 1 hops
*May 20 12:22:28.867: 10.30.3.0/24 via 0.0.0.0 in 2 hopsR2 が R1 に送る Update の中身は「R2 自身が直結で持つ 10.0.2.1/32・10.20.2.0/24・10.23.0.0/31 (R2↔R3 リンク)」「R3 から学んだ 10.0.3.1/32 と 10.30.3.0/24」だけで、R1 から学んだ経路は 1 つも含まれていません。Split Horizon が機械的に「同じ IF からは消す」を実行している証拠です。
逆方向の R1 → R3 向け Update (Gi3) も同様で、R1 が R3 から学んだ 10.0.3.1/32, 10.23.0.0/31, 10.30.3.0/24 は含まれず、R1 直結と R2 経由のものだけが流れます。
Split Horizon が無いと何が起きるかは §10 の count-to-infinity 再現実験で扱います。
8. Route Poisoning と Triggered Update
Split Horizon が「広告しない」という静的なフィルタなのに対し、Route Poisoning は「経路が消えた瞬間に metric=16 で能動的にアナウンスする」という積極的な振る舞いです。
距離ベクトル型でもっとも怖いのは「経路が消えたという事実が伝わらないまま、誰かが古い情報で広告し続ける」状況で、Update の周期 30 秒 + Invalid 180 秒の組み合わせを愚直に待つと最悪 3 分以上、隣接が古い経路を信じたままになります。Route Poisoning はこの待ち時間を圧縮するための工夫で、自分が知っていた経路が消えたと判断した瞬間 (= 直結リンクが落ちた、隣接からの Update が止まった) に metric=16 (infinity) を載せた即時 Update を全 IF に放出します。poison を受け取った隣接は、その poison を現用経路の更新として採用する場合 (= 同じ next-hop から学んでいた、または他に有効な代替経路を持たない場合) に「もうこの経路は到達不能だ」と認識し、転送に使う経路表 (RIB) から外したうえで自分も同じ poison を伝言します。逆に別の next-hop から同じ宛先のより良い経路を持っていれば、poison は採用されずそちらが残ります。採用された場合でも RIP の内部 database 上では、metric=16 になった経路はすぐに消えるのではなく「変更あり」フラグを立てたまま garbage-collection (= Flush) タイマー満了まで残し、その間に隣接へ訃報を伝え続けます。
これと並んで動くのが Triggered Update で、経路の状態が変わった瞬間に通常の 30 秒周期を待たずに即座に Update を送る機構です。RFC 2453 §3.4.4 はメトリック変化時のほぼ即時 update を求めており、特に経路削除 (deleted route) については triggered update を実装しなければならない (MUST) と規定しています (任意の推奨機能ではありません)。Cisco 実装でもデフォルトで動作し、show ip protocols の Triggered RIP 列が No と表示されていても (これは Demand Circuit RIP という別機能のフラグ)、Route Poisoning は自動的に Triggered で送出されます。
実機でどう効くかは §9 のタイマー実測ではっきり見えます。
9. タイマー実測: poison が伝播する瞬間をミリ秒で撮る
§5 で並べた Invalid 180s / Flush 240s というタイマーが、経路喪失のたびに本当に待たされるのかを確かめます。素朴に 30 秒ごとの show で経路の消滅を追うだけでは「いつの間にか消えていた」としか分からず、教科書の数字と実機の差は判別できません。そこで R3 (発生源) / R2 (中継) / R1 (受信) の 3 台で同時に debug ip rip を流しながら R3 の Loopback30 を 1 回 shutdown し、service timestamps debug datetime msec のミリ秒タイムスタンプで「いつ metric 16 が発生し、何ミリ秒で隣接へ転送されたか」を撮ります。
まず poison の 発生源 である R3 の視点です。Loopback30 が落ちた瞬間に何が起きるかが 1 行で見えます。
*Jun 11 06:12:26.139: %LINEPROTO-5-UPDOWN: Line protocol on Interface Loopback30, changed state to down
*Jun 11 06:12:26.140: %LINK-5-CHANGED: Interface Loopback30, changed state to administratively down
*Jun 11 06:12:26.144: RIP: sending v2 flash update to 224.0.0.9 via GigabitEthernet2 (10.23.0.1)
*Jun 11 06:12:26.144: RIP: build flash update entries
*Jun 11 06:12:26.144: 10.30.3.0/24 via 0.0.0.0, metric 16, tag 0
*Jun 11 06:12:26.144: RIP: sending v2 flash update to 224.0.0.9 via GigabitEthernet3 (10.13.0.1)
*Jun 11 06:12:26.144: RIP: build flash update entries
*Jun 11 06:12:26.144: 10.30.3.0/24 via 0.0.0.0, metric 16, tag 0%LINEPROTO-5-UPDOWN で link-down を検知した 06:12:26.139 から、わずか +5 ms 後の 06:12:26.144 に R3 は 10.30.3.0/24 を metric 16 (= 到達不能) で全 RIP IF へ送り出しています。debug の文言が通常の build update entries ではなく build flash update entries になっている点が重要で、この flash update こそ §8 で説明した Triggered Update (= 変化時に 30 秒周期を待たず即送する) の Cisco 実装名です。1 つの宛先 10.30.3.0/24 だけを metric 16 で載せて即送しており、これが Route Poisoning (metric 16 での能動的な訃報) の中身です。30 秒周期の Update を待っていません。
次に、この poison を隣接の R2 / R1 がいつ受け取ったかを、同じ時刻軸で並べます。CML のノードはハイパーバイザのクロックを共有しているため 3 台の時計はほぼ揃っており、送信ログと受信ログの差 (数 ms) を観測差として読めます。厳密には debug のタイムスタンプには IOS の処理時間・仮想 NIC・ログ出力のタイミングも混ざるため「純粋な配線遅延」ではありませんが、本ラボの条件では「送出から受信ログ記録まで数 ms」という桁感が、教科書のタイマー (秒〜分) とどれだけ違うかを示すには十分です。
R2#
*Jun 11 06:12:26.146: RIP: received v2 update from 10.23.0.1 on GigabitEthernet3
*Jun 11 06:12:26.146: 10.30.3.0/24 via 0.0.0.0 in 16 hops (inaccessible)R1#
*Jun 11 06:12:26.148: RIP: received v2 update from 10.13.0.1 on GigabitEthernet3
*Jun 11 06:12:26.148: 10.30.3.0/24 via 0.0.0.0 in 16 hops (inaccessible)R3 が送出した 06:12:26.144 に対し、R2 の受信は 06:12:26.146 (+2 ms)、R1 の受信は 06:12:26.148 (+4 ms) です。本ラボは三角構成で R3 が R2・R1 の両方に直結しているため、poison は両隣接に直接届きます。link-down (.139) から R1 が到達不能を知る (.148) まで、実測で わずか 9 ms でした。Invalid 180 秒・Flush 240 秒という数字は一切待っていません。
さらに R2 は受け取った poison を R1 へも中継します。受信 (.26.146) から約 2 秒後に、R2 自身が flash update で metric 16 を R1 (Gi2 = 10.12) 側へ送り出しています。
R2#
*Jun 11 06:12:28.146: RIP: sending v2 flash update to 224.0.0.9 via GigabitEthernet2 (10.12.0.1)
*Jun 11 06:12:28.146: RIP: build flash update entries
*Jun 11 06:12:28.146: 10.30.3.0/24 via 0.0.0.0, metric 16, tag 0R2 の中継が受信の直後 (0 秒) ではなく約 2 秒後なのは、triggered update に短いランダム遅延 (RFC 2453 §3.4.4 が同期回避のために置く 1〜5 秒の jitter) があるためで、「即時 = 0 秒」ではない点に注意します。とはいえ R1 は既に R3 から直接 metric 16 を受け取っているので、RIB からの削除はこの中継を待ちません。30 秒ごとの show でも結果は裏取りできます。poison 到達の直後には RIB から外れ、続く周期で database からも消えます。
R1#show ip route 10.30.3.0
% Subnet not in table
R1#show ip rip database 10.30.3.0 255.255.255.0
%Route not in database教科書の「RIP は収束に数分かかる」という記述は、あくまで poison が出ない病的なケース (隣接装置が機械的にハングして訃報を出さずに沈黙し、受信側が Invalid タイマー満了でようやく見切るような状況) の上限です。Invalid / Holddown / Flush の長いタイマーはこの「沈黙への保険」であって、明示的な poison が届けば待つ必要はありません。本ラボでは三角構成のため R1 は poison を R3 から直接 (1 ホップ) 受け取り、link-down からわずか 9 ms で到達不能を知りました。一方で R2 が中継する 2 ホップ目は、前述のとおり triggered update のランダム遅延を挟むため約 2 秒かかっており、「ホップを越えるたびに秒オーダーの遅延が積み上がる」のが距離ベクトル型の本質です。ルータが直線状に連なるほど poison が末端へ届くまでの時間は伸びるため、収束が速いのは「直接 poison が届く近傍」に限られる点には注意します。
復活させた挙動も対称で、R3 で no shutdown を打って 45 秒待つと R1 の database と RIB に metric 1 で戻ってきます。
R1#show ip route 10.30.3.0
Routing entry for 10.30.3.0/24
Known via "rip", distance 120, metric 1
Redistributing via rip
Last update from 10.13.0.1 on GigabitEthernet3, 00:00:09 ago
Routing Descriptor Blocks:
* 10.13.0.1, from 10.13.0.1, 00:00:09 ago, via GigabitEthernet3
Route metric is 1, traffic share count is 1
R1#show ip rip database 10.30.3.0 255.255.255.0
10.30.3.0/24
[1] via 10.13.0.1, 00:00:14, GigabitEthernet310. count-to-infinity を再現できるか
距離ベクトル型の教科書には必ず登場する count-to-infinity は、Split Horizon が効かない状況で経路喪失情報が遅れて伝わる時に発生します。「R3 で経路が消えた」と R2 がまだ知らない間に、R1 が「私はこの経路を持っている (R2 経由で metric 2)」と R2 に広告すると、R2 はそれを信じて metric 3 で再学習してしまいます。次の周期で R2 が R1 に「metric 3 で持っている」と返し、R1 は metric 4 に上がります。この応酬で metric が 1 周期ごとに +1 されていき、16 に達して初めて「到達不能」と判断されます。
本ラボで Split Horizon を全 P2P IF で無効化し、R3 で Lo30 を落として再現を試みます。
R1(config)# interface GigabitEthernet2
R1(config-if)# no ip split-horizon
R1(config-if)# interface GigabitEthernet3
R1(config-if)# no ip split-horizon
(R2 / R3 も同様に Gi2 / Gi3 で Split Horizon 無効化)debug ip rip を 200 秒回して R1 視点で 10.30.3.0/24 のメトリック変化を追うと、教科書とは異なる結果になります。R1 が隣接から受け取る Update では、当該経路は最初から in 16 hops (inaccessible) で届きます。
*May 20 12:32:41.230: RIP: received v2 update from 10.12.0.1 on GigabitEthernet2
*May 20 12:32:41.234: 10.0.2.1/32 via 0.0.0.0 in 1 hops
*May 20 12:32:41.234: 10.0.3.1/32 via 0.0.0.0 in 2 hops
*May 20 12:32:41.234: 10.12.0.0/31 via 0.0.0.0 in 1 hops
*May 20 12:32:41.235: 10.20.2.0/24 via 0.0.0.0 in 1 hops
*May 20 12:32:41.235: 10.23.0.0/31 via 0.0.0.0 in 1 hops
*May 20 12:32:41.235: 10.30.3.0/24 via 0.0.0.0 in 16 hops (inaccessible)200 秒の debug を通して 10.30.3.0/24 の値は 常に 16 で、3, 4, 5 ... と段階的に上がる count-up は 1 度も現れませんでした。metric は 2 から 16 へ直接ジャンプし、その間の中間値を取りません。Split Horizon を切ったにもかかわらず count-to-infinity が再現しないのは、§9 でミリ秒単位に撮ったとおり Route Poisoning + Triggered Update が独立に動いているからです。§9 では R3 が Lo30 を落とした +5 ms 後に metric 16 の flash update を出し、隣接が +2〜4 ms で受け取っていました。R1 はこの metric 16 を受け取って即座に「到達不能」とマークし、その状態で R1 → R2 へ Update を送る時にはもう metric 16 で送り返します。互いに「(古い metric 2 で) 持っている」と主張し合う隙が、ミリ秒のオーダーで潰されているわけです。
教科書で count-to-infinity が紹介される時の前提は「Route Poisoning / Triggered Update が無い、もしくは効かない実装」であり、現代の Cisco IOS-XE のようにこれらが標準で動く環境では、Split Horizon を切るだけでは再現できません。ここで用語を整理しておくと、Route Poisoning は「経路が消えた時に metric 16 で能動的に広告する」動作、Triggered Update は「変化時に 30 秒周期を待たず即座に送る」動作、Poison Reverse は「学習元へ返す Update に当該経路をあえて metric 16 で載せて打ち消す」Split Horizon の強化版で、それぞれ別物です (RFC 2453 でも区別されています)。本ラボで count-to-infinity が再現しなかった主因は Route Poisoning + Triggered Update が即座に metric 16 を末端まで伝えたことにあり、これらを止めずに再現させるのは IOS-XE では困難です (Poison Reverse 含め、これらの即時収束機構は IF 単位で無効化するコマンドが用意されていません)。
実機ベースで言うなら、距離ベクトル型のループ防止は Split Horizon / Poison Reverse と Route Poisoning + Triggered Update の複数の安全網 で守られており、本ラボの 3 台構成で Split Horizon を切っただけでは (Route Poisoning が速く poison を伝えたため) 再現しませんでした。ただし注意したいのは、これらの機構が count-to-infinity を 完全に排除するわけではない点です。RFC 2453 §3.4.4 も、split horizon with poisoned reverse が防げるのは 2 ルータ間のループで、3 ルータ以上が互いに誤った経路を信じ合うパターンはなお起こり得ること、triggered update は収束を速める工夫であって count-to-infinity を根絶するものではないことを述べています。本ラボの「再現しなかった」という結果は「この 3 台・IOS-XE・Lo shutdown という条件で poison が速く伝わった」ことを示すものであって、「複数の安全網が同時に失われない限り絶対に起きない」という一般化までは保証しません。
11. 落とし穴・補足
実運用と検証で踏みやすい注意点を 5 点挙げます。
no auto-summaryを忘れると VLSM が壊れる: 既定有効の auto-summary は、classful 境界を跨ぐ IF から経路を出す時にだけ、subprefix を classful network (10.0.0.0/8 など) へ強制集約します。本ラボのように全 IF が10.0.0.0/8内に収まっている構成では classful 境界を跨がないため、単に/24と/31が混在するだけで全サブネットが10.0.0.0/8に丸められるわけではありません。auto-summary が牙を剥くのは、たとえば 10.x 網と 172.16.x 網のように 異なる major network が不連続 (discontiguous) に配置された ケースで、境界ルータが配下の 10.x サブネットを10.0.0.0/81 本に集約してしまい、別の場所にある 10.x サブネットと衝突して経路が混線します。VLSM / 不連続ネットワークを正しく扱うため、RIPv2 ではno auto-summaryが事実上必須です。network文の意味は IF セレクタ: 3-2 §11 と同じ注意で、network 10.0.0.0は「10.x.x.x のサブネットを広告する」ではなく「IP が 10.x.x.x の IF で RIP を起動する」と読みます。RIP は OSPF / EIGRP のような隣接 (neighbor) 状態を形成しないため、passive-interfaceの効果は「その IF から RIP Update を送信しない」ことです (受信は続け、その IF の connected network 自体はnetworkに含まれていれば他の RIP IF から広告され得ます)。特定プレフィックスの広告を絞りたいならdistribute-listで出力フィルタをかけます。- マルチキャスト 224.0.0.9 は TTL=1 で局所配信: RIPv2 の Update 配信先は 224.0.0.9 ですが、これは TTL=1 の「直接接続セグメント限定」マルチキャストで、ルータを跨いで広がりません。v2 で multicast 化された主な動機は、v1 の broadcast (255.255.255.255) だと同一セグメント上の RIP を喋らないホストにも届いて無駄な割り込み処理をさせてしまうのに対し、224.0.0.9 を listen する RIP ルータだけに配信を限定できる点にあります (v1 broadcast も通常は同一ブロードキャストドメイン内に留まり、スイッチを越えて別 VLAN に漏れるものではありません)。
- Triggered RIP と Triggered Update を混同しない:
show ip protocolsのTriggered RIP列は WAN 用途の最適化機能 (Demand Circuit RIP, RFC 2091) のフラグで、§8 で扱った「経路変化時の即時 Update」とは別物です。後者は実装の動作で常に有効、前者は明示的に enable する珍しいオプションになっています。 - RIPng は IPv6 用で v3 ではない: RIP の IPv6 版は RIPng (next generation) で、UDP/521、マルチキャスト FF02::9、メトリックは依然ホップ数となっています。RIPv3 という名前のプロトコルは存在しません。本シリーズでは IPv6 編 (3-12) で軽く触れます。
12. 次節 (3-4 EIGRP) への接続
本節で距離ベクトル型の伝播モデル、ホップ数メトリックの限界、4 つのタイマーと Route Poisoning による収束、Split Horizon によるループ防止、そして count-to-infinity が実機ではどこまで再現するかを CSR1000v 3 台と debug ip rip で確認しました。RIP はホップ数だけで実回線速度を見ない設計と、隣接の Update に全面依存する伝言ゲーム的な伝播モデルが現代規模のネットワークで通用しなくなり、1990 年代後半以降は OSPF と EIGRP に席を譲ってきました。それでも距離ベクトル型の動作原理は EIGRP の DUAL (Diffusing Update Algorithm) を理解する土台として欠かせません。
3-4 EIGRP では Cisco が距離ベクトル型を拡張した EIGRP (Enhanced IGRP) を扱います。RIP のホップ数オンリーから複合メトリック (既定では帯域 + 遅延。K 値の設定次第で信頼性・負荷も加味できますが、MTU は経路情報として運ばれるだけで既定の計算式には入りません) へ、Update の周期広告から差分更新 + Hello ベースの隣接管理へ、Holddown 任せの収束から DUAL による事前計算済みバックアップ経路 (Feasible Successor) へと、距離ベクトル型がどう進化したかを実機で見ていきましょう。
version 2、そして no auto-summary で classful 集約を切るのが最低限の作法だ。R1#show ip route rip
R 10.30.3.0/24 [120/1] via 10.13.0.1, GigabitEthernet3
R 10.23.0.0/31 [120/1] via 10.13.0.1, GigabitEthernet3
[120/1] via 10.12.0.1, GigabitEthernet2[120/1] は AD=120 / メトリック=1 だ。3-2 で見た [1/0](static)や [110/2](OSPF)と同じ並び順だね。注目は 10.23.0.0/31 が R3 直結経由と R2 経由の両方に [120/1] で載っていること。ホップ数で並んだから ECMP として両方 RIB に入れて負荷分散しているんだ。仮に片方が 1Gbps、片方が 56kbps でも、RIP は両方「1 ホップ」と数えて区別しない。これが RIP がインデックスに負けて置き換えられていった最大の理由だ。R1# (R2 から受信した Update に R1 由来の経路が含まれない)
*12:22:28: RIP: received v2 update from 10.12.0.1 on GigabitEthernet2
*12:22:28: 10.0.3.1/32 via 0.0.0.0 in 2 hops
*12:22:28: 10.30.3.0/24 via 0.0.0.0 in 2 hopsR3#
*06:12:26.139: %LINEPROTO-5-UPDOWN: Interface Loopback30, changed state to down
*06:12:26.144: RIP: sending v2 flash update to 224.0.0.9 via GigabitEthernet2
*06:12:26.144: 10.30.3.0/24 via 0.0.0.0, metric 16, tag 0.139 から、わずか +5 ms 後の .144 に R3 は 10.30.3.0/24 を metric 16 で全 IF へ送り出している。debug の文言が build flash update entries になっているのが重要で、この flash update が Triggered Update の Cisco 実装名だ。30 秒周期なんて待っていない。R2# *06:12:26.146: received ... 10.30.3.0/24 in 16 hops (inaccessible)
R1# *06:12:26.148: received ... 10.30.3.0/24 in 16 hops (inaccessible)R1# *12:32:41: received v2 update from 10.12.0.1
*12:32:41: 10.30.3.0/24 via 0.0.0.0 in 16 hops (inaccessible)10.30.3.0/24 の値は常に 16で、3, 4, 5 … と上がる count-up は 1 度も出なかった。metric は 2 から 16 へ直接ジャンプして、中間値を取らない。Split Horizon を切ったのに再現しないのは、Route Poisoning + Triggered Update が独立に動いているからだ。さっきミリ秒で撮ったとおり、R3 が落ちた +5 ms 後に metric 16 を撒いて、隣が +2〜4 ms で受け取る。R1 はそれを即「到達不能」とマークするから、R2 と「(古い metric 2 で)持ってる」と主張し合う隙が、ミリ秒で潰されているんだ。