L3VPN service
This page documents the L3VPN service end-to-end: how an operator creates one through the Streamlit catalog, what Infrahub does automatically, and how each vendor's configuration differs.
For field-level schema details see schema-reference.
User flowā
Step-by-stepā
-
Catalog form ā The operator fills in the VPN name, selects a tenant, picks a PE and a customer subnet for each site, and chooses a routing protocol (eBGP / static / connected).
-
Branch + objects ā The catalog allocates a
vpn_idfromvpn_id_pool, opens a feature branch, and writesServiceL3Vpn+ServiceL3VpnSiteobjects to Infrahub. All objects start withstatus = draft / provisioning. -
Generator run (event-driven) ā Once the sites exist, the catalog adds the
ServiceL3Vpnto thel3vpnsgroup. That membership change fires thetrigger-l3vpn-generatorgroup trigger (aCoreGroupTriggerRule+CoreGeneratorAction, seeobjects/events/00_triggers.yml), which runsL3VpnGeneratoron the branch. The generator is deliberately not run inside the proposed-change pipeline (execute_in_proposed_change: falsein.infrahub.yml): in the pipeline it races artifact rendering, which render before the VRF/IPs exist and produce an empty configuration diff. Running it on the branch-change event guarantees the data lands before artifacts render.L3VpnGeneratordoes all heavy lifting:- Creates an
IpamVRFwithvrf_rd = <provider-asn>:<vpn_id>. - Creates matching import and export route targets.
- Selects the lowest-numbered free interface on the PE.
- Allocates a /30 from
pe_ce_pooland creates the PE and CE IP addresses. - Places the customer subnet prefix into the VRF.
- Creates a
RoutingBGPSession(eBGP,session_type = EXTERNAL) whenrouting_protocol = ebgp. - Promotes
ServiceL3Vpn.statustoactiveandServiceL3VpnSite.statustoactive.
- Creates an
-
Proposed Change ā Once the generator has finished (service
active) and the per-PE artifacts are rendered, the catalog opens aCoreProposedChangefrom the feature branch intomainfor review. -
Checks ā Four checks must pass before merge is allowed (see Checks below).
-
Transforms ā Configuration artifacts are rendered for each PE that has at least one L3VPN site.
-
Merge ā The operator reviews the diff in the Infrahub UI and merges. On the target platform the artifact is pushed via the relevant
invoketask (for example,invoke lab.push-arista).
Checksā
| Check | File | What it enforces |
|---|---|---|
l3vpn_overlap | checks/l3vpn_overlap.py | No two active ServiceL3Vpn objects share the same vpn_id |
l3vpn_site_subnet | checks/l3vpn_site_subnet.py | The customer subnet is not already claimed by another site in the same VRF |
pe_interface_alloc | checks/pe_interface_alloc.py | The nominated PE interface exists and has status = free |
backbone_session_count | checks/backbone_session_count.py | Every PE in the backbone has its full-mesh complement of Nā1 iBGP sessions (N = PE count ā 7 each on the 8-PE financial backbone, 3 each on the 4-PE isp backbone) |
batfish_backbone | checks/batfish_backbone.py | Static validation of the rendered backbone configs via Batfish ā see Batfish validation for the full query battery |
A failing check blocks merge of the proposed change. The operator can see the check result in the Infrahub UI under the proposed change's Checks tab.
Generator: what it createsā
| Object | Created by | Notes |
|---|---|---|
IpamVRF | _ensure_vrf | Name = VPN name; vrf_rd = <asn>:<vpn_id> |
IpamRouteTarget | find_or_create_route_target | Same value used for import and export RT |
InterfacePhysical (updated) | next_free_physical_interface | Lowest free-status interface on the PE; role set to cust |
IpamPrefix (/30) | allocate_prefix_from_pool | Allocated from pe_ce_pool; placed in VRF |
IpamIPAddress Ć 2 | direct create | .1 = PE, .2 = CE within the /30 |
RoutingBGPSession | _ensure_ebgp_session | Only when routing_protocol = ebgp; session_type = EXTERNAL |
The generator is idempotent: re-running it on an L3VPN that already has a VRF and allocated addresses will skip re-creation and only update missing fields.
Per-vendor configurationā
The transform layer renders a full configuration fragment for each PE
when that PE has at least one active L3VPN site. Configuration sections
differ by vendor. On the default financial backbone every PE is Arista,
so trading-floor-vpn renders the Arista EOS form at all four sites; the
isp dataset, with one PE per vendor, exercises all four forms below:
Arista EOSā
vrf instance <name>
rd <asn>:<vpn_id>
!
router bgp <asn>
vrf <name>
rd <asn>:<vpn_id>
route-target import <rt>
route-target export <rt>
neighbor <ce-ip> remote-as <ce-asn>
network <customer-subnet>
Cisco IOS-XRā
vrf <name>
address-family ipv4 unicast
import route-target
<rt>
export route-target
<rt>
!
router bgp <asn>
vrf <name>
rd <asn>:<vpn_id>
address-family ipv4 unicast
neighbor <ce-ip>
remote-as <ce-asn>
Juniper Junosā
routing-instances {
<name> {
instance-type vrf;
interface <pe-iface>;
vrf-target target:<rt>;
protocols {
bgp {
group PE-CE {
neighbor <ce-ip> {
peer-as <ce-asn>;
}
}
}
}
}
}
Nokia SR OSā
service {
vprn "<name>" customer "1" create
route-distinguisher <rd>
vrf-target target:<rt>
interface "<pe-iface>" create
address <pe-ip>/<mask>
sap <port>:0 create
exit
exit
bgp-vpn-backup
no shutdown
exit
}