Implementing VPLS part 2: BGP Autodiscovery

In the previous post about VPLS implementation we configured the remote PEs manually under the VFI, like this:

R6_PE2(config-vfi)#neighbor 150.7.7.7 encapsulation mpls 

This solution is not scalable if the customer has a lot of remote sites and we have to configure each PE router manually, since we need a full-mesh configuration between the PEs. To solve this issue we can use BGP autodiscovery. The topology is exactly the same as in the previous post:

VPLS Topology

The difference is that I replaced the Route Reflector to a CSR1000v router because I had troubles configuring the l2vpn vpls address family with the regular IOSv router. So CSR5 is going to be the iBGP Route Reflector in this topology and CSR6, CSR7 and CSR8 are the Provider Edge routers. We configure the VPLS service for Customer 1 (purple), you can just ignore the Customer 2 (yellow) CEs in the topology. First we need to build the BGP peering between the PEs: instead of the full-mesh configuration we're going to configure CSR5 as the Route Reflector. Each PE has a single neighborship with the RR, here is the BGP configuration of CSR6 for example:

CSR6_PE2(config)#router bgp 18
CSR6_PE2(config-router)#no bgp default ipv4-unicast 
CSR6_PE2(config-router)#neighbor 150.5.5.5 remote-as 18
CSR6_PE2(config-router)#neighbor 150.5.5.5 update-source lo0
CSR6_PE2(config-router)#address-family l2vpn vpls
CSR6_PE2(config-router-af)#neighbor 150.5.5.5 activate 
CSR6_PE2(config-router-af)#neighbor 150.5.5.5 send-community extended 

We need to activate the neighbor under the l2vpn vpls address family, and also enable extended communities. Unlike the vpnv4 AF, according to my experience we have to manually enable the communities, they won't be just enabled automatically after activating the neighbor under the l2vpn address family.

BGP OPEN l2vpn vpls AF
BGP peers must support the l2vpn vpls address family in the BGP OPEN message

And here is the configuration of the RR (CSR5):

CSR5_REFL(config)#router bgp 18
CSR5_REFL(config-router)#no bgp default ipv4-unicast 
CSR5_REFL(config-router)#neighbor 150.6.6.6 remote-as 18
CSR5_REFL(config-router)#neighbor 150.6.6.6 update-source lo0
CSR5_REFL(config-router)#neighbor 150.7.7.7 remote-as 18
CSR5_RELF(config-router)#neighbor 150.7.7.7 update-source lo0
CSR5_REFL(config-router)#neighbor 150.8.8.8 remote-as 18
CSR5_REFL(config-router)#neighbor 150.8.8.8 update-source lo0

CSR5_REFL(config-router)#address-family l2vpn vpls 
CSR5_REFL(config-router-af)#neighbor 150.6.6.6 activate 
CSR5_REFL(config-router-af)#neighbor 150.6.6.6 route-reflector-client 
CSR5_REFL(config-router-af)#neighbor 150.6.6.6 send-community extended 
CSR5_REFL(config-router-af)#neighbor 150.7.7.7 activate 
CSR5_REFL(config-router-af)#neighbor 150.7.7.7 route-reflector-client 
CSR5_REFL(config-router-af)#neighbor 150.7.7.7 send-community extended 
CSR5_REFL(config-router-af)#neighbor 150.8.8.8 activate                
CSR5_REFL(config-router-af)#neighbor 150.8.8.8 route-reflector-client 
CSR5_REFL(config-router-af)#neighbor 150.8.8.8 send-community extended 

For verification we can use the following command, CSR5 should have three neighbors:

CSR5_REFL#show bgp l2vpn vpls all summary 
BGP router identifier 150.5.5.5, local AS number 18
BGP table version is 1, main routing table version 1

Neighbor        V           AS MsgRcvd MsgSent   TblVer  InQ OutQ Up/Down  State/PfxRcd
150.6.6.6       4           18      11      11        1    0    0 00:06:37        0
150.7.7.7       4           18       9       7        1    0    0 00:04:57        0
150.8.8.8       4           18       6       4        1    0    0 00:02:29        0

At this point the Prefix Received column is 0, because we haven't exchanged NLRIs yet. The l2 vfi, service instance and the configuration of the bridge domain is exactly the same as with manual configuration, except that instead of the individual neighbor ... statements we have the autodiscovery bgp command:

CSR6_PE2(config)#int g3
CSR6_PE2(config-if)#no shut                       
CSR6_PE2(config-if)#service instance 678 ethernet 
CSR6_PE2(config-if-srv)#encapsulation default 

CSR6_PE2(config)#l2vpn vfi context CUST1
CSR6_PE2(config-vfi)#vpn id 100
CSR6_PE2(config-vfi)#autodiscovery bgp signaling ldp 

CSR6_PE2(config)#bridge-domain 1 
CSR6_PE2(config-bdomain)#member gigabitEthernet 3 service-instance 678
CSR6_PE2(config-bdomain)#member vfi CUST1

*Jul 24 11:29:41.850: %VFI-6-VFI_STATUS_CHANGED: Status of VFI CUST1 changed from DOWN to UP
CSR6_PE2(config-bdomain)#
*Jul 24 11:32:20.136: %LDP-5-NBRCHG: LDP Neighbor 150.8.8.8:0 (3) is UP
CSR6_PE2(config-bdomain)#
*Jul 24 11:35:25.193: %LDP-5-NBRCHG: LDP Neighbor 150.7.7.7:0 (4) is UP

With BGP autodiscovery we have two options how we can distribute the service label (inner label) between the PEs: signaling ldp means that we establish a targeted LDP session between the PEs and send the service label via LDP Label Mapping message:

LDP Label Mapping Message
Targeted LDP session between CSR7 and CSR8: CSR7 send its service label (7016) to CSR8 via LDP

That's what we've already seen in the previous post, the difference is that we use BGP to discover the other PE's address. So we use BGP to learn the addresses of the other PEs and then establish a targeted LDP session with each PE to exchange the label information. After convergence the BGP table on CSR7 looks like this:

CSR7_PE1(config-if)#do show bgp l2vpn vpls all
BGP table version is 8, local router ID is 150.7.7.7
Status codes: s suppressed, d damped, h history, * valid, > best, i - internal, 
              r RIB-failure, S Stale, m multipath, b backup-path, f RT-Filter, 
              x best-external, a additional-path, c RIB-compressed, 
              t secondary path, L long-lived-stale,
Origin codes: i - IGP, e - EGP, ? - incomplete
RPKI validation codes: V valid, I invalid, N Not found

     Network          Next Hop            Metric LocPrf Weight Path
Route Distinguisher: 18:100
 *>i  18:100:150.6.6.6/96
                      150.6.6.6                0    100      0 ?
 *>   18:100:150.7.7.7/96
                      0.0.0.0                            32768 ?
 *>i  18:100:150.8.8.8/96
                      150.8.8.8                0    100      0 ?

The NLRIs look very similar to the VPNv4 NLRIs: they have the <ASN>:<VPN ID> prepended as the RD in front of the remote PEs loopback address. If we take a look at the details:

CSR7_PE1#show bgp l2vpn vpls all 150.6.6.6          
BGP routing table entry for 18:100:150.6.6.6/96, version 12
Paths: (1 available, best #1, table L2VPN-VPLS-BGP-Table)
  Not advertised to any peer
  Refresh Epoch 2
  Local
    150.6.6.6 (metric 4) from 150.5.5.5 (150.5.5.5)
      Origin incomplete, metric 0, localpref 100, valid, internal, best, AGI version(1258291202)
      Extended Community: RT:18:100 L2VPN AGI:18:100
      Originator: 150.6.6.6, Cluster list: 150.5.5.5
      mpls labels in/out 2342793/16777215
      rx pathid: 0, tx pathid: 0x0
      Updated on Jul 24 2025 11:56:48 UTC

We can see that we have the same RD and RT value auto generated and advertised as extended community. Like with L3VPNs (import/export RT) the RTs must match with each other on each PE.

The second option is that we use BGP for label allocation: the PEs learn the service label with BGP, and it is transferred in the BGP UPDATE message with the NLRI. To use BGP for signaling we have issue the following command for every neighbor under the vpls address family:

CSR7_PE1(config-router-af)#neighbor 150.5.5.5 suppress-signaling-protocol ldp

We have to issue this for each PE, and also on the Route Reflector itself (!) for every neighbor. We also have turn on the BGP signaling under the VFI:

CSR6_PE2(config)#l2vpn vfi context CUST1
CSR6_PE2(config-vfi)#autodiscovery bgp signaling bgp
CSR6_PE2(config-vfi-autodiscovery)#ve id ?
  <1-16384>  VPLS Edge device ID value

CSR6_PE2(config-vfi-autodiscovery)#ve id 1

If we use BGP for label allocation we also have to define the ve id (VPLS Endpoint ID) value under the vfi: this value should be unique for each PE and its also prepended to the NLRI (making it a /136). Using BGP for signaling each PE advertises a Label Block Base and a Label Block Size (which is 10 by default) with the NLRI and the each remote PE picks a label from the advertised block. The ve is used to define this Label Block. It's quite a difficult concept, this article explains it very well why it is needed, this post is mainly focusing on the configuration.

BGP UPDATE message using BGP singaling
BGP UPDATE: NLRI and path attributes sent when using BGP for signaling

In the BGP UPDATE we can see that the ve id is transferred as the CE-ID, and right under that we can see the Label Block. The Route Target (18:100) and the MTU are sent as an Extended Community which have to match on each PE. This UPDATE was sent by the Route Reflector so the two additional attribute (Cluster-list and Originator ID) are also sent with the NLRI. For verification we can use the following command on the PEs:

CSR6_PE2#show bgp l2vpn vpls all detail 

Route Distinguisher: 18:100
BGP routing table entry for 18:100:VEID-1:Blk-1/136, version 13
  Paths: (1 available, best #1, table L2VPN-VPLS-BGP-Table)
  Advertised to update-groups:
     6         
  Refresh Epoch 1
  Local
    0.0.0.0 from 0.0.0.0 (150.6.6.6)
      Origin incomplete, localpref 100, weight 32768, valid, sourced, local, best
      AGI version(0), VE Block Size(10) Label Base(6018)
      Extended Community: RT:18:100 L2VPN L2:0x0:MTU-1500
      mpls labels in/out exp-null/6018
      rx pathid: 0, tx pathid: 0x0
      Updated on Jul 24 2025 12:25:20 UTC
BGP routing table entry for 18:100:VEID-2:Blk-1/136, version 18
  Paths: (1 available, best #1, table L2VPN-VPLS-BGP-Table)
  Not advertised to any peer
  Refresh Epoch 2
  Local
    150.7.7.7 (metric 4) from 150.5.5.5 (150.5.5.5)
      Origin incomplete, metric 0, localpref 100, valid, internal, best
      AGI version(0), VE Block Size(10) Label Base(7019)
      Extended Community: RT:18:100 L2VPN L2:0x0:MTU-1500
      Originator: 150.7.7.7, Cluster list: 150.5.5.5
      mpls labels in/out exp-null/7019
      rx pathid: 0, tx pathid: 0x0
      Updated on Jul 24 2025 12:25:24 UTC
BGP routing table entry for 18:100:VEID-3:Blk-1/136, version 19
  Paths: (1 available, best #1, table L2VPN-VPLS-BGP-Table)
  Not advertised to any peer
  Refresh Epoch 2
  Local   
    150.8.8.8 (metric 3) from 150.5.5.5 (150.5.5.5)
      Origin incomplete, metric 0, localpref 100, valid, internal, best
      AGI version(0), VE Block Size(10) Label Base(8018)
      Extended Community: RT:18:100 L2VPN L2:0x0:MTU-1500
      Originator: 150.8.8.8, Cluster list: 150.5.5.5
      mpls labels in/out exp-null/8018
      rx pathid: 0, tx pathid: 0x0
      Updated on Jul 24 2025 12:25:24 UTC

The one above shows all of the BGP path attribute information, just for label information we can use the following:

CSR6_PE2#show bgp l2vpn vpls all labels 
   Network          Next Hop      In label/Out label
Route Distinguisher: 18:100
   18:100:VEID-1:Blk-1/136
                   0.0.0.0         exp-null/6018
   18:100:VEID-2:Blk-1/136
                   150.7.7.7       exp-null/7019
   18:100:VEID-3:Blk-1/136
                   150.8.8.8       exp-null/8018

With either solution (signaling with LDP/BGP) the CEs are able to establish OSPF adjacency with each other:

R13_CE5(config-if)#do show ip ospf neigh

Neighbor ID     Pri   State           Dead Time   Address         Interface
1.9.9.9           1   FULL/DR         00:00:38    192.168.0.9     GigabitEthernet0/0
1.11.11.11        1   FULL/BDR        00:00:35    192.168.0.11    GigabitEthernet0/0

So that we can confirm that the data plane is also functional.