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:

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.

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:

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.

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.