EVPN Hub-and-Spoke Layer-3 VPN
Now that we figured out how to implement a hub-and-spoke VPN design on a single PE-router, let’s do the same thing with EVPN. It turns out to be trivial:
- We’ll split the single PE router into three PE devices (pe_a, pe_b, and pe_h)
- We’ll add a core router (p) and connect it with all three PE devices.
As we want to use EVPN and have a larger core network, we’ll also have to enable VLANs, VXLAN, BGP, and OSPF on the PE devices.
This is the topology of our expanded lab:
And this is the netlab description of the lab topology:
defaults.device: eos
provider: clab
module: [ bgp ]
plugin: [ bgp.session ]
groups:
ce:
device: frr
members: [ ce_s1, ce_s2, ce_hub ]
pe:
members: [ pe_a, pe_b, pe_h ]
module: [ bgp, vrf, evpn, vlan, vxlan, ospf ]
bgp.as: 65000
vrfs:
s_1:
links: [ pe_a-ce_s1 ]
export: [ hub_egress ]
import: [ hub_ingress ]
evpn.transit_vni: True
s_2:
links: [ pe_b-ce_s2 ]
export: [ hub_egress ]
import: [ hub_ingress ]
evpn.transit_vni: True
hub_ingress:
links:
- pe_h:
ce_hub:
bgp.as_override: True
evpn.transit_vni: True
hub_egress:
links: [ pe_h-ce_hub ]
evpn.transit_vni: True
nodes:
pe_a:
pe_b:
pe_h:
p:
module: [ ospf ]
ce_hub:
bgp.as: 65100
ce_s1:
bgp.as: 65101
ce_s2:
bgp.as: 65102
links: [ pe_a-p, pe_b-p, pe_h-p ]
Most of the topology has been explained in the previous blog post; here’s the gist of the changes:
- We need three PE routers (lines 11-14). They have to run VRFs (or we wouldn’t have L3VPN), OSPF (to connect to the P router), BGP (to support EVPN), VXLAN (to transport customer data), VLANs (because the VXLAN module relies on the VLAN module), and EVPN (for obvious reasons: see blog title)
- We have to enable EVPN transit VNI in all VRFs (lines 21, 26, 32, 35). Each VRF will have a different transit VNI.
- We need a core router running OSPF (lines 41-42) and links between the PE routers and the core router (line 50).
Does this really work? You bet!
$ netlab connect ce_s1 traceroute ce_s2
Connecting to container clab-l3vpn-hub-sp-ce_s1, executing traceroute ce_s2
traceroute to ce_s2 (10.0.0.7), 30 hops max, 46 byte packets
1 pe_a-s_1 (10.1.0.14) 0.004 ms 0.002 ms 0.001 ms
2 pe_h-hub_ingress (10.1.0.22) 3.403 ms 0.995 ms 0.951 ms
3 ce_hub (10.1.0.25) 0.969 ms 0.836 ms 0.793 ms
4 pe_h-hub_egress (10.1.0.26) 0.973 ms 0.740 ms 0.643 ms
5 pe_b-s_2 (10.1.0.18) 2.079 ms 1.212 ms 1.082 ms
6 ce_s2 (10.0.0.7) 1.051 ms 1.057 ms 0.989 ms
Want to try it out yourself?
- Open the netlab-examples repository in a GitHub Codespace
- Copy the cEOS container into the codespace
- Change directory to
EVPN/l3vpn-hub-spoke
- Execute netlab up
Alternatively, you could execute netlab up -d frr if you don’t want to waste time with Arista cEOS containers.
Behind the Scenes
Let’s track the propagation of the BGP route for the CE_S2 loopback prefix (10.0.0.7/32) to see how the whole setup works. Here’s the table of loopback prefixes to make it easier to track what’s going on:
Node/Interface | IPv4 Address | IPv6 Address | Description |
---|---|---|---|
ce_hub | 10.0.0.5/32 | Loopback | |
ce_s1 | 10.0.0.6/32 | Loopback | |
ce_s2 | 10.0.0.7/32 | Loopback | |
p | 10.0.0.4/32 | Loopback | |
pe_a | 10.0.0.1/32 | Loopback | |
pe_b | 10.0.0.2/32 | Loopback | |
pe_h | 10.0.0.3/32 | Loopback |
Here’s the overview of the VRFs we’re using:
VRF | RD | Export RT | Import RT | EVPN VNI |
---|---|---|---|---|
hub_egress | 65000:4 | 65000:4 | 65000:4 | 200003 |
hub_ingress | 65000:3 | 65000:3 | 65000:3 | 200002 |
s_1 | 65000:1 | 65000:4 | 65000:3 | 200000 |
s_2 | 65000:2 | 65000:4 | 65000:3 | 200001 |
CE_S2 is connected to VRF s_2
on PE_B, so let’s start there:
pe-b>show ip bgp 10.0.0.7/32 vrf s_2
BGP routing table information for VRF s_2
Router identifier 10.0.0.2, local AS number 65000
BGP routing table entry for 10.0.0.7/32
Paths: 2 available
65102
10.1.0.17 from 10.1.0.17 (10.0.0.7)
Origin IGP, metric 0, localpref 100, IGP metric 0, weight 0, tag 0
Received 01:00:20 ago, valid, external, best
Rx SAFI: Unicast
65100 65100 65102
10.0.0.3 from 10.0.0.3 (10.0.0.3), imported EVPN route, RD 65000:3
Origin IGP, metric 0, localpref 100, IGP metric 30, weight 0, tag 0
Received 01:00:12 ago, valid, internal
Extended Community: Route-Target-AS:65000:3 TunnelEncap:tunnelTypeVxlan EvpnRouterMac:00:1c:73:8d:a1:38
Remote VNI: 200002
Rx SAFI: Unicast
PE_B has two BGP paths for the 10.0.0.7/32 prefix: the VRF BGP path advertised by CE_S2 and the BGP-EVPN path advertised by the PE_H (10.0.0.3). According to the RT community, the BGP-EVPN path originated in the hub_ingress
VRF.
PE_B prefers the path advertised by CE_S2, and as the s_2
VRF has EVPN transit VNI, it copies that path into a route-type-5 (ip-prefix) EVPN update (the top path in the following printout):
pe-b>show bgp evpn route-type ip-prefix 10.0.0.7/32
BGP routing table information for VRF default
Router identifier 10.0.0.2, local AS number 65000
BGP routing table entry for ip-prefix 10.0.0.7/32, Route Distinguisher: 65000:2
Paths: 1 available
65102
- from - (0.0.0.0)
Origin IGP, metric 0, localpref 100, weight 0, tag 0, valid, external, best
Extended Community: Route-Target-AS:65000:4 TunnelEncap:tunnelTypeVxlan EvpnRouterMac:00:1c:73:0d:a4:e5
VNI: 200001
BGP routing table entry for ip-prefix 10.0.0.7/32, Route Distinguisher: 65000:3
Paths: 1 available
65100 65100 65102
10.0.0.3 from 10.0.0.3 (10.0.0.3)
Origin IGP, metric -, localpref 100, weight 0, tag 0, valid, internal, best
Extended Community: Route-Target-AS:65000:3 TunnelEncap:tunnelTypeVxlan EvpnRouterMac:00:1c:73:8d:a1:38
VNI: 200002
How do we know the top path originated from PE_B? The next hop is not set yet; it’s usually set to the source IP address of the BGP session when the BGP updates are sent.
Moving on to PE_H. It has two entries for 10.0.0.7/32 in its BGP EVPN table and an entry in each VRF (hub_ingress
and hub_egress
)
pe-h>show bgp evpn route-type ip-prefix 10.0.0.7/32
BGP routing table information for VRF default
Router identifier 10.0.0.3, local AS number 65000
BGP routing table entry for ip-prefix 10.0.0.7/32, Route Distinguisher: 65000:2
Paths: 1 available
65102
10.0.0.2 from 10.0.0.2 (10.0.0.2)
Origin IGP, metric 0, localpref 100, weight 0, tag 0, valid, internal, best
Extended Community: Route-Target-AS:65000:4 TunnelEncap:tunnelTypeVxlan EvpnRouterMac:00:1c:73:0d:a4:e5
VNI: 200001
BGP routing table entry for ip-prefix 10.0.0.7/32, Route Distinguisher: 65000:3
Paths: 1 available
65100 65100 65102
- from - (0.0.0.0)
Origin IGP, metric -, localpref 100, weight 0, tag 0, valid, external, best
Extended Community: Route-Target-AS:65000:3 TunnelEncap:tunnelTypeVxlan EvpnRouterMac:00:1c:73:8d:a1:38
VNI: 200002
pe-h>show ip bgp 10.0.0.7/32 vrf all
BGP routing table information for VRF hub_egress
Router identifier 10.0.0.3, local AS number 65000
BGP routing table entry for 10.0.0.7/32
Paths: 1 available
65102
10.0.0.2 from 10.0.0.2 (10.0.0.2), imported EVPN route, RD 65000:2
Origin IGP, metric 0, localpref 100, IGP metric 30, weight 0, tag 0
Received 01:11:09 ago, valid, internal, best
Extended Community: Route-Target-AS:65000:4 TunnelEncap:tunnelTypeVxlan EvpnRouterMac:00:1c:73:0d:a4:e5
Remote VNI: 200001
Rx SAFI: Unicast
BGP routing table information for VRF hub_ingress
Router identifier 10.0.0.3, local AS number 65000
BGP routing table entry for 10.0.0.7/32
Paths: 1 available
65100 65100 65102
10.1.0.21 from 10.1.0.21 (10.0.0.5)
Origin IGP, metric 0, localpref 100, IGP metric 0, weight 0, tag 0
Received 01:11:09 ago, valid, external, best
Rx SAFI: Unicast
PE_H is using the EVPN route advertised by PE_B (now we can see that the BGP next hop is 10.0.0.2). It imports the EVPN route into the hub_egress
VRF and advertises it to the Hub router.
The Hub router advertises the same prefix back to PE_B over the hub_ingress
EBGP session (notice a change in the next hop and the AS path), and PE_H exports that prefix back into EVPN (the second EVPN route with no next-hop information).
Finally, PE_A received two EVPN routes for 10.0.0.7/32, one from PE_B (10.0.0.2), the other one from PE_H (10.0.0.3):
pe-a>show bgp evpn route-type ip-prefix 10.0.0.7/32
BGP routing table information for VRF default
Router identifier 10.0.0.1, local AS number 65000
BGP routing table entry for ip-prefix 10.0.0.7/32, Route Distinguisher: 65000:2
Paths: 1 available
65102
10.0.0.2 from 10.0.0.2 (10.0.0.2)
Origin IGP, metric 0, localpref 100, weight 0, tag 0, valid, internal, best
Extended Community: Route-Target-AS:65000:4 TunnelEncap:tunnelTypeVxlan EvpnRouterMac:00:1c:73:0d:a4:e5
VNI: 200001
BGP routing table entry for ip-prefix 10.0.0.7/32, Route Distinguisher: 65000:3
Paths: 1 available
65100 65100 65102
10.0.0.3 from 10.0.0.3 (10.0.0.3)
Origin IGP, metric -, localpref 100, weight 0, tag 0, valid, internal, best
Extended Community: Route-Target-AS:65000:3 TunnelEncap:tunnelTypeVxlan EvpnRouterMac:00:1c:73:8d:a1:38
VNI: 200002
The route advertised by PE_H has a route target that matches the import route target of the s_1
VRF and gets imported into it:
pe-a>show ip bgp 10.0.0.7/32 vrf s_1
BGP routing table information for VRF s_1
Router identifier 10.0.0.1, local AS number 65000
BGP routing table entry for 10.0.0.7/32
Paths: 1 available
65100 65100 65102
10.0.0.3 from 10.0.0.3 (10.0.0.3), imported EVPN route, RD 65000:3
Origin IGP, metric 0, localpref 100, IGP metric 30, weight 0, tag 0
Received 06:39:40 ago, valid, internal, best
Extended Community: Route-Target-AS:65000:3 TunnelEncap:tunnelTypeVxlan EvpnRouterMac:00:1c:73:8d:a1:38
Remote VNI: 200002
Rx SAFI: Unicast
The data-plane setup is very similar to the one we’ve observed in the common services VRF scenario. You can find the details there or start the lab and explore.
For the sake of completeness, I’ll do another blog post implementing hub-and-spoke VPN with MPLS transport and MPLS/VPN control plane, but it might take a while.Reference Information
This is the relevant part of the PE_H configuration (EVPN, BGP, VRFs). It was generated exclusively with netlab configuration templates; all I did was run netlab up and enjoy the results. You can find the other router configurations in the netlab-examples GitHub repository.
vrf instance hub_egress
rd 65000:4
!
vrf instance hub_ingress
rd 65000:3
!
interface Vxlan1
vxlan source-interface Loopback0
vxlan udp-port 4789
vxlan vrf hub_egress vni 200003
vxlan vrf hub_ingress vni 200002
!
ip routing vrf hub_egress
ip routing vrf hub_ingress
!
router bgp 65000
router-id 10.0.0.3
neighbor 10.0.0.1 remote-as 65000
neighbor 10.0.0.1 update-source Loopback0
neighbor 10.0.0.1 description pe_a
neighbor 10.0.0.1 send-community standard extended large
neighbor 10.0.0.2 remote-as 65000
neighbor 10.0.0.2 update-source Loopback0
neighbor 10.0.0.2 description pe_b
neighbor 10.0.0.2 send-community standard extended large
!
address-family evpn
neighbor 10.0.0.1 activate
neighbor 10.0.0.2 activate
!
!
vrf hub_egress
rd 65000:4
route-target import evpn 65000:4
route-target export evpn 65000:4
router-id 10.0.0.3
neighbor 10.1.0.25 remote-as 65100
neighbor 10.1.0.25 description ce_hub
neighbor 10.1.0.25 send-community standard large
redistribute connected
!
address-family ipv4
neighbor 10.1.0.25 activate
redistribute connected
!
vrf hub_ingress
rd 65000:3
route-target import evpn 65000:3
route-target export evpn 65000:3
router-id 10.0.0.3
neighbor 10.1.0.21 remote-as 65100
neighbor 10.1.0.21 description ce_hub
neighbor 10.1.0.21 send-community standard large
redistribute connected
!
address-family ipv4
neighbor 10.1.0.21 activate
redistribute connected