MPLSのラベル操作(push / swap / pop)とPHP
MPLSのルータ(LSR)がパケットに対して行う操作は、ラベルを積むpush、先頭のラベルを付け替えるswap、先頭のラベルを外すpopの3つです。本記事では、1つのパケットが入口のPEから出口のPEまで運ばれる間に各ルータがどの表を引いて何をするか、出口の1つ手前でラベルを外すPHP(Penultimate Hop Popping)とは何かを解説し、IOS XR(XRd)のラボで1ホップずつ確かめます。ラベルの形式はMPLSラベルとラベルスタックで解説しています。
3つの操作と2つの表
| 操作 | 行うLSR | 内容 |
|---|---|---|
| push | 入口LSR(PE1) | ラベルの付いていないIPパケットを受け取り、宛先で決まるFECのラベルを積んで送り出す。 |
| swap | 中継LSR(P1) | 先頭のラベルを、次のLSRから受け取ったラベルに付け替える。 |
| pop | 出口LSRの1つ手前(P2)または出口LSR | 先頭のラベルを外す。外した結果ラベルが無くなれば、IPパケットとして扱う。 |
どの表を引くかは、受け取ったパケットにラベルが付いているかで決まります。
| 受け取ったパケット | 引く表 | IOS XRの確認コマンド |
|---|---|---|
| ラベル無し(IPパケット) | FIB。宛先IPアドレスをロンゲストマッチで検索し、結果に付いているラベルをpushする(RFC 3031のFTN) | show cef <プレフィックス> detail(labels imposed {…}) |
| ラベル付き | LFIB。先頭のラベル値を索引に、操作(swap / pop)と出力先を引く(同ILM) | show mpls forwarding(Local / Outgoing 列) |
中継LSRはIPヘッダーを見ません。先頭のラベルだけを引き、Outgoing Labelの値に付け替える(swap)か、Popなら外して送り出します。
show mpls forwardingのOutgoing Label列には、ラベル値のほかに次の表示が出ます。
| 表示 | 意味 |
|---|---|
| 数値 | その値にswapする。 |
Pop | 先頭のラベルを外す。次のLSRがこのプレフィックスに対してimplicit-null(3)を配ってきたとき。 |
Exp-Null-v4 | 先頭のラベルを0(IPv4 Explicit NULL)にswapする。次のLSRがexplicit-nullを配ってきたとき。 |
Unlabelled | ラベルを外してIPパケットとして送る。次ホップがMPLSを話さない(CE向けなど)か、次ホップからそのプレフィックスのラベルを受け取っていないとき。 |
Aggregate | ラベルを外し、改めてIP検索を行う。集約したプレフィックスやVRFの経路に使われる。 |
ラベルは1ホップごとに付け替わる
ラベルは隣接する2台の間でだけ意味を持つ値なので、パケットがLSRを通るたびに値が変わります。上の図では、PE1が付けた24003(P1が5.5.5.5/32に割り当てた値)を、P1が30000(P2が同じプレフィックスに割り当てた値)に付け替えています。値の決まり方はLDPの記事で解説します。各LSRは自分のローカルラベルを独立に割り当てるため、隣どうしでたまたま同じ値になることもあります。その場合も付け替えは行われており、値が一致しているだけです。割り当てはルータを再起動すると変わるので、ラベルの値そのものに意味はありません。
PHP(Penultimate Hop Popping)
出口LSRは、自分に直結する経路(ループバックなど)に対してimplicit-null(3) を配ります。これを受け取った1つ手前のLSRは、そのプレフィックス宛てのパケットからラベルを外して(pop)送ります。この動作がPHPです。
PHPの目的は、出口LSRの検索回数を減らすことです(RFC 3031 3.16節)。出口LSRがラベル付きのまま受け取ると、まずLFIBでラベルを引いて「自分宛てなので外す」と判断し、続けてIPヘッダーでFIBを引く、という2回の検索が必要になります。1つ手前でラベルを外しておけば、出口LSRはIPパケットとして受け取り、FIBの検索1回で済みます。
PHPを使わない場合は、出口LSRにexplicit-null(0) を配らせます。1つ手前のLSRはラベルを0にswapして送り、出口LSRはラベル0を外してからIP検索を行います。ラベルのTC(QoSのクラス)を出口LSRまで届けたいときに使います。IOS XRではmpls ldp配下のlabel local advertise explicit-nullで切り替えます。
| 出口LSRが配る値 | 1つ手前のLSRの操作 | 出口LSRの検索 |
|---|---|---|
| implicit-null(3、既定) | pop | IP検索1回 |
| explicit-null(0) | 0にswap | ラベル検索 + IP検索 |
PHPはあくまで「最後のラベルを1つ手前で外す」動作です。MPLS VPNのようにラベルが2段ある場合、1つ手前のLSRが外すのは先頭のトランスポートラベルだけで、VPNラベルは出口PEまで届きます。
実機での検証
検証環境
CE1 — PE1 — P1 — P2 — PE2 — CE2 を直列につないでいます。AS 65001のPE1 / P1 / P2 / PE2がMPLSネットワークで、OSPF(area 0、全リンクnetwork point-to-point)で経路を、LDPでラベルを配っています。CE1とCE2はPEとeBGPで経路を交換し、PE1とPE2はLoopback0間のiBGP(next-hop-self)で外部経路のネクストホップを自分のLoopback0にします。CE3 / CE4はVRF CUST-Aの顧客で、本記事ではVPNラベルがPHPで外れないことの確認に使います。
| ルータ | 役割 | Lo0 | リンク |
|---|---|---|---|
| CE1 | 顧客側(AS 65101)、192.168.1.0/24を広告 | 1.1.1.1/32 | Gi0/0/0/0 10.1.2.1 |
| PE1 | 入口 / 出口LSR(AS 65001) | 2.2.2.2/32 | Gi0/0/0/0 10.1.2.2 / Gi0/0/0/1 10.2.3.2 |
| P1 | 中継LSR | 3.3.3.3/32 | Gi0/0/0/0 10.2.3.3 / Gi0/0/0/1 10.3.4.3 |
| P2 | 中継LSR | 4.4.4.4/32 | Gi0/0/0/0 10.3.4.4 / Gi0/0/0/1 10.4.5.4 |
| PE2 | 入口 / 出口LSR(AS 65001) | 5.5.5.5/32 | Gi0/0/0/0 10.4.5.5 / Gi0/0/0/1 10.5.6.5 |
| CE2 | 顧客側(AS 65102)、192.168.6.0/24を広告 | 6.6.6.6/32 | Gi0/0/0/0 10.5.6.6 |
| CE3 | VRF CUST-Aの顧客側(AS 65107)、192.168.7.0/24を広告 | 7.7.7.7/32 | Gi0/0/0/0 10.2.7.7(PE1 Gi0/0/0/2) |
| CE4 | VRF CUST-Aの顧客側(AS 65108)、192.168.8.0/24を広告 | 8.8.8.8/32 | Gi0/0/0/0 10.5.8.8(PE2 Gi0/0/0/2) |
CE1から192.168.6.1へのpingを、PE1 - P1、P1 - P2、P2 - PE2の3リンクで同時にキャプチャーし、1つのパケットが各リンクでどのラベルを持つかを、各ルータのFIB / LFIBと突き合わせます。
検証のSTEP
| STEP | 操作 | 狙い |
|---|---|---|
| 0 | 初期状態(既定 = PHP) | PE1がpush、P1がswap、P2がpopすること。3リンクのpcapとFIB / LFIBを突き合わせる |
| 1 | P2の動的ラベルの範囲をmpls label range table 0 30000 1048575に変更 | ラベルがLSRごとに独立して割り当てられること。P2の割り当てが30000台になり、P1がswapする先の値だけが変わる。範囲の変更はルータの再起動後に有効になる |
| 2 | PE2にlabel local advertise explicit-null | popの場所が P2 から PE2 に移り、P2の操作がExp-Null-v4へのswapになること。VPNの2段ではVPNラベルが残ったままPE2に届くこと |
| 3 | STEP 1・2を削除(最終状態) | STEP 0と同じに戻ること |
STEP 0:初期状態(既定 = PHP)
PE1は192.168.6.0/24をiBGPでネクストホップ5.5.5.5(PE2のLoopback0)として学んでいるため、pushするラベルは再帰先の5.5.5.5/32のエントリに入っています。
via 10.2.3.3/32, GigabitEthernet0/0/0/1, 6 dependencies, weight 0, class 0 [flags 0x0]
path-idx 0 NHID 0x4 [0x8a76a5a0 0x0]
next hop 10.2.3.3/32
local adjacency
local label 24004 labels imposed {24003}labels imposed {24003}が、PE1がpushする値です。続いてP1とP2のLFIBを見ます。
Local Outgoing Prefix Outgoing Next Hop Bytes
Label Label or ID Interface Switched
------ ----------- ------------------ ------------ --------------- ------------
24000 Pop 2.2.2.2/32 Gi0/0/0/0 10.2.3.2 10426
24001 Pop 4.4.4.4/32 Gi0/0/0/1 10.3.4.4 1230
24002 Pop 10.4.5.0/24 Gi0/0/0/1 10.3.4.4 0
24003 24003 5.5.5.5/32 Gi0/0/0/1 10.3.4.4 7547Local Outgoing Prefix Outgoing Next Hop Bytes
Label Label or ID Interface Switched
------ ----------- ------------------ ------------ --------------- ------------
24000 24000 2.2.2.2/32 Gi0/0/0/0 10.3.4.3 9028
24001 Pop 3.3.3.3/32 Gi0/0/0/0 10.3.4.3 1248
24002 Pop 10.2.3.0/24 Gi0/0/0/0 10.3.4.3 0
24003 Pop 5.5.5.5/32 Gi0/0/0/1 10.4.5.5 9148P1は24003を受けて24003へswapし、P2は24003を受けてPopします。この採取ではP1とP2が5.5.5.5/32に同じ24003を割り当てており、値が一致しています。3リンクのキャプチャーでCE1 - CE2のecho requestを追うと次のようになります。
| リンク | EtherType | ラベル |
|---|---|---|
| PE1 - P1 | 0x8847 | 24003 |
| P1 - P2 | 0x8847 | 24003 |
| P2 - PE2 | 0x0800 | (無し) |
P2 - PE2でEtherTypeが0x0800に戻り、ラベルが消えています。PHPによってP2が最後のラベルを外したためです。
VRF CUST-Aの経路では、PE1が2つのラベルをpushします。
via 5.5.5.5/32, 3 dependencies, recursive [flags 0x6000]
path-idx 0 NHID 0x0 [0x8857f048 0x0]
recursion-via-/32
next hop VRF - 'default', table - 0xe0000000
next hop 5.5.5.5/32 via 24004/0/21
next hop 10.2.3.3/32 GigabitEthernet0/0/0/1 labels imposed {24003 24005}CE3 - CE4のecho requestは3リンクで次のように流れます。
| リンク | ラベル |
|---|---|
| PE1 - P1 | 24003, 24005 |
| P1 - P2 | 24003, 24005 |
| P2 - PE2 | 24005 |
P2が外したのは先頭の24003だけで、VPNラベル24005はPE2まで届いています。
STEP 1:P2の動的ラベルの範囲を変更
P2にmpls label range table 0 30000 1048575を投入してP2を再起動すると、P2のローカルラベルが30000台になります。P1のLFIBはswap先だけが変わります。
Local Outgoing Prefix Outgoing Next Hop Bytes
Label Label or ID Interface Switched
------ ----------- ------------------ ------------ --------------- ------------
24000 Pop 2.2.2.2/32 Gi0/0/0/0 10.2.3.2 19988
24001 Pop 4.4.4.4/32 Gi0/0/0/1 10.3.4.4 840
24002 Pop 10.4.5.0/24 Gi0/0/0/1 10.3.4.4 0
24003 30000 5.5.5.5/32 Gi0/0/0/1 10.3.4.4 6747P1が受け取る24003は変わらず、送り出す値だけが30000になりました。PE1 - P1は24003のまま、P1 - P2が30000に変わります。CE1のtracerouteにも同じ値が出ます。
1 10.1.2.2 11 msec 4 msec 4 msec
2 10.2.3.3 [MPLS: Label 24003 Exp 0] 14 msec 12 msec 13 msec
3 10.3.4.4 [MPLS: Label 30000 Exp 0] 17 msec 17 msec 12 msec
4 10.4.5.5 12 msec 13 msec 10 msec
5 10.5.6.6 18 msec * 23 msecSTEP 2:PE2にlabel local advertise explicit-null
PE2がimplicit-null(3)ではなくexplicit-null(0)を配るようにすると、P2のOutgoing LabelがExp-Null-v4に変わります。
Local Outgoing Prefix Outgoing Next Hop Bytes
Label Label or ID Interface Switched
------ ----------- ------------------ ------------ --------------- ------------
30000 Exp-Null-v4 5.5.5.5/32 Gi0/0/0/1 10.4.5.5 6684
30001 24000 2.2.2.2/32 Gi0/0/0/0 10.3.4.3 14107
30002 Pop 3.3.3.3/32 Gi0/0/0/0 10.3.4.3 1608
30003 Pop 10.2.3.0/24 Gi0/0/0/0 10.3.4.3 0P2はラベルを外さず0にswapするので、P2 - PE2にもMPLSヘッダーが乗ります。VRFのパケットでは0とVPNラベルの2段になります。
MultiProtocol Label Switching Header, Label: 0 (IPv4 Explicit-Null), Exp: 0, S: 0, TTL: 252
0000 0000 0000 0000 0000 .... .... .... = MPLS Label: IPv4 Explicit-Null (0)
.... .... .... .... .... 000. .... .... = MPLS Experimental Bits: 0
.... .... .... .... .... ...0 .... .... = MPLS Bottom Of Label Stack: 0
.... .... .... .... .... .... 1111 1100 = MPLS TTL: 252
MultiProtocol Label Switching Header, Label: 24005, Exp: 0, S: 1, TTL: 254
0000 0101 1101 1100 0101 .... .... .... = MPLS Label: 24005 (0x05dc5)
.... .... .... .... .... 000. .... .... = MPLS Experimental Bits: 0
.... .... .... .... .... ...1 .... .... = MPLS Bottom Of Label Stack: 1
.... .... .... .... .... .... 1111 1110 = MPLS TTL: 254
Internet Protocol Version 4, Src: 192.168.7.1, Dst: 192.168.8.1先頭のラベルは0でS = 0、2段目の24005でS = 1です。explicit-nullにしても外れるのは先頭だけで、VPNラベルはPE2まで届きます。PE2はラベル0を外してからIP検索を行うため、検索が2回になります。
STEP 3:STEP 1・2を削除(最終状態)
両方の設定を外してP2を再起動すると、P2のラベル範囲は24000台に戻り、Outgoing LabelもPopに戻ります。
Local Outgoing Prefix Outgoing Next Hop Bytes
Label Label or ID Interface Switched
------ ----------- ------------------ ------------ --------------- ------------
24000 Pop 5.5.5.5/32 Gi0/0/0/1 10.4.5.5 7892
24001 24000 2.2.2.2/32 Gi0/0/0/0 10.3.4.3 7050
24002 Pop 3.3.3.3/32 Gi0/0/0/0 10.3.4.3 512
24003 Pop 10.2.3.0/24 Gi0/0/0/0 10.3.4.3 0再起動でP2の割り当て順が変わり、5.5.5.5/32には24000が付きました。STEP 0と違ってP1の値(24003)と一致しないため、tracerouteでも付け替えが見えます。
1 10.1.2.2 6 msec 5 msec 5 msec
2 10.2.3.3 [MPLS: Label 24003 Exp 0] 15 msec 14 msec 13 msec
3 10.3.4.4 [MPLS: Label 24000 Exp 0] 13 msec 14 msec 21 msec
4 10.4.5.5 15 msec 15 msec 14 msec
5 10.5.6.6 19 msec * 19 msecVRFのパケットでは、PHPで先頭が外れてVPNラベルだけが残る様子がそのまま出ます。
1 10.2.7.2 6 msec 14 msec 5 msec
2 10.2.3.3 [MPLS: Labels 24003/24005 Exp 0] 20 msec 16 msec 16 msec
3 10.3.4.4 [MPLS: Labels 24000/24005 Exp 0] 17 msec 16 msec 17 msec
4 10.4.5.5 [MPLS: Label 24005 Exp 0] 17 msec 18 msec 19 msec
5 10.5.8.8 20 msec * 18 msecキャプチャー
3リンクを同時に採取したpcapです。MPLSを含むためフィルターなしで取得しています。
STEP 0 PE1 - P1
STEP 0 P1 - P2
STEP 0 P2 - PE2
STEP 1 PE1 - P1
STEP 1 P1 - P2
STEP 1 P2 - PE2
STEP 2 PE1 - P1
STEP 2 P1 - P2
STEP 2 P2 - PE2
STEP 3 PE1 - P1
STEP 3 P1 - P2
STEP 3 P2 - PE2
検証Configおよびshow結果
各STEPで8台すべてから、次の種類をルータごとに分けて取得しています。検証Configはこの..._run.txtです(最終状態は最後のSTEPのもの)。
| ファイル | 内容 |
|---|---|
..._show.txt | show version / show interface description / show route / OSPF・LDP・MPLS転送・BGPの一式 |
..._log.txt | そのSTEPの範囲だけに絞ったshow logging。各STEPの開始時にlogmsgでマーカーを入れ、その時刻をshow logging startに指定して取得したもの |
..._run.txt | そのSTEP時点のshow running-config(=そのSTEPの検証Config) |
..._trace.txt | コア4台のLDPとLSDのトレース(show mpls ldp trace binding / peer / discovery / route / forwarding、show mpls lsd trace) |
..._debug.txt | CE1とCE3のtraceroute(最終STEPのみ) |
STEP 0:初期状態(既定 = PHP)
| ルータ | show出力 | syslog | running-config | trace |
|---|---|---|---|---|
| CE1 | show | log | run | - |
| PE1 | show | log | run | trace |
| P1 | show | log | run | trace |
| P2 | show | log | run | trace |
| PE2 | show | log | run | trace |
| CE2 | show | log | run | - |
| CE3 | show | log | run | - |
| CE4 | show | log | run | - |
STEP 1:P2の動的ラベルの範囲を変更
| ルータ | show出力 | syslog | running-config | trace |
|---|---|---|---|---|
| CE1 | show | log | run | - |
| PE1 | show | log | run | trace |
| P1 | show | log | run | trace |
| P2 | show | log | run | trace |
| PE2 | show | log | run | trace |
| CE2 | show | log | run | - |
| CE3 | show | log | run | - |
| CE4 | show | log | run | - |
STEP 2:PE2にlabel local advertise explicit-null
| ルータ | show出力 | syslog | running-config | trace |
|---|---|---|---|---|
| CE1 | show | log | run | - |
| PE1 | show | log | run | trace |
| P1 | show | log | run | trace |
| P2 | show | log | run | trace |
| PE2 | show | log | run | trace |
| CE2 | show | log | run | - |
| CE3 | show | log | run | - |
| CE4 | show | log | run | - |
STEP 3:STEP 1・2を削除(最終状態)
| ルータ | show出力 | syslog | running-config | trace | traceroute |
|---|---|---|---|---|---|
| CE1 | show | log | run | - | traceroute |
| PE1 | show | log | run | trace | - |
| P1 | show | log | run | trace | - |
| P2 | show | log | run | trace | - |
| PE2 | show | log | run | trace | - |
| CE2 | show | log | run | - | - |
| CE3 | show | log | run | - | traceroute |
| CE4 | show | log | run | - | - |
参考
| RFC | タイトル | 概要 |
|---|---|---|
| RFC 3031 | Multiprotocol Label Switching Architecture | NHLFE / ILM / FTN(3.10〜3.12節)、ラベルの付け替え(3.13節)、PHP(3.16節)。 |
| RFC 3032 | MPLS Label Stack Encoding | 予約ラベル(implicit-null 3、explicit-null 0)。 |
| RFC 4182 | Removing a Restriction on the use of MPLS Explicit NULL | Explicit NULLをボトム以外にも置けるようにした改訂。 |
書籍: Luc De Ghein『MPLS Fundamentals』(Cisco Press, 2006)Chapter 3