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?

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.

Next: One-Arm Hub-and-Spoke VPN with MPLS/VPN Continue

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

Blog posts in this series

Add comment
Sidebar