STUDY · NETWORK GUIDE

6-1 MPLS 基礎 — ラベルで転送する仕組みと LSP の作られ方

ラベルで転送する MPLS を csr1000v 4 台の実機で扱います。LDP が配ったラベルを LIB と LFIB の差で読み分け、出口の手前で外す PHP と、経路が変わると使うラベルだけが入れ替わる様子を逐語の show 出力で確かめます。

1. 前節の振り返りと本節の内容

前章の最終節 5-11 QoS トラブルシュート では、組んだ QoS が効いていないときに、分類・マーキング・キューイングのどの段で崩れているかを特定する手順を扱いました。第 5 章を通しての主題は、1 つの機器あるいは 1 本の経路の中で通信に優先順位をつけることでした。

本章の主題は、その経路そのものをどう作るかです。拠点と拠点をどう結ぶか、という視点に変わります。その 1 節目である本節は、ラベルで転送する MPLS (Multi-Protocol Label Switching) を扱います。

MPLS という言葉は本書の最初のページにも出てきました。1-1 ネットワークの基本概念 では、広域を自前で敷設するのは現実的でないため通信事業者の回線を契約する、という文脈で「専用線・MPLS・インターネット VPN など」と回線メニューの 1 つとして挙げています。本節はその中身を開けます。

何が IP 転送と違うのか

第 3 章で扱った IP ルーティングでは、ルータは受け取ったパケットの宛先 IP アドレスを見て、経路表の中からいちばん長く一致するプレフィックスを選びます (最長一致)。MPLS の網の中では、これが変わります。入口のルータがパケットに固定長の番号を貼り、以降のルータはその番号だけを見て転送します。宛先 IP アドレスは読みません。

この図を大きく開く ↗

同じパケットを 2 通りに引く。左は入口の R1 で、宛先 10.0.4.20 を IP の転送表から最長一致で引き、次ホップを決めるのと同時にラベル 19 を貼る。右はその先の R2 で、届いたラベル 19 を LFIB から完全一致で引き、20 に付け替えて送り出す。R2 はこのパケットの宛先 IP を一度も読まない。違うのは引き方 (可変長プレフィックスの最長一致か、固定長 20 ビットの完全一致か) と、見る対象 (宛先 IP かラベルか) の 2 点である。

ここで「ラベルのほうが速いから MPLS が生まれた」と要約したくなりますが、その言い方は原典に無いものを足しています。MPLS のアーキテクチャを定義した RFC 3031 §2.1 は、この方式の利点を 5 つ挙げています。1 つ目はこうです。

MPLS forwarding can be done by switches which are capable of doing label lookup and replacement, but are either not capable of analyzing the network layer headers, or are not capable of analyzing the network layer headers at adequate speed. — RFC 3031 §2.1

「ネットワーク層ヘッダを解析できない、あるいは十分な速度で解析できない装置でも、ラベルの検索と置換ならできる」— 速度で勝るという話ではなく、当時のハードウェアでの実行可能性の話です。

そして 5 つ目には、こう書かれています。

Sometimes it is desirable to force a packet to follow a particular route which is explicitly chosen at or before the time the packet enters the network, rather than being chosen by the normal dynamic routing algorithm as the packet travels through the network. This may be done as a matter of policy, or to support traffic engineering. — RFC 3031 §2.1

経路を明示的に選ぶこと (トラフィックエンジニアリング) は、後から見つかった応用ではなく、最初から挙げられている動機です。 残りの 3 つも「入口で FEC を決められる」「入口ごとに違う扱いができる」「分類がどれだけ複雑になっても中継ルータには波及しない」で、いずれも入口で決めて中では番号だけ見るという構造から出てきます。

一方、顧客ごとの経路表を運ぶ用途 (MPLS L3VPN) は、この 5 つには入っていません。別の RFC が定める応用で、§12 で予告します。

本節では、csr1000v 4 台で組んだ MPLS 網の上で、ラベルは誰が決めて、どこで付け替わり、どこで外れるのかを、実機の show 出力で 1 本の経路について追い切ります。

2. 登場人物と 1 パケットの旅

2.1 用語は 2 つの軸に分かれる

MPLS の説明でつまずきやすいのは、装置の種別を表す語その経路上での位置を表す語が混ざって出てくる点です。この 2 つは別の軸です。

意味
装置の種別LSR (Label Switching Router)ラベルを扱えるルータ全般
装置の種別LER (Label Edge Router)MPLS 網の縁にいて、IP の世界と接する LSR
経路上の位置ingressその LSP の入口。ラベルを貼る (push)
経路上の位置transit途中。ラベルを付け替える (swap)
経路上の位置penultimate出口の 1 つ手前。条件が揃えばここでラベルを外す (pop・§7)
経路上の位置egressその LSP の出口

位置は装置の属性ではなく、LSP ごとに決まります。 本節のラボでは R3 は「出口の 1 つ手前」ですが、向きが逆の経路では途中の 1 台にすぎません。同じ機器が経路によって別の役を演じます。

残りの用語も並べておきます。

  • FEC (Forwarding Equivalence Class): 同じ扱いを受けるパケットの集合。本節では「宛先が 10.0.4.0/24 のパケット」がひとまとまりの FEC です。
  • LSP (Label Switched Path): ある FEC のパケットが通る、ラベルで作られた片方向の道。
  • LDP (Label Distribution Protocol): 隣り合う LSR どうしがラベルを配り合うプロトコル。

2.2 1 パケットの旅

用語を並べただけでは動きが見えないので、先に 1 つのパケットを最後まで追います。数値はすべてこのあと実機で確かめるものです。

この図を大きく開く ↗

PC1 が出した素の IP パケットが PC2 へ届くまで。R1 は宛先 IP を最長一致で引き、次ホップ R2 が配ってきたラベル 19 を貼る (push)。R2 は届いたラベル 19 を完全一致で引き、次の R3 が配った 20 に付け替える (swap)。R3 は出口の R4 から implicit-null を受け取っているので、付け替えではなくラベルを外して渡す (pop)。R4 は素の IP として顧客セグメントへ転送する。貼られる値は毎ホップ「下流の LSR が配った値」であり、R1 と R3 がどちらも自分のラベルを 20 と決めていても互いに無関係である。

要点は 1 つです。ラベルの値を決めるのは、そのパケットを受け取る側 (下流) の LSR で、送る側 (上流) はそれをそのまま使います。

3. ラベルの中身

ラベルは 32 ビットのかたまりで、データリンク層ヘッダの後ろ、ネットワーク層ヘッダの手前に入ります。RFC 3032 の記述はそのまま次のとおりです。

The label stack entries appear AFTER the data link layer headers, but BEFORE any network layer headers. — RFC 3032 §2.1

L2 でも L3 でもない位置にあることから、L2.5 と呼ばれます。5-2 マーキング体系 で「L2 と L3 の中間に位置づけられる L2.5 の MPLS ラベル」と触れたのは、この構造のことです。

この図を大きく開く ↗

MPLS ラベルの位置と 32 ビットの内訳。上段はラベルが入る位置で、L2 ヘッダの後・L3 ヘッダの前にあることから L2.5 と呼ばれる。下段は内訳をビット数に比例した幅で描いたもので、Label が 20 ビット、Traffic Class (旧 EXP) が 3 ビット、Bottom of Stack が 1 ビット、TTL が 8 ビット。予約ラベルは 0 が IPv4 Explicit NULL、1 が Router Alert、2 が IPv6 Explicit NULL、3 が Implicit NULL。
フィールドビット数役割
Label20転送に使う番号
Traffic Class (旧 EXP)3優先度の印
S (Bottom of Stack)1このラベルが最下段かどうか
TTL8ループ防止のホップ数。IP ヘッダの TTL とは別物

3.1 3 ビットの印 — 5-2 で預けた宿題

5-2 では、優先度の印が階層ごとに別の場所にあること (L2 は 802.1Q タグ内の CoS、L3 は IP ヘッダの DS フィールド、L2.5 は MPLS の 3 ビット) を確認し、その 3 ビットの詳細は本章に預けてありました

まず名前です。RFC 3032 はこのフィールドを EXP (Experimental Use) と定義していましたが、実験用という名前が誤解を招いたため、RFC 5462 が TC (Traffic Class) へ改名しました。改名の理由は原文にこう書かれています。

The designation “for experimental use” has led other Standards Development Organizations (SDOs) and implementors to assume that it is possible to use the field for other purposes. This document changes the name of the field to clearly indicate its use as a traffic classification field. — RFC 5462 §1 Introduction

ただし Cisco の CLI と show 出力は現在も Exp と表示します。 本節の後半で出てくる [MPLS: Label 19 Exp 0] がその表記です。規格上は TC、機器の画面上は Exp と覚えておくと、実機を触ったときに戸惑いません。

次に幅です。3 ビットは 8 通りしか表せません。 5-2 で扱った DSCP は 6 ビット 64 通りですから、DSCP をそのままラベルの印へ写すことはできず、64 通りを 8 通りへ畳むことになります。

ここで大事なのは、畳む動作は設定しなくても既に起きていることです。本ラボでは TC に関する設定を一切していません。その状態で印を付けたパケットを流し、ラベルが乗っている区間 (R1 → R2) を捕まえてラベルの中身を読むと、こうなります。

snippet
! R1 から ping 10.0.4.20 source Loopback0 tos 160 (IP precedence 5) を 5 回
! R2 の受信側でキャプチャした MPLS フレームの先頭
0000:  525400E0 29E05254 00E45F31 88470001
0010:  3BFF45A0 ...
!
! 先頭から数えて 0x0C-0x0D = 88 47        … イーサタイプ (MPLS unicast)
!             0x0E-0x11 = 00 01 3B FF  … 32 ビットのラベルエントリ
!               → Label = 19 / TC(EXP) = 5 / S = 1 / TTL = 255  (5 パケットとも同じ)
!             0x12 以降 = 45 A0 ...      … ここから内側の IP ヘッダ

送った IP パケットの precedence は 5 で、ラベルの TC も 5 になっています。

この測定には限界が 2 つあります。1 つは、印を付けたのが R1 自身が出したパケットであること (端末側の ping に ToS を指定する手段が本ラボの環境に無いため)。端末から流したトラフィックで同じことが起きるかは確かめていません。もう 1 つは、比較対象として precedence 0 のラベルも撮ろうとしたのですが、コマンドが受け付けられず precedence 0 側のラベル値は取れていないことです。

DSCP の上位 3 ビット (= IP precedence) が、何も設定しなくてもそのまま TC へ写ります。 ここで注意したいのは、§8 以降に出てくる Exp 0 は、この動きの証拠にも反証にもならないことです。Exp 0 は「写像が起きていない」でも「precedence 0 を写した結果」でも同じ値になります。印の付いていないトラフィックをいくら見ても、この 2 つは区別できません。 上の測定に意味があるのは、印を付けた側を撮ったからです。

写るのは上位 3 ビットだけです。ただし顧客の IP ヘッダそのものは書き換わりません。 上のキャプチャでラベルの直後に続く内側 IP ヘッダは 45 A0 で始まっており、送ったときの ToS 0xA0 (DSCP 40) がそのまま残っています。分解能が落ちるのは「ラベルへ写したコピー」のほうで、5-2 で扱った DSCP は IP ヘッダに乗ったまま網を通り抜けます。

網の中の LSR が見るのは 3 ビットに畳まれたコピーなので、網の中でどこまで細かく扱い分けられるかがここで決まります。ただし §7 で見るとおり出口の 1 つ手前でラベルは外れるので、このコピーを見られるのはラベルが乗っている区間だけです。どう写すか、出口で何をするかは事業者と顧客の間で決める設計事項で、本節では扱いません。

3.2 S ビット — ラベルは積める

S ビットが 1 なら、そのラベルが最下段で、次に来るのは L3 ヘッダです。0 なら、その下にもう 1 枚ラベルがあります。つまりラベルは重ねられます。 本節はグローバルな経路表だけを扱うので登場するラベルは常に 1 枚ですが、S ビットが存在する理由は重ねられることにあります。2 枚重ねの実物は 6-2 で扱います。

3.3 予約ラベル

0 から 15 までは特別な用途に予約されています。RFC 3032 §2.1 が定義しているのは次の 4 つです。

名前
0IPv4 Explicit NULL Label
1Router Alert Label
2IPv6 Explicit NULL Label
3Implicit NULL Label

このうち 3 の Implicit NULL が本節の後半で主役になります。RFC 3032 の定義はこうです。

A value of 3 represents the “Implicit NULL Label”. This is a label that an LSR may assign and distribute, but which never actually appears in the encapsulation. — RFC 3032 §2.1

「配ることはあるが、実際のパケットには決して現れない」ラベルです。何のためにあるのかは §7 で確かめます。

残りの 4 から 15 について、RFC 3032 は「予約」とだけ書いていました。その後 RFC 7274 が登録簿を Special-Purpose MPLS Label Values と改称し、その前後で 4 (RFC 9994)・7 (RFC 6790)・13 (RFC 5586)・14 (RFC 3429)・15 (RFC 7274) に用途が割り当てられています (割り当てた RFC は値ごとに違い、RFC 7274 自身が割り当てたのは 15 です)。「4 以降は未使用」で止めると 2001 年時点の知識になります。

4. ラボ構成

csr1000v を 4 台つないで MPLS 網を作り、両端に検証用の端末を置きます。4 台とも同じソフトウェアです。

snippet
Cisco IOS XE Software, Version 17.03.08a

以降に出てくる出力・既定値は、すべてこのバージョンで実測したものです。

この図を大きく開く ↗

ラボ構成。csr1000v 4 台を OSPF area 0 と LDP で結び、両端に顧客セグメントをぶら下げた。10.0.4.0/24 宛の LSP では R1 が ingress、R2 が transit、R3 が penultimate、R4 が egress になる。R1-R3 のバックアップリンクは OSPF コスト 100 で既定では通らないが、LDP 隣接は張られているので R1 の LIB には R3 が配ったラベルも入っている。顧客セグメント (R1 Gi2 / R4 Gi3) には mpls ip を入れていないため、ラベルが乗るのは MPLS 網の中の区間だけである。
ノード役割Loopback0主なインタフェース
R1ingress LER1.1.1.1/32Gi2 = 10.0.1.1/24 (顧客側) / Gi3 = 10.10.12.1/30 (→R2) / Gi4 = 10.10.13.1/30 (→R3)
R2transit LSR2.2.2.2/32Gi2 = 10.10.12.2/30 / Gi3 = 10.10.23.1/30
R3penultimate LSR3.3.3.3/32Gi2 = 10.10.23.2/30 / Gi3 = 10.10.34.1/30 / Gi4 = 10.10.13.2/30
R4egress LER4.4.4.4/32Gi2 = 10.10.34.2/30 / Gi3 = 10.0.4.1/24 (顧客側)
PC1送信側の端末10.0.1.10/24
PC2受信側の端末10.0.4.20/24

4.1 設定は 3 種類に分けて考える

インタフェースは役割ごとに設定が違います。この区別を間違えると、エラーは出ないのに MPLS が成立しません。

掲載する設定は実機の show running-config interface からの抜粋です。

snippet
interface GigabitEthernet3
 description to R2 Gi2 (core / primary)
 ip address 10.10.12.1 255.255.255.252
 ip ospf 1 area 0
 ip ospf cost 3
 negotiation auto
 mpls ip
 no mop enabled
 no mop sysid
end

コア向けのインタフェースには ip ospfmpls ip の両方を入れます。一方、顧客セグメント側は次のようになります。

snippet
interface GigabitEthernet2
 description PC1 LAN (customer side / no MPLS)
 ip address 10.0.1.1 255.255.255.0
 ip ospf 1 area 0
 negotiation auto
 no mop enabled
 no mop sysid
end

mpls ip がありません。ラベルを載せるのは網の中の区間だけで、顧客が受け取るのは素の IP パケットです。ただし ip ospf 1 area 0 は入れます。ここを外すと 10.0.1.0/24 が IGP に載らず、PC2 から PC1 への復路が消えます。本節が主に追う 10.0.4.0/24 の経路は R4 Gi3 側なので、そちらを OSPF から外すと今度は追う対象そのものが無くなります。顧客側のインタフェースは、MPLS には入れないが IGP には入れる、という扱いです。

Loopback0 にも ip ospf 1 area 0 が要ります。理由は次項の LDP セッションにあります。グローバル側は次の 2 か所です。

snippet
router ospf 1
 router-id 1.1.1.1
snippet
mpls ldp router-id Loopback0 force

上の 2 つを別々に撮っているのには理由があります。show running-config | include ^mpls|^router ospf のように行頭一致で抜き出すと、router ospf 1 の子行である router-id 1.1.1.1 が落ちます| include は行単位のフィルタで、ブロックの中身までは連れてこないためです。掲載用には | section router ospf のようにブロックごと撮るほうが安全です。

この抜粋に mpls label protocol ldp が出てこないのは、IOS-XE の既定のラベル配布プロトコルが LDP で、既定値は running-config に表示されないためです (本ラボでは明示的に投入していますが、表示されません)。書いても構いませんが、必須の 1 行ではありません。

mpls ldp router-id Loopback0 force の末尾の force は、「安定させる」という意味ではなく その場で即座に反映するという意味です。R2 で試すと、こうなります。

snippet
! (1) 開始時 — mpls ldp router-id Loopback0 force
 Local LDP Identifier:
    2.2.2.2:0
! (2) mpls ldp router-id GigabitEthernet2 force を投入した直後
 Local LDP Identifier:
    10.10.12.2:0
! (3) mpls ldp router-id Loopback0 force で戻した後
 Local LDP Identifier:
    2.2.2.2:0
	State: Oper; Msgs sent/rcvd: 13/13; Downstream
	State: Oper; Msgs sent/rcvd: 13/13; Downstream

指定を変えた瞬間に識別子が入れ替わっています。 確かめられたのはここまでで、セッションが実際に張り直されたかどうかは、この観測だけでは言えません (Msgs sent/rcvd は機器ごと・セッションごとの累積値なので、別の機器や別の時刻の値と比べても意味がありません)。

この 1 行をまったく書かなかったときに何が選ばれるかは、本ラボでは確かめていません。分かっているのは、識別子が LDP の接続先アドレスそのものになることと、それを設定で固定できることです。

4.2 LDP セッションを確認する

snippet
    Peer LDP Ident: 3.3.3.3:0; Local LDP Ident 1.1.1.1:0
	TCP connection: 3.3.3.3.22186 - 1.1.1.1.646
	State: Oper; Msgs sent/rcvd: 19/19; Downstream
	Up time: 00:05:59
	LDP discovery sources:
	  GigabitEthernet4, Src IP addr: 10.10.13.2
        Addresses bound to peer LDP Ident:
          3.3.3.3         10.10.23.2      10.10.34.1      10.10.13.2      

R1 から見た R3 とのセッションです。読みどころが 3 つあります。

  • Local LDP Ident 1.1.1.1:0 — 識別子が Loopback0 のアドレスになっています。この識別子へ TCP で接続するので、Loopback0 が IGP に載っていないとセッションが張れません。4.1 で Loopback0 を OSPF に入れたのはこのためです。
  • TCP connection: ... - 1.1.1.1.646 — LDP セッションは TCP の 646 番で張られます。
  • GigabitEthernet4 — これは OSPF コスト 100 を入れたバックアップリンクです。既定では通らない経路でも、LDP のセッションは張られています。 この事実が §6 で効いてきます。

show mpls interfaces で、MPLS が有効なインタフェースを確認できます。

snippet
Interface              IP            Tunnel   BGP Static Operational
GigabitEthernet3       Yes (ldp)     No       No  No     Yes        
GigabitEthernet4       Yes (ldp)     No       No  No     Yes        

R1 で有効なのは Gi3 と Gi4 だけで、顧客側の Gi2 と管理用の Gi1 は並んでいません。設計どおりです。

ラベルの割当範囲も見ておきます。

snippet
Downstream Generic label region: Min/Max label: 16/1048575

予約されている 0 から 15 を避けて 16 から始まります。この先に出てくるラベル値が 19 や 20 なのは、範囲の先頭付近から順に払い出されているからです。

5. ラベルは誰が決めるのか

宛先 10.0.4.0/24 に対して、4 台がそれぞれ自分のラベルをどう決めたかを並べます。show mpls ldp bindingslocal binding が「自分が配った値」です。

LSRlocal binding
R120
R219
R320
R4imp-null

R1 と R3 がどちらも 20 を選んでいます。 これは珍しいことではありません。本ラボで LDP が配ったラベルをすべて並べると、10 個の FEC のうち 4 つで R1 と R3 の値が一致していました (数値の一致が 3 つ、imp-null どうしの一致が 1 つ)。4 台は同じ機種・同じソフトウェアで、同じ範囲の先頭から順に払い出しているので、こうした一致は珍しくありません。ただし揃うのが普通というわけでもなく、一致するかどうかは規格の保証外です。

そもそも規格は、ラベルの値を「その場限りのもの」として定義しています。

A label is a short, fixed length, locally significant identifier which is used to identify a FEC. — RFC 3031 §3.1

locally significant — 意味を持つ範囲がその場に限られる、という一語がすべてです。R1 の 20 は R1 の表の中でだけ意味を持ち、R3 の 20 とは何の関係もありません。値が一致するかどうかは規格の関心事ですらなく、どう割り当てるかは各 LSR に委ねられています。

したがって「ラベルの値が機器ごとに違う」ことを、ローカルな意味しか持たないことの証拠にはできません。 示すべきなのは値の相違ではなく、値の帰属です。

5.1 上流は下流が配った値を使う

その帰属を確かめるには、各 LSR の転送表 (LFIB) と、隣の LSR の local binding を突き合わせます。

snippet
! ===== [R1] show mpls forwarding-table 10.0.4.0 24 =====
Local      Outgoing   Prefix           Bytes Label   Outgoing   Next Hop    
Label      Label      or Tunnel Id     Switched      interface              
20         19         10.0.4.0/24      0             Gi3        10.10.12.2  
! ===== [R2] show mpls forwarding-table 10.0.4.0 24 =====   (以降は同じ列見出しなので省略)
19         20         10.0.4.0/24      1420          Gi3        10.10.23.2  
! ===== [R3] show mpls forwarding-table 10.0.4.0 24 =====
20         Pop Label  10.0.4.0/24      1864          Gi3        10.10.34.2  
! ===== [R4] show mpls forwarding-table 10.0.4.0 24 =====
None       No Label   10.0.4.0/24      0             punt

読み方は「Local Label で入ってきたら、Outgoing Label に付け替えて Outgoing interface から出す」です。

  • R1 の Outgoing Label は 19。これは R2 の local binding と同じ値です。
  • R2 の Outgoing Label は 20。これは R3 の local binding と同じ値です。

貼る値は毎回、次に渡す相手が配ってきたものです。 連鎖が 2 段そろっているので、これは偶然の一致ではありません。

5.2 入口でラベルを貼るのは LFIB ではない

R1 の LFIB には行がありますが、Bytes Label Switched は 0 のままです。これは動いていないという意味ではありません。この列は「ラベル付きで入ってきたパケット」を数えるもので、R1 に入ってくるのは PC1 からの素の IP パケットだからです。

なお Bytes Label Switched累積カウンタなので、撮った時刻が違えば値も変わります (本節の別の場所には同じ R2 の行が別の値で出てきます)。本節はこの列を主張の根拠に使いません。

では R1 はどこでラベルを貼っているのか。IP の転送表 (CEF) の側です。

snippet
10.0.4.0/24, epoch 2
  dflt local label info: global/20 [0x3]
  nexthop 10.10.12.2 GigabitEthernet3 label 19-(local:20)

最後の 1 行に両方が書かれています。label 19 が貼る値 (R2 の値)、(local:20) が R1 自身が配っている値です。ラベルを貼る判断は宛先 IP の最長一致と同時に行われ、その結果が CEF のエントリに焼き込まれています。

6. 配られた全部と、いま使う 1 本

ここまで local binding だけを見てきましたが、show mpls ldp bindings にはもう 1 種類の行があります。R1 の出力をそのまま示します。

snippet
  lib entry: 10.0.4.0/24, rev 18
	local binding:  label: 20
	remote binding: lsr: 2.2.2.2:0, label: 19
	remote binding: lsr: 3.3.3.3:0, label: 20

remote binding が 2 行あります。 R2 (2.2.2.2) から 19、R3 (3.3.3.3) から 20。R3 経由はコスト 100 のバックアップ経路で、いま使っていません。それでも広告は届き、R1 はそれを捨てずに持っています。

この表を LIB (Label Information Base) と呼びます。一方、§5.1 で見た show mpls forwarding-table の表は LFIB (Label Forwarding Information Base) です。同じ FEC について、LIB には 3 行、LFIB には 1 行しかありません。

この図を大きく開く ↗

LIB と LFIB の関係。R1 は 10.0.4.0/24 に対して R2 から 19、R3 から 20 の 2 つの広告を受け取り、どちらも LIB に保持している。一方 LFIB が使うのは最短経路の隣接が配った 1 本だけである。後半ではバックアップリンクの OSPF コストを 100 から 1 に下げて経路を切り替え、LIB は 3 行とも変わらないまま LFIB の出力ラベルと出力インタフェースだけが入れ替わる様子を見る。切替後の LFIB は Local 20 / Outgoing 20 になるが、左の 20 は R1 が配った値、右の 20 は R3 が配った値で、同じ数字が別のものを指している。

LIB は「誰が何を配ってきたか」の記録、LFIB は「いまどれを使うか」の判断です。 使わない広告を捨てずに持っておくことで、経路が変わったときに配り直しを待たずに切り替えられます。

なお R3 の LIB はもっと賑やかで、remote binding が 3 件あります。出口側の R4 からの広告に加えて、上流の R1 と R2 が配った値まで持っています。LDP は隣接している相手すべてにラベルを配るので、こうなります。

この記事で扱っている範囲について: ここで見た「最短経路でない隣接からの広告も保持する」という動作は、RFC 5036 が liberal label retention と呼ぶ振る舞いに一致します。ただし IOS-XE の既定がそれであると明記した Cisco の資料は見つけられていません (明記があるのは IOS-XR の資料です)。本節はあくまで「この機器・このバージョンで実測するとこう動いた」として記録します。

7. 出口の手前でラベルを外す — PHP

R4 の local binding だけが数値ではなく imp-null でした。もう一度、R4 の出力を見ます。

snippet
  lib entry: 10.0.4.0/24, rev 6
	local binding:  label: imp-null
	remote binding: lsr: 3.3.3.3:0, label: 20

imp-null は §3.3 で見た予約ラベル 3 の Implicit NULL です。R4 はこれを R3 へ配っています。受け取った側の R3 の LFIB はこうなります。

snippet
Local      Outgoing   Prefix           Bytes Label   Outgoing   Next Hop    
Label      Label      or Tunnel Id     Switched      interface              
20         Pop Label  10.0.4.0/24      1864          Gi3        10.10.34.2  

Outgoing Label の列が Pop Label になっています。 付け替える値がないので、R3 はラベルを外して素の IP パケットとして R4 へ渡します。この動作を PHP (Penultimate Hop Popping、最後から 2 番目のホップでのラベル除去) と呼びます。

紛らわしい略語: 5-2 で扱った PHB (Per-Hop Behavior) とは別物です。PHB は「印を見た機器がどう扱うか」の規定、PHP は「出口の 1 つ手前でラベルを外す」という転送の動作です。

外した結果、R4 は同じ FEC についてこうなります。

snippet
None       No Label   10.0.4.0/24      0             punt

Local Label が None です。R4 はこの FEC に数値のラベルを割り当てていないので、ラベルで引く表に入る余地がありません。届いたときにはもうラベルは無く、普通の IP パケットとして処理されます。

7.1 出口の手前で外す理由

出口の R4 で外しても動作しますが、その場合 R4 は「ラベルを外す」処理と「宛先 IP を引く」処理を続けて行うことになります。手前で外しておけば、R4 は届いた素の IP パケットを 1 回引くだけで済みます。出口の仕事を 1 回分減らすのが PHP の狙いです。

7.2 imp-null が配られる条件

R4 が imp-null を配ったのは、10.0.4.0/24 が R4 に直結しているプレフィックスだからです。R4 の Loopback0 である 4.4.4.4/32 も同様です。

snippet
  lib entry: 4.4.4.4/32, rev 2
	local binding:  label: imp-null
	remote binding: lsr: 3.3.3.3:0, label: 19

「出口の LSR は常に imp-null を配る」ではありません。 本ラボで imp-null が付いていたのは、その LSR に直結しているプレフィックスだけでした (4 台分の show mpls ldp bindings を全件見て確認しています)。

規格の側は、ラベルを 1 つ手前で外す条件をこう定めています。

the penultimate node pops the label stack only if this is specifically requested by the egress node, OR if the next node in the LSP does not support MPLS — RFC 3031 §3.16

imp-null の広告が、この「egress からの要求」にあたります。どのプレフィックスについて要求するかは実装の判断で、本ラボの IOS-XE ではそれが直結のプレフィックスと一致していました。

8. 通しで見る

ここまで各機器の表を個別に見てきました。1 本の経路として通しで見るには、入口の R1 から traceroute を打ちます。

snippet
Type escape sequence to abort.
Tracing the route to 10.0.4.20
VRF info: (vrf in name/id, vrf out name/id)
  1 10.10.12.2 [MPLS: Label 19 Exp 0] 3 msec
  2 10.10.23.2 [MPLS: Label 20 Exp 0] 2 msec
  3 10.10.34.2 4 msec
  4 10.0.4.20 3 msec

各行のラベル表示は、そのホップに届いた時点でパケットが背負っていたラベルです。

  • 1 行目 (R2 に届いた時点): ラベル 19 — R1 が貼った値
  • 2 行目 (R3 に届いた時点): ラベル 20 — R2 が付け替えた値
  • 3 行目 (R4 に届いた時点): ラベル表示なし — R3 が外した後
  • 4 行目: 宛先の PC2

§5 から §7 で個別に見たものが、そのまま 1 本に並んでいます。 末尾の Exp 0 については §3.1 のとおりで、この表示だけでは「印が付いていない」のか「precedence 0 が写った」のかは区別できません (上の traceroute は ToS を指定していません)。

2 点、読み方の注意があります。

  • この表示は、TTL の伝播が既定のまま有効であることを前提にしています。 次の §9 でこの前提を外すと、この表示自体が変わります。
  • 各行の時間を距離として読まないでください。 同じ経路を測り直すだけで値は動きます (この節の 3 ホップ目は 4 msec ですが、§9.1 に載せた同じ経路の測定では 2 msec でした)。本ラボの規模では、時間の列から読み取れることはほとんどありません。

9. TTL の伝播は制御できる

§3 で見たとおり、ラベルには IP ヘッダとは別に 8 ビットの TTL があります。既定では、入口でラベルを貼るときに IP の TTL がラベルの TTL へ写され、ラベルを外すノードで IP ヘッダへ書き戻されます。本節のラボは PHP が効いているので、外すのは出口の R4 ではなく その 1 つ手前の R3 です (§7)。その結果、MPLS 網の中の各ホップが、外から見ても 1 ホップとして数えられます

顧客側の PC1 から traceroute を打つと、既定ではこう見えます。

snippet
traceroute to 10.0.4.20 (10.0.4.20), 8 hops max, 46 byte packets
 1  10.0.1.1  0.553 ms
 2  10.10.12.2  2.477 ms
 3  10.10.23.2  1.696 ms
 4  10.10.34.2  2.371 ms
 5  10.0.4.20  2.390 ms

R1・R2・R3・R4 が全部見えています。事業者の立場からすると、顧客に網の中の構成が見えている状態です。これを止める設定が no mpls ip propagate-ttl です。

入口の R1 に no mpls ip propagate-ttl forwarded を入れると、顧客側の見え方はこうなります。

snippet
traceroute to 10.0.4.20 (10.0.4.20), 8 hops max, 46 byte packets
 1  10.0.1.1  0.535 ms
 2  10.10.34.2  2.175 ms
 3  10.0.4.20  2.349 ms

消えたのは中継の R2 (10.10.12.2) と R3 (10.10.23.2) です。 入口の R1 と出口の R4 (10.10.34.2) は見えたままです。「コアが丸ごと消える」のではなく、通り抜けるだけの LSR が数えられなくなる、という変化です。

9.1 forwarded を付けるかどうかで、誰から隠れるかが変わる

forwarded を付けた状態で、R1 自身から traceroute を打つとこうなります。

snippet
  1 10.10.12.2 [MPLS: Label 19 Exp 0] 3 msec
  2 10.10.23.2 [MPLS: Label 20 Exp 0] 2 msec
  3 10.10.34.2 2 msec
  4 10.0.4.20 3 msec

ホップの並びもラベル表示も変わっていません (時間だけは測るたびに動くので、§8 とは 3 ホップ目が違います)。顧客が通す (転送される) パケットからは隠れ、事業者自身が出したパケットからは見える、という状態です。

引数なしの no mpls ip propagate-ttl に変えると、R1 自身から打った結果も変わります。

snippet
  1 10.10.34.2 2 msec
  2 10.0.4.20 3 msec

事業者からも見えなくなりました。 運用上は、顧客からは隠しつつ自分の切り分け手段は残したい場面が多いので、この違いは実務的です。

9.2 受信側で TTL を直接読む

traceroute で見えるホップ数は、TTL がいくつ減ったかの間接的な表れです。実際の値を見るために、受信側の PC2 で届いたパケットの IP ヘッダを直接読みました。送出時の TTL は §9.3 で別途実測しており、64 です。

設定PC2 に届いたときの IP TTL
既定60
R1 に no mpls ip propagate-ttl62
R1 と R3 の両方に投入62
既定へ復旧60

既定では 4 減り、伝播を止めると 2 減ります。§9 の traceroute で応答したルータが 4 台 (R1・R2・R3・R4) から 2 台 (R1・R4) へ減ったこと (forwarded 付きでも引数なしでも 2 台でした) と、TTL の減り方が同じ数を指しています。 応答した固有 IP の 5 件・3 件には宛先の PC2 が含まれますが、PC2 は TTL を減らさないので数から外しています。traceroute は応答したルータを数え、こちらは TTL の値そのものを読んでいるので、別々の読み方で同じ結論に届いた形です。 traceroute はホップを数え、こちらは TTL の値そのものを読んでいるので、別々の読み方で同じ結論に届いた形です。

9.3 どの装置が TTL を減らしているのか

上の表は「PC2 に届いた時点」の値なので、網の中のどこで減ったのかは見えません。そこで R4 の受信側 (R3 からのリンク) でパケットを直接捕まえ、R3 がラベルを外した直後の IP TTL を読みました。

設定R4 に届いた時点PC2 に届いた時点
既定6160
R1 に no mpls ip propagate-ttl6362

PC1 が送り出す時点の TTL は、R1 の受信側でパケットを捕まえて 64 であることを確かめてあります。

  • 既定: R1 が IP の TTL を 63 にしてラベルへ写し、ラベル側の TTL が R2 で 62、R3 で 61 と減ります。R3 はラベルを外すときにこの 61 を IP ヘッダへ書き戻すので、R4 には 61 で届き、R4 が減らして 60 になります。網の中の各ホップがきちんと 1 ずつ数えられています。
  • 伝播を止めた場合: R1 が減らした 63 のまま R4 に届いています。 R2 と R3 は IP ヘッダの TTL に触っていません。ラベルを外した R3 も、ラベル側の TTL を IP ヘッダへ書き戻していません。

この「書き戻していない」ことが、§9.2 で TTL が 2 しか減らなかった理由です。

本ラボで確かめていないこと: Cisco の資料は、入口と出口の両方でこの設定を行うよう求めており、その理由として「MPLS 網を出るときの IP TTL が、入るときより大きくなることがある」ことを挙げています。本ラボではその症状は起きませんでした。 上のとおり書き戻しが発生していないためです。どのような条件でその症状が現れるのか (ラベルを出口の装置で外す構成、別のバージョンなど) は確かめていないので、「入口だけで十分」という一般化はしません。 出口側にも設定を入れるかどうかは、自分の構成で同じ測定をしてから決めるべき事項です。

10. 経路が変われば、使うラベルが変わる

最後に、§6 で見た LIB と LFIB の関係を動かして確かめます。バックアップリンク (R1 Gi4 と R3 Gi4) の OSPF コストを 100 から 1 へ下げます。触るのは IGP のコストだけで、LDP には何も設定しません。

まず経路が変わります。

snippet
Routing entry for 10.0.4.0/24
  Known via "ospf 1", distance 110, metric 3, type intra area
  Last update from 10.10.13.2 on GigabitEthernet4, 00:00:20 ago
  Routing Descriptor Blocks:
  * 10.10.13.2, from 4.4.4.4, 00:00:20 ago, via GigabitEthernet4
      Route metric is 3, traffic share count is 1

メトリックが 6 から 3 になり、出口が Gi3 から Gi4 へ移りました。ここで 2 つの表を見比べます。

LIB は変わりません。

snippet
  lib entry: 10.0.4.0/24, rev 18
	local binding:  label: 20
	remote binding: lsr: 2.2.2.2:0, label: 19
	remote binding: lsr: 3.3.3.3:0, label: 20

3 行とも切替前と同じです。R2 が配った 19 も、依然として持っています。

LFIB だけが入れ替わります。

snippet
Local      Outgoing   Prefix           Bytes Label   Outgoing   Next Hop    
Label      Label      or Tunnel Id     Switched      interface              
20         20         10.0.4.0/24      0             Gi4        10.10.13.2  

Outgoing Label が 19 から 20 へ、Outgoing interface が Gi3 から Gi4 へ変わりました。LIB は配布の記録なので変わらず、LFIB は現在の判断なので変わる、という非対称がそのまま出ています。

10.1 同じ 20 が 2 つの別のものを指している

切替後の LFIB の行をもう一度見てください。Local Label が 20、Outgoing Label も 20 です。

  • 左の 20 = R1 自身が配った値 (上流から入ってくるときの鍵)
  • 右の 20 = R3 が配った値 (下流へ出ていくときに貼る値)

§5 で見た「R1 と R3 がどちらも 20 を選んだ」という重なりが、ここで 1 行の中に並びました。左の 20 と右の 20 は、それぞれ別の LSR が配った値です。§5.1 で追った帰属の連鎖が、切替後は R1 と R3 のあいだで閉じている、というだけの行です。

10.2 ラベルを付け替えるホップが消えた

切替後に R1 から traceroute を打つと、こうなります。

snippet
  1 10.10.13.2 [MPLS: Label 20 Exp 0] 7 msec
  2 10.10.34.2 2 msec
  3 10.0.4.20 3 msec

ラベル表示のある行が 1 つだけになりました。R2 がこの LSP から外れたためです。経路が R1 → R3 → R4 の 2 ホップになったことで、R1 の次が penultimate の R3 になり、付け替え (swap) を行うホップ自体が無くなりました

では R2 側はどうなったのか。切替の前後で R2 の表を撮ると、こうなります。

snippet
! ===== [R2] show mpls ldp bindings 10.0.4.0 24 =====   (切替の前後で同一)
  lib entry: 10.0.4.0/24, rev 14
	local binding:  label: 19
	remote binding: lsr: 1.1.1.1:0, label: 20
	remote binding: lsr: 3.3.3.3:0, label: 20
! ===== [R2] show mpls forwarding-table 10.0.4.0 24 =====   (切替の前後で同一)
Local      Outgoing   Prefix           Bytes Label   Outgoing   Next Hop    
Label      Label      or Tunnel Id     Switched      interface              
19         20         10.0.4.0/24      7814          Gi3        10.10.23.2  

何も変わっていません。 R2 は自分のラベル 19 を配り続け、19 で入ってきたら 20 に付け替えて出す、という用意もそのまま持っています。変わったのは「この LSP が R2 を通らなくなった」ことだけで、それは上の traceroute が示しています。

R2 は装置としては LSR のままで、失ったのは「この LSP 上での位置」だけです。§2.1 で位置と装置の種別を分けて整理したのは、この場面のためです。

11. 落とし穴・補足

  • 等コストになると LFIB の行が増えます。 本ラボは R1–R2 区間のコストを 3 に置いてあります。(本ラボでは試していない机上の計算ですが) 全リンクを既定のコスト 1 にすると、§10 の切替後に 10.10.23.0/30 が via R2 = 2 / via R3 = 2 の等コストになり、LFIB に 2 行現れる計算になります。「使うのは 1 本」という説明と食い違って見えるので、対比を見せたいときは等コストを避けた設計にしておくと読みやすくなります。本ラボでは切替後に R1 の主要な 5 つのプレフィックス (10.0.4.0/24・10.10.23.0/30・10.10.34.0/30・2.2.2.2/32・4.4.4.4/32) を確認し、いずれも出口が 1 つでした。
  • 設定の抜き出し方で中身が落ちます。 show running-config | section mpls だと mpls ip はインタフェース設定の中の 1 行なので、interface の行が付かず、どのインタフェースの設定か分からない裸の行が並びます。| include ^router ospf のような行頭一致では、子行の router-id が落ちます (§4.1)。掲載用には show running-config interface <IF>| section router ospf のように、ブロックごと撮るほうが確実です。
  • traceroute の時間を距離として読まないでください (§8 の再掲)。同じ経路を測り直すだけで値が動きます。
  • ping が表示する TTL は環境によって当てになりません。 本ラボの端末 (BusyBox) では、1 ホップ先を叩いても 4 ホップ先を叩いても同じ値が表示されました。TTL を根拠にするなら、§9.2 のように受信側でパケットを直接読むほうが確実です。
  • 予約ラベルは 4 以降も割り当てが進んでいます (§3.3)。RFC 3032 だけを見て「4 から 15 は未使用」と覚えると古い知識になります。
  • mpls label protocol ldp は IOS-XE では既定です (§4.1)。設定例に入っていても「必須の 1 行」ではありません。

12. 次節

本節では、ラベルで転送するとはどういうことかを 1 本の経路について追いました。ラベルの値は下流の LSR が決め、上流はそれを使うこと、配られた広告を全部持つ LIB と使う 1 本だけを持つ LFIB が別物であること、出口の 1 つ手前でラベルが外れること、経路が変わると LFIB だけが入れ替わることを、実機の出力で確かめました。ここまでのラベルは常に 1 枚で、運んでいたのはグローバルな経路表の宛先です。

次節 6-2 MPLS L3VPN では、この仕組みの上に顧客ごとの経路表を載せます。顧客ごとに経路表を分ける VRF、経路を区別するための RD、どの経路をどの顧客に配るかを決める RT、そして事業者側の PE と顧客側の CE の関係が登場します。§3.2 で触れた「ラベルは積める」が、そこで実際の形を取ります。