L3VPN – Hub & Spoke VPN: One Interface
1. Intro
In an MPLS L3VPN, Hub-and-Spoke is a VPN topology where spoke sites cannot communicate directly with each other. Traffic between spokes must pass through the hub PE/site. This is commonly used when the hub provides centralized firewalling, Internet access, shared services, security inspection, or centralized routing.
The are two main categories for configuring a Hub & Spoke VPN:
- One-interface H&S
- Two-interfaces H&S
In this post we will focus on the One-interface H&S model. For the Two-interface H&S model visit the next post.
One-Interface H&S VPN:
- only one interface between Hub-CE and Hub-PE
- only a default route is advertised from the hub to the spokes
- limitation: if more specific routes must be exchanged through the hub or between spokes, then the two-interfaces model is needed.
- this is the option that is recommended by Juniper.
- limitation: with label forwarding at the hub (no IP forwarding lookup), we can’t have egress features.
In our scenario we have a customer, we will call it ‘HS‘ customer, and it wants a Hub & Spoke VPN (with one interface at the hub), the topology is the following:

The logic is not that complicated:
- We will use two Route Targets
- One Route Target will be exported by the Spoke VRF, which will then be imported at the Hub VRF.
- The other Route Target will be exported by the HUB VRF, which will then be imported by Spoke VRF.
- Traffic originated by the Spokes will use the default route originated by the HUB CE for forwarding.
- The catch, we must not configure
vrf-table-labelat the HUB VRF, this will send any Spoke packet to HUB CE.
If we translate this to Route Targets, it will look like this:
- VRF Spoke
- vrf-export target:64512:302
- vrf-import target:64512:301
- VRF Hub
- vrf-import target:64512:302
- vrf-export target:64512:301
- interface ge-0/0/4.311 (PE-CE)
- don’t configure
vrf-table-label
2. Configuring H&S VPN
Considering the details from the introduction, let’s see the configuration:
### Spoke config (R7 and R8 have similar config):
set routing-instances HS-SPOKE instance-type vrf
set routing-instances HS-SPOKE protocols bgp group PE-CE advertise-peer-as
set routing-instances HS-SPOKE protocols bgp group PE-CE peer-as 64512
set routing-instances HS-SPOKE protocols bgp group PE-CE as-override
set routing-instances HS-SPOKE protocols bgp group PE-CE neighbor 172.16.0.5
set routing-instances HS-SPOKE interface ge-0/0/4.317
set routing-instances HS-SPOKE interface lo0.101
set routing-instances HS-SPOKE route-distinguisher 17.0.0.7:312
set routing-instances HS-SPOKE vrf-import HS-Hub
set routing-instances HS-SPOKE vrf-export HS-Spoke
set routing-instances HS-SPOKE vrf-table-label
set policy-options policy-statement HS-Hub term IMPORT from community CM-HS-Hub
set policy-options policy-statement HS-Hub term IMPORT then accept
set policy-options policy-statement HS-Hub then reject
set policy-options policy-statement HS-Spoke then community add CM-HS-Spoke
set policy-options policy-statement HS-Spoke then accept
set policy-options community CM-HS-Hub members target:64512:301
set policy-options community CM-HS-Spoke members target:64512:302
### HUB PE configuration:
### (notice vrf-table-label is not configured)
set routing-instances HS-HUB instance-type vrf
set routing-instances HS-HUB protocols bgp group PE-CE peer-as 64512
set routing-instances HS-HUB protocols bgp group PE-CE as-override
set routing-instances HS-HUB protocols bgp group PE-CE neighbor 172.16.0.1
set routing-instances HS-HUB interface ge-0/0/4.311
set routing-instances HS-HUB interface lo0.311
set routing-instances HS-HUB route-distinguisher 17.0.0.1:310
set routing-instances HS-HUB vrf-import HS-Spoke-import
set routing-instances HS-HUB vrf-export HS-Hub-export
set policy-options policy-statement HS-Hub-export then community add CM-HS-Hub
set policy-options policy-statement HS-Hub-export then accept
set policy-options policy-statement HS-Spoke-import term IMPORT from community CM-HS-Spoke
set policy-options policy-statement HS-Spoke-import term IMPORT then accept
set policy-options policy-statement HS-Spoke-import then reject
set policy-options community CM-HS-Hub members target:64512:301
set policy-options community CM-HS-Spoke members target:64512:302
### HUB CE configuration:
set logical-systems HS-HUB protocols bgp group PE-CE export DEFAULT-ROUTE
set logical-systems HS-HUB protocols bgp group PE-CE peer-as 100
set logical-systems HS-HUB protocols bgp group PE-CE neighbor 172.16.0.0
set logical-systems HS-HUB policy-options policy-statement DEFAULT-ROUTE from route-filter 0.0.0.0/0 exact
set logical-systems HS-HUB policy-options policy-statement DEFAULT-ROUTE then accept
set logical-systems HS-HUB routing-options autonomous-system 64512
set logical-systems HS-HUB routing-options static route 0.0.0.0/0 discard
Note: We use as-override to overcome the AS loop prevention issue we typically have when using the same AS on all customers sites,
Now let’s see how the R8 to R7 advertisements (via the HUB):
### Spoke to Spoke traffic is based on the default route originated by the HUB CE:
root@R8# run show route table HS-SPOKE.inet.0 192.168.7.1
HS-SPOKE.inet.0: 7 destinations, 7 routes (7 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both
0.0.0.0/0 *[BGP/170] 02:18:21, localpref 100, from 17.0.0.4
AS path: 64512 I, validation-state: unverified
> to 17.6.8.6 via ge-0/0/2.0, Push 47, Push 801001(top)
### At the PE HUB, we have the default route originated by the HUB CE.
root@R1# run show route table HS-HUB.inet.0 0/0 exact
HS-HUB.inet.0: 10 destinations, 10 routes (10 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both
0.0.0.0/0 *[BGP/170] 02:20:28, localpref 100
AS path: 64512 I, validation-state: unverified
> to 172.16.0.1 via ge-0/0/4.311
### But we also have a specific route to R7.
root@R1# run show route table HS-HUB.inet.0 192.168.7.0/24
HS-HUB.inet.0: 10 destinations, 10 routes (10 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both
192.168.7.0/24 *[BGP/170] 02:36:16, localpref 100, from 17.0.0.4
AS path: 64512 I, validation-state: unverified
> to 17.1.3.3 via ge-0/0/2.0, Push 38, Push 801007(top)
How can we use the default route instead of the more specific route to R7? This will work because we are missing the vrf-table-label configuration on the HUB PE VRF. Instead of striping off the VPN label and doing an IP lookup, the HUB PE will use the VPN label to just forward any packet directly to the CE, skipping the IP lookup.
### Notice the VPN Label:
root@R1# run show route advertising-protocol bgp 17.0.0.4 table HS-HUB.inet.0 0/0 exact detail
HS-HUB.inet.0: 10 destinations, 10 routes (10 active, 0 holddown, 0 hidden)
* 0.0.0.0/0 (1 entry, 1 announced)
BGP group iBGP type Internal
Route Distinguisher: 17.0.0.1:310
VPN Label: 49
Nexthop: Self
Flags: Nexthop Change
Localpref: 100
AS path: [100] 64512 I
Communities: target:64512:301
root@R8# run show route table HS-SPOKE.inet.0 192.168.7.1 extensive | match "Label operation:"
Label operation: Push 49, Push 801001(top)
Label operation: Push 49
Label operation: Push 49
### VPN label 49 action is POP and forward to ge-0/0/4.311 (HUB CE interface):
root@R1# run show route table mpls.0 label 49
mpls.0: 30 destinations, 30 routes (30 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both
49 *[VPN/170] 00:01:44
> to 172.16.0.1 via ge-0/0/4.311, Pop
### HUB CE will receive the packet (with no label) and send it back to the PE:
root@R9# run show route 192.168.7.1 logical-system HS-HUB
inet.0: 11 destinations, 11 routes (11 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both
192.168.7.0/24 *[BGP/170] 02:37:34, localpref 100
AS path: 100 100 I, validation-state: unverified
> to 172.16.0.0 via ge-0/0/1.311
### This time the HUB PE will make an IP lookup, and will use the route to R7:
root@R1# run show route table HS-HUB.inet.0 192.168.7.1
HS-HUB.inet.0: 10 destinations, 10 routes (10 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both
192.168.7.0/24 *[BGP/170] 02:54:44, localpref 100, from 17.0.0.4
AS path: 64512 I, validation-state: unverified
> to 17.1.3.3 via ge-0/0/2.0, Push 38, Push 801007(top)
### At this point this is normal VPN forwarding.
### Ping from R8 to R7 is working:
root@R9# run ping 192.168.7.1 source 192.168.8.1 logical-system HS-CE-R8 rapid count 10000
PING 192.168.7.1 (192.168.7.1): 56 data bytes
!!!!!!!!!!!!!!!!!!!!!!!!
### The interface (HUB-CE to HUB-PE interface) counters are showing that traffic is going via the HUB-PE:
root@R1# run monitor interface ge-0/0/4.311
Interface: ge-0/0/4.311, Enabled, Link is Up
Flags: SNMP-Traps 0x4000
Encapsulation: ENET2
VLAN-Tag [ 0x8100.311 ]
Local statistics: Current delta
Input bytes: 55092 [0]
Output bytes: 68956 [0]
Input packets: 893 [0]
Output packets: 865 [0]
Remote statistics:
Input bytes: 1087128 (123688 bps) [156324]
Output bytes: 1087128 (123688 bps) [156324]
Input packets: 12942 (184 pps) [1861]
Output packets: 12942 (184 pps) [1861]
Traffic statistics:
Input bytes: 1142220 [156324]
Output bytes: 1156084 [156324]
Input packets: 13835 [1861]
Output packets: 13807 [1861]
Protocol: inet, MTU: 1500, Flags: Is-Primary
3. Egress features
There is another flavor of this Hub & Spoke VPN with one interface. A flavor that will allow us to make an IP forwarding lookup on the hub PE and this way have egress features. This is particularly important for firewall filters, CoS, accounting, sampling, service features, etc. that need the hub PE to actually process the packet.
In order to have egress features on the hub PE, we will configure a 2nd VRF on the HUB PE that will make the IP forwarding lookup:
### On the original HUB VRF:
### 1. we will block all VPN advertisements
### 2. we will activate auto-export
set routing-instances HS-HUB no-vrf-advertise
set routing-instances HS-HUB routing-options auto-export
### The original vrf-export policy will stay as it was.
### It will not advertise any route to MP-BGP,
### but instead it will be used for auto-export route to the 2nd VRF we will create.
set routing-instances HS-HUB vrf-export HS-Hub-export
set policy-options policy-statement HS-Hub-export then community add CM-HS-Hub
set policy-options community CM-HS-Hub members target:64512:301
### Create the 2nd VRF, activate auto-export and use the same Route Target:
set routing-instances HS-HUB-downstream instance-type vrf
set routing-instances HS-HUB-downstream routing-options auto-export
set routing-instances HS-HUB-downstream route-distinguisher 17.0.0.1:311
set routing-instances HS-HUB-downstream vrf-target target:64512:301
### This is it, we have the default route from the 1st VRF copied to the 2nd VRF.
### Also important, we have no spoke prefixes in this VRF, so all traffic will go to the HUB CE.
root@R1# run show route table HS-HUB-downstream.inet.0
HS-HUB-downstream.inet.0: 4 destinations, 4 routes (4 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both
0.0.0.0/0 *[BGP/170] 00:17:55, localpref 100
AS path: 64512 I, validation-state: unverified
> to 172.16.0.1 via ge-0/0/4.311
172.16.0.0/31 *[Direct/0] 00:17:55
> via ge-0/0/4.311
172.16.0.0/32 *[Local/0] 00:17:55
Local via ge-0/0/4.311
192.82.31.1/32 *[Direct/0] 00:1
### Notice that even if we are doing a show route for table HS-HUB, the route is exported by the HS-HUB-downstream VRF:
root@R1# run show route advertising-protocol bgp 17.0.0.4 0/0 exact table HS-HUB
HS-HUB-downstream.inet.0: 4 destinations, 4 routes (4 active, 0 holddown, 0 hidden)
Prefix Nexthop MED Lclpref AS path
* 0.0.0.0/0 Self 100 64512 I
### Notice the label:
root@R1# run show route advertising-protocol bgp 17.0.0.4 0/0 exact table HS-HUB extensive
HS-HUB-downstream.inet.0: 4 destinations, 4 routes (4 active, 0 holddown, 0 hidden)
* 0.0.0.0/0 (1 entry, 1 announced)
BGP group iBGP type Internal
Route Distinguisher: 17.0.0.1:311
VPN Label: 55
Nexthop: Self
Flags: Nexthop Change
Localpref: 100
AS path: [100] 64512 I
Communities: target:64512:301
### Notice the label action, PoP and send to VRF HS-HUB-downstream for an IP lookup:
root@R1# run show route table mpls.0 label 55
mpls.0: 29 destinations, 29 routes (29 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both
55 *[VPN/0] 00:00:53
> via lsi.14 (HS-HUB-downstream), Pop
### Ping works:
root@R9# run ping 192.168.7.1 source 192.168.8.1 logical-system HS-CE-R8 rapid count 10000
PING 192.168.7.1 (192.168.7.1): 56 data bytes
!!!!!!!!!!!!!!!!!!!!!
### Traffic is going via the HUB CE interface:
root@R1# run monitor interface ge-0/0/4.311
Interface: ge-0/0/4.311, Enabled, Link is Up
Flags: SNMP-Traps 0x4000
Encapsulation: ENET2
VLAN-Tag [ 0x8100.311 ]
Local statistics: Current delta
Input bytes: 103727 [52]
Output bytes: 131501 [89]
Input packets: 1685 [1]
Output packets: 1656 [1]
Remote statistics:
Input bytes: 3236468 (123616 bps) [31532]
Output bytes: 3236520 (123616 bps) [31584]
Input packets: 38529 (183 pps) [375]
Output packets: 38530 (183 pps) [376]
Traffic statistics:
Input bytes: 3340195 [31584]
Output bytes: 3368021 [31673]
Input packets: 40214 [376]
Output packets: 40186 [377]
To conclude, by having a more complex solution, two HUB VRFs instead of one, we get the IP lookup we want.
External Resources: