MPLS

MPLS L3VPN Configuration Step by Step: VRF, RD, RT, and MP-BGP

MPLS L3VPN configuration on Cisco IOS XE: show ip bgp vpnv4 all output with two route distinguishers
In: MPLS, BGP, Labs

MPLS L3VPN is the service that made MPLS a service-provider standard, and it is still what most enterprises are actually buying when their carrier sells them "an MPLS circuit." The concept is simple to state and surprisingly fiddly to configure: many customers, each with their own private (and possibly overlapping) IP addressing, carried across one shared provider backbone, with each customer seeing only their own routes.

This article is the configuration walkthrough. Every command and every piece of output below comes from a five-router Cisco IOS XE lab (CE1 - PE1 - P - PE2 - CE2) running OSPF as the core IGP, LDP for label distribution, and MP-iBGP carrying VPNv4 between the two PE routers. A real Debian host sits behind CE1 and pings across the VPN at the end. If you want the wider context first, start with the MPLS cluster guide.

The Four Moving Parts

Before touching a keyboard, get the pieces straight. An L3VPN is built from four things stacked on top of each other, and if you configure them out of order you will spend an hour chasing a symptom caused by a missing layer underneath.

1. Core IGP
Job
Gives every P and PE router loopback-to-loopback reachability
TypicalOSPF or IS-IS
2. LDP
Job
Assigns a transport label to every IGP prefix, building the LSP
Runs onPE and P routers
3. VRF + RD + RT
Job
Separates customers on the PE; makes overlapping prefixes unique and controls who imports what
Runs onPE routers only
4. MP-BGP VPNv4
Job
Carries customer prefixes plus their RD, RT, and VPN label between PEs
Runs onPE routers only

Notice what is missing from that list: the P router in the middle of the core runs items 1 and 2 and nothing else. It has no VRFs, no BGP, and no idea that any customer exists. We will prove that with real output later, because it is the single most important architectural fact about L3VPN and the thing most people get wrong when they first draw the diagram.

The Lab

Five IOS XE routers in a line, plus a real Linux host bridged in behind CE1 so the final ping is not simulated:

  VM 192.168.99.100
        |
      [ CE1 ] --- [ PE1 ] --- [  P  ] --- [ PE2 ] --- [ CE2 ]
    10.20.1.2/30   .1/30                              10.20.2.2/30
                  10.30.30.1/24  .2  .1  10.30.31.2/24
   Loopbacks:  PE1 10.255.0.1   P 10.255.0.3   PE2 10.255.0.2
   Customer site 1 LAN: 192.168.99.0/24 (the real VM)
   Customer site 2 LAN: 10.20.20.0/24  (CE2 Loopback1)

Provider AS is 65000. Customer VRF is CUST-A with route distinguisher 65000:1 and route target 65000:1 imported and exported. A second VRF, CUST-B, gets RD/RT 65000:2 and deliberately overlapping addressing, which we use at the end to prove the separation is real.

Step 1: Core IGP and Loopbacks

Everything hangs off the PE loopbacks, so configure them first and get them into the IGP. The loopback is the LDP router ID, the BGP update source, and the BGP next hop that the far-end PE resolves against. If the loopback is not in the IGP with a /32, nothing else will work.

PE1(config)# interface Loopback0
PE1(config-if)#  ip address 10.255.0.1 255.255.255.255
PE1(config-if)# interface Ethernet0/1
PE1(config-if)#  description Core link to P
PE1(config-if)#  ip address 10.30.30.1 255.255.255.0
PE1(config-if)# router ospf 1
PE1(config-router)#  router-id 10.255.0.1
PE1(config-router)#  network 10.255.0.1 0.0.0.0 area 0
PE1(config-router)#  network 10.30.30.0 0.0.0.255 area 0

P and PE2 get the same treatment. The P router carries both core links:

P(config)# router ospf 1
P(config-router)#  router-id 10.255.0.3
P(config-router)#  network 10.255.0.3 0.0.0.0 area 0
P(config-router)#  network 10.30.30.0 0.0.0.255 area 0
P(config-router)#  network 10.30.31.0 0.0.0.255 area 0

Verify before moving on. On a broadcast Ethernet segment the adjacency sits at 2WAY/DROTHER for a few tens of seconds during DR election, so do not panic if the first look is not FULL:

PE1#show ip ospf neighbor

Neighbor ID     Pri   State           Dead Time   Address         Interface
10.255.0.3        1   FULL/DR         00:00:33    10.30.30.2      Ethernet0/1

If you need the OSPF side of this in more depth, the OSPF cluster guide covers adjacency states and area design.

Step 2: LDP Across the Core

The IGP gives you IP reachability. LDP turns that into a label switched path. Two lines globally, one line per core-facing interface:

PE1(config)# mpls label protocol ldp
PE1(config)# mpls ldp router-id Loopback0 force
PE1(config)# interface Ethernet0/1
PE1(config-if)#  mpls ip

The mpls ldp router-id Loopback0 force line is not optional in practice. Without it, LDP picks the highest interface address as its ID, and when that interface goes down the LDP session resets. Pin it to the loopback that the IGP already advertises.

mpls ip goes only on core-facing interfaces. Never on the interface facing a CE router, because the customer does not speak MPLS. Verify:

PE1#show mpls ldp neighbor
    Peer LDP Ident: 10.255.0.3:0; Local LDP Ident 10.255.0.1:0
	TCP connection: 10.255.0.3.60695 - 10.255.0.1.646
	State: Oper; Msgs sent/rcvd: 8/8; Downstream
	Up time: 00:00:34
	LDP discovery sources:
	  Ethernet0/1, Src IP addr: 10.30.30.2
        Addresses bound to peer LDP Ident:
          10.255.0.3      10.30.30.2      10.30.31.1

PE1#show mpls interfaces
Interface              IP            Tunnel   BGP Static Operational
Ethernet0/1            Yes (ldp)     No       No  No     Yes

State: Oper is what you want. Anything else and the LSP is not built. The LDP deep-dive covers discovery, session establishment, and label advertisement modes.

Once LDP is up, the P router has already built the transport LSPs to both PE loopbacks:

P#show mpls forwarding-table
Local      Outgoing   Prefix           Bytes Label   Outgoing   Next Hop
Label      Label      or Tunnel Id     Switched      interface
16         Pop Label  10.255.0.1/32    4146          Et0/0      10.30.30.1
17         Pop Label  10.255.0.2/32    3665          Et0/1      10.30.31.2

"Pop Label" is penultimate hop popping. P is one hop away from each PE, so instead of swapping the transport label it strips it and hands the egress PE a packet with only the VPN label left. That saves the egress PE a lookup and is the default behaviour, not something you configure.

Step 3: Define the VRF, the RD, and the RTs

Now the customer. On both PEs:

PE1(config)# vrf definition CUST-A
PE1(config-vrf)#  rd 65000:1
PE1(config-vrf)#  address-family ipv4
PE1(config-vrf-af)#   route-target export 65000:1
PE1(config-vrf-af)#   route-target import 65000:1
PE1(config-vrf-af)#  exit-address-family

Three values, three different jobs, and confusing them is the number-one L3VPN mistake:

VRF name (CUST-A)Local to this router. Cosmetic. Nothing is carried in BGP.
RD (65000:1)Prepended to the IPv4 prefix to make a globally unique 96-bit VPNv4 route. Does not control import.
RT (65000:1)An extended community tagged onto the route. Export stamps it; import filters on it. This is what builds the VPN topology.

If that distinction is still fuzzy, read RD vs RT: the most confused concepts in MPLS L3VPN, which proves the difference with two overlapping prefixes in one BGP table.

Now put the customer-facing interface into the VRF. Do this before configuring the IP address, because moving an interface into a VRF wipes its IP configuration:

PE1(config)# interface Ethernet0/0
PE1(config-if)#  description To CE1 - VRF CUST-A
PE1(config-if)#  vrf forwarding CUST-A
PE1(config-if)#  ip address 10.20.1.1 255.255.255.252

Check it landed:

PE1#show vrf
  Name                             Default RD            Protocols   Interfaces
  CUST-A                           65000:1               ipv4        Et0/0
  CUST-B                           65000:2               ipv4        Lo11

This is exactly the same VRF construct used in VRF-Lite. The difference is that VRF-Lite stops here and hand-carries the separation hop by hop, while L3VPN hands the VRF's routes to MP-BGP and lets the backbone do the work.

Step 4: MP-BGP VPNv4 Between the PEs

The PEs peer with each other over their loopbacks, inside the provider AS, and activate the VPNv4 address family:

PE1(config)# router bgp 65000
PE1(config-router)#  no bgp default ipv4-unicast
PE1(config-router)#  neighbor 10.255.0.2 remote-as 65000
PE1(config-router)#  neighbor 10.255.0.2 update-source Loopback0
PE1(config-router)#  address-family vpnv4
PE1(config-router-af)#   neighbor 10.255.0.2 activate
PE1(config-router-af)#   neighbor 10.255.0.2 send-community extended
PE1(config-router-af)#  exit-address-family

Four lines matter here and every one of them is a classic outage:

update-source Loopback0The BGP next hop must be the loopback that LDP has a label for. Peer off a physical interface and the LSP resolution breaks.
address-family vpnv4Without it the session comes up and carries plain IPv4. VPN routes go nowhere.
send-community extendedRTs are extended communities. Omit this and the routes arrive with no RT, get filtered on import, and silently vanish.
no bgp default ipv4-unicastKeeps the global table out of it. Optional, but it stops you leaking provider routes into the customer's world by accident.

The MP-BGP details, including how the VPNv4 address family relates to the IPv4 and IPv6 ones, are covered in MP-BGP: Multiprotocol BGP address families explained. For BGP session mechanics generally, see the BGP cluster guide.

Confirm the session:

PE1#show ip bgp vpnv4 all summary
BGP router identifier 10.255.0.1, local AS number 65000

Neighbor        V           AS MsgRcvd MsgSent   TblVer  InQ OutQ Up/Down  State/PfxRcd
10.255.0.2      4        65000      19      17       40    0    0 00:06:40        4

Step 5: PE-CE Routing

The PE now needs to learn the customer's prefixes and give the customer the far-end's. You have four options: static, OSPF, EIGRP, or eBGP. All four are covered in detail with lab output in PE-CE routing protocols in MPLS L3VPN. Here is the simplest one to get you to a working VPN, static:

PE1(config)# ip route vrf CUST-A 192.168.99.0 255.255.255.0 10.20.1.2
PE1(config)# ip route vrf CUST-A 1.1.1.1 255.255.255.255 10.20.1.2
PE1(config)# router bgp 65000
PE1(config-router)#  address-family ipv4 vrf CUST-A
PE1(config-router-af)#   redistribute connected
PE1(config-router-af)#   redistribute static

Two things are happening. The ip route vrf lines put the customer's prefixes into the CUST-A table. The redistribute lines under the VRF address family push them into MP-BGP, where they become VPNv4 routes tagged with RT 65000:1 and given a VPN label.

Forgetting the redistribution is the classic "I can see the route on the PE but the far side never gets it" failure, and it is walked through step by step in Troubleshooting MPLS L3VPN.

The CE side is boringly simple. The CE is not VRF-aware and does not run MPLS; as far as it knows, PE1 is just a router:

CE1(config)# ip route 0.0.0.0 0.0.0.0 10.20.1.1

Verifying the VPN

Start at the VRF routing table on PE1. The customer's local routes are connected or static; the far-end site arrives as BGP:

PE1#show ip route vrf CUST-A

Routing Table: CUST-A
      1.0.0.0/32 is subnetted, 1 subnets
S        1.1.1.1 [1/0] via 10.20.1.2
      2.0.0.0/32 is subnetted, 1 subnets
B        2.2.2.2 [200/0] via 10.255.0.2, 00:00:11
      10.0.0.0/8 is variably subnetted, 4 subnets, 3 masks
C        10.20.1.0/30 is directly connected, Ethernet0/0
L        10.20.1.1/32 is directly connected, Ethernet0/0
B        10.20.2.0/30 [200/0] via 10.255.0.2, 00:00:11
B        10.20.20.0/24 [200/0] via 10.255.0.2, 00:00:11
S     192.168.99.0/24 [1/0] via 10.20.1.2

Note the next hop on the BGP routes: 10.255.0.2, PE2's loopback. Not a physical interface, not a P router. The VRF's forwarding decision is "hand this to PE2 over the LSP."

Now the VPNv4 table, which is where the RD and the VPN label live:

PE1#show ip bgp vpnv4 vrf CUST-A 10.20.20.0
BGP routing table entry for 65000:1:10.20.20.0/24, version 13
Paths: (1 available, best #1, table CUST-A)
  Refresh Epoch 1
  Local
    10.255.0.2 (metric 21) (via default) from 10.255.0.2 (10.255.0.2)
      Origin incomplete, metric 0, localpref 100, valid, internal, best
      Extended Community: RT:65000:1
      mpls labels in/out nolabel/22

Read that carefully, because it contains the whole protocol. The prefix is 65000:1:10.20.20.0/24, RD glued to the front. The extended community is RT:65000:1, which is why the CUST-A VRF imported it. And the outgoing MPLS label is 22, the VPN label PE2 allocated for this prefix. PE1 will push 22 as the inner label on every packet destined for that subnet.

The label forwarding state on PE1:

PE1#show mpls forwarding-table
Local      Outgoing   Prefix           Bytes Label   Outgoing   Next Hop
Label      Label      or Tunnel Id     Switched      interface
16         Pop Label  10.255.0.3/32    0             Et0/1      10.30.30.2
17         Pop Label  10.30.31.0/24    0             Et0/1      10.30.30.2
18         17         10.255.0.2/32    0             Et0/1      10.30.30.2
19         No Label   10.20.20.0/24[V] 500           aggregate/CUST-B
20         No Label   10.20.1.0/30[V]  1140          aggregate/CUST-A
21         No Label   1.1.1.1/32[V]    0             Et0/0      10.20.1.2
22         No Label   192.168.99.0/24[V]   \
                                       570           Et0/0      10.20.1.2

Entry 18 is the transport LSP toward PE2: swap local label 18 for outgoing label 17 and send it to P. The [V] entries are the VPN labels PE1 has handed out for its own customer prefixes, telling PE2 which VRF and which next hop to use for traffic coming back the other way. The label format article breaks down the two-label stack byte by byte.

The P Router Is BGP-Free (and That Is the Whole Point)

Here is the capture that should reframe how you think about L3VPN. This is the P router, sitting in the middle of the path, forwarding customer traffic all day:

P#show ip bgp summary
% BGP not active

P#show ip route 10.20.20.0
% Subnet not in table

P#show mpls forwarding-table
Local      Outgoing   Prefix           Bytes Label   Outgoing   Next Hop
Label      Label      or Tunnel Id     Switched      interface
16         Pop Label  10.255.0.1/32    4146          Et0/0      10.30.30.1
17         Pop Label  10.255.0.2/32    3665          Et0/1      10.30.31.2

The P router does not run BGP. It has never heard of 10.20.20.0/24. It has no VRFs. Its entire forwarding table is two labels pointing at two PE loopbacks. Customer traffic crosses it as a labelled packet and it swaps or pops the outer label without ever looking at the IP header.

That is why L3VPN scales. Add a thousand customers with a million routes and the P routers do not grow at all; only the PEs carry VPN state. It is also why "the core is BGP-free" is a design goal you will hear repeated in every service provider architecture conversation.

End to End: A Real Host Across the VPN

A real Debian machine sits on the 192.168.99.0/24 LAN behind CE1. It has no idea MPLS exists. It pings the far site:

j@llmbits:~$ ping -c 4 10.20.20.1
PING 10.20.20.1 (10.20.20.1) 56(84) bytes of data.
64 bytes from 10.20.20.1: icmp_seq=1 ttl=251 time=7.16 ms
64 bytes from 10.20.20.1: icmp_seq=2 ttl=251 time=7.02 ms
64 bytes from 10.20.20.1: icmp_seq=3 ttl=251 time=7.07 ms
64 bytes from 10.20.20.1: icmp_seq=4 ttl=251 time=6.99 ms

--- 10.20.20.1 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3004ms

And the traceroute from CE1, which is where the label stack finally becomes visible:

CE1#traceroute 10.20.20.1 source Ethernet0/0
Type escape sequence to abort.
Tracing the route to 10.20.20.1

  1 10.20.1.1 2 msec
  2 10.30.30.2 [MPLS: Labels 17/22 Exp 0] 5 msec
  3 10.20.2.1 [MPLS: Label 22 Exp 0] 5 msec
  4 10.20.2.2 6 msec

Hop 2 is the P router, and the packet arrives carrying two labels: 17 (transport, "get me to PE2") and 22 (VPN, "this belongs to CUST-A and goes out toward CE2"). Hop 3 is PE2, and the packet now has only one label, because P performed penultimate hop popping and stripped the transport label. PE2 pops the VPN label, does an IP lookup in the CUST-A table, and forwards to CE2.

That single traceroute is the entire L3VPN forwarding plane on one screen.

Proving the Separation: A Second VRF with Overlapping Addressing

The claim is that two customers can use the same IP space. Let us actually test it rather than take it on faith. VRF CUST-B gets RD/RT 65000:2, and on PE1 it is given a network that deliberately collides with CUST-A's site-2 LAN:

PE1(config)# vrf definition CUST-B
PE1(config-vrf)#  rd 65000:2
PE1(config-vrf)#  address-family ipv4
PE1(config-vrf-af)#   route-target export 65000:2
PE1(config-vrf-af)#   route-target import 65000:2
PE1(config)# interface Loopback11
PE1(config-if)#  vrf forwarding CUST-B
PE1(config-if)#  ip address 10.20.20.1 255.255.255.0

10.20.20.0/24 now exists twice on the same router. Here is the same prefix in the same BGP table, twice, under two different RDs:

PE1#show ip bgp vpnv4 all
     Network          Next Hop            Metric LocPrf Weight Path
Route Distinguisher: 65000:1 (default for vrf CUST-A)
 *>   1.1.1.1/32       10.20.1.2                0             0 65001 i
 *>i  2.2.2.2/32       10.255.0.2               0    100      0 65001 i
 *>   10.20.1.0/30     0.0.0.0                  0         32768 ?
 *>i  10.20.2.0/30     10.255.0.2               0    100      0 ?
 *>i  10.20.20.0/24    10.255.0.2               0    100      0 65001 i
 *>   192.168.99.0     10.20.1.2                0             0 65001 i
Route Distinguisher: 65000:2 (default for vrf CUST-B)
 *>   10.20.20.0/24    0.0.0.0                  0         32768 ?
 *>i  10.20.99.0/24    10.255.0.2               0    100      0 ?

Both entries for 10.20.20.0/24 coexist because to BGP they are different routes: 65000:1:10.20.20.0/24 and 65000:2:10.20.20.0/24. That is the entire job of the route distinguisher.

And the forwarding follows:

PE1#show ip route vrf CUST-A 10.20.20.0 255.255.255.0
Routing entry for 10.20.20.0/24
  Known via "bgp 65000", distance 200, metric 0, type internal
  * 10.255.0.2 (default), from 10.255.0.2
      MPLS label: 22
      MPLS Flags: MPLS Required

PE1#show ip route vrf CUST-B 10.20.20.0 255.255.255.0
Routing entry for 10.20.20.0/24
  Known via "connected", distance 0, metric 0 (connected, via interface)
  * directly connected, via Loopback11

Same prefix, same router, two completely different forwarding decisions. In CUST-A it goes across the MPLS core to PE2 with VPN label 22. In CUST-B it is a local interface. Neither customer can see the other's route, and no configuration effort was required to keep them apart beyond giving them different RDs and RTs.

The Complete PE Configuration

Everything above, on PE1, condensed:

vrf definition CUST-A
 rd 65000:1
 address-family ipv4
  route-target export 65000:1
  route-target import 65000:1
!
mpls label protocol ldp
mpls ldp router-id Loopback0 force
!
interface Loopback0
 ip address 10.255.0.1 255.255.255.255
!
interface Ethernet0/0
 vrf forwarding CUST-A
 ip address 10.20.1.1 255.255.255.252
!
interface Ethernet0/1
 ip address 10.30.30.1 255.255.255.0
 mpls ip
!
router ospf 1
 router-id 10.255.0.1
 network 10.255.0.1 0.0.0.0 area 0
 network 10.30.30.0 0.0.0.255 area 0
!
router bgp 65000
 no bgp default ipv4-unicast
 neighbor 10.255.0.2 remote-as 65000
 neighbor 10.255.0.2 update-source Loopback0
 address-family vpnv4
  neighbor 10.255.0.2 activate
  neighbor 10.255.0.2 send-community extended
 exit-address-family
 address-family ipv4 vrf CUST-A
  redistribute connected
  redistribute static
 exit-address-family

The P router config is a quarter of that length and contains no customer state at all. That asymmetry is the design.

FAQ

No, and you should not put it there. The CE is a plain IP router. mpls ip belongs only on core-facing interfaces between PE and P routers.

Can the RD and RT be the same value?

Yes, and in a simple any-to-any VPN they usually are (65000:1 for both, as in this lab). They are still doing completely different jobs. In hub-and-spoke or extranet designs they diverge immediately.

Why iBGP between the PEs and not eBGP?

Both PEs are inside the same provider AS, so the VPNv4 session is iBGP. In a real network you would not full-mesh every PE; you would point them all at a pair of route reflectors configured for the VPNv4 address family.

What happens if the core IGP is fine but LDP is down?

The VPN dies while every IP-level test passes. It is the single most confusing MPLS failure mode, and it has its own article: Troubleshooting MPLS: LDP neighbors, label bindings, and broken LSPs.

Key Takeaways

  • Build the layers in order: IGP, then LDP, then VRF/RD/RT, then MP-BGP VPNv4, then PE-CE routing. Verify each before moving up.
  • The PE loopback is load-bearing. It is the LDP router ID, the BGP update source, and the next hop that must resolve to a label.
  • RD makes prefixes unique; RT decides who imports them. Same syntax, unrelated jobs.
  • send-community extended is mandatory. Without it the RTs are stripped and every VPN route is silently discarded on import.
  • Customer prefixes only reach MP-BGP if you redistribute them under address-family ipv4 vrf. This is the most common "route is on the PE but not in BGP" cause.
  • The P router runs no BGP and holds no customer routes. Two labels in its forwarding table carry every customer.
  • A labelled traceroute (Labels 17/22 then Label 22) shows the transport and VPN labels, and PHP, in one command.

Next in the cluster: the four PE-CE routing options compared, and the MPLS cluster guide for everything else.

Written by
More from Ping Labz
Great! You’ve successfully signed up.
Welcome back! You've successfully signed in.
You've successfully subscribed to Ping Labz.
Your link has expired.
Success! Check your email for magic link to sign-in.
Success! Your billing info has been updated.
Your billing was not updated.