↓ Skip to main content
  1. Network Articles/
  2. MPLS Articles/

MPLS Labels and the Label Stack

Table of Contents

MPLS Labels and the Label Stack

An MPLS label is attached to a packet as a 4-byte label stack entry defined in RFC 3032. This article explains the contents of those 4 bytes, how several labels are stacked, and the label values that are reserved, then captures labeled packets in an IOS XR (XRd) lab to confirm it. The role of the label and terms such as LSR and LSP are covered in What Is MPLS.

Label Stack Entry Format

FieldBitsMeaning
Label20Label value, 0 to 1048575. Values 0 to 15 are reserved for special purposes (see below).
EXP / TC3Carries the QoS class. RFC 3032 only defined it as “experimental use” (EXP); RFC 5462 renamed it TC (Traffic Class). Neither the size nor the position changed, and IOS XR and Wireshark still say EXP, so this site calls it EXP. IOS XR still calls it EXP in show output and configuration, and by default copies the IP Precedence (upper 3 bits of the DSCP) into it when the label is pushed.
S (Bottom of Stack)1Set to 1 only on the last (bottom) entry. The receiver uses it to find the end of the label stack.
TTL (Time to Live)8Same role as the IP TTL. The IP TTL is copied in when the first label is pushed, only the label TTL is decremented at each LSR, and it is written back to the IP TTL when the label is popped. A packet whose TTL reaches 0 is not forwarded.

Labels that IOS XR assigns dynamically start at 24000 by default (show mpls label range).

The Label Stack

Label stack entries sit right after the Layer 2 header and right before the Layer 3 header, as many as needed. The first is the top and the last is the bottom, and forwarding looks only at the top label.

On Ethernet, the EtherType 0x8847 (unicast) or 0x8848 (multicast) says that a label stack follows (RFC 3032 section 5). The label stack itself has no field that says whether the payload is IPv4 or IPv6 (section 2.2). The LSR that pops the last label tells the payload type from the bottom label value or from the first byte of the IP header (the version).

The depth of the stack depends on the application: one entry for IGP prefixes carried by LDP, two in MPLS VPN (one to reach the egress PE and one to identify the VPN), and one more on a traffic engineering backup path. The lab in this article observes both a global route (one entry) and an MPLS VPN route (two entries).

Reserved Labels (0 to 15)

Label values 0 to 15 are reserved for special purposes. RFC 3032 defines 0 to 3, and 4 to 15 are managed by IANA (Special-Purpose MPLS Label Values).

ValueNameMeaning
0IPv4 Explicit NULLThe receiving LSR pops this entry and forwards by the label beneath it, or by the IPv4 header if there is none. RFC 3032 allowed it only at the bottom; RFC 4182 removed that restriction.
1Router AlertLegal anywhere except the bottom. When on top, the packet is handed to software and the label is pushed back before forwarding.
2IPv6 Explicit NULLThe IPv6 counterpart of 0.
3Implicit NULLNever appears on the wire. The egress LSR distributes it to tell the previous LSR to pop the label (PHP).
4MPLS Network ActionsRFC 9994.
7Entropy Label Indicator (ELI)RFC 6790. Says that an entropy label for load balancing follows.
13Generic Associated Channel Label (GAL)RFC 5586. Control channel for OAM and similar.
14OAM AlertRFC 3429.
15Extension Label (XL)RFC 7274, RFC 9017. Says that an extended special-purpose label follows.

The values missing from the table, 5 and 6 and 8 to 12, are unassigned (as of September 2026).

The two you see every day are 0 and 3. The default is implicit-null (3): the egress LSR distributes 3 for its directly connected prefixes (such as loopbacks) and the previous LSR pops the label, so unlabeled IP packets flow on the link before the egress LSR. To deliver the EXP value to the egress LSR, have it distribute explicit-null (0) instead. The previous LSR then swaps the label to 0, and the egress LSR reads the EXP from label 0 before popping it. On IOS XR this is label local advertise explicit-null under mpls ldp.

Value distributed by the egress LSRAction at the previous LSRLink before the egress LSR
3 (implicit-null, default)PopNo label
0 (explicit-null)Swap to label 0Label 0

The label operations themselves are covered in MPLS Label Operations (push / swap / pop) and PHP.

Lab Verification

Lab Setup

CE1, PE1, P1, P2, P3, PE2, and CE2 are connected in a row. PE1 / P1 / P2 / P3 / PE2 in AS 65001 form the MPLS network, with OSPF (area 0, every link network point-to-point) distributing routes and LDP distributing labels. CE1 and CE2 are customer routers without MPLS that exchange routes with the PEs over eBGP. PE1 and PE2 peer over iBGP between their Loopback0 addresses with next-hop-self, so the next hop of an external route is the PE’s own Loopback0. P1, P2, and P3 do not run BGP.

In addition, CE3 is attached to PE1 and CE4 to PE2 in VRF CUST-A (RD / RT 65001:1), and VPNv4 is added to the PE1 - PE2 iBGP session. Traffic between CE3 and CE4 is carried by the MPLS VPN route and gets two labels. VRFs and VPNv4 are covered in the MPLS L3VPN series; this article looks only at the label stack.

There are three P routers so that all four core links can be captured. That makes it possible to follow, without a gap, how the label is swapped at every hop, how the TTL drops by one each time, and how the label is removed by PHP on the last hop.

RouterRoleLo0Links
CE1Customer side (AS 65101), advertises 192.168.1.0/241.1.1.1/32Gi0/0/0/0 10.1.2.1
PE1Ingress / egress LSR (AS 65001)2.2.2.2/32Gi0/0/0/0 10.1.2.2 / Gi0/0/0/1 10.2.3.2
P1Intermediate LSR3.3.3.3/32Gi0/0/0/0 10.2.3.3 / Gi0/0/0/1 10.3.4.3
P2Intermediate LSR4.4.4.4/32Gi0/0/0/0 10.3.4.4 / Gi0/0/0/1 10.4.5.4
P3Intermediate LSR (performs PHP)5.5.5.5/32Gi0/0/0/0 10.4.5.5 / Gi0/0/0/1 10.5.6.5
PE2Ingress / egress LSR (AS 65001)6.6.6.6/32Gi0/0/0/0 10.5.6.6 / Gi0/0/0/1 10.6.7.6
CE2Customer side (AS 65102), advertises 192.168.2.0/247.7.7.7/32Gi0/0/0/0 10.6.7.7
CE3Customer side in VRF CUST-A (AS 65103), advertises 192.168.3.0/248.8.8.8/32Gi0/0/0/0 10.2.8.8 (PE1 Gi0/0/0/2)
CE4Customer side in VRF CUST-A (AS 65104), advertises 192.168.4.0/249.9.9.9/32Gi0/0/0/0 10.6.9.9 (PE2 Gi0/0/0/2)

CE1 pings 192.168.2.1 (global) and CE3 pings 192.168.4.1 (VPN) while we capture on all four core links (PE1 - P1, P1 - P2, P2 - P3, and P3 - PE2). The BGP next hop of both routes is PE2’s Loopback0 (6.6.6.6), so the top label PE1 pushes is the one P1 distributed for 6.6.6.6/32.

Steps

STEPActionWhat to confirm
0Initial state (default = implicit-null)Labeled packets (EtherType 0x8847) on PE1 - P1, one entry for the global route and two for the VPN route. The label is swapped at every hop and the TTL drops by one. No label is left on P3 - PE2
1Send pings with a ToS value (no configuration change)The IP precedence is copied into the label EXP. The IP DSCP itself does not change
2label local advertise explicit-null on PE2Label 0 appears on P3 - PE2, on top of the VPN label for VPN traffic
3Remove STEP 2 (final state)Back to the same state as STEP 0

Inside a Labeled Packet

For 192.168.2.0/24, whose BGP next hop is 6.6.6.6, PE1 pushes label 24005, the value it received from P1. In show mpls forwarding, the row for local label 24006 (the value PE1 itself assigned to 6.6.6.6/32) carries outgoing label 24005. The last row, 24007, is the VPN label PE1 assigned to 192.168.3.0/24 in VRF CUST-A ([V]).

PE1 show mpls forwarding (STEP 0)
RP/0/RP0/CPU0:PE1#show mpls forwarding
Sun Sep 20 12:31:04.869 UTC
Local  Outgoing    Prefix             Outgoing     Next Hop        Bytes
Label  Label       or ID              Interface                    Switched
------ ----------- ------------------ ------------ --------------- ------------
24000  Pop         3.3.3.3/32         Gi0/0/0/1    10.2.3.3        150
24001  Pop         10.3.4.0/24        Gi0/0/0/1    10.2.3.3        0
24002  24001       4.4.4.4/32         Gi0/0/0/1    10.2.3.3        0
24003  24002       10.4.5.0/24        Gi0/0/0/1    10.2.3.3        0
24004  24003       5.5.5.5/32         Gi0/0/0/1    10.2.3.3        0
24005  24004       10.5.6.0/24        Gi0/0/0/1    10.2.3.3        0
24006  24005       6.6.6.6/32         Gi0/0/0/1    10.2.3.3        36750
24007  Unlabelled  192.168.3.0/24[V]  Gi0/0/0/2    10.2.8.8        13088

Dynamic labels start at 24000, as show mpls label range shows.

P1 show mpls label range (STEP 0)
RP/0/RP0/CPU0:P1#show mpls label range
Sun Sep 20 12:32:03.861 UTC
Range for dynamic labels: Min/Max: 24000/1048575

No.37 of the PE1 - P1 capture, taken while CE1 pinged 192.168.2.1, is the labeled echo request. The EtherType is 0x8847 and the entry carries label 24005, TC 0 (tshark calls it Experimental Bits), S = 1, and TTL 254, with the IPv4 header following the four bytes. The label TTL of 254 is the TTL 255 that CE1 sent, decremented once by PE1 and copied into the label.

No.37 Echo request (PE1 → P1) tshark -V
    Type: MPLS label switched packet (0x8847)
    [Stream index: 4]
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.1.1, Dst: 192.168.2.1
    0100 .... = Version: 4
    .... 0101 = Header Length: 20 bytes (5)
    Differentiated Services Field: 0x00 (DSCP: CS0, ECN: Not-ECT)
        0000 00.. = Differentiated Services Codepoint: Default (0)
        .... ..00 = Explicit Congestion Notification: Not ECN-Capable Transport (0)
    Total Length: 100
    Identification: 0x0000 (0)
    000. .... = Flags: 0x0
        0... .... = Reserved bit: Not set
        .0.. .... = Don't fragment: Not set
        ..0. .... = More fragments: Not set
    ...0 0000 0000 0000 = Fragment Offset: 0
    Time to Live: 254
Download the pcap of the packet in the tshark output above (No.37 Echo request)

The TTL Changes at Every Hop, the IP Header Does Not

The label TTL drops by one at every LSR. Looking at the same echo request on all four core links, it goes 254, 253, 252 — and on the last link there is no label at all.

The same echo request on the four core links (STEP 0)
$ tshark -r - -Y 'icmp.type==8 && ip.src==192.168.1.1' -T fields -e frame.number -e mpls.label -e mpls.ttl -e ip.ttl \
    < mpls-label-step0-pe1p1.pcap
37	24005	254	254
$ tshark -r - -Y 'icmp.type==8 && ip.src==192.168.1.1' -T fields -e frame.number -e mpls.label -e mpls.ttl -e ip.ttl \
    < mpls-label-step0-p1p2.pcap
33	24005	253	254
$ tshark -r - -Y 'icmp.type==8 && ip.src==192.168.1.1' -T fields -e frame.number -e mpls.label -e mpls.ttl -e ip.ttl \
    < mpls-label-step0-p2p3.pcap
34	24005	252	254
$ tshark -r - -Y 'icmp.type==8 && ip.src==192.168.1.1' -T fields -e frame.number -e mpls.label -e mpls.ttl -e ip.ttl \
    < mpls-label-step0-p3pe2.pcap
35			251

The last line has neither a label value nor a label TTL: P3 removed the label (PHP), so the frame carries EtherType 0x0800 and a plain IP packet. That is where the IP TTL changes from 254 to 251. While a label is present the IP TTL stays untouched, and the decrements made on the label are written back when the label is popped (RFC 3032 2.4.2 and 2.4.3).

No.35 Echo request (P3 → PE2, after the label was popped) tshark -V
    Type: IPv4 (0x0800)
    [Stream index: 2]
Internet Protocol Version 4, Src: 192.168.1.1, Dst: 192.168.2.1
    0100 .... = Version: 4
    .... 0101 = Header Length: 20 bytes (5)
    Differentiated Services Field: 0x00 (DSCP: CS0, ECN: Not-ECT)
        0000 00.. = Differentiated Services Codepoint: Default (0)
        .... ..00 = Explicit Congestion Notification: Not ECN-Capable Transport (0)
    Total Length: 100
    Identification: 0x0000 (0)
    000. .... = Flags: 0x0
        0... .... = Reserved bit: Not set
        .0.. .... = Don't fragment: Not set
        ..0. .... = More fragments: Not set
    ...0 0000 0000 0000 = Fragment Offset: 0
    Time to Live: 251
Download the pcap of the packet in the tshark output above (No.35 Echo request)

A traceroute from CE1 shows the same thing. Hops 2 to 4 carry a label; PE2 (10.5.6.6) does not.

CE1 traceroute (STEP 0)
RP/0/RP0/CPU0:CE1#traceroute 192.168.2.1 source 192.168.1.1 timeout 1 probe 2 maxttl 7
Sun Sep 20 12:30:19.585 UTC

Type escape sequence to abort.
Tracing the route to 192.168.2.1

 1  10.1.2.2 5 msec  5 msec
 2  10.2.3.3 [MPLS: Label 24005 Exp 0] 18 msec  16 msec
 3  10.3.4.4 [MPLS: Label 24005 Exp 0] 17 msec  46 msec
 4  10.4.5.5 [MPLS: Label 24005 Exp 0] 20 msec  18 msec
 5  10.5.6.6 17 msec  37 msec
 6  10.6.7.7 21 msec  *

A Label Value Is Only Meaningful Between Two Adjacent Routers

Every LSR allocates labels on its own. In this lab P1, P2, and P3 happened to assign 24005 to 6.6.6.6/32, so the value does not appear to change in the captures above. The show mpls forwarding output of each router shows what actually happens: each one pops its own local label and pushes the label of the next router.

P1 show mpls forwarding (STEP 0)
RP/0/RP0/CPU0:P1#show mpls forwarding
Sun Sep 20 12:32:03.269 UTC
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        38119
24001  Pop         4.4.4.4/32         Gi0/0/0/1    10.3.4.4        274
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        0
24004  24004       10.5.6.0/24        Gi0/0/0/1    10.3.4.4        0
24005  24005       6.6.6.6/32         Gi0/0/0/1    10.3.4.4        37705
P2 show mpls forwarding (STEP 0)
RP/0/RP0/CPU0:P2#show mpls forwarding
Sun Sep 20 12:32:27.311 UTC
Local  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        38421
24001  Pop         3.3.3.3/32         Gi0/0/0/0    10.3.4.3        318
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        318
24004  Pop         10.5.6.0/24        Gi0/0/0/1    10.4.5.5        0
24005  24005       6.6.6.6/32         Gi0/0/0/1    10.4.5.5        38553
P3 show mpls forwarding (STEP 0)
RP/0/RP0/CPU0:P3#show mpls forwarding
Sun Sep 20 12:32:52.214 UTC
Local  Outgoing    Prefix             Outgoing     Next Hop        Bytes
Label  Label       or ID              Interface                    Switched
------ ----------- ------------------ ------------ --------------- ------------
24000  24000       2.2.2.2/32         Gi0/0/0/0    10.4.5.4        37510
24001  24001       3.3.3.3/32         Gi0/0/0/0    10.4.5.4        0
24002  Pop         4.4.4.4/32         Gi0/0/0/0    10.4.5.4        380
24003  24002       10.2.3.0/24        Gi0/0/0/0    10.4.5.4        0
24004  Pop         10.3.4.0/24        Gi0/0/0/0    10.4.5.4        0
24005  Pop         6.6.6.6/32         Gi0/0/0/1    10.5.6.6        38246

A Two-Entry Label Stack

Pings from CE3 to 192.168.4.1 (CE4) are carried by the VRF CUST-A route. For that destination PE1 pushes the VPN label 24007, which PE2 distributed over VPNv4, and on top of it 24005 to reach the egress PE2. That is the labels imposed {24005 24007} in show cef vrf CUST-A.

PE1 show cef vrf CUST-A 192.168.4.0/24 detail (STEP 0)
RP/0/RP0/CPU0:PE1#show cef vrf CUST-A 192.168.4.0/24 detail
Sun Sep 20 12:31:10.752 UTC
192.168.4.0/24, version 7, internal 0x5000001 0x30 (ptr 0x885196d0) [1], 0x0 (0x0), 0x208 (0x89a22518)
 Updated Sep 20 12:05:56.194
 Prefix Len 24, traffic index 0, precedence n/a, priority 3
  gateway array (0x8972ea58) reference count 1, flags 0x2038, source rib (7), 0 backups
                [1 type 1 flags 0x48441 (0x89a5b4b8) ext 0x0 (0x0)]
  LW-LDI[type=0, refc=0, ptr=0x0, sh-ldi=0x0]
  gateway array update type-time 1 Sep 20 12:05:56.194
 LDI Update time Sep 20 12:05:56.194
   via 6.6.6.6/32, 3 dependencies, recursive [flags 0x6000]
    path-idx 0 NHID 0x0 [0x8856ecc8 0x0]
    recursion-via-/32
    next hop VRF - 'default', table - 0xe0000000
    next hop 6.6.6.6/32 via 24006/0/21
     next hop 10.2.3.3/32 GigabitEthernet0/0/0/1 labels imposed {24005 24007}

    Load distribution: 0 (refcount 1)

No.524 of the PE1 - P1 capture is that echo request with two entries. The top entry, 24005, has S = 0 and the bottom entry, 24007, has S = 1, with the IPv4 header following the S = 1 entry. Both labels carry TTL 254.

No.524 Echo request (PE1 → P1, two entries) tshark -V
    Type: MPLS label switched packet (0x8847)
    [Stream index: 4]
MultiProtocol Label Switching Header, Label: 24005, Exp: 0, S: 0, TTL: 254
    0000 0101 1101 1100 0101 .... .... .... = MPLS Label: 24005 (0x05dc5)
    .... .... .... .... .... 000. .... .... = MPLS Experimental Bits: 0
    .... .... .... .... .... ...0 .... .... = MPLS Bottom Of Label Stack: 0
    .... .... .... .... .... .... 1111 1110 = MPLS TTL: 254
MultiProtocol Label Switching Header, Label: 24007, Exp: 0, S: 1, TTL: 254
    0000 0101 1101 1100 0111 .... .... .... = MPLS Label: 24007 (0x05dc7)
    .... .... .... .... .... 000. .... .... = MPLS Experimental Bits: 0
    .... .... .... .... .... ...1 .... .... = MPLS Bottom Of Label Stack: 1
    .... .... .... .... .... .... 1111 1110 = MPLS TTL: 254
Internet Protocol Version 4, Src: 192.168.3.1, Dst: 192.168.4.1
    0100 .... = Version: 4
    .... 0101 = Header Length: 20 bytes (5)
    Differentiated Services Field: 0x00 (DSCP: CS0, ECN: Not-ECT)
        0000 00.. = Differentiated Services Codepoint: Default (0)
        .... ..00 = Explicit Congestion Notification: Not ECN-Capable Transport (0)
    Total Length: 100
    Identification: 0x0000 (0)
    000. .... = Flags: 0x0
        0... .... = Reserved bit: Not set
        .0.. .... = Don't fragment: Not set
        ..0. .... = More fragments: Not set
    ...0 0000 0000 0000 = Fragment Offset: 0
    Time to Live: 254
Download the pcap of the packet in the tshark output above (No.524 Echo request, two entries)

No.523 of the P3 - PE2 capture is the same packet after P3 popped the top label. Only the VPN label 24007 is left and its TTL is 251: when P3 popped the top entry it copied the TTL of that moment (251) into the entry that became the new top (RFC 3032 2.4.2).

No.523 Echo request (P3 → PE2, VPN label only) tshark -V
    Type: MPLS label switched packet (0x8847)
    [Stream index: 2]
MultiProtocol Label Switching Header, Label: 24007, Exp: 0, S: 1, TTL: 251
    0000 0101 1101 1100 0111 .... .... .... = MPLS Label: 24007 (0x05dc7)
    .... .... .... .... .... 000. .... .... = MPLS Experimental Bits: 0
    .... .... .... .... .... ...1 .... .... = MPLS Bottom Of Label Stack: 1
    .... .... .... .... .... .... 1111 1011 = MPLS TTL: 251
Internet Protocol Version 4, Src: 192.168.3.1, Dst: 192.168.4.1
    0100 .... = Version: 4
    .... 0101 = Header Length: 20 bytes (5)
    Differentiated Services Field: 0x00 (DSCP: CS0, ECN: Not-ECT)
        0000 00.. = Differentiated Services Codepoint: Default (0)
        .... ..00 = Explicit Congestion Notification: Not ECN-Capable Transport (0)
    Total Length: 100
    Identification: 0x0000 (0)
    000. .... = Flags: 0x0
        0... .... = Reserved bit: Not set
        .0.. .... = Don't fragment: Not set
        ..0. .... = More fragments: Not set
    ...0 0000 0000 0000 = Fragment Offset: 0
    Time to Live: 254
Download the pcap of the packet in the tshark output above (No.523 Echo request)

On PE2, local label 24007 maps to 192.168.4.0/24 in VRF CUST-A ([V]), and that label sends the packet out of the interface towards CE4. How VPN labels are distributed is covered in the MPLS L3VPN series.

PE2 show mpls forwarding (STEP 0)
RP/0/RP0/CPU0:PE2#show mpls forwarding
Sun Sep 20 12:31:34.064 UTC
Local  Outgoing    Prefix             Outgoing     Next Hop        Bytes
Label  Label       or ID              Interface                    Switched
------ ----------- ------------------ ------------ --------------- ------------
24000  24000       2.2.2.2/32         Gi0/0/0/0    10.5.6.5        36599
24001  24001       3.3.3.3/32         Gi0/0/0/0    10.5.6.5        0
24002  24002       4.4.4.4/32         Gi0/0/0/0    10.5.6.5        0
24003  Pop         5.5.5.5/32         Gi0/0/0/0    10.5.6.5        212
24004  24003       10.2.3.0/24        Gi0/0/0/0    10.5.6.5        0
24005  24004       10.3.4.0/24        Gi0/0/0/0    10.5.6.5        0
24006  Pop         10.4.5.0/24        Gi0/0/0/0    10.5.6.5        0
24007  Unlabelled  192.168.4.0/24[V]  Gi0/0/0/2    10.6.9.9        13088

TC Receives a Copy of the IP Precedence

When it pushes a label, IOS XR copies the IP precedence (the top three bits of the DSCP) into TC by default. Pinging from CE1 with a ToS value set through type gives TC 5 for packets with DSCP 46 (EF). The DSCP in the IP header itself is not rewritten.

ToS sent by CE1IP DSCPLabel TC
00 (CS0)0
9624 (CS3)3
18446 (EF)5
No.219 Echo request (PE1 → P1, DSCP 46) tshark -V
    Type: MPLS label switched packet (0x8847)
    [Stream index: 4]
MultiProtocol Label Switching Header, Label: 24005, Exp: 5, S: 1, TTL: 254
    0000 0101 1101 1100 0101 .... .... .... = MPLS Label: 24005 (0x05dc5)
    .... .... .... .... .... 101. .... .... = MPLS Experimental Bits: 5
    .... .... .... .... .... ...1 .... .... = MPLS Bottom Of Label Stack: 1
    .... .... .... .... .... .... 1111 1110 = MPLS TTL: 254
Internet Protocol Version 4, Src: 192.168.1.1, Dst: 192.168.2.1
    0100 .... = Version: 4
    .... 0101 = Header Length: 20 bytes (5)
    Differentiated Services Field: 0xb8 (DSCP: EF, ECN: Not-ECT)
        1011 10.. = Differentiated Services Codepoint: Expedited Forwarding (46)
Download the pcap of the packet in the tshark output above (No.219 Echo request, DSCP 46)

How TC is used is covered in MPLS QoS.

Implicit-Null Never Appears as a Label

PE2, the egress LSR, distributes implicit-null (3) for its own Loopback0 (6.6.6.6/32). In P3’s show mpls ldp bindings the value from PE2 is ImpNull, and show mpls forwarding shows Pop as the outgoing label.

P3 show mpls ldp bindings 6.6.6.6/32 (STEP 0)
RP/0/RP0/CPU0:P3#show mpls ldp bindings 6.6.6.6/32
Sun Sep 20 12:32:51.320 UTC
6.6.6.6/32, rev 18
	Local binding: label: 24005
	Remote bindings: (2 peers)
	    Peer                Label
	    -----------------   ---------
	    4.4.4.4:0           24005
	    6.6.6.6:0           ImpNull
P3 show mpls forwarding (STEP 0)
RP/0/RP0/CPU0:P3#show mpls forwarding
Sun Sep 20 12:32:52.214 UTC
Local  Outgoing    Prefix             Outgoing     Next Hop        Bytes
Label  Label       or ID              Interface                    Switched
------ ----------- ------------------ ------------ --------------- ------------
24000  24000       2.2.2.2/32         Gi0/0/0/0    10.4.5.4        37510
24001  24001       3.3.3.3/32         Gi0/0/0/0    10.4.5.4        0
24002  Pop         4.4.4.4/32         Gi0/0/0/0    10.4.5.4        380
24003  24002       10.2.3.0/24        Gi0/0/0/0    10.4.5.4        0
24004  Pop         10.3.4.0/24        Gi0/0/0/0    10.4.5.4        0
24005  Pop         6.6.6.6/32         Gi0/0/0/1    10.5.6.6        38246

show mpls label table detail lists the reserved labels a router actually holds. The LSD (Label Switching Database) holds 0 (IPv4 Explicit NULL), 1 (Router Alert), 2 (IPv6 Explicit NULL), and 13 (GAL); 3 is absent. Implicit-null is a value carried by LDP, and it appears neither in the label table nor on the wire.

P3 show mpls label table detail (STEP 0)
RP/0/RP0/CPU0:P3#show mpls label table detail
Sun Sep 20 12:32:52.919 UTC
Table Label   Owner                           State  Rewrite
----- ------- ------------------------------- ------ -------
0     0       LSD(A)                          InUse  Yes
0     1       LSD(A)                          InUse  Yes
0     2       LSD(A)                          InUse  Yes
0     13      LSD(A)                          InUse  Yes
0     24000   LDP(A)                          InUse  Yes
  (IPv4, vers:0, 'default':4U, 2.2.2.2/32)

Explicit-Null Appears on the Wire

Now label local advertise explicit-null is configured on PE2.

PE2 (STEP 2)
mpls ldp\n address-family ipv4\n  label local advertise explicit-null

The value P3 receives becomes ExpNullv4, and the outgoing label becomes Exp-Null-v4.

P3 show mpls ldp bindings 6.6.6.6/32 (STEP 2)
RP/0/RP0/CPU0:P3#show mpls ldp bindings 6.6.6.6/32
Sun Sep 20 12:44:24.835 UTC
6.6.6.6/32, rev 18
	Local binding: label: 24005
	Remote bindings: (2 peers)
	    Peer                Label
	    -----------------   ---------
	    4.4.4.4:0           24005
	    6.6.6.6:0           ExpNullv4
P3 show mpls forwarding (STEP 2)
RP/0/RP0/CPU0:P3#show mpls forwarding
Sun Sep 20 12:44:25.602 UTC
Local  Outgoing    Prefix             Outgoing     Next Hop        Bytes
Label  Label       or ID              Interface                    Switched
------ ----------- ------------------ ------------ --------------- ------------
24000  24000       2.2.2.2/32         Gi0/0/0/0    10.4.5.4        37581
24001  24001       3.3.3.3/32         Gi0/0/0/0    10.4.5.4        0
24002  Pop         4.4.4.4/32         Gi0/0/0/0    10.4.5.4        362
24003  24002       10.2.3.0/24        Gi0/0/0/0    10.4.5.4        0
24004  Pop         10.3.4.0/24        Gi0/0/0/0    10.4.5.4        0
24005  Exp-Null-v4 6.6.6.6/32         Gi0/0/0/1    10.5.6.6        39763

In No.85 of the P3 - PE2 capture, the echo request from CE1 arrives with label 0 on it. The label TTL is 251 while the IP TTL is still 254 — again, the IP TTL does not change while a label is present.

No.85 Echo request (P3 → PE2, explicit-null) tshark -V
    Type: MPLS label switched packet (0x8847)
    [Stream index: 4]
MultiProtocol Label Switching Header, Label: 0 (IPv4 Explicit-Null), Exp: 0, S: 1, TTL: 251
    0000 0000 0000 0000 0000 .... .... .... = MPLS Label: IPv4 Explicit-Null (0)
    .... .... .... .... .... 000. .... .... = MPLS Experimental Bits: 0
    .... .... .... .... .... ...1 .... .... = MPLS Bottom Of Label Stack: 1
    .... .... .... .... .... .... 1111 1011 = MPLS TTL: 251
Internet Protocol Version 4, Src: 192.168.1.1, Dst: 192.168.2.1
    0100 .... = Version: 4
    .... 0101 = Header Length: 20 bytes (5)
    Differentiated Services Field: 0x00 (DSCP: CS0, ECN: Not-ECT)
        0000 00.. = Differentiated Services Codepoint: Default (0)
        .... ..00 = Explicit Congestion Notification: Not ECN-Capable Transport (0)
    Total Length: 100
    Identification: 0x0000 (0)
    000. .... = Flags: 0x0
        0... .... = Reserved bit: Not set
        .0.. .... = Don't fragment: Not set
        ..0. .... = More fragments: Not set
    ...0 0000 0000 0000 = Fragment Offset: 0
    Time to Live: 254
Download the pcap of the packet in the tshark output above (No.85 Echo request)

For the VPN route the stack has two entries, as in No.574: label 0 on top (S = 0) and the VPN label 24007 at the bottom (S = 1). RFC 3032 allowed explicit-null only at the bottom of the stack; RFC 4182 removed that restriction and states that an LSR receiving it on top pops the entry and processes the label beneath. PE2 pops label 0 and then forwards into VRF CUST-A by 24007.

No.574 Echo request (P3 → PE2, explicit-null plus VPN label) tshark -V
    Type: MPLS label switched packet (0x8847)
    [Stream index: 4]
MultiProtocol Label Switching Header, Label: 0 (IPv4 Explicit-Null), Exp: 0, S: 0, TTL: 251
    0000 0000 0000 0000 0000 .... .... .... = MPLS Label: IPv4 Explicit-Null (0)
    .... .... .... .... .... 000. .... .... = MPLS Experimental Bits: 0
    .... .... .... .... .... ...0 .... .... = MPLS Bottom Of Label Stack: 0
    .... .... .... .... .... .... 1111 1011 = MPLS TTL: 251
MultiProtocol Label Switching Header, Label: 24007, Exp: 0, S: 1, TTL: 254
    0000 0101 1101 1100 0111 .... .... .... = MPLS Label: 24007 (0x05dc7)
    .... .... .... .... .... 000. .... .... = MPLS Experimental Bits: 0
    .... .... .... .... .... ...1 .... .... = MPLS Bottom Of Label Stack: 1
    .... .... .... .... .... .... 1111 1110 = MPLS TTL: 254
Internet Protocol Version 4, Src: 192.168.3.1, Dst: 192.168.4.1
    0100 .... = Version: 4
    .... 0101 = Header Length: 20 bytes (5)
    Differentiated Services Field: 0x00 (DSCP: CS0, ECN: Not-ECT)
        0000 00.. = Differentiated Services Codepoint: Default (0)
        .... ..00 = Explicit Congestion Notification: Not ECN-Capable Transport (0)
    Total Length: 100
    Identification: 0x0000 (0)
    000. .... = Flags: 0x0
        0... .... = Reserved bit: Not set
        .0.. .... = Don't fragment: Not set
        ..0. .... = More fragments: Not set
    ...0 0000 0000 0000 = Fragment Offset: 0
    Time to Live: 254
Download the pcap of the packet in the tshark output above (No.574 Echo request, two entries)

Removing the configuration brings the value P3 receives back to ImpNull. Filtering P3’s LDP trace on 6.6.6.6/32 lines up the history of what PE2 advertised: 3, then 0, then 3. In both changes there are ten seconds between the withdrawal of the old value (Remove) and the arrival of the new one (Add).

P3 show mpls ldp trace binding (STEP 3)
RP/0/RP0/CPU0:P3#show mpls ldp trace binding | include "6.6.6.6/32"
Sun Sep 20 12:50:26.582 UTC
Sep 20 12:04:48.308 mpls/ldp/bndg 0/RP0/CPU0 t4208  [BNDG]:161: VRF(0x60000000): Prefix(6.6.6.6/32): Created, reason 3
Sep 20 12:04:48.309 mpls/ldp/bndg 0/RP0/CPU0 t4208  [BNDG]:221: VRF(0x60000000): Prefix(6.6.6.6/32): label 24005 (rev#18) assigned
Sep 20 12:05:00.768 mpls/ldp/bndg 0/RP0/CPU0 t4208  [BNDG]:279: VRF(0x60000000): Prefix(6.6.6.6/32): Add label 24005 from peer 4.4.4.4:0
Sep 20 12:05:10.171 mpls/ldp/bndg 0/RP0/CPU0 t4208  [BNDG]:279: VRF(0x60000000): Prefix(6.6.6.6/32): Add label 3 from peer 6.6.6.6:0
Sep 20 12:40:20.170 mpls/ldp/bndg 0/RP0/CPU0 t4208  [BNDG]:339: VRF(0x60000000): Prefix(6.6.6.6/32): Remove label 3 from peer 6.6.6.6:0
Sep 20 12:40:30.170 mpls/ldp/bndg 0/RP0/CPU0 t4208  [BNDG]:279: VRF(0x60000000): Prefix(6.6.6.6/32): Add label 0 from peer 6.6.6.6:0
Sep 20 12:46:00.170 mpls/ldp/bndg 0/RP0/CPU0 t4208  [BNDG]:339: VRF(0x60000000): Prefix(6.6.6.6/32): Remove label 0 from peer 6.6.6.6:0
Sep 20 12:46:10.170 mpls/ldp/bndg 0/RP0/CPU0 t4208  [BNDG]:279: VRF(0x60000000): Prefix(6.6.6.6/32): Add label 3 from peer 6.6.6.6:0

Reachability

Traffic between the CEs keeps flowing in every STEP. Changing how labels are assigned does not affect forwarding itself.

CE1 to CE2 (global, STEP 0)
RP/0/RP0/CPU0:CE1#ping 192.168.2.1 source 192.168.1.1 count 50 timeout 1
Sun Sep 20 12:30:17.594 UTC
Type escape sequence to abort.
Sending 50, 100-byte ICMP Echos to 192.168.2.1 timeout is 1 seconds:
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Success rate is 100 percent (50/50), round-trip min/avg/max = 18/23/44 ms
CE3 to CE4 (VPN, STEP 0)
RP/0/RP0/CPU0:CE3#ping 192.168.4.1 source 192.168.3.1 count 50 timeout 1
Sun Sep 20 12:30:44.808 UTC
Type escape sequence to abort.
Sending 50, 100-byte ICMP Echos to 192.168.4.1 timeout is 1 seconds:
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Success rate is 100 percent (50/50), round-trip min/avg/max = 18/21/35 ms

Configurations and show Output

For every STEP the following files were collected from all nine routers, one file per router. The verification config is the ..._run.txt (the final state is the one from the last STEP). Each STEP was collected in the order “clear the counters, ping, then show”, so Bytes Switched in show mpls forwarding and the counters in show interface cover only the pings sent during that STEP.

FileContents
..._clear.txtThe counters cleared at the start of the STEP (clear counters interface and clear mpls forwarding counters)
..._ping.txtping (50 packets) and traceroute: CE1 - CE2 (global), CE3 - CE4 (VPN), PE1 - PE2 (the core LSP)
..._show.txtshow version / show interface description / show route / OSPF / LDP / show mpls forwarding / show mpls label table detail / show cef and more; the PEs add BGP and VPN output
..._log.txtshow logging limited to the range of that STEP
..._run.txtshow running-config at that STEP (that is, the verification config for the STEP)
..._trace.txtThe five core routers only: show mpls ldp trace binding / peer / discovery / forwarding and show mpls lsd trace
..._commit.cfgThe configuration actually committed in that STEP (only the routers that were changed)

STEP 4 and STEP 5 were collected to check when IOS XR starts using a changed dynamic label range. They are not discussed in this article, but the files are kept as a record.

STEP 0: Initial state (implicit-null)

Routershowpingclearsyslogrunning-configtracecommitted configoperation run
CE1showpingclearlogrun———
PE1showpingclearlogruntrace——
P1show—clearlogruntrace——
P2show—clearlogruntrace——
P3show—clearlogruntrace——
PE2showpingclearlogruntrace——
CE2showpingclearlogrun———
CE3showpingclearlogrun———
CE4showpingclearlogrun———

STEP 1: Pings with a ToS value

Routershowpingclearsyslogrunning-configtracecommitted configoperation run
CE1showpingclearlogrun———
PE1showpingclearlogruntrace——
P1show—clearlogruntrace——
P2show—clearlogruntrace——
P3show—clearlogruntrace——
PE2showpingclearlogruntrace——
CE2showpingclearlogrun———
CE3showpingclearlogrun———
CE4showpingclearlogrun———

STEP 2: label local advertise explicit-null on PE2

Routershowpingclearsyslogrunning-configtracecommitted configoperation run
CE1showpingclearlogrun———
PE1showpingclearlogruntrace——
P1show—clearlogruntrace——
P2show—clearlogruntrace——
P3show—clearlogruntrace——
PE2showpingclearlogruntracecfg—
CE2showpingclearlogrun———
CE3showpingclearlogrun———
CE4showpingclearlogrun———

STEP 3: Remove explicit-null

Routershowpingclearsyslogrunning-configtracecommitted configoperation run
CE1showpingclearlogrun———
PE1showpingclearlogruntrace——
P1show—clearlogruntrace——
P2show—clearlogruntrace——
P3show—clearlogruntrace——
PE2showpingclearlogruntracecfg—
CE2showpingclearlogrun———
CE3showpingclearlogrun———
CE4showpingclearlogrun———

STEP 4: Change the label ranges on P2 and P3

Routershowpingclearsyslogrunning-configtracecommitted configoperation run
CE1showpingclearlogrun———
PE1showpingclearlogruntrace——
P1show—clearlogruntrace——
P2show—clearlogruntracecfgclearldp
P3show—clearlogruntracecfgclearldp
PE2showpingclearlogruntrace——
CE2showpingclearlogrun———
CE3showpingclearlogrun———
CE4showpingclearlogrun———

STEP 5: Restart LDP on P2 and P3 (final state)

Routershowpingclearsyslogrunning-configtracecommitted configoperation run
CE1showpingclearlogrun———
PE1showpingclearlogruntrace——
P1show—clearlogruntrace——
P2show—clearlogruntrace—restart
P3show—clearlogruntrace—restart
PE2showpingclearlogruntrace——
CE2showpingclearlogrun———
CE3showpingclearlogrun———
CE4showpingclearlogrun———

Packet captures were taken per STEP on all four core links.

STEPPE1 - P1P1 - P2P2 - P3P3 - PE2
0pcappcappcappcap
1pcappcappcappcap
2pcappcappcappcap
3pcappcappcappcap
4pcappcappcappcap
5pcappcappcappcap

References

RFCTitleSummary
RFC 3032MPLS Label Stack EncodingLabel stack entry format, reserved labels 0 to 3, TTL processing, EtherTypes.
RFC 5462Multiprotocol Label Switching (MPLS) Label Stack Entry: “EXP” Field Renamed to “Traffic Class” FieldRenames the EXP field to the Traffic Class field.
RFC 4182Removing a Restriction on the use of MPLS Explicit NULLAllows Explicit NULL anywhere in the stack, not only at the bottom.
RFC 3031Multiprotocol Label Switching ArchitecturePHP (section 3.16).

Reserved label registry: IANA Special-Purpose MPLS Label Values

Book: Luc De Ghein, MPLS Fundamentals (Cisco Press, 2006), Chapters 2 to 3

Related Articles