Skip to content

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

S3·E1Analyze~30 minsources checked todaylab mutates hardwareverified 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

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

The situation · Bridge call, 08:10, the morning after a change window

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]

Endpoint (RFC 8986 §4.1-§4.3)
Decap services (§4.4-§4.9)
Binding (§4.13, §4.15)
Headend (§5)
uSID names (RFC 9800)
flavor
implementation

End.DT46

IANA codepoint 20FRR staticd/bgpd: supported
RFC 8986 §4.8 · End.DT46 — decap and dual-stack table lookup

One 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.

LOC:FUNCT:ARG — 128 bitsFRR zebra locator defaults (/48 locator)LOC = B:N (48b)B 24bN 24bARG 64bbit 0bit 127B 24b · N 24b · FUNCT 16b · ARG 64b
RFC 8986 / 9800supported

Defined in RFC 8986 §4.8.

Linux seg6localsupported

`action End.DT46 vrftable 100 dev vrf100` — needs a real VRF device and `net.vrf.strict_mode=1`.

FRR staticd/bgpdsupported

`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`.

source

Cumulus on Spectrum-4not supported

Not implemented — default VRF only.

community SONiCsupported

Phase 1: "Support END.DT46". SAI: SAI_MY_SID_ENTRY_ENDPOINT_TYPE_DT46.

DOCA Flow 3.5.0not supported

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`.

End.DT46 is the per-VRF dual-stack service SID. Switch the implementation column and read where it is actually available.

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.

SymptomBlueField-3 on a Dell Pow…SymptomSRv6: a SID is configured…SymptomAn L3VPN service SID does…CheckDid the SID Manager actu…
Symptom

An L3VPN service SID does not forward

Work the service-SID branch: SID Manager, then BGP ownership, then the SID structure, then nexthop validity.

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_Red

5Break 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]

An SRv6 L3VPN that does not forward: worked, faded, problem

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]

The ladder, in the order that costs least

How it ended

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

Mutating steps ahead. Needs a maintenance window, out-of-band access (BMC/iDRAC/rshim console) and a rollback path. Record the pre-flight inventory before changing anything. Never on a production host.

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.

  1. 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> nomaster and restore the addresses from the pre-flight dump.
  2. Export a per-VRF SID. Configure a locator and sid vpn per-vrf export auto on the Arm, peering with a PE in the containerlab fabric. Expected: show segment-routing srv6 sid on the Arm lists a bgp-owned SID for that VRF.[4] Rollback: remove the sid vpn per-vrf export line, which releases the SID back to the pool.
  3. 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.
  4. Capture the encapsulation. tcpdump -nni <uplink> -vv ip6 on 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 with encap-behavior H_Encaps_Red there is no SRH at all — only the outer IPv6 header.[3][5] If not: you are on H_Encaps, or the policy has more than one segment.
  5. 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 detail on the ingress side and the behavior column in show segment-routing srv6 sid on the egress side.[3][4]
  6. 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]
  7. 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 sid no longer lists the released SID.

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.

14 flashcards for this lesson — 0 in deck. Spaced review lives at /review.

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.

  1. RFC 9252 — BGP Overlay Services Based on Segment Routing over IPv6 (SRv6) · fetched 2026-09-07
  2. RFC 9819 — Argument Signaling for BGP Services in SRv6 · fetched 2026-09-07
  3. FRR documentation — BGP (latest) · fetched 2026-09-07
  4. FRR documentation — Zebra (latest) · fetched 2026-09-07
  5. RFC 8986 — SRv6 Network Programming · fetched 2026-09-07
  6. FRR documentation — Static (latest) · fetched 2026-09-07
  7. Segment Routing | Cumulus Linux 5.18 · fetched 2026-09-07
  8. GitHub API: FRRouting/frr releases · fetched 2026-09-07
  9. RFC 8754 — IPv6 Segment Routing Header (SRH) · fetched 2026-09-07
  10. 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.