Lately, I have been playing around with a lab that involves Cisco IOS XR and Juniper devices. The main intention I have with it is to be able to quickly test something or to check how I could automate something.
The topology I am using is the following:

Nodes in this small SP lab have the following functions:
- rr:
vmx14andvmx15 - p:
vmx1-vmx4 - pe:
ios_xr_1,ios_xr_2,vmx5andvmx6
In this post, I will walk you through the following configuration and verification:
- Interfaces
- OSPF IGP configuration with p2p links, authentication, BFD and load-balancing
- MPLS LDP with authentication and LDP synchronization
- BGP sessions that supports VPN routes and MD5 authentication
- MPLS L3VPN with static routes
Interface configuration
The interface, OSPF and LDP configuration is going to be the same on every device. For that reason, the example configuration for these sections will include the configuration and verification steps on ios_xr_1 and vmx1.
The vmx1 and ios_xr_1 device are connected together using the following interfaces:

First we configure the interfaces on ios_xr_1:
interface Loopback0
ipv4 address 10.0.1.1 255.255.255.255
interface GigabitEthernet0/0/0/1
no shutdown
interface GigabitEthernet0/0/0/1.10
description vmx1
ipv4 address 10.0.2.1 255.255.255.254
encapsulation dot1q 1
Then we move on to vmx1:
set interfaces ge-0/0/3 flexible-vlan-tagging
set interfaces ge-0/0/3 encapsulation flexible-ethernet-services
set interfaces ge-0/0/3 unit 10 description ios_xr_1
set interfaces ge-0/0/3 unit 10 vlan-id 10
set interfaces ge-0/0/3 unit 10 family inet address 10.0.2.0/31
set interfaces lo0 unit 1 family inet address 10.0.0.1/32
Apart from the obvious difference in syntax, we notice two things. The Cisco interfaces need to be enabled using no shutdown before they can be used. On the Juniper device, we need to ready the interface to allow different encapsulation on the main interface as well as on the sub-interface. Because flexible-vlan-tagging and encapsulation flexible-ethernet-services will enable most of the scenario’s, I generally default to using that.
To verify the interface status on ios_xr_1, we issue to the following command:
RP/0/RP0/CPU0:ios_xr_1#show interface GigabitEthernet0/0/0/1.10
GigabitEthernet0/0/0/1.10 is up, line protocol is up
Interface state transitions: 1
Hardware is VLAN sub-interface(s), address is 5246.8457.a004
Description: vmx1
Internet address is 10.0.2.1/31
MTU 1518 bytes, BW 1000000 Kbit (Max: 1000000 Kbit)
reliability 255/255, txload 0/255, rxload 0/255
Encapsulation 802.1Q Virtual LAN, VLAN Id 10, loopback not set,
Last link flapped 01:32:09
ARP type ARPA, ARP timeout 04:00:00
Last input 00:00:00, output 00:00:00
Last clearing of "show interface" counters never
5 minute input rate 6000 bits/sec, 11 packets/sec
5 minute output rate 6000 bits/sec, 11 packets/sec
58949 packets input, 4244122 bytes, 130 total input drops
0 drops for unrecognized upper-level protocol
Received 9 broadcast packets, 1939 multicast packets
62790 packets output, 4514492 bytes, 0 total output drops
Output 2 broadcast packets, 1819 multicast packets
To verify the interface on vmx1:
salt@vmx1> show interfaces ge-0/0/3.10
Logical interface ge-0/0/3.10 (Index 414) (SNMP ifIndex 584)
Description: ios_xr_1
Flags: Up SNMP-Traps 0x4000 VLAN-Tag [ 0x8100.10 ] Encapsulation: ENET2
Input packets : 1578085
Output packets: 1575117
Protocol inet, MTU: 1500
Max nh cache: 75000, New hold nh limit: 75000, Curr nh cnt: 1, Curr new hold cnt: 0, NH drop cnt: 0
Flags: Sendbcast-pkt-to-re
Addresses, Flags: Is-Preferred Is-Primary
Destination: 10.0.2.0/31, Local: 10.0.2.0
Protocol multiservice, MTU: Unlimited
To wrap up interfaces configuration, we verify that we are able to ping across the interface:
salt@vmx1> ping 10.0.2.1 rapid PING 10.0.2.1 (10.0.2.1): 56 data bytes !!!!! --- 10.0.2.1 ping statistics --- 5 packets transmitted, 5 packets received, 0% packet loss round-trip min/avg/max/stddev = 3.581/4.794/5.613/0.668 ms
Configuring and verifying the IGP
What will be included in the IGP configuration is the following:
- OSPF area 0 interface configuration
- Take into account an OSPF reference bandwidth of 100G
- Authentication
- BFD
- Load-balancing
The following is the ios_xr_1 configuration:
router ospf 1
router-id 10.0.1.1
auto-cost reference-bandwidth 100000
area 0
interface Loopback0
passive enable
!
interface GigabitEthernet0/0/0/1.10
bfd minimum-interval 100
bfd fast-detect
bfd multiplier 5
authentication message-digest
message-digest-key 1 md5 encrypted 04480A0A1B701E1D
network point-to-point
!
The equivalent configuration for vmx1 is as follows:
set protocols ospf reference-bandwidth 100g
set protocols ospf area 0.0.0.0 interface lo0.1 passive
set protocols ospf area 0.0.0.0 interface ge-0/0/3.10 interface-type p2p
set protocols ospf area 0.0.0.0 interface ge-0/0/3.10 authentication md5 1 key "$9$HmQn/9p1RStuWL7NbwP5T"
set protocols ospf area 0.0.0.0 interface ge-0/0/3.10 bfd-liveness-detection minimum-interval 100
set protocols ospf area 0.0.0.0 interface ge-0/0/3.10 bfd-liveness-detection multiplier 5
Since load balancing is not something that is automatically enabled on Juniper devices, we need to add the following to vmx1:
set policy-options policy-statement lbpp term 1 then load-balance per-packet
set routing-options forwarding-table export lbpp
Do not let the load-balance per-packet name fool you. This action will have the Juniper load balance traffic per flow.
After the configuration is applied on both devices, we can start our verification.
First, we check the OSPF neighbor status on both sides.
On the Cisco, we check the following:
RP/0/RP0/CPU0:ios_xr_1#show ospf neighbor 10.0.0.1
* Indicates MADJ interface
# Indicates Neighbor awaiting BFD session up
Neighbors for OSPF 1
Neighbor 10.0.0.1, interface address 10.0.2.0
In the area 0 via interface GigabitEthernet0/0/0/1.10 , BFD enabled, Mode: Default
Neighbor priority is 128, State is FULL, 7 state changes
DR is 0.0.0.0 BDR is 0.0.0.0
Options is 0x52
LLS Options is 0x1 (LR)
Dead timer due in 00:00:37
Neighbor is up for 23:05:08
Number of DBD retrans during last exchange 0
Index 2/2, retransmission queue length 0, number of retransmission 18
First 0(0)/0(0) Next 0(0)/0(0)
Last retransmission scan length is 1, maximum is 2
Last retransmission scan time is 0 msec, maximum is 0 msec
LS Ack list: NSR-sync pending 0, high water mark 0
Neighbor BFD status: Session up
The neighbor state is FULL and the BFD session that provides us with fast failure detection is also up. Other details can be verified by issuing the show ospf 1 interface command, like so:
RP/0/RP0/CPU0:ios_xr_1#show ospf 1 interface GigabitEthernet0/0/0/1.10
GigabitEthernet0/0/0/1.10 is up, line protocol is up
Internet Address 10.0.2.1/31, Area 0
Label stack Primary label 1 Backup label 3 SRTE label 10
Process ID 1, Router ID 10.0.1.1, Network Type POINT_TO_POINT, Cost: 100
Transmit Delay is 1 sec, State POINT_TO_POINT, MTU 1500, MaxPktSz 1500
Forward reference No, Unnumbered no, Bandwidth 1000000
BFD enabled, BFD interval 100 msec, BFD multiplier 5, Mode: Default
Timer intervals configured, Hello 10, Dead 40, Wait 40, Retransmit 5
Hello due in 00:00:07:661
Index 2/2, flood queue length 0
Next 0(0)/0(0)
Last flood scan length is 1, maximum is 4
Last flood scan time is 0 msec, maximum is 0 msec
LS Ack List: current length 0, high water mark 22
Neighbor Count is 1, Adjacent neighbor count is 1
Adjacent with neighbor 10.0.0.1
Suppress hello for 0 neighbor(s)
Message digest authentication enabled
Youngest key id is 1
Multi-area interface Count is 0
Here we see that the interface is of the type POINT_TO_POINT and that the link has a cost of 100. We can also see the BFD settings here. And lastly, we see what authentication is configured.
In case we really wanted to know about all the details on the BFD session, we should turn to the following command:
RP/0/RP0/CPU0:ios_xr_1#show bfd session interface GigabitEthernet0/0/0/1.10 detail
I/f: GigabitEthernet0/0/0/1.10, Location: 0/0/CPU0
Dest: 10.0.2.0
Src: 10.0.2.1
State: UP for 0d:0h:27m:40s, number of times UP: 1
Session type: PR/V4/SH
Received parameters:
Version: 1, desired tx interval: 100 ms, required rx interval: 100 ms
Required echo rx interval: 0 ms, multiplier: 5, diag: None
My discr: 32, your discr: 2148532225, state UP, D/F/P/C/A: 0/0/0/1/0
Transmitted parameters:
Version: 1, desired tx interval: 100 ms, required rx interval: 100 ms
Required echo rx interval: 1 ms, multiplier: 5, diag: None
My discr: 2148532225, your discr: 32, state UP, D/F/P/C/A: 0/0/0/1/0
Timer Values:
Local negotiated async tx interval: 100 ms
Remote negotiated async tx interval: 100 ms
Desired echo tx interval: 100 ms, local negotiated echo tx interval: 0 ms
Echo detection time: 0 ms(0 ms*5), async detection time: 500 ms(100 ms*5)
Local Stats:
Intervals between async packets:
Tx: Number of intervals=100, min=1 ms, max=112 ms, avg=47 ms
Last packet transmitted 71 ms ago
Rx: Number of intervals=100, min=81 ms, max=119 ms, avg=100 ms
Last packet received 89 ms ago
Intervals between echo packets:
Tx: Number of intervals=0, min=0 s, max=0 s, avg=0 s
Last packet transmitted 0 s ago
Rx: Number of intervals=0, min=0 s, max=0 s, avg=0 s
Last packet received 0 s ago
Latency of echo packets (time between tx and rx):
Number of packets: 0, min=0 ms, max=0 ms, avg=0 ms
Session owner information:
Desired Adjusted
Client Interval Multiplier Interval Multiplier
-------------------- --------------------- ---------------------
ospf-1 100 ms 5 100 ms 5
Let’s verify the same things on the Juniper side and start off checking the OSPF neighbor state:
salt@vmx1> show ospf neighbor 10.0.1.1 extensive Address Interface State ID Pri Dead 10.0.2.1 ge-0/0/3.10 Full 10.0.1.1 1 37 Area 0.0.0.0, opt 0x52, DR 0.0.0.0, BDR 0.0.0.0 Up 23:08:38, adjacent 23:08:38 Topology default (ID 0) -> Bidirectional
The neighbor state is Full. To verify some of the other features we configured, we check the following:
salt@vmx1> show ospf interface ge-0/0/3.10 extensive Interface State Area DR ID BDR ID Nbrs ge-0/0/3.10 PtToPt 0.0.0.0 0.0.0.0 0.0.0.0 1 Type: P2P, Address: 10.0.2.0, Mask: 255.255.255.254, MTU: 1500, Cost: 1 Adj count: 1 Hello: 10, Dead: 40, ReXmit: 5, Not Stub Auth type: MD5, Active key ID: 1, Start time: 1970 Jan 1 00:00:00 UTC Protection type: None Topology default (ID 0) -> Cost: 100
The previous output reveals the interface is acting as a P2P OSPF interface, we have MD5 authentication enabled and the cost for the link is 100.
BFD can be verified using the following:
salt@vmx1> show bfd session extensive
Detect Transmit
Address State Interface Time Interval Multiplier
10.0.2.1 Up ge-0/0/3.10 0.500 0.100 5
Client OSPF realm ospf-v2 Area 0.0.0.0, TX interval 0.100, RX interval 0.100
Session up time 00:21:55
Local diagnostic None, remote diagnostic None
Remote state Up, version 1
Logical system 1, routing table index 9
Session type: Single hop BFD
Min async interval 0.100, min slow interval 1.000
Adaptive async TX interval 0.100, RX interval 0.100
Local min TX interval 0.100, minimum RX interval 0.100, multiplier 5
Remote min TX interval 0.100, min RX interval 0.100, multiplier 5
Local discriminator 17, remote discriminator 2148532225
Echo mode disabled/inactive
Remote is control-plane independent
Session ID: 0x16f
Both routers should have an OSPF route towards their neighboring loopback IP now. We can verify things on ios_xr_1 like so:
RP/0/RP0/CPU0:ios_xr_1#show route 10.0.0.1
Routing entry for 10.0.0.1/32
Known via "ospf 1", distance 110, metric 100, type intra area
Installed Aug 26 08:23:57.320 for 00:04:51
Routing Descriptor Blocks
10.0.2.0, from 10.0.0.1, via GigabitEthernet0/0/0/1.10
Route metric is 100
No advertising protos.
On vmx1, we do the following:
salt@vmx1> show route 10.0.1.1
inet.0: 30 destinations, 30 routes (30 active, 0 holddown, 0 hidden)
@ = Routing Use Only, # = Forwarding Use Only
+ = Active Route, - = Last Active, * = Both
10.0.1.1/32 *[OSPF/10] 00:04:24, metric 101
> to 10.0.2.1 via ge-0/0/3.10
Notice the difference in cost. Both Juniper and Cisco assigned a cost of 100 to the link, but the cost assigned to the loopback interface differs.
We can see the following on ios_xr_1:
RP/0/RP0/CPU0:ios_xr_1#show ospf interface loopback 0 Loopback0 is up, line protocol is up Internet Address 10.0.1.1/32, Area 0 Label stack Primary label 0 Backup label 0 SRTE label 0 Process ID 1, Router ID 10.0.1.1, Network Type LOOPBACK, Cost: 1 Loopback interface is treated as a stub Host
On vmx1, we see the following:
salt@vmx1> show ospf interface lo0.1 detail Interface State Area DR ID BDR ID Nbrs lo0.1 DRother 0.0.0.0 0.0.0.0 0.0.0.0 0 Type: LAN, Address: 10.0.0.1, Mask: 255.255.255.255, MTU: 65535, Cost: 0 Adj count: 0, Passive Hello: 10, Dead: 40, ReXmit: 5, Not Stub Auth type: None Protection type: None Topology default (ID 0) -> Passive, Cost: 0
After enabling the same OSPF configuration on every device in our topology, we can check the route table to see if we have an OSPF route towards the loopback interface of every other device.
On the ios_xr_1, we issue the following command:
RP/0/RP0/CPU0:ios_xr_1#show route ipv4 ospf | include 32 O 10.0.0.1/32 [110/100] via 10.0.2.0, 00:15:27, GigabitEthernet0/0/0/1.10 O 10.0.0.2/32 [110/100] via 10.0.2.4, 00:02:24, GigabitEthernet0/0/0/2.12 O 10.0.0.3/32 [110/200] via 10.0.2.4, 00:02:24, GigabitEthernet0/0/0/2.12 O 10.0.0.4/32 [110/200] via 10.0.2.0, 00:15:27, GigabitEthernet0/0/0/1.10 O 10.0.0.5/32 [110/300] via 10.0.2.4, 00:02:24, GigabitEthernet0/0/0/2.12 O 10.0.0.6/32 [110/300] via 10.0.2.4, 00:02:24, GigabitEthernet0/0/0/2.12 O 10.0.0.14/32 [110/300] via 10.0.2.0, 00:15:27, GigabitEthernet0/0/0/1.10 O 10.0.0.15/32 [110/200] via 10.0.2.0, 00:15:27, GigabitEthernet0/0/0/1.10 O 10.0.1.2/32 [110/201] via 10.0.2.4, 00:02:24, GigabitEthernet0/0/0/2.12
On vmx1, we issue the following command:
salt@vmx1> show route protocol ospf | match 32 10.0.0.2/32 *[OSPF/10] 00:00:05, metric 100 10.0.0.3/32 *[OSPF/10] 00:00:05, metric 200 10.0.0.4/32 *[OSPF/10] 00:00:05, metric 100 10.0.0.5/32 *[OSPF/10] 00:00:05, metric 200 10.0.0.6/32 *[OSPF/10] 00:00:05, metric 200 10.0.0.14/32 *[OSPF/10] 00:00:05, metric 200 10.0.0.15/32 *[OSPF/10] 00:00:05, metric 100 10.0.1.1/32 *[OSPF/10] 00:00:05, metric 101 10.0.1.2/32 *[OSPF/10] 00:00:05, metric 101
The last thing we can do is verify load-balancing. Since there is only 1 link between ios_xr_1 and vmx1, we check the routes towards a node in the network to which the devices have multiple equal cost paths. On the Cisco, we check the route towards ios_xr_2:
RP/0/RP0/CPU0:ios_xr_1#show route 10.0.1.2
Routing entry for 10.0.1.2/32
Known via "ospf 1", distance 110, metric 201, type intra area
Installed Aug 20 08:52:01.911 for 00:00:29
Routing Descriptor Blocks
10.0.2.0, from 10.0.1.2, via GigabitEthernet0/0/0/1.10
Route metric is 201
10.0.2.4, from 10.0.1.2, via GigabitEthernet0/0/0/2.12
Route metric is 201
No advertising protos.
On vmx1, we check the route towards vmx3:
salt@vmx1> show route 10.0.0.3
inet.0: 30 destinations, 30 routes (30 active, 0 holddown, 0 hidden)
@ = Routing Use Only, # = Forwarding Use Only
+ = Active Route, - = Last Active, * = Both
10.0.0.3/32 *[OSPF/10] 00:00:12, metric 200
to 192.168.4.1 via ge-0/0/1.4
> to 192.168.1.1 via ge-0/0/1.1
Here we see two equal cost routes are available. To see if they are actually being used, we need to issue the following command:
salt@vmx1> show route forwarding-table destination 10.0.0.3
Logical system: r1
Routing table: default.inet
Internet:
Enabled protocols: Bridging, Dual VLAN,
Destination Type RtRef Next hop Type Index NhRef Netif
10.0.0.3/32 user 0 ulst 1048587 4
192.168.4.1 ucst 843 12 ge-0/0/1.4
192.168.1.1 ucst 842 8 ge-0/0/1.1
The ulst is indicative of a List of unicast next hops that will be used to forward traffic.
Configuring and verifying LDP
In order to get any MPLS L3VPN going, we require an MPLS network. We’ll take the easy route and settle for LDP. The LDP configuration will include the following:
- authentication
- advertisement of the loopback IP only
Additionally, we want to ensure that the IGP metrics are reflected in the LDP routes on the Juniper device.
The LDP configuration is the same on every router, so again, we focus on the configuration between ios_xr_1 and vmx1. First, we check the MPLS configuration on ios_xr_1:
mpls ldp
router-id 10.0.1.1
neighbor
password encrypted 15010A00107B7977
!
address-family ipv4
label
local
allocate for host-routes
!
!
!
!
interface GigabitEthernet0/0/0/1.10
!
!
router ospf 1
area 0
interface GigabitEthernet0/0/0/1.10
mpls ldp sync
!
!
!
Now the configuration on vmx1:
set interfaces ge-0/0/3 unit 10 family mpls
set protocols mpls interface ge-0/0/3.10
set protocols ldp track-igp-metric
set protocols ldp interface ge-0/0/3.10
set protocols ldp session-group 0.0.0.0/0 authentication-key "$9$.539AtOEcl0BX7dVY2TzF"
set protocols ospf area 0.0.0.0 interface ge-0/0/3.10 ldp-synchronization
Notice how specifying the interfaces under LDP is enough for IOS XR. For the Juniper, configuring the interfaces under LDP is not enough. We also need to enable the interfaces for MPLS processing by specifying the MPLS address family under the interface configuration and under protocols mpls.
Another thing worth knowing is that the defaults for Juniper and Cisco are slightly different.
For Juniper, we configured the track-igp-metric option. This will copy the IGP metric into the inet.3 table. Normally, Juniper will default all LDP routes to 1.
For Cisco, we configured the mpls ldp address-family ipv4 label local allocate for host-routes option. This causes for the IOS XR router to advertise a label for the lo0 interface only. Unlike on a Juniper, the default is to advertise a label for every interface.
The other configuration items are pretty similar. Though different in syntax, on both Juniper and Cisco we configure the device to:
- enable LDP synchronization by specifying that under the OSPF stanza
- authenticate all LDP sessions through a single configuration command
Both Cisco as well as Juniper offer ways to inject into LDP whatever routes you want, both offer BFD protection and many more configuration options, but I do not want to go off to far into the weeds here. Instead, we will verify what we have configured so far.
First, we check the LDP adjacency state on Cisco:
RP/0/RP0/CPU0:ios_xr_1#show mpls ldp discovery
Local LDP Identifier: 10.0.1.1:0
Discovery Sources:
Interfaces:
GigabitEthernet0/0/0/1.10 : xmit/recv
VRF: 'default' (0x60000000)
LDP Id: 10.0.0.1:0, Transport address: 10.0.0.1
Hold time: 15 sec (local:15 sec, peer:15 sec)
Established: Aug 20 11:56:03.380 (00:00:53 ago)
Next on Juniper:
salt@vmx1> show ldp neighbor Address Interface Label space ID Hold time 10.0.2.1 ge-0/0/3.10 10.0.1.1:0 10
After forming this adjacency, an LDP session is established. To verify the LDP session, we issue the following command on the Cisco device:
RP/0/RP0/CPU0:ios_xr_1#show mpls ldp neighbor
Peer LDP Identifier: 10.0.0.1:0
TCP connection: 10.0.0.1:646 - 10.0.1.1:33317; MD5 on
Graceful Restart: No
Session Holdtime: 30 sec
State: Oper; Msgs sent/rcvd: 25/23; Downstream-Unsolicited
Up time: 00:01:52
LDP Discovery Sources:
IPv4: (1)
GigabitEthernet0/0/0/1.10
IPv6: (0)
Addresses bound to this peer:
IPv4: (5)
10.0.2.0 10.0.2.2 192.168.1.0 192.168.4.0
192.168.15.0
IPv6: (0)
And on the Juniper device:
salt@vmx1> show ldp session detail
Address: 10.0.1.1, State: Operational, Connection: Open, Hold time: 29
Session ID: 10.0.0.1:0--10.0.1.1:0
Next keepalive in 7 seconds
Passive, Maximum PDU: 4096, Hold time: 30, Neighbor count: 1
Neighbor types: discovered
Keepalive interval: 10, Connect retry interval: 1
Local address: 10.0.0.1, Remote address: 10.0.1.1
Up for 00:02:32
Capabilities advertised: none
Capabilities received: p2mp
Protection: disabled
Session flags: none
Authentication type: MD5 (0.0.0.0/0)
Local - Restart: disabled, Helper mode: enabled
Remote - Restart: disabled, Helper mode: disabled
Local maximum neighbor reconnect time: 120000 msec
Local maximum neighbor recovery time: 240000 msec
Local Label Advertisement mode: Downstream unsolicited
Remote Label Advertisement mode: Downstream unsolicited
Negotiated Label Advertisement mode: Downstream unsolicited
MTU discovery: disabled
Nonstop routing state: Not in sync
Next-hop addresses received:
10.0.1.1
10.0.2.1
10.0.2.5
Next we check the LDP synchronization. It was configured under OSPF, and on both Cisco as well as Juniper, that is the place where we verify it as well. First, we check the Cisco:
RP/0/RP0/CPU0:ios_xr_1#show ospf interface GigabitEthernet0/0/0/1.10 | include LDP LDP Sync Enabled, Sync Status: Achieved
Then we check the Juniper:
salt@vmx1> show ospf interface ge-0/0/3.10 extensive | match ldp LDP sync state: in sync, for: 00:13:39, reason: LDP session up
If all is well, both devices have now signaled an LDP LSP between them. Let’s verify this on the Cisco side:
RP/0/RP0/CPU0:ios_xr_1#show cef 10.0.0.1
10.0.0.1/32, version 7, internal 0x1000001 0x0 (ptr 0xdf13d88) [1], 0x0 (0xe0d79e8), 0xa20 (0xe710228)
remote adjacency to GigabitEthernet0/0/0/1.10
Prefix Len 32, traffic index 0, precedence n/a, priority 3
via 10.0.2.0/32, GigabitEthernet0/0/0/1.10, 8 dependencies, weight 0, class 0 [flags 0x0]
path-idx 0 NHID 0x0 [0xf141140 0xf141380]
next hop 10.0.2.0/32
remote adjacency
local label 24002 labels imposed {ImplNull}
RP/0/RP0/CPU0:ios_xr_1#show mpls ldp ipv4 forwarding 10.0.0.1/32
Codes:
- = GR label recovering, (!) = LFA FRR pure backup path
{} = Label stack with multi-line output for a routing path
G = GR, S = Stale, R = Remote LFA FRR backup
Prefix Label Label(s) Outgoing Next Hop Flags
In Out Interface G S R
--------------- ------- -------------- ------------ ------------------- -----
10.0.0.1/32 24002 ImpNull Gi0/0/0/1.10 10.0.2.0
And now on the Juniper side:
salt@vmx1> show route 10.0.1.2/32
inet.0: 31 destinations, 40 routes (31 active, 0 holddown, 0 hidden)
@ = Routing Use Only, # = Forwarding Use Only
+ = Active Route, - = Last Active, * = Both
10.0.1.2/32 @[OSPF/10] 00:03:22, metric 101
> to 10.0.2.3 via ge-0/0/3.11
#[LDP/9] 00:02:48, metric 1
> to 10.0.2.3 via ge-0/0/3.11
inet.3: 9 destinations, 9 routes (9 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both
10.0.1.2/32 *[LDP/9] 00:02:48, metric 1
> to 10.0.2.3 via ge-0/0/3.11
The LDP configuration is the same for every device in the topology. After enabling the configuration on every device, we can start verifying whether or not we have all the required routes.
We can check this on the Cisco using the following commands:
RP/0/RP0/CPU0:ios_xr_1#show mpls ldp ipv4 forwarding
Codes:
- = GR label recovering, (!) = LFA FRR pure backup path
{} = Label stack with multi-line output for a routing path
G = GR, S = Stale, R = Remote LFA FRR backup
Prefix Label Label(s) Outgoing Next Hop Flags
In Out Interface G S R
--------------- ------- -------------- ------------ ------------------- -----
10.0.0.1/32 24002 ImpNull Gi0/0/0/1.10 10.0.2.0
10.0.0.2/32 24000 ImpNull Gi0/0/0/2.12 10.0.2.4
10.0.0.3/32 24001 338 Gi0/0/0/2.12 10.0.2.4
10.0.0.4/32 24003 175 Gi0/0/0/1.10 10.0.2.0
10.0.0.5/32 24004 176 Gi0/0/0/1.10 10.0.2.0
340 Gi0/0/0/2.12 10.0.2.4
10.0.0.6/32 24005 177 Gi0/0/0/1.10 10.0.2.0
341 Gi0/0/0/2.12 10.0.2.4
10.0.0.14/32 24006 178 Gi0/0/0/1.10 10.0.2.0
10.0.0.15/32 24007 172 Gi0/0/0/1.10 10.0.2.0
10.0.1.2/32 24008 182 Gi0/0/0/1.10 10.0.2.0
348 Gi0/0/0/2.12 10.0.2.4
Next, we check the the same thing on the Juniper:
salt@vmx1> show route protocol ldp table inet.3
inet.3: 9 destinations, 9 routes (9 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both
10.0.0.2/32 *[LDP/9] 00:14:32, metric 1
> to 192.168.1.1 via ge-0/0/1.1
10.0.0.3/32 *[LDP/9] 00:14:32, metric 1
to 192.168.4.1 via ge-0/0/1.4, Push 316
> to 192.168.1.1 via ge-0/0/1.1, Push 338
10.0.0.4/32 *[LDP/9] 00:14:32, metric 1
> to 192.168.4.1 via ge-0/0/1.4
10.0.0.5/32 *[LDP/9] 00:14:32, metric 1
> to 192.168.4.1 via ge-0/0/1.4, Push 318
10.0.0.6/32 *[LDP/9] 00:14:32, metric 1
> to 192.168.4.1 via ge-0/0/1.4, Push 319
10.0.0.14/32 *[LDP/9] 00:14:32, metric 1
> to 192.168.4.1 via ge-0/0/1.4, Push 320
10.0.0.15/32 *[LDP/9] 00:14:32, metric 1
> to 192.168.15.1 via ge-0/0/1.15
10.0.1.1/32 *[LDP/9] 00:05:04, metric 1
> to 10.0.2.1 via ge-0/0/3.10
10.0.1.2/32 *[LDP/9] 00:04:02, metric 1
> to 10.0.2.3 via ge-0/0/3.11
In case we wanted to see the labels advertised/received acrros the LDP session, we can use the following command on ios_xr_1:
RP/0/RP0/CPU0:ios_xr_1#show mpls ldp bindings neighbor 10.0.0.1
Mon Aug 26 20:27:15.289 UTC
10.0.0.1/32, rev 16
Local binding: label: 24002
Remote bindings: (2 peers)
Peer Label
----------------- ---------
10.0.0.1:0 ImpNull
10.0.0.2/32, rev 12
Local binding: label: 24000
Remote bindings: (2 peers)
Peer Label
----------------- ---------
10.0.0.1:0 173
10.0.0.3/32, rev 14
Local binding: label: 24001
Remote bindings: (2 peers)
Peer Label
----------------- ---------
10.0.0.1:0 174
10.0.0.4/32, rev 18
Local binding: label: 24003
Remote bindings: (2 peers)
Peer Label
----------------- ---------
10.0.0.1:0 175
10.0.0.5/32, rev 20
Local binding: label: 24004
Remote bindings: (2 peers)
Peer Label
----------------- ---------
10.0.0.1:0 176
10.0.0.6/32, rev 22
Local binding: label: 24005
Remote bindings: (2 peers)
Peer Label
----------------- ---------
10.0.0.1:0 177
10.0.0.14/32, rev 24
Local binding: label: 24006
Remote bindings: (2 peers)
Peer Label
----------------- ---------
10.0.0.1:0 178
10.0.0.15/32, rev 26
Local binding: label: 24007
Remote bindings: (2 peers)
Peer Label
----------------- ---------
10.0.0.1:0 172
10.0.1.1/32, rev 10
Local binding: label: ImpNull
Remote bindings: (2 peers)
Peer Label
----------------- ---------
10.0.0.1:0 181
10.0.1.2/32, rev 28
Local binding: label: 24008
Remote bindings: (2 peers)
Peer Label
----------------- ---------
10.0.0.1:0 182
On vmx1, we can issue the following command:
salt@vmx01:r1> show ldp database session 10.0.1.1
Input label database, 10.0.0.1:0--10.0.1.1:0
Labels received: 10
Label Prefix
24002 10.0.0.1/32
24000 10.0.0.2/32
24001 10.0.0.3/32
24003 10.0.0.4/32
24004 10.0.0.5/32
24005 10.0.0.6/32
24006 10.0.0.14/32
24007 10.0.0.15/32
3 10.0.1.1/32
24008 10.0.1.2/32
Output label database, 10.0.0.1:0--10.0.1.1:0
Labels advertised: 10
Label Prefix
3 10.0.0.1/32
173 10.0.0.2/32
174 10.0.0.3/32
175 10.0.0.4/32
176 10.0.0.5/32
177 10.0.0.6/32
178 10.0.0.14/32
172 10.0.0.15/32
181 10.0.1.1/32
182 10.0.1.2/32
Additional verification of label information will be done when the VPN configuration is completed.
Configuring and verifying BGP
In this section, we will configure the following BGP sessions:

We will enable the BGP session to carry VPN routes and we will authenticate the sessions.
Let’s examine the configuration on the route reflectors first.
On vmx14:
set protocols bgp group rr type internal
set protocols bgp group rr local-address 10.0.0.14
set protocols bgp group rr family inet-vpn unicast
set protocols bgp group rr authentication-key "$9$SKwe87-dsoJDwYP5zF/9KMW"
set protocols bgp group rr cluster 0.0.0.1
set protocols bgp group rr neighbor 10.0.1.1
set protocols bgp group rr neighbor 10.0.1.2
set protocols bgp group rr neighbor 10.0.0.5
set protocols bgp group rr neighbor 10.0.0.6
set protocols bgp log-updown
routing-options autonomous-system 1
On vmx15:
set protocols bgp group rr type internal
set protocols bgp group rr local-address 10.0.0.15
set protocols bgp group rr family inet-vpn unicast
set protocols bgp group rr authentication-key "$9$SKwe87-dsoJDwYP5zF/9KMW"
set protocols bgp group rr cluster 0.0.0.1
set protocols bgp group rr neighbor 10.0.1.1
set protocols bgp group rr neighbor 10.0.1.2
set protocols bgp group rr neighbor 10.0.0.5
set protocols bgp group rr neighbor 10.0.0.6
set protocols bgp log-updown
routing-options autonomous-system 1
We name the BGP group rr and specify the neighbors configured under it will be IBGP neighbors by using type internal. Using local-address we source the session from the loopback address
The family inet-vpn unicast ensures that the BGP session will be signaled with the proper address family. The authentication-key knob authenticates the BGP session.
The cluster 0.0.0.1 statement is what turns a Juniper router into a route reflector.
The log-updown statement is to have the system log BGP session state changes and finally, we notice that the autonomous system itself is configured under the routing-options.
Now on to the Juniper PE configurations.
On vmx5:
set protocols bgp group rr-client type internal
set protocols bgp group rr-client local-address 10.0.0.5
set protocols bgp group rr-client family inet-vpn unicast
set protocols bgp group rr-client authentication-key "$9$SKwe87-dsoJDwYP5zF/9KMW"
set protocols bgp group rr-client neighbor 10.0.0.15
set protocols bgp group rr-client neighbor 10.0.0.14
set protocols bgp log-updown
routing-options autonomous-system 1
On vmx6:
set protocols bgp group rr-client type internal
set protocols bgp group rr-client local-address 10.0.0.6
set protocols bgp group rr-client family inet-vpn unicast
set protocols bgp group rr-client authentication-key "$9$SKwe87-dsoJDwYP5zF/9KMW"
set protocols bgp group rr-client neighbor 10.0.0.15
set protocols bgp group rr-client neighbor 10.0.0.14
set protocols bgp log-updown
routing-options autonomous-system 1
Except for the cluster statement, the configuration is pretty much the same.
On to the Cisco PE configurations. On ios_xr_1, we configure the following:
router bgp 1
bgp router-id 10.0.1.1
address-family vpnv4 unicast
!
neighbor-group rr-client
remote-as 1
password encrypted 08324D421D485744
update-source Loopback0
address-family vpnv4 unicast
soft-reconfiguration inbound always
!
!
neighbor 10.0.0.14
use neighbor-group rr-client
!
neighbor 10.0.0.15
use neighbor-group rr-client
!
!
On ios_xr_2:
router bgp 1
bgp router-id 10.0.1.2
address-family vpnv4 unicast
!
neighbor-group rr-client
remote-as 1
password encrypted 08324D421D485744
update-source Loopback0
address-family vpnv4 unicast
soft-reconfiguration inbound always
!
!
neighbor 10.0.0.14
use neighbor-group rr-client
!
neighbor 10.0.0.15
use neighbor-group rr-client
!
!
To verify that all the BGP sessions properly formed, the easiest thing is to check out the route reflectors.
First we check vmx14:
salt@vmx14> show bgp summary
Threading mode: BGP I/O
Groups: 1 Peers: 4 Down peers: 0
Table Tot Paths Act Paths Suppressed History Damp State Pending
inet.0
0 0 0 0 0 0
inet6.0
0 0 0 0 0 0
bgp.l3vpn.0
8 8 0 0 0 0
Peer AS InPkt OutPkt OutQ Flaps Last Up/Dwn State|#Active/Received/Accepted/Damped...
10.0.0.5 1 4 5 0 4 2 Establ
bgp.l3vpn.0: 2/2/2/0
10.0.0.6 1 4 5 0 4 2 Establ
bgp.l3vpn.0: 2/2/2/0
10.0.1.1 1 4 4 0 1 8 Establ
bgp.l3vpn.0: 2/2/2/0
10.0.1.2 1 4 4 0 1 11 Establ
Then we check vmx15:
salt@vmx15> show bgp summary
Threading mode: BGP I/O
Groups: 1 Peers: 4 Down peers: 0
Table Tot Paths Act Paths Suppressed History Damp State Pending
inet.0
0 0 0 0 0 0
inet6.0
0 0 0 0 0 0
bgp.l3vpn.0
8 8 0 0 0 0
Peer AS InPkt OutPkt OutQ Flaps Last Up/Dwn State|#Active/Received/Accepted/Damped...
10.0.0.5 1 4 5 0 2 20 Establ
bgp.l3vpn.0: 2/2/2/0
10.0.0.6 1 4 5 0 2 20 Establ
bgp.l3vpn.0: 2/2/2/0
10.0.1.1 1 4 4 0 1 22 Establ
bgp.l3vpn.0: 2/2/2/0
10.0.1.2 1 4 4 0 1 24 Establ
After verifying that all sessions are established, we can start verifying that the BGP sessions were properly configured. Since the rest of the BGP session verification will be the same, we will only verify the session between vmx15 and ios_xr_1
First, we check vmx15:
salt@vmx15> show bgp neighbor 10.0.1.1
Peer: 10.0.1.1+35276 AS 1 Local: 10.0.0.15+179 AS 1
Group: rr Routing-Instance: master
Forwarding routing-instance: master
Type: Internal State: Established (route reflector client)Flags: <Sync>
Last State: OpenConfirm Last Event: RecvKeepAlive
Last Error: Cease
Options: <Preference LocalAddress AuthKey LogUpDown Cluster AddressFamily Rib-group Refresh>
Authentication key is configured
Address families configured: inet-vpn-unicast
Local Address: 10.0.0.15 Holdtime: 90 Preference: 170
Number of flaps: 1
Last flap event: Stop
Error: 'Cease' Sent: 1 Recv: 0
Peer ID: 10.0.1.1 Local ID: 10.0.0.15 Active Holdtime: 90
Keepalive Interval: 30 Group index: 0 Peer index: 0 SNMP index: 0
I/O Session Thread: bgpio-0 State: Enabled
BFD: disabled, down
NLRI for restart configured on peer: inet-vpn-unicast
NLRI advertised by peer: inet-vpn-unicast
NLRI for this session: inet-vpn-unicast
Peer supports Refresh capability (2)
Stale routes from peer are kept for: 300
Peer does not support Restarter functionality
Peer does not support Receiver functionality
Peer does not support LLGR Restarter or Receiver functionality
Peer supports 4 byte AS extension (peer-as 1)
Peer does not support Addpath
NLRI that peer supports extended nexthop encoding for: inet-unicast inet-multicast inet-vpn-unicast
Table bgp.l3vpn.0 Bit: 40000
RIB State: BGP restart is complete
RIB State: VPN restart is complete
Send state: in sync
Active prefixes: 2
Received prefixes: 2
Accepted prefixes: 2
Suppressed due to damping: 0
Advertised prefixes: 6
Last traffic (seconds): Received 15 Sent 20 Checked 380
Input messages: Total 16 Updates 2 Refreshes 0 Octets 459
Output messages: Total 17 Updates 3 Refreshes 0 Octets 618
Output Queue[3]: 0 (bgp.l3vpn.0, inet-vpn-unicast)
This output reveals all we need to know. It displays the source and destination IP for the BGP session as well as the AS that is configured on both routers. Additionally, we can see the state of the BGP session as well as the configuration options for this session. We see that the session is authenticated and we can see that the BGP session supports the inet-vpn-unicast address family.
Let’s move to ios_xr_1 and check the session from there:
RP/0/RP0/CPU0:ios_xr_1#show bgp neighbors 10.0.0.15
BGP neighbor is 10.0.0.15
Remote AS 1, local AS 1, internal link
Remote router ID 10.0.0.15
BGP state = Established, up for 14:07:55
NSR State: None
Last read 00:00:02, Last read before reset 14:08:09
Hold time is 90, keepalive interval is 30 seconds
Configured hold time: 180, keepalive: 60, min acceptable hold time: 3
Last write 00:00:17, attempted 19, written 19
Second last write 00:00:47, attempted 19, written 19
Last write before reset 14:08:36, attempted 19, written 19
Second last write before reset 14:09:06, attempted 19, written 19
Last write pulse rcvd Aug 21 03:47:03.536 last full not set pulse count 3879
Last write pulse rcvd before reset 14:08:22
Socket not armed for io, armed for read, armed for write
Last write thread event before reset 14:08:22, second last 14:08:23
Last KA expiry before reset 14:08:36, second last 14:09:06
Last KA error before reset 00:00:00, KA not sent 00:00:00
Last KA start before reset 14:08:36, second last 14:09:06
Precedence: internet
Non-stop routing is enabled
Multi-protocol capability received
Neighbor capabilities:
Route refresh: advertised (old + new) and received (old + new)
Graceful Restart (GR Awareness): received
4-byte AS: advertised and received
Address family VPNv4 Unicast: advertised and received
Received 2074 messages, 1 notifications, 0 in queue
Sent 1838 messages, 1 notifications, 0 in queue
Minimum time between advertisement runs is 0 secs
Inbound message logging enabled, 3 messages buffered
Outbound message logging enabled, 3 messages buffered
For Address Family: VPNv4 Unicast
BGP neighbor version 846
Update group: 0.1 Filter-group: 0.2 No Refresh request being processed
Inbound soft reconfiguration allowed (override route-refresh)
Extended Nexthop Encoding: advertised
Route refresh request: received 0, sent 0
6 accepted prefixes, 0 are bestpaths
Exact no. of prefixes denied : 0.
Cumulative no. of prefixes denied: 0.
Prefix advertised 2, suppressed 0, withdrawn 0
Maximum prefixes allowed 2097152
Threshold for warning message 75%, restart interval 0 min
AIGP is enabled
An EoR was received during read-only mode
Last ack version 846, Last synced ack version 0
Outstanding version objects: current 0, max 1, refresh 0
Additional-paths operation: None
Send Multicast Attributes
Connections established 8; dropped 7
Local host: 10.0.1.1, Local port: 179, IF Handle: 0x00000000
Foreign host: 10.0.0.15, Foreign port: 51470
Last reset 14:08:09, due to BGP Notification received: peer unconfigured
Time since last notification sent to neighbor: 14:15:05
Error Code: configuration change
Notification data sent:
None
Time since last notification received from neighbor: 14:08:09
Error Code: peer unconfigured
Notification data received:
None
Save for the fact that the session is authenticated, we can see most of the same information in this output.
Configuring and verifying the MPLS L3VPN
The MPLS L3VPN we will configure will be a very basic one:

On every PE, we will configure a vrf called cust-1. We will place 1 interface inside the VRF and we will configure a single static route towards the customer device. We will cover the example configuration on ios_xr_1 and on vmx5.
Let’s check out the configuration on ios_xr_1 first. We start by configuring the vrf:
vrf cust-1
rd 1:1
address-family ipv4 unicast
import route-target
1:1
!
export route-target
1:1
!
!
!
Next, we configure the interface and place that interface in the vrf:
interface GigabitEthernet0/0/0/3.2002
vrf cust-1
ipv4 address 10.0.0.9 255.255.255.252
encapsulation dot1q 2002
After this, we configure a static route for the vrf under router static:
router static
vrf cust-1
address-family ipv4 unicast
192.168.1.1/32 10.0.0.10
!
!
!
Finally, we need to configure BGP to advertise the connected routes and the static for this vrf:
router bgp 1
vrf cust-1
address-family ipv4 unicast
label mode per-vrf
redistribute connected
redistribute static
!
!
!
You’ll also notice the label mode per-vrf. This is not really necessary, but I configured it since I will also use per vrf label allocation on the Juniper.
Now we move to the Juniper configuration on vmx5. The configuration components are the same, but everything is organized a bit differently:
set interfaces ge-0/0/1 unit 2000 vlan-id 2000
set interfaces ge-0/0/1 unit 2000 family inet address 10.0.0.1/30
set routing-instances cust-1 instance-type vrf
set routing-instances cust-1 interface ge-0/0/1.2000
set routing-instances cust-1 route-distinguisher 1:1
set routing-instances cust-1 vrf-target target:1:1
set routing-instances cust-1 vrf-table-label
set routing-instances cust-1 routing-options static route 192.168.1.3/32 next-hop 10.0.0.2
The first thing you’ll notice is that the interface configuration is no different from one that is not part of any vrf. After the interface, there is the routing-instance configuration. Basically, on a Juniper, everything that has to do with the routing-instance is configured there. From routing-protocols to RDs and RTs.
In our example, we start out specifying what type of vrf it is by specifying the instance-type. For MPLS L3VPN, we define the instance as a vrf.
Next, we define the interfaces that should be part of the vrf. This is followed by the route-target and route-distinguisher. Through the use of vrf-target, we tell the device to export and import the route-target community that is referenced.
After this is vrf-table-label, which ensures the creation of a logical internal interface that enables per vrf label allocation and simplified configuration.
The last configuration item is the static route, which is configured under the routing-options in the routing-instance.
Now that we have configured the vrf everywhere, we can start our verification. First we verify connectivity from a test device I have connected to the vrf on ios_xr_1:
salt@test-vmx> ping routing-instance c1-1 rapid source 192.168.1.1 192.168.1.2 PING 192.168.1.2 (192.168.1.2): 56 data bytes !!!!! --- 192.168.1.2 ping statistics --- 5 packets transmitted, 5 packets received, 0% packet loss round-trip min/avg/max/stddev = 1.715/1.964/2.244/0.229 ms salt@test-vmx> ping routing-instance c1-1 rapid source 192.168.1.1 192.168.1.3 PING 192.168.1.3 (192.168.1.3): 56 data bytes !!!!! --- 192.168.1.3 ping statistics --- 5 packets transmitted, 5 packets received, 0% packet loss round-trip min/avg/max/stddev = 3.084/3.295/3.457/0.138 ms salt@test-vmx> ping routing-instance c1-1 rapid source 192.168.1.1 192.168.1.4 PING 192.168.1.4 (192.168.1.4): 56 data bytes !!!!! --- 192.168.1.4 ping statistics --- 5 packets transmitted, 5 packets received, 0% packet loss round-trip min/avg/max/stddev = 3.004/3.490/5.213/0.863 ms
So we can exchange ICMP. But there is a lot more to check. Let’s check the routing tables on ios_xr_1 and vmx5 first:
RP/0/RP0/CPU0:ios_xr_1#show route vrf cust-1
Codes: C - connected, S - static, R - RIP, B - BGP, (>) - Diversion path
D - EIGRP, EX - EIGRP external, O - OSPF, IA - OSPF inter area
N1 - OSPF NSSA external type 1, N2 - OSPF NSSA external type 2
E1 - OSPF external type 1, E2 - OSPF external type 2, E - EGP
i - ISIS, L1 - IS-IS level-1, L2 - IS-IS level-2
ia - IS-IS inter area, su - IS-IS summary null, * - candidate default
U - per-user static route, o - ODR, L - local, G - DAGR, l - LISP
A - access/subscriber, a - Application route
M - mobile route, r - RPL, t - Traffic Engineering, (!) - FRR Backup path
Gateway of last resort is not set
B 10.0.0.0/30 [200/0] via 10.0.0.5 (nexthop in vrf default), 00:06:36
B 10.0.0.4/30 [200/0] via 10.0.0.6 (nexthop in vrf default), 00:06:36
C 10.0.0.8/30 is directly connected, 00:07:22, GigabitEthernet0/0/0/3.2002
L 10.0.0.9/32 is directly connected, 00:07:22, GigabitEthernet0/0/0/3.2002
B 10.0.0.12/30 [200/0] via 10.0.1.2 (nexthop in vrf default), 00:05:05
S 192.168.1.1/32 [1/0] via 10.0.0.10, 00:07:22
B 192.168.1.2/32 [200/0] via 10.0.1.2 (nexthop in vrf default), 00:05:05
B 192.168.1.3/32 [200/0] via 10.0.0.5 (nexthop in vrf default), 00:06:36
B 192.168.1.4/32 [200/0] via 10.0.0.6 (nexthop in vrf default), 00:06:36
Now on the Juniper:
salt@vmx5> show route table cust-1
cust-1.inet.0: 9 destinations, 15 routes (9 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both
10.0.0.0/30 *[Direct/0] 5d 12:57:01
> via ge-0/0/1.2000
10.0.0.1/32 *[Local/0] 5d 12:57:01
Local via ge-0/0/1.2000
10.0.0.4/30 *[BGP/170] 00:55:09, localpref 100, from 10.0.0.14
AS path: I, validation-state: unverified
to 192.168.5.1 via ge-0/0/1.5, Push 273, Push 319(top)
> to 192.168.7.1 via ge-0/0/1.7, Push 273, Push 327(top)
[BGP/170] 00:55:09, localpref 100, from 10.0.0.15
AS path: I, validation-state: unverified
to 192.168.5.1 via ge-0/0/1.5, Push 273, Push 319(top)
> to 192.168.7.1 via ge-0/0/1.7, Push 273, Push 327(top)
10.0.0.8/30 *[BGP/170] 00:06:42, MED 0, localpref 100, from 10.0.0.14
AS path: ?, validation-state: unverified
> to 192.168.5.1 via ge-0/0/1.5, Push 24009, Push 325(top)
to 192.168.7.1 via ge-0/0/1.7, Push 24009, Push 333(top)
[BGP/170] 00:06:42, MED 0, localpref 100, from 10.0.0.15
AS path: ?, validation-state: unverified
> to 192.168.5.1 via ge-0/0/1.5, Push 24009, Push 325(top)
to 192.168.7.1 via ge-0/0/1.7, Push 24009, Push 333(top)
10.0.0.12/30 *[BGP/170] 00:05:21, MED 0, localpref 100, from 10.0.0.14
AS path: ?, validation-state: unverified
> to 192.168.5.1 via ge-0/0/1.5, Push 24009, Push 326(top)
to 192.168.7.1 via ge-0/0/1.7, Push 24009, Push 334(top)
[BGP/170] 00:05:21, MED 0, localpref 100, from 10.0.0.15
AS path: ?, validation-state: unverified
> to 192.168.5.1 via ge-0/0/1.5, Push 24009, Push 326(top)
to 192.168.7.1 via ge-0/0/1.7, Push 24009, Push 334(top)
192.168.1.1/32 *[BGP/170] 00:06:42, MED 0, localpref 100, from 10.0.0.14
AS path: ?, validation-state: unverified
to 192.168.5.1 via ge-0/0/1.5, Push 24009, Push 325(top)
> to 192.168.7.1 via ge-0/0/1.7, Push 24009, Push 333(top)
[BGP/170] 00:06:42, MED 0, localpref 100, from 10.0.0.15
AS path: ?, validation-state: unverified
to 192.168.5.1 via ge-0/0/1.5, Push 24009, Push 325(top)
> to 192.168.7.1 via ge-0/0/1.7, Push 24009, Push 333(top)
192.168.1.2/32 *[BGP/170] 00:05:21, MED 0, localpref 100, from 10.0.0.14
AS path: ?, validation-state: unverified
to 192.168.5.1 via ge-0/0/1.5, Push 24009, Push 326(top)
> to 192.168.7.1 via ge-0/0/1.7, Push 24009, Push 334(top)
[BGP/170] 00:05:21, MED 0, localpref 100, from 10.0.0.15
AS path: ?, validation-state: unverified
to 192.168.5.1 via ge-0/0/1.5, Push 24009, Push 326(top)
> to 192.168.7.1 via ge-0/0/1.7, Push 24009, Push 334(top)
192.168.1.3/32 *[Static/5] 5d 12:57:01
> to 10.0.0.2 via ge-0/0/1.2000
192.168.1.4/32 *[BGP/170] 00:55:09, localpref 100, from 10.0.0.14
AS path: I, validation-state: unverified
to 192.168.5.1 via ge-0/0/1.5, Push 273, Push 319(top)
> to 192.168.7.1 via ge-0/0/1.7, Push 273, Push 327(top)
[BGP/170] 00:55:09, localpref 100, from 10.0.0.15
AS path: I, validation-state: unverified
to 192.168.5.1 via ge-0/0/1.5, Push 273, Push 319(top)
> to 192.168.7.1 via ge-0/0/1.7, Push 273, Push 327(top)
On both PE devices, we can see that we have learned all of the static routes. Let’s check things in a little more detail now and move on to check the route-advertisements and forwarding entries.
First, we check the signaling from the Juniper to the Cisco and the forwarding from the Cisco to the Juniper.

For this reasons, we start out on the Juniper vmx5 router. We can check the following to see what information is advertised to the route reflector:
salt@vmx5> show route advertising-protocol bgp 10.0.0.15 table cust-1 detail
cust-1.inet.0: 9 destinations, 15 routes (9 active, 0 holddown, 0 hidden)
* 10.0.0.0/30 (1 entry, 1 announced)
BGP group rr-client type Internal
Route Distinguisher: 1:1
VPN Label: 282
Nexthop: Self
Flags: Nexthop Change
Localpref: 100
AS path: [1] I
Communities: target:1:1
* 192.168.1.3/32 (1 entry, 1 announced)
BGP group rr-client type Internal
Route Distinguisher: 1:1
VPN Label: 282
Nexthop: Self
Flags: Nexthop Change
Localpref: 100
AS path: [1] I
Communities: target:1:1
When we move to the ios_xr_1, we can use the following to see the routes that were advertised by vmx5:
RP/0/RP0/CPU0:ios_xr_1#show bgp vpnv4 unicast vrf cust-1 192.168.1.3/32
BGP routing table entry for 192.168.1.3/32, Route Distinguisher: 1:1
Versions:
Process bRIB/RIB SendTblVer
Speaker 12 12
Last Modified: Aug 26 19:28:18.892 for 00:09:43
Paths: (2 available, best #1)
Not advertised to any peer
Path #1: Received by speaker 0
Not advertised to any peer
Local, (received & used)
10.0.0.5 (metric 300) from 10.0.0.14 (10.0.0.5)
Received Label 282
Origin IGP, localpref 100, valid, internal, best, group-best, import-candidate, imported
Received Path ID 0, Local Path ID 1, version 12
Extended community: RT:1:1
Originator: 10.0.0.5, Cluster list: 0.0.0.1
Source AFI: VPNv4 Unicast, Source VRF: cust-1, Source Route Distinguisher: 1:1
Path #2: Received by speaker 0
Not advertised to any peer
Local, (received & used)
10.0.0.5 (metric 300) from 10.0.0.15 (10.0.0.5)
Received Label 282
Origin IGP, localpref 100, valid, internal, import-candidate, imported
Received Path ID 0, Local Path ID 0, version 0
Extended community: RT:1:1
Originator: 10.0.0.5, Cluster list: 0.0.0.1
Source AFI: VPNv4 Unicast, Source VRF: cust-1, Source Route Distinguisher: 1:1
RP/0/RP0/CPU0:ios_xr_1#show bgp vpnv4 unicast vrf cust-1 10.0.0.0/30
BGP routing table entry for 10.0.0.0/30, Route Distinguisher: 1:1
Versions:
Process bRIB/RIB SendTblVer
Speaker 10 10
Last Modified: Aug 26 19:28:18.892 for 00:09:57
Paths: (2 available, best #1)
Not advertised to any peer
Path #1: Received by speaker 0
Not advertised to any peer
Local, (received & used)
10.0.0.5 (metric 300) from 10.0.0.14 (10.0.0.5)
Received Label 282
Origin IGP, localpref 100, valid, internal, best, group-best, import-candidate, imported
Received Path ID 0, Local Path ID 1, version 10
Extended community: RT:1:1
Originator: 10.0.0.5, Cluster list: 0.0.0.1
Source AFI: VPNv4 Unicast, Source VRF: cust-1, Source Route Distinguisher: 1:1
Path #2: Received by speaker 0
Not advertised to any peer
Local, (received & used)
10.0.0.5 (metric 300) from 10.0.0.15 (10.0.0.5)
Received Label 282
Origin IGP, localpref 100, valid, internal, import-candidate, imported
Received Path ID 0, Local Path ID 0, version 0
Extended community: RT:1:1
Originator: 10.0.0.5, Cluster list: 0.0.0.1
Source AFI: VPNv4 Unicast, Source VRF: cust-1, Source Route Distinguisher: 1:1
So we received the routes with VPN label 282. If we wanted to understand what the device will do with this information, we can check the following:
RP/0/RP0/CPU0:ios_xr_1#show cef vrf cust-1 192.168.1.3
192.168.1.3/32, version 8, internal 0x5000001 0x0 (ptr 0xdf127ec) [1], 0x0 (0xe0d7b28), 0xa08 (0xe7103a8)
Updated Aug 26 19:28:18.664
Prefix Len 32, traffic index 0, precedence n/a, priority 3
via 10.0.0.5/32, 3 dependencies, recursive [flags 0x6000]
path-idx 0 NHID 0x0 [0xd436d90 0x0]
recursion-via-/32
next hop VRF - 'default', table - 0xe0000000
next hop 10.0.0.5/32 via 24004/0/21
next hop 10.0.2.0/32 Gi0/0/0/1.10 labels imposed {176 282}
next hop 10.0.2.4/32 Gi0/0/0/2.12 labels imposed {340 282}
The 282 label makes sense, we just saw that one in the BGP advertisement. The 176 and 340 are transport labels towards vmx5. We can see that using:
RP/0/RP0/CPU0:ios_xr_1#show mpls ldp forwarding 10.0.0.5/32
Codes:
- = GR label recovering, (!) = LFA FRR pure backup path
{} = Label stack with multi-line output for a routing path
G = GR, S = Stale, R = Remote LFA FRR backup
Prefix Label Label(s) Outgoing Next Hop Flags
In Out Interface G S R
--------------- ------- -------------- ------------ ------------------- -----
10.0.0.5/32 24004 176 Gi0/0/0/1.10 10.0.2.0
340 Gi0/0/0/2.12 10.0.2.4
Following that label is pretty easy. Let’s follow the 176 label. The outgoing interface leads us to vmx1, so we hop on over to that router and use the following:
salt@vmx1> show route table mpls.0 label 176
mpls.0: 18 destinations, 18 routes (18 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both
176 *[LDP/9] 00:23:18, metric 1
> to 192.168.4.1 via ge-0/0/1.4, Swap 318
This leads us to vmx4:
salt@vmx4> show route table mpls.0 label 318
mpls.0: 18 destinations, 18 routes (18 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both
318 *[LDP/9] 01:02:12, metric 1
> to 192.168.5.0 via ge-0/0/2.5, Pop
318(S=0) *[LDP/9] 01:02:12, metric 1
> to 192.168.5.0 via ge-0/0/2.5, Pop
Prior to sending traffic to vmx5, the outer label is swapped to 0. On vmx5, the router will only have to deal with the VPN label (282):
salt@vmx5> show route table mpls.0 label 282
mpls.0: 16 destinations, 16 routes (16 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both
282 *[VPN/0] 5d 13:04:17
> via lsi.117440513 (cust-1), Pop
The label is popped and further route lookups are done inside the vrf. Following the path to 192.168.1.3, we would issue the following:
salt@vmx5> show route 192.168.1.3
cust-1.inet.0: 9 destinations, 15 routes (9 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both
192.168.1.3/32 *[Static/5] 00:59:53
> to 10.0.0.2 via ge-0/0/1.2000
Let’s go the other way around and verify the routing information advertised by ios_xr_1, check what is received on vmx5 and then finish up tracing the forwarding path from vmx5 to ios_xr_1.

First, we check the route advertisement from ios_xr_1 by issuing the following command:
RP/0/RP0/CPU0:ios_xr_1#show bgp vpnv4 unicast vrf cust-1 advertised neighbor 10.0.0.15
Route Distinguisher: 1:1
10.0.0.8/30 is advertised to 10.0.0.15
Path info:
neighbor: Local neighbor router id: 10.0.1.1
valid redistributed best import-candidate
Received Path ID 0, Local Path ID 1, version 17
Attributes after inbound policy was applied:
next hop: 0.0.0.0
MET ORG AS EXTCOMM
origin: incomplete metric: 0
aspath:
extended community: RT:1:1
Attributes after outbound policy was applied:
next hop: 10.0.1.1
MET ORG AS EXTCOMM
origin: incomplete metric: 0
aspath:
extended community: RT:1:1
Route Distinguisher: 1:1
192.168.1.1/32 is advertised to 10.0.0.15
Path info:
neighbor: Local neighbor router id: 10.0.1.1
valid redistributed best import-candidate
Received Path ID 0, Local Path ID 1, version 18
Attributes after inbound policy was applied:
next hop: 10.0.0.10
MET ORG AS EXTCOMM
origin: incomplete metric: 0
aspath:
extended community: RT:1:1
Attributes after outbound policy was applied:
next hop: 10.0.1.1
MET ORG AS EXTCOMM
origin: incomplete metric: 0
aspath:
extended community: RT:1:1
To see the local label, we can use the following:
RP/0/RP0/CPU0:ios_xr_1#show mpls forwarding vrf cust-1
Local Outgoing Prefix Outgoing Next Hop Bytes
Label Label or ID Interface Switched
------ ----------- ------------------ ------------ --------------- ------------
24009 Aggregate cust-1: Per-VRF Aggr[V] \
cust-1 1260
To check what routing information is received on the Juniper device, we can issue the following command on vmx5:
salt@vmx5> show route receive-protocol bgp 10.0.0.15 table cust-1 next-hop 10.0.1.1 detail
cust-1.inet.0: 9 destinations, 15 routes (9 active, 0 holddown, 0 hidden)
10.0.0.8/30 (2 entries, 1 announced)
Import Accepted
Route Distinguisher: 1:1
VPN Label: 24009
Nexthop: 10.0.1.1
MED: 0
Localpref: 100
AS path: ? (Originator)
Cluster list: 0.0.0.1
Originator ID: 10.0.1.1
Communities: target:1:1
192.168.1.1/32 (2 entries, 1 announced)
Import Accepted
Route Distinguisher: 1:1
VPN Label: 24009
Nexthop: 10.0.1.1
MED: 0
Localpref: 100
AS path: ? (Originator)
Cluster list: 0.0.0.1
Originator ID: 10.0.1.1
Communities: target:1:1
The corresponding forwarding entry can be checked like this:
salt@vmx5> show route forwarding-table vpn cust-1 destination 192.168.1.1
Logical system: r5
Routing table: cust-1.inet
Internet:
Enabled protocols: Bridging, All VLANs,
Destination Type RtRef Next hop Type Index NhRef Netif
192.168.1.1/32 user 0 indr 1048604 3
ulst 1048602 2
192.168.5.1 Push 24009, Push 325(top) 1690 2 ge-0/0/1.5
192.168.7.1 Push 24009, Push 333(top) 1691 2 ge-0/0/1.7
We just saw that the VPN label, 24009, was learned through BGP. The top labels, 325 and 333, are the transport labels. We can see this when we check the routing information to the ios_xr_1 device (which the VPN routes will resolve to):
salt@vmx5> show route 10.0.1.1 table inet.3
inet.3: 9 destinations, 9 routes (9 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both
10.0.1.1/32 *[LDP/9] 00:18:20, metric 301
to 192.168.5.1 via ge-0/0/1.5, Push 325
> to 192.168.7.1 via ge-0/0/1.7, Push 333
Let’s follow label 325, which is send to vmx4:
salt@vmx4> show route table mpls.0 label 325
mpls.0: 18 destinations, 18 routes (18 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both
325 *[LDP/9] 00:18:49, metric 1
> to 192.168.4.0 via ge-0/0/2.4, Swap 181
This is forwarded to vmx1:
salt@vmx1> show route table mpls.0 label 181
mpls.0: 18 destinations, 18 routes (18 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both
181 *[LDP/9] 00:19:07, metric 1
> to 10.0.2.1 via ge-0/0/3.10, Pop
181(S=0) *[LDP/9] 00:19:07, metric 1
> to 10.0.2.1 via ge-0/0/3.10, Pop
This will leave the Cisco to deal with the explicit null label and the VPN label we saw earlier (24009).
Closing thoughts
These are the basics to get a VRF going between Juniper and Cisco IOS XR. There are differences in configuration constructs, but coming from a Juniper background and having done some IOS a long time ago, I found the IOS XR not too difficult to understand or work with.