L3VPN and EVPN services over SRv6
S3·E1Every VPN prefix went invalid at 02:00 · Bridge call, 08:10, the morning after a change window
Builds on: SRv6 in the Linux kernel: seg6 and seg6local, FRR: locators, the SID Manager, IS-IS SRv6 and TI-LFA
Before you read: what do you already know?
3 quick questions. Wrong answers are fine and expected; trying first makes the lesson stick.
After this lesson you can
- Decompose an SRv6 service advertisement into the Prefix-SID attribute, the SID Information sub-TLV and the SID Structure sub-sub-TLV.
- Explain why the Transposition Scheme packs per-VRF service SIDs well and per-PW service SIDs badly.
- Configure an SRv6 L3VPN in FRR and read the verification ladder from the SID Manager up to the VPN nexthop.
- Distinguish what RFC 9252 defines for EVPN over SRv6 from what FRR and Cumulus Linux actually document.
Episode 1 — Every VPN prefix went invalid at 02:00
Eleven people on the bridge and only the customer’s network lead is talking. His team pre-staged SRv6 on one FRR provider edge ahead of the bake-off, and by 02:00 every MPLS L3VPN prefix on that router had gone invalid. The night-shift operator called it in at 02:10, then labelled the console cable on his way out. Nothing crashed: the session stayed up, the configuration parsed, and a few thousand routes carrying imaging studies between four reading rooms stopped being valid paths. The Dell SE is muted, coffee cold, promise spreadsheet open. You know the fix is one word: srv6-only, which FRR turns on the moment any locator is configured, so that any BGP update without an SRv6 prefix-SID attribute is no longer considered valid.[3]
SRv6 services exist because an SR-MPLS VPN label means nothing outside the two routers that agreed on it, while a 128-bit IPv6 address is the instruction itself and is still forwarded by every ordinary hop in between. RFC 9252 carries those service SIDs inside the existing BGP Prefix-SID attribute, so there is no new path attribute and no new address family to negotiate, and RFC 9819 tightened the argument signalling for them in July 2025.[1][2] Failing closed is the price: once an operator declares an SRv6 data plane, installing a VPN path you cannot encapsulate is worse than refusing it.
A router that refuses a path tells you something; one that installs a path it cannot encapsulate does not.
The lead opens his notebook and asks what that attribute actually carries. That is segment 1.
1A service SID is an address that carries its own instruction
In SR-MPLS a VPN route carries a 20-bit label and the label means nothing outside the pair of routers that agreed on it. In SRv6 the same job is done by a 128-bit IPv6 address that is itself the instruction, and the standard that defines how BGP carries it is RFC 9252, published July 2022 and updated by RFC 9819 in July 2025.[1][2]
The encoding reuses machinery that already exists. SRv6 service SIDs ride in SRv6 Services TLVs inside the BGP Prefix-SID attribute, so there is no new path attribute and no new address family to negotiate.[1] Inside that, the SRv6 SID Information Sub-TLV is Service Sub-TLV Type 1, laid out as Type (1 octet), Length (2 octets), RESERVED1 (1 octet), the SRv6 SID Value (16 octets), a Svc SID Flags octet with no flags currently defined, the SRv6 Endpoint Behavior (2 octets), RESERVED2, and then any Service Data Sub-Sub-TLVs.[1]
Two details in that layout matter on a call. The behavior field takes codepoints “from IANA’s ‘SRv6 Endpoint Behaviors’ subregistry”, and the opaque value 0xFFFF “MAY be used when the advertising router wishes to abstract the actual behavior” — so a decode showing 0xFFFF is a deliberate choice, not a bug.[1] And when several SID Information sub-TLVs are present, “the ingress PE SHOULD use the SRv6 SID from the first instance of the Sub-TLV.”[1]
The second piece is the SRv6 SID Structure Sub-Sub-TLV, Service Data Sub-Sub-TLV Type 1, which advertises the lengths of the Locator Block, Locator Node, Function and Argument fields.[1] That is the same LOC:FUNCT:ARG carve-up RFC 8986 defines, now signalled explicitly so the receiver does not have to guess where the function begins.[5]
End.DT46
IANA codepoint 20FRR staticd/bgpd: supportedOne SID per VRF that decapsulates and looks the inner packet up in either the IPv4 or the IPv6 table. The per-VRF dual-stack service SID.
Defined in RFC 8986 §4.8.
`action End.DT46 vrftable 100 dev vrf100` — needs a real VRF device and `net.vrf.strict_mode=1`.
`sid vpn per-vrf export auto` (one SID for both AFs) or `sid export ... behavior dt46`: "When both address-families specify behavior dt46, the SID Manager allocates a single SID". staticd: `behavior uDT46 vrf VRF`.
Not implemented — default VRF only.
Phase 1: "Support END.DT46". SAI: SAI_MY_SID_ENTRY_ENDPOINT_TYPE_DT46.
Not applicable.
FAE angle: customers conflate End.DT46 with "the VPN label". Reframe it: SR-MPLS carries a 20-bit label, SRv6 carries a 128-bit address that IS the instruction. When an SRv6 L3VPN will not forward, check first that the egress PE's service SID is routable from the ingress PE — `show bgp nexthop detail`.
2Transposition: why per-VRF packs and per-PW does not
The Transposition Scheme is the part people skip and then cannot decode. Instead of putting the whole 128-bit SID in the TLV, the advertiser puts only the common part there — the locator — and carries the variable part, the Function or Argument bits, in the label field the service encoding already has. “Transposition Offset indicates the bit position, and Transposition Length indicates the number of bits that are being taken out of the SID and put into the label field”, and the RFC states that “the use of the Transposition Scheme is RECOMMENDED for the specific service encodings that allow it.”[1]
Why it exists is a packing argument, and it is the cleanest design lesson in the whole document. Per-VRF SID allocation — End.DT4 or End.DT6, one SID shared by every prefix in the VRF — has a locator that repeats across many NLRIs, so factoring it out saves real bytes. Per-PW allocation, as in EVPN VPWS with End.DX2, needs a unique SID per NLRI, so there is nothing common to factor out and transposition buys nothing.[1]
For IPv4 VPN over SRv6 the MP_REACH_NLRI is encoded per RFC 8950 with an IPv6 next hop, the 20-bit Label Value carries the transposed Function bits, “otherwise it is set to Implicit NULL”, and the hard constraint is that “the Transposition Length MUST be less than or equal to 20 and less than or equal to the FL.”[1] The same section recommends the behavior be one of End.DX4, End.DT4 or End.DT46.[1]
That 20-bit ceiling is the number to remember. A design with a 32-bit function length cannot transpose the whole function into an IPv4-VPN label field, and an implementation that tries will either truncate or fall back to carrying the full SID.
3EVPN over SRv6: defined, recently tightened, thinly implemented
RFC 9252 section 6 is explicit: “To support SRv6-based EVPN overlays, one or more SRv6 Service SIDs are advertised with Route Types 1, 2, 3, and 5”, with per-route-type sections for Ethernet A-D (per-ES and per-EVI), MAC/IP Advertisement, Inclusive Multicast Ethernet Tag, Ethernet Segment and IP Prefix.[1] Signalling a service SID does two jobs at once: it “indicates that the BGP egress device supports SRv6 overlay, and the BGP ingress device receiving this route MUST perform IPv6 encapsulation and insert an SRH when required”, and it carries the SID value.[1][9]
The interoperability fault line is the argument. For an Ethernet A-D per-ES route only the Argument is signalled, and it “MAY be transposed in the Ethernet Segment Identifier (ESI) Label field of the ESI Label extended community, and the SID value in the SRv6 Services TLV is set to 0”; the receiver then reassembles the End.DT2M SID from the Route Type 3 SID plus that label.[1] That reassembly was underspecified, which is exactly why RFC 9819 exists: it “provides more detailed specifications for the signaling and processing of SRv6 Segment Identifier advertisements for BGP overlay service routes associated with SRv6 Endpoint Behaviors that support arguments”, and its section 3 is entirely End.DT2M signalling for EVPN ESI filtering.[2]
Now the part you say out loud rather than assume. FRR’s BGP guide documents L3VPN and global-table SRv6 only; there is no EVPN-over-SRv6 section in it. Whether FRR implements RFC 9252 section 6 at all is unverified from the documentation, and a re-check of that page on 2026-09-07 for this lesson found no SRv6-plus-EVPN material.[3] The Cumulus Linux Segment Routing page documents no BGP service SIDs of any kind — SRv6 there is fabric transport for NIC-originated traffic with static uN and uA SIDs, not an EVPN encapsulation.[7] Neither statement is a claim of non-support; both are statements about what is published.
There is also a service model that avoids the VPN encodings entirely: RFC 9723, BGP Colored Prefix Routing, steers by color instead.[10] Worth naming when a customer’s real requirement is intent-based steering rather than tenancy.
An L3VPN service SID does not forward
4The FRR realisation, command by command
FRR splits the work between zebra and bgpd. Zebra owns the locators: segment-routing then srv6 then locators then locator NAME, with prefix X:X::X:X/M [block-len (16-64)] [node-len (16-64)] [func-bits (0-64)].[4] The constraint is verbatim: “block-len, node-len and func-bits may be any value as long as block-len+node-len = locator-len and block-len+node-len+func-bits <= 128”, the defaults are “block-len = 24, node-len = prefix-len-24”, and “the function bits range is 16 bits by default.”[4] Declaring behavior usid on the locator changes what BGP advertises: uDT4, uDT6 and uDT46 instead of the classic End.DT4, End.DT6 and End.DT46.[4]
bgpd then attaches to that locator with router bgp ASN, segment-routing srv6, locator NAME — and “the Locator name must be set in zebra, but user can set it in any order.”[3] Per-VRF service SIDs come from sid vpn per-vrf export <(1..1048575)|auto|explicit X:X::X:X>, where “a single SID is used for both IPv4 and IPv6 address families”, and FRR notes that “a VRF can have at most one L3 service enabled simultaneously.”[3]
Two defaults cause most of the tickets. First, encap-behavior <H_Encaps|H_Encaps_Red>: by default an SRH is added in addition to the IPv6 header, and with H_Encaps_Red, “if the number of segments is only 1, and there are no other specific options, then the segment-routing-header is removed, and only the IPv6 header is appended.”[3] That is RFC 8986 section 5.2 exactly.[5] Second, srv6-only is on by default: once any locator is configured, “any BGP update without an SRv6 prefix SID attribute will not be considered as valid”, and no srv6-only restores mixed MPLS and SRv6 operation.[3] Per peer you can be finer: neighbor X:X::X:X encapsulation-srv6 or encapsulation-mpls filters outgoing updates and validates incoming ones.[3]
The silent failure is worth its own sentence. If zebra is not running, or the command is missing, or SID allocation fails, “automatic or explicit SID assignment will not complete, which will block corresponding route export” — with no error on the BGP side.[3] Check show segment-routing srv6 sid before you debug BGP.[4]
! egress PE
segment-routing
srv6
locators
locator loc1
prefix fcbb:bbbb:2::/48 block-len 32 node-len 16 func-bits 16
behavior usid
!
router bgp 65001
segment-routing srv6
locator loc1
!
address-family ipv4 vpn
neighbor fcbb:bbbb:1::1 activate
exit-address-family
!
router bgp 65001 vrf vrf10
sid vpn per-vrf export auto
encap-behavior H_Encaps_Red5Break it three ways and read the ladder
The verification ladder runs bottom-up because each rung depends on the one below: show segment-routing srv6 sid (does the SID exist at all, and who allocated it), show bgp segment-routing srv6 (does bgpd own the locator chunk and its functions), show bgp ipv6 vpn detail (is the SID in the path, and does its structure vector match), show bgp nexthop detail (is that service SID a valid nexthop).[4][3] The show segment-routing srv6 sid output columns are SID, Behavior, Context, Daemon/Instance, Locator and Allocation Type, with rows such as fcbb:bbbb:1:fe00:: uDT6 VRF 'vrf10' bgp(0) MAIN dynamic.[4] The nexthop rule is explicit: “in MPLS-VPN or SRv6-VPN, an L3VPN next-hop entry requires that the path chosen respectively contains a labelled path or a valid SID IPv6 address.”[3]
Symptom. VRF vrf10 on PE1 cannot reach vrf10 on PE2. BGP is up, IS-IS is up, the underlay pings.
Rung 1 — does the SID exist? On PE2: show segment-routing srv6 sid. Expected: a row whose Behavior is uDT46 (or uDT6), Context is VRF 'vrf10', Daemon is bgp(0) and Allocation Type is dynamic.[4] Here the table is empty of any bgp-owned row. That is the answer already: the SID was never allocated, so route export is blocked and everything above this rung is noise.[3]
Why it was empty. The locator under router bgp was named LOC1 while zebra’s locator is loc1. FRR accepts the configuration in either order, so there is no error at commit time — the SID Manager simply never matches a locator and never allocates.[3][4]
Fix and re-check. Correct the name, then walk up: show bgp segment-routing srv6 now prints the locator name, its chunks and the functions bgpd manages.[3] show bgp ipv6 vpn detail on PE1 shows the received path with the SID and a structure vector of [32 16 16 0 0 0], matching the locator’s block-len 32, node-len 16, func-bits 16.[4][1] show bgp nexthop detail shows the service SID as valid with an IGP metric. Ping across the VRF succeeds.
Second break, deliberately. Filter the egress PE’s locator prefix out of IS-IS on PE1. Now show bgp ipv6 vpn detail still shows the SID, but show bgp nexthop detail no longer lists it as valid and the VPN prefix does not install — the RFC’s “SHOULD be routable within the AS of the egress PE” made concrete.[1][3]
Third break. Add an MPLS-only peer. Its updates go invalid because srv6-only is on by default; no srv6-only under the BGP instance restores them, or neighbor X:X::X:X encapsulation-mpls scopes the tolerance to that peer.[3]
Fill the blanks against your own containerlab fabric.
- PE2:
show ____________________ srv6 sid. Expected columns: SID, ______, Context, ______, Locator, Allocation Type. A bgp-owned service SID row has ContextVRF '____'and Allocation Type ______. - If no bgp-owned row exists, the cause is one of: zebra is ______, the command
sid vpn per-vrf export ____is missing, or the ______ name does not match between zebra and bgpd. All three block ______ ______ with no BGP-side error. - PE1:
show bgp ipv6 vpn detail. The bracketed vector[__ __ __ 0 0 0]is the ______ ______ Sub-Sub-TLV and its first three values are the ______, ______ and ______ lengths. - PE1:
show bgp ______ detail. If the service SID is not ______, the VPN prefix will not install even though the path is present. - Existing MPLS VPN peers went invalid the moment the locator appeared. The default responsible is ____________, and the two fixes are ____________ globally or
neighbor X:X::X:X ____________________per peer.
Do it on the fabric, timed. On the containerlab FRR topology from module 2, bring up an SRv6 L3VPN between two PEs with sid vpn per-vrf export auto on each. Then have someone else introduce one of the three breaks above without telling you which, and find it using only the four-rung ladder. Record the time to diagnosis for each of the three breaks.
Acceptance criteria. For each break you name the rung that first showed the fault and quote the exact output line that proved it. Your write-up states which rung would have been misleading if you had started at the top instead of the bottom. You record, in one sentence, that FRR’s BGP guide documents no EVPN-over-SRv6 section, so nothing in this exercise demonstrates EVPN parity.[3] Finally, you note the FRR version you ran against and whether it is on or after the 2026-08-31 patch line, which carried SRv6 parser hardening across 10.7.1, 10.6.2, 10.5.5 and 10.4.5.[8]
The ladder, in the order that costs least
By the second bridge call the router is mixed-mode again with no srv6-only, and the four rungs are in the network lead’s notebook: does the SID exist in zebra’s SID Manager, does bgpd own the locator and its functions, is the SID and its structure vector in the VPN path, is that service SID a valid nexthop.[4][3] What you say to him: “Your L3VPN is one per-VRF address the far PE has to be able to route to, and Cumulus Linux documents no BGP service SIDs at all, so do not plan EVPN over SRv6 on Spectrum-4 today.”[1][7] He writes that down too. Tonight’s window has a SID that answers a ping with an ICMP error, and he wants to know whether that is worse than one that answers nothing.
Lab
Pre-flight inventory (read-only). On the Dell-lab BlueField-3: cat /etc/mlnx-release, uname -r, ip -br link, ip -6 route show | grep -c seg6local, and on the Arm vtysh -c "show version" if FRR is installed. Record the host-side representor names and which are currently in use. Write down the current ip -6 route show output for the representor you are about to move — that dump is your rollback reference.
- Make the Arm a PE. Run FRR on the BlueField-3 Arm and put the host-facing representor into a VRF. Expected: the representor shows as a member of the VRF and the host workload still has link. Rollback:
ip link set <rep> nomasterand restore the addresses from the pre-flight dump. - Export a per-VRF SID. Configure a locator and
sid vpn per-vrf export autoon the Arm, peering with a PE in the containerlab fabric. Expected:show segment-routing srv6 sidon the Arm lists a bgp-owned SID for that VRF.[4] Rollback: remove thesid vpn per-vrf exportline, which releases the SID back to the pool. - Prove reachability. From a host workload in the VRF, reach the far VRF across the fabric. Expected: ping succeeds and the far PE’s VRF route table shows the prefix.
- Capture the encapsulation.
tcpdump -nni <uplink> -vv ip6on the wire between the Arm and the first fabric hop. Expected: the outer destination address equals the far PE’s service SID, and if the policy is a single segment withencap-behavior H_Encaps_Redthere is no SRH at all — only the outer IPv6 header.[3][5] If not: you are onH_Encaps, or the policy has more than one segment. - Match the wire to the control plane. Confirm the behavior you captured matches what BGP advertised by reading the structure vector in
show bgp ipv6 vpn detailon the ingress side and the behavior column inshow segment-routing srv6 sidon the egress side.[3][4] - Optional, customer lab only. If the customer already runs EVPN over SRv6 on another vendor, collect their route dump and decode the Route Type 1 and Route Type 3 argument transposition by hand against RFC 9252 section 4 and RFC 9819 section 3.[1][2] Do this instead of claiming FRR parity, which the documentation does not support.[3]
- Leave it clean. Undo steps 1 and 2 in reverse order, confirm the representor is back where the pre-flight dump says it was, and confirm
show segment-routing srv6 sidno longer lists the released SID.
Goal. Build an SRv6 L3VPN on the containerlab FRR fabric, then break it three ways and time yourself.
- Pre-flight. On the containerlab host:
sysctl net.ipv6.conf.all.seg6_enabled net.vrf.strict_modeandvtysh -c "show version"on each FRR node. Expected: seg6 enabled and an FRR at or after 10.4 so that uSID behaviors exist.[6] If not: set the sysctls on the host (containerlab nodes share the host kernel) and pull a newer FRR image before continuing. - VRFs. Add
vrf10on two PE nodes with a customer-facing interface enslaved to it. Expected:ip -br link show type vrflists the VRF up on both. - Locators. Configure
locator loc1 prefix fcbb:bbbb:N::/48 block-len 32 node-len 16 func-bits 16on each PE and confirm withshow segment-routing srv6 locator detail.[4] Expected: chunk ownership shows a daemon, notsystem, once BGP claims it. - Service SIDs. Under
router bgp ASN vrf vrf10, addsid vpn per-vrf export auto. Expected:show segment-routing srv6 sidgains a row with Behavior uDT46 or uDT6 and ContextVRF 'vrf10'.[4] If not: the locator name does not match, or zebra is down — route export is blocked with no BGP error.[3] - Ladder. Walk all four rungs in order and paste each output into your notes:
show segment-routing srv6 sid,show bgp segment-routing srv6,show bgp ipv6 vpn detail,show bgp nexthop detail, then ping between the VRFs.[4][3] - Break 1 — rename the locator under
router bgponly. Expected: the bgp-owned SID row disappears and the far PE loses the VPN prefix, with no error message anywhere. Rollback: restore the original locator name and re-check rung 1. - Break 2 — make the egress service SID unroutable from the ingress PE (filter the locator prefix in IS-IS on the ingress node). Expected: the path is still in
show bgp ipv6 vpn detailbutshow bgp nexthop detailno longer shows it valid.[3] Rollback: remove the filter and confirm the nexthop returns to valid. - Break 3 — add an MPLS-only peer. Expected: its updates are invalid while
srv6-onlyis in effect;no srv6-onlymakes them valid again.[3] Rollback: re-entersrv6-onlyif you want the fail-closed default back, and note which state you left the lab in. - Write the negative result. In your notes, record explicitly: FRR’s BGP guide documents L3VPN and global-table SRv6 only, with no EVPN-over-SRv6 section, so this lab demonstrates nothing about EVPN parity.[3]
Retrieval check
10 questions from memory. Answer before looking anything up; misses become flashcards.
Explain it to a Dell SE
Explain to a Dell SE, in five sentences, what actually moves between two PEs when an SRv6 L3VPN comes up, and what you would check first when it does not.
Sources
Facts in this lesson were checked against RFC 9252 and RFC 9819 (rfc-editor), FRR latest docs (bgp/zebra/static) and the FRR release list re-checked 2026-09-07; Cumulus Linux 5.18 Segment Routing page 2026-09-07. Dates are when each page was fetched.
- RFC 9252 — BGP Overlay Services Based on Segment Routing over IPv6 (SRv6) · fetched 2026-09-07
- RFC 9819 — Argument Signaling for BGP Services in SRv6 · fetched 2026-09-07
- FRR documentation — BGP (latest) · fetched 2026-09-07
- FRR documentation — Zebra (latest) · fetched 2026-09-07
- RFC 8986 — SRv6 Network Programming · fetched 2026-09-07
- FRR documentation — Static (latest) · fetched 2026-09-07
- Segment Routing | Cumulus Linux 5.18 · fetched 2026-09-07
- GitHub API: FRRouting/frr releases · fetched 2026-09-07
- RFC 8754 — IPv6 Segment Routing Header (SRH) · fetched 2026-09-07
- RFC 9723 — BGP Colored Prefix Routing (CPR) for Services Based on SRv6 · fetched 2026-09-07
The same idea elsewhere
Other lessons that cover this ground, sometimes from another course's angle.
- SIDs as instructions: LOC:FUNCT:ARG and the behavior zooElsewhere in this course · Same ground: iana, evpn and services
- FRR: locators, the SID Manager, IS-IS SRv6 and TI-LFAElsewhere in this course · Same ground: frr, sid and srv6-only
- BGP-EVPN symmetric IRB for tenant isolationSpectrum-X course · Same ground: evpn, bgp and vrf