An IPv6 destination address can name an operation. Route the packet to that address, and the receiving router advances a path instruction, selects an adjacency, or removes an outer header and looks up the enclosed packet in a particular routing table. Routers between those operations can remain ordinary IPv6 routers.

That is the architectural attraction of Segment Routing over IPv6, or SRv6. Reachability and network programming share an address space. A path can carry instructions for transport and service delivery without requiring every transit router to understand every instruction.

The architectural elegance is ahead of the ecosystem. The gap is narrower than “the hardware isn’t ready” or “nobody deploys it,” though. Production networks exist at substantial scale, and the core standards extend well beyond a header format. The uneven parts are the features exposed by particular platforms, the cost of fitting instructions into packets, and the operational machinery needed to explain a failure across an encapsulation boundary. A packet that forwards correctly at one size and disappears at the next byte makes that distinction concrete.

Packet-level tests prepared for this article supply evidence for those mechanisms. They use Linux 6.12.113+, network namespaces (isolated network stacks within one kernel) and virtual Ethernet, or veth, links. Routes are static rather than learned through a routing protocol. The results describe that kernel, configuration and topology. They aren’t measurements of router ASIC performance or a survey of vendor defaults. Protocol and deployment references reflect the source versions checked on 7 November 2026, Europe/Ljubljana (CET).

Start with the IPv6 destination

An IPv6 packet has a 40-byte base header. Its destination address determines where ordinary forwarding sends it; its Next Header field identifies what follows. An SRv6-encapsulated IPv6 packet can therefore look like this:

Outer IPv6 header (40 bytes)
  Source       = SR ingress address
  Destination  = currently active SID
  Next Header  = 43 (Routing header)
    |
Segment Routing Header
  Routing Type = 4
  Next Header  = 41 (encapsulated IPv6)
    |
Original IPv6 packet, including its own IPv6 header

The original packet survives inside another packet. The outer destination changes as the SR program advances; the inner destination continues to identify the original receiver. RFC 8754 [1] specifies the Segment Routing Header, or SRH. RFC 8986 [2] specifies endpoint behaviors and encapsulation operations.

A segment identifier, or SID, is a 128-bit IPv6 address with meaning at its owner. RFC 8986 describes its structure as LOC:FUNCT:ARG: a locator that gets the packet to the node, a function identifying local behavior, and optional arguments. Those fields don’t have one universal allocation of bits. The locator itself can be divided into a common block and a node identifier.

Ordinary transit forwarding needs the routable locator. It needn’t interpret the function bits. At the node that owns the SID, local state binds the destination to an operation. The address doesn’t contain executable machine code, and another router cannot infer arbitrary behavior merely by reading its suffix. “Network programming” here means selecting installed, defined behaviors and composing their execution through packet instructions.

The distinction from MPLS is at the forwarding layer. An MPLS label-stack entry [3] occupies four bytes. SR-MPLS represents segments using labels and retains MPLS forwarding. SRv6 uses IPv6 addresses and IPv6 forwarding between segment endpoints. SR-MPLS over IP [4], where an IP or UDP encapsulation carries MPLS labels, is a separate mechanism. An IPv6 tunnel to an SRv6 service SID isn’t SR-MPLS in disguise.

What an endpoint actually does

The basic End behavior advances the segment program. When an SRH still has segments left, the endpoint performs validation and hop-limit checks, decrements the hop limit and Segments Left, copies Segment List[Segments Left] into the IPv6 destination address, then submits the packet to IPv6 forwarding. The next SID can be several ordinary IP hops away. An instruction list needn’t enumerate every router.

End.X adds a specified layer-3 adjacency: after advancing the active segment, it forwards through the adjacency associated with that SID. This is how an instruction can constrain a particular link rather than merely require arrival at a node. End.DT6 is different. At the end of the segment program it removes the outer IPv6 encapsulation and looks up the enclosed IPv6 packet in the table associated with the SID. End.DT4 and End.DT46 provide related IPv4 and dual-stack table-lookup behaviors. End.DX6 instead identifies a decapsulation cross-connect to an IPv6 adjacency. These distinctions live in the behavior definitions [2], not in a generic meaning of “endpoint.”

They also explain the appeal for VPN services. An egress SID can select a service table, doing the service-selection work that an MPLS VPN label performs in another data plane. Routing gets the outer packet there. The SID determines what happens after it arrives.

The test topology makes these bindings explicit:

h1 -- r1 -- r2 -- p -- r3 -- r4 -- h2

r1: ingress       r2: End       p: ordinary IPv6 transit
r3: End.X         r4: End.DT6

Hosts h1 and h2 send and receive the original packets. Router r1 is the headend that applies the policy; r4 is the decapsulating egress. Here p has IPv6 forwarding enabled, no local SIDs, and seg6_enabled=0. Interfaces named r face right and those named l face left. Each row below is a directly connected /64 link:

Link Left endpoint Right endpoint
h1–r1 2001:db8:10::1 2001:db8:10::2
r1–r2 2001:db8:11::1 2001:db8:11::2
r2–p 2001:db8:12::1 2001:db8:12::2
p–r3 2001:db8:13::1 2001:db8:13::2
r3–r4 2001:db8:14::1 2001:db8:14::2
r4–h2 2001:db8:15::1 2001:db8:15::2

Router rN owns locator 5f00:0:N::/48 for N = 1 through 4. Static routes send remote link prefixes and locators toward the adjacent router in the appropriate direction. The return path stays plain IPv6; only traffic toward h2 is encapsulated. The following commands show the relevant forwarding configuration, with ip -n selecting a network namespace:

ip -n r2 -6 route replace 5f00:0:2::100/128 \
  encap seg6local action End count dev r
ip -n r3 -6 route replace 5f00:0:3::200/128 \
  encap seg6local action End.X nh6 2001:db8:14::2 count dev r
ip -n r4 -6 route replace 5f00:0:4::d6/128 \
  encap seg6local action End.DT6 table 254 count dev r

ip -n r1 -6 route replace 2001:db8:15::/64 \
  encap seg6 mode encap \
  segs 5f00:0:2::100,5f00:0:3::200,5f00:0:4::d6 dev r

The commands describe forwarding state on that topology, rather than a complete machine-setup recipe. Table 254 is the main table: this test demonstrates decapsulation and a table lookup, not a tenant Virtual Routing and Forwarding instance (VRF), which would provide a separate routing context. The kernel build lacks VRF support, so it cannot establish anything about Linux VRF-based VPN isolation.

The last command is a headend policy. Matching a route to the destination prefix causes r1 to encapsulate the received packet with the listed segments. That is a local policy decision made before the packet starts executing the program.

Reading the bytes

The SRH has an eight-byte fixed portion, followed by 128-bit segment-list entries and optional type-length-value (TLV) fields. The fixed fields are:

Byte   0       1          2        3
       Next    Hdr Ext    Routing  Segments
       Header  Len        Type     Left
Byte   4       5          6..7
       Last    Flags      Tag
       Entry
Byte   8 onward
       Segment List[0], Segment List[1], ... ; optional TLVs

Hdr Ext Len counts eight-byte units after the first eight bytes. Last Entry is the last array index, not the number of entries. Most importantly, the segment array is stored in reverse execution order. Segment List[0] is the final segment.

For the three-SID experiment, the fixed SRH bytes leaving r1 are:

29 06 04 02 02 00 00 00

0x29 means the next header is IPv6, protocol number 41. 0x06 describes 48 bytes after the fixed eight-byte portion: three 16-byte addresses. Routing Type is 4. Segments Left and Last Entry are both 2, and Flags and Tag are zero. The outer IPv6 destination already holds 5f00:0:2::100, the first active SID. In this full encapsulation form it is also present at Segment List[2].

The captures show the state transition:

Capture point  Outer destination   Segments Left
r1 -> r2       5f00:0:2::100        2
r2 -> p        5f00:0:3::200        1
p  -> r3       5f00:0:3::200        1
r3 -> r4       5f00:0:4::d6         0
r4 -> h2       original packet     no outer SRH

The unchanged destination and Segments Left across p are the evidence for plain IPv6 transit. Its forwarding changes the outer hop limit, but it doesn’t advance the segment program. This matches RFC 8754 section 3.2 [1]: a transit node needn’t process a segment or understand the SRH.

Three ICMPv6 echo requests from 2001:db8:10::1 to 2001:db8:15::2, each with a 100-byte echo payload, were delivered. ICMPv6 is IPv6’s control and error-reporting protocol; ping uses its echo request and reply messages. The captured fields are shown above. With an initial host hop limit of 64, the inner packet arrived with hop limit 63, compared with 59 on the plain routed path. In this configuration, traversal inside the encapsulation doesn’t decrement the inner hop limit. The customer packet and the transport packet have separate forwarding lives.

Validation matters too. In a malformed-header test, an SRH with Segments Left set to 5, beyond its segment list, elicited ICMPv6 Parameter Problem with pointer 43, the Segments Left byte measured from the IPv6 header. For these packets, with the routing header immediately after the 40-byte IPv6 header, the kernel also rejected a Type 0 routing header with pointer 42, the Routing Type byte. SRH is Routing Type 4; it doesn’t restore the deprecated IPv6 RH0 mechanism [5].

SRv6 without a routing header

The SRH is optional in a meaningful and useful case. If a policy contains one service SID, an outer IPv6 destination can carry the entire instruction. There is no remaining segment to encode.

RFC 8986’s H.Encaps.Red [2] omits the first segment from the SRH because the destination address already contains it. With one segment and no need for SRH-carried information, it omits the SRH itself. An IPv6 service packet then looks like:

Outer IPv6: DA = egress service SID, Next Header = 41
Inner IPv6: DA = original receiver

The egress SID still invokes an SRv6 behavior, such as decapsulation and lookup in a selected table. This is genuine SRv6. The absence of Routing Type 4 doesn’t make it incomplete by definition, and looking only for SRHs will miss it.

This model helps separate service delivery from explicit traffic engineering. A service SID reached over ordinary IPv6 shortest-path routing supplies service programming at the edge. A list containing node and adjacency behaviors also supplies transport instructions. Both belong to SRv6; they exercise different parts of the architecture.

Public deployment descriptions cover that range. The deployment-status Internet-Draft [6] includes service-focused entries describing End.DT4 and BGP VPNs, as well as transport and repair functions elsewhere. It is vendor/operator co-authored material last revised in 2022, not a current independent census. It cannot establish what “most” deployments do.

Compression changes the arithmetic

Full encapsulation with N uncompressed segment-list entries adds:

40 bytes  outer IPv6 header
 8 bytes  fixed SRH
16 x N    segment addresses
--------------------------------
48 + 16N bytes, excluding TLVs

For the three-SID path, that is 96 bytes. Tests using 100-byte ICMPv6 echo payloads measured the following increases over the same plain IPv6 packet. No optional TLVs were present.

Listed SIDs Full encapsulation Reduced encapsulation
1 64 bytes 40 bytes
2 80 bytes 64 bytes
3 96 bytes 80 bytes
10 208 bytes 192 bytes

Reduced encapsulation has the multi-segment formula 40 + 8 + 16(N - 1). Comparing packet lengths at the same link layer isolates the added network-layer bytes; Ethernet framing isn’t part of these overhead formulas.

Even the 40-byte minimum costs more than a small MPLS label stack. Two MPLS entries occupy eight bytes. But treating 16 bytes per segment as an unavoidable SRv6 cost now ignores RFC 9800 [7], published on the Standards Track in June 2025. It standardizes compressed segment lists with both NEXT-CSID and REPLACE-CSID flavors. Neither became the sole standardized winner.

NEXT-CSID uses a full 128-bit address as a container. A common locator block remains at the front; shorter node/function identifiers occupy the remaining bits. At an endpoint, the active short identifier is consumed and the following identifier moves into its place. In the tested format, a 32-bit block and 16-bit node/function field leave room for six identifiers in one address:

128 bits = 32-bit block + six 16-bit identifiers

5f00:0000 | 0002 | 0003 | 0004 | 0000 | 0000 | 0000
             active    remaining identifiers / zero padding

Observed destination sequence:
5f00:0:2:3:4::  ->  5f00:0:3:4::  ->  5f00:0:4::

This is more than shrinking an SRH entry. Several instructions travel in the destination address itself. With reduced encapsulation, one container needs no SRH. When the remaining argument is exhausted, NEXT-CSID processing can resume advancement through the SRH to another container.

The three-identifier test uses these bindings and headend configuration on the same topology:

ip -n r2 -6 route replace 5f00:0:2::/48 \
  encap seg6local action End flavors next-csid \
  lblen 32 nflen 16 count dev r
ip -n r3 -6 route replace 5f00:0:3::/48 \
  encap seg6local action End flavors next-csid \
  lblen 32 nflen 16 count dev r
ip -n r4 -6 route replace 5f00:0:4::/48 \
  encap seg6local action End.DT6 table 254 count dev r
ip -n r1 -6 route replace 2001:db8:15::/64 \
  encap seg6 mode encap.red segs 5f00:0:2:3:4:: dev r

The six-identifier test deliberately revisits r2 and r3 before ending at r4; it doesn’t invent six distinct routers. It delivered with 40 bytes of overhead. Eleven identifiers in two containers delivered with 64 bytes: 40 for IPv6, eight for the SRH fixed portion, and 16 for the second container. Eleven classic entries under full encapsulation would cost 224 bytes by the earlier formula. That last figure is an arithmetic comparison, not a separate eleven-SID classic capture or a throughput result.

The actual destination containers make the distinction between identifiers and routers visible:

Identifiers First container, initially the outer destination Second container, carried in the reduced SRH Measured overhead
3 5f00:0:2:3:4:: none 40 bytes
6 5f00:0:2:3:2:3:2:4 none 40 bytes
11 5f00:0:2:3:2:3:2:3 5f00:0:2:3:2:3:4:: 64 bytes

These tests demonstrate Linux NEXT-CSID. They don’t test REPLACE-CSID, which this kernel doesn’t implement.

REPLACE-CSID follows a different advancement procedure: its first element is a full SID with an index argument, and subsequent containers in the SRH hold packed identifiers. RFC 9800 defines both procedures and their consequences for endpoint processing, checksums and ICMP interpretation. Compression saves bytes by giving the address and argument more structure. Operational tools must interpret that structure to recover the intended destination. The RFC permits coexistence but says operators should generally avoid instantiating different CSID flavors within the same routing domain or locator block.

Advancing an indexed container

REPLACE-CSID can be made equally concrete without claiming a second measured implementation. RFC 9800 section 4.2 [7] includes a seven-CSID example with a 48-bit locator block and 32-bit node/function identifiers. The walkthrough below uses its Figure 5 layout and the End-with-REPLACE-CSID algorithm in section 4.2.1. The symbols label packet fields, not addresses captured from the Linux tests.

Let B be the common 48-bit block and C1 through C7 the successive 32-bit identifiers. A fully formed destination is B | Cn | Argument: 48 + 32 + 48 bits. A packed SRH container has no repeated block. It holds four 32-bit positions, numbered 0 through 3 from the most significant word to the least significant word. The next identifier is consumed from the low end first. The index occupies the least significant two bits of the destination’s Argument; other Argument bits are zero in this example.

Using a full, non-reduced SRH makes the initial list indexes explicit:

Outer DA = B | C1 | 0
Segments Left = 2; Last Entry = 2

SRH entry        Contents, most significant word/field first
Segment List[2]  B | C1 | Argument 0       (fully formed SID)
Segment List[1]  C5 | C4 | C3 | C2         (four packed 32-bit CSIDs)
Segment List[0]   0 |  0 | C7 | C6         (two packed CSIDs, unused words zero)

Packed position   0    1    2    3
                  high          low

At C1, the index is zero, so the endpoint decrements Segments Left to 1 and resets the index to K - 1 = 3, where K = 128 / 32 = 4. It reads position 3 of Segment List[1], which contains C2, and replaces only the destination’s 32-bit node/function field. The block remains B. The destination is now B | C2 | 3.

The next endpoints keep reading that same packed entry until its positions are exhausted:

Processing point Segments Left afterward Index afterward Destination afterward
Initial state at C1 2 0 B | C1 | 0
C1 advances to C2 1 3 B | C2 | 3
C2 advances to C3 1 2 B | C3 | 2
C3 advances to C4 1 1 B | C4 | 1
C4 advances to C5 1 0 B | C5 | 0
C5 advances to C6 0 3 B | C6 | 3
C6 advances to C7 0 2 B | C7 | 2

Segments Left counts movement between containers. The destination’s index tracks movement within one packed container. In particular, SL = 0 at C6 doesn’t mean there is no remaining compressed instruction: position 2 of the current entry still supplies C7.

At C7, the termination check finds Segments Left equal to zero and the next lower position, position 1 of Segment List[0], equal to zero. End-with-REPLACE-CSID stops SRH advancement and proceeds to next-header processing. This trace covers advancement and termination, not successful delivery of an encapsulated service packet; accepting or decapsulating the next header still depends on the endpoint behavior. A zero word isn’t a decapsulation instruction.

NEXT-CSID’s earlier example moves remaining identifiers through the destination’s Argument. REPLACE-CSID leaves packed identifiers in the SRH and changes an index plus the active destination field. Copying a packed container wholesale into the IPv6 destination would be wrong: the endpoint constructs a complete SID using the retained locator block and one identifier. These are different processing rules standardized in the same RFC, not evidence that one flavor won or that the tested kernel supports both.

Who chooses the program?

The packet carries instructions. It doesn’t choose the business or routing policy that produced them.

The static route above chooses a segment list for a destination prefix. In a dynamic network, the control plane distributes reachability, endpoint information and service bindings, while a policy selects which traffic enters which path. IS-IS and OSPFv3 are interior routing protocols; BGP distributes routes and can carry service information between peers. IS-IS SRv6 extensions [8] advertise SRv6 information and capability limits. OSPFv3 [9] has SRv6 extensions too. BGP SRv6 services [10] distribute service SIDs for VPN and EVPN applications.

An SR Policy [11] associates a headend, color and endpoint with candidate paths and segment lists. Selection and steering happen at the headend; executing a segment doesn’t decide which candidate path should have won. BGP SR Policy SAFI [12] and PCEP SRv6 extensions [13] provide ways to distribute policy information. SRv6 doesn’t remove the control plane by putting a list into a packet. It changes how a selected policy is represented and executed in the data plane.

From a service route to an imposed list

A concrete example separates route selection, policy selection and packet forwarding. This is an illustrative control-plane walkthrough derived from the specifications, not a BGP experiment on the static test topology. Reuse its addresses, but add an illustrative End SID S4 = 5f00:0:4::100 at the egress. The other names are S2 = 5f00:0:2::100, S3 = 5f00:0:3::100 and service SID D = 5f00:0:4::d6.

Assume ordinary BGP route selection has accepted a global IPv6 service route for 2001:db8:15::/64. Under RFC 9252 section 5 [10], the advertisement supplies an SRv6 L3 Service SID in the BGP Prefix-SID attribute. The BGP next hop tracks the egress’s reachability; the service SID identifies the operation to perform there. They needn’t be the same address.

The relevant logical state is shown below. This is explanatory state, not configuration syntax from a tested network operating system.

Accepted service route at r1:
  prefix          2001:db8:15::/64
  BGP next hop E  2001:db8:14::2
  service SID D   5f00:0:4::d6, End.DT6
  route color     100

SR Policy at headend r1:
  color           100
  endpoint        E
  candidate A     preference 200, one segment list <S2, S3, S4>
  candidate B     preference 100, one segment list <S4>

The example uses one routing context and assumes both candidate paths are valid. Local BGP policy authorizes using color 100 for steering. The route’s next hop and color then match the policy’s endpoint and color, as RFC 9256 section 8.4 [11] describes. Color 100 is control-plane metadata here, not an instruction field copied into the SRH.

Within that policy, candidate A wins because it has the higher preference among valid candidates. RFC 9256 section 2.9 [11] defines this selection separately from ordinary BGP best-path selection. If A becomes invalid while B remains valid, B can become active. There is no load-balancing choice in this example because each candidate has one segment list. RFC 9830 [12] provides BGP encodings for candidate preferences and SRv6 segment lists, including the Type B sub-TLV carrying a full SRv6 SID. Advertising those inputs doesn’t make BGP the per-packet execution engine.

The selected transport list still has to deliver the service. The service SID follows the transport list, so the unoptimized execution order would be <S2, S3, S4, D>. RFC 9252 permits omitting the transport list’s final egress SID when the service SID belongs to that same locator. Here S4 and D both belong to 5f00:0:4::/48. Choosing that permitted optimization gives:

Selected transport list:       <S2, S3, S4>
Service operation:                        D
Effective execution order:     <S2, S3, D>

Full H.Encaps representation:
  outer DA          S2
  Segments Left     2
  Segment List[2]   S2
  Segment List[1]   S3
  Segment List[0]   D

A packet for 2001:db8:15::2 now matches the installed prefix entry and enters that policy. The forwarding entry already embodies the route and policy decisions; those control-plane selections aren’t rerun for each packet. S2 and S3 advance the transport instructions, while D removes the encapsulation and selects the egress table. If candidate B became active and the same permitted substitution were used, the effective list would contain only D, which fits the no-SRH service case described earlier.

The ingress has composed reachability, steering policy and a service binding into packet state. Routers executing the resulting list don’t need to know that color 100 or candidate preference 200 ever existed.

There is also a distinction between an explicit list and the routing algorithm used between its entries. Flexible Algorithm [14] constrains path computation using an algorithm definition. A packet can reach a SID along that computed topology without listing every hop. Traffic engineering therefore isn’t synonymous with a long SRH.

The same separation applies to network slicing, the assignment of traffic to selected logical network resources and policies. A SID can select a table or behavior; a chosen path can avoid or prefer particular resources. Neither the spelling of the SID nor a color gives a packet bandwidth, queue separation or a security boundary. Those properties depend on the forwarding resources and policies behind the selection. A service table lookup is a concrete operation. “Slice isolation” is a broader claim that needs more than an address.

Service chaining depends on similarly explicit behavior. A segment can identify a service or a proxy acting for a service that cannot process SRv6. The service-programming working-group draft [15] describes proxies that remove or hide SR information before delivery to an SR-unaware function and restore the continuation afterward. Some use configured continuation state; others cache it. That draft isn’t a published standard. VPP’s SRv6 implementation [16] documents proxy behaviors that the tested Linux kernel doesn’t supply. Naming a service in a list doesn’t make the service understand the encapsulation.

Through a service that cannot read the SRH

The proxy has two jobs: present a usable packet to the service, then recover the continuation when traffic returns. The service-programming draft’s dynamic proxy [15] gives a precise example in section 6.2. It is an interface-associated mechanism proposed by an Internet-Draft, not a proxy measured in the Linux tests.

Use symbolic SID names to keep the header changes visible. Let P identify the proxy’s dynamic behavior, commonly named End.AD, N a following End waypoint, and D the decapsulating service SID at the egress. This illustrative policy executes <P, N, D> around an SR-unaware function F. The original packet is IPv6 traffic from h1 to h2.

Before the proxy:
  outer IPv6: SA = ingress, DA = P
  SRH:        SL = 2, Segment List = [D, N, P]
  inner IPv6: h1 -> h2

                 proxy --service-out--> F
                    ^                   |
                    +----service-in-----+

After return from F and re-encapsulation:
  outer IPv6: SA = saved ingress, DA = N
  SRH:        SL = 1, Segment List = [D, N, P]
  inner IPv6: packet returned by F

On receiving the packet addressed to P, the dynamic proxy first applies End processing. Segments Left becomes 1 and the active destination becomes N. It saves that updated IPv6 encapsulation in the cache associated with the service-return interface. The saved information therefore describes where processing should continue after the function, not a request to execute P again.

The proxy then removes the outer IPv6 header and its extension headers, including the SRH, and sends the inner packet out the configured service-facing interface. Function F sees the original IPv6 packet from h1 to h2; it doesn’t need to parse a Routing Type 4 header or understand a SID. For this walk, assume F passes the packet back without changing its inner addresses. The draft also allows a service to modify, drop or generate traffic; the return mechanism isn’t based on finding an identical copy of an earlier packet.

When the packet comes back on service-in, the inbound policy uses that interface’s cached continuation to encapsulate it again. The outer destination is now N. Normal SRv6 processing resumes: N advances to D, and D decapsulates for the egress lookup. The service has participated in the chain without carrying the SR program through its own processing.

Where the continuation is stored determines what this design can distinguish. The draft’s cache is associated with the return interface and service policy, not with each packet or each flow. It is updated when arriving SR information differs. The mechanism therefore doesn’t provide a separate saved continuation for every simultaneously outstanding packet. Multiple flows can share one continuation, but a shared return interface isn’t by itself a selector among unrelated continuations.

The static proxy in section 6.1 removes and restores the same kinds of outer information, but its source address and continuation list are configured rather than learned from arriving packets. The draft limits one static proxy segment to one service policy at a time and one receiving interface to one proxy segment at a time. A bidirectional service policy requires a different proxy segment and receiving interface for the other direction. Static and dynamic refer to how this continuation is supplied; neither makes return-path context unnecessary.

VPP’s SRv6 proxy documentation [16] establishes that proxy implementations exist. It doesn’t turn this specification walkthrough into a performance measurement or establish the same state model across every product. The packet’s service SID names an operation; the proxy still needs an interface and continuation model that can return traffic to the intended program.

Where eBPF actually enters

eBPF is a Linux facility for loading small programs that the kernel verifies before allowing them to execute at defined attachment points, called hooks. A hook determines when a program runs and which operations it can use. Programs can retain state in maps, kernel-managed key/value stores, and call kernel-provided helper functions. The SRv6/eBPF design paper [17] describes these building blocks and their application to SRv6.

SRv6 connects to eBPF at specific Linux hooks, rather than requiring eBPF underneath it. The endpoint and encapsulation examples above use the kernel’s native SRv6 implementation without a BPF program.

End.BPF attaches a program to an SRv6 endpoint. The execution order is important: Linux performs the End advancement before invoking that program. A program observing Segments Left is looking at the state after the decrement, with the destination already advanced to the next segment. This is visible in Linux’s endpoint implementation [18], and the endpoint test verifies it in packet fields and a BPF map.

The endpoint test binds 5f00:0:2::bf on r2 to End.BPF. Its headend list is 5f00:0:2::bf, 5f00:0:3::100, 5f00:0:4::d6; the middle SID uses End and the last uses End.DT6. The program counts packets, records Segments Left, and writes an SRH Tag of 0xbeef. Here is the tested program, reformatted for readability:

#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_endian.h>
struct ip6_srh_t {
    __u8 nexthdr, hdrlen, type, segments_left, first_segment, flags;
    __u16 tag;
} __attribute__((packed));
struct {
    __uint(type, BPF_MAP_TYPE_ARRAY);
    __uint(max_entries, 2);
    __type(key, __u32);
    __type(value, __u64);
} stats SEC(".maps");
SEC("lwt_seg6local")
int end_bpf_tag(struct __sk_buff *skb)
{
    void *data = (void *)(long)skb->data;
    void *end = (void *)(long)skb->data_end;
    __u8 *ip6 = data;
    if (data + 40 + sizeof(struct ip6_srh_t) > end) return BPF_DROP;
    /* IPv6, with a Routing header immediately after its base header. */
    if ((ip6[0] >> 4) != 6 || ip6[6] != 43) return BPF_DROP;
    struct ip6_srh_t *srh = data + 40;
    if (srh->type != 4) return BPF_DROP;
    __u32 k0 = 0, k1 = 1;
    __u64 *cnt = bpf_map_lookup_elem(&stats, &k0);
    __u64 *sl = bpf_map_lookup_elem(&stats, &k1);
    if (cnt) __sync_fetch_and_add(cnt, 1);
    if (sl) *sl = srh->segments_left;
    __u16 tag = bpf_htons(0xbeef);
    if (bpf_lwt_seg6_store_bytes(skb, 40 + 6,
                                 &tag, sizeof(tag)) < 0)
        return BPF_DROP;
    return BPF_OK;
}
char _license[] SEC("license") = "GPL";

Offset 40 + 6 is the Tag position for this deliberately simple layout: a base IPv6 header immediately followed by the SRH. It isn’t a general extension-header parser. The program observed Segments Left equal to 1, although the packet arrived at that endpoint with 2. The observed state was:

Observation Value
Segments Left arriving at r2 2
Segments Left recorded by the program, map key 1 1
Packet count, map key 0 3
Tag in the outgoing SRH 0xbeef

The packet-count update and Tag write therefore happened after End advancement, not before it.

The helper isn’t permission to rewrite any byte in the segment program. Linux [19] documents bpf_lwt_seg6_store_bytes as modifying only the Flags, Tag and TLV space of the outermost SRH. The tested program writes the Tag field. Other helpers adjust TLV space or invoke supported SRv6 actions. The programmable extension is bounded by the kernel’s hook and helper semantics.

A second integration point is a route-attached lightweight-tunnel (LWT) BPF program using bpf_lwt_push_encap. LWT attaches processing to a route: lwt_in runs on its input path, while lwt_xmit is a transmit-side hook. The test program built a 40-byte SRH with these values:

SRH field Value
Hdr Ext Len 4
Routing Type 4
Segments Left / Last Entry 1 / 1
Tag 0x0b9f
Segment List[0], final SID 5f00:0:4::d6
Segment List[1], first SID 5f00:0:2::100

It passed that header to bpf_lwt_push_encap with type BPF_LWT_ENCAP_SEG6. Attached at lwt_in, it delivered three of three packets. Loading the same program on lwt_xmit succeeded, and both test packets were dropped. In the v6.12 source, the xmit helper variant accepts only BPF_LWT_ENCAP_IP and returns -EINVAL otherwise [19]. In this tested kernel, BPF_LWT_ENCAP_SEG6 is accepted on lwt_in; its presence in a shared helper API doesn’t imply acceptance at every hook.

Linux traffic control (tc) hooks run in the networking stack’s ingress or egress processing. Express Data Path (XDP) attaches earlier on receive, at the driver-level packet path in native mode. Programs at these generic hooks provide a third route: construct and parse SRv6 headers themselves, outside the native seg6 path. Examples include Cilium’s tc datapath source [20] and Vinbero [21]. Source code establishes that an implementation exists. It doesn’t establish Cilium product support, edition availability, or a performance comparison. A separate datapath also owns the protocol handling it doesn’t delegate to the native implementation.

The relationship is precise enough without claiming a necessary pairing. SRv6 names forwarding operations through addresses; eBPF extends selected Linux processing points with verified programs. Their intersection is useful, but it doesn’t describe how every SRv6 router works.

The byte after the working packet

Header overhead becomes operationally significant when a packet crosses a maximum transmission unit (MTU) boundary. Path MTU Discovery (PMTUD) depends on the sender learning the usable limit. Counting bytes explains where the failure starts; following ICMP explains why it persists.

The MTU test uses three classic segments under full encapsulation: 5f00:0:2::100 and 5f00:0:3::100 invoke End at r2 and r3, then 5f00:0:4::d6 invokes End.DT6 at r4. This replaces the baseline End.X step with End. The overhead remains 96 bytes and all veth links initially have an MTU of 1500. ping -s specifies only the echo payload, so an inner ICMPv6 echo packet has 40 bytes of IPv6 header plus eight bytes of ICMPv6 header before that payload:

ping payload 1356 + 48 = inner packet 1404
inner 1404 + SRv6 overhead 96 = outer packet 1500: delivered

ping payload 1357 + 48 = inner packet 1405
inner 1405 + SRv6 overhead 96 = outer packet 1501: not delivered

The probes use -M do to request PMTU-discovery behavior without local fragmentation:

ip netns exec h1 ping -6 -c2 -i0.3 -W1 -M do \
  -s 1356 2001:db8:15::2
ip netns exec h1 ping -6 -c2 -i0.3 -W1 -M do \
  -s 1357 2001:db8:15::2

The 1404-byte inner packet passed. The 1405-byte inner packet blackholed from the sender’s perspective. That threshold belongs to this exact overhead and MTU configuration; it isn’t an SRv6-wide maximum packet size.

Captures of the failed packets show why “ICMP was blocked” would be an inaccurate diagnosis. r1 generated Packet Too Big messages. They appeared on its loopback interface with both source and destination 2001:db8:11::1, its outer source address. Its ICMP counters recorded the errors; h1 received none. The encapsulation had changed whose packet the network considered too large.

Then the test reduced the p–r3 link MTU to 1400 and sent a 1300-byte echo payload. The inner packet was 1348 bytes, and the encapsulated packet was 1444 bytes. It fit at ingress but failed in transit. p sent an ICMPv6 Packet Too Big advertising MTU 1400 to the outer source, r1. Again, r1 did not relay an error to h1 in this configuration. The host’s route to its real destination acquired no useful PMTU information.

h1 sends inner packet                 r1 adds outer header
h1 ---------------------------------> r1 -----------------> p
                                      ^                     |
                                      | PTB to outer source |
                                      +---------------------+

h1 needs an error about its inner packet; none arrives.

The relevant decoded error fields were:

Failure ICMPv6 error Error destination Error received at h1
1501-byte outer packet at r1, egress MTU 1500 Packet Too Big, MTU 1500 2001:db8:11::1, r1 itself no
1444-byte outer packet at p, egress MTU 1400 Packet Too Big, MTU 1400 2001:db8:11::1, r1 no
Hop limit 3 set at h1, expiry inside the encapsulated path Time Exceeded 2001:db8:11::1, r1 no

Plain IPv6 comparison tests on the same path delivered Packet Too Big and Time Exceeded messages directly to h1. The failure was at the encapsulation boundary, not the absence of an ICMP-generating transit router.

RFC 8754 section 5.3 [1] recommends a larger MTU inside the SR domain than at its edges and points to IPv6 tunneling considerations. Reduced encapsulation and compressed lists lower the amount of headroom required. They don’t remove the outer source address or the need for correct tunnel-error handling.

The limits of this experiment matter. The kernel build has CONFIG_IPV6_TUNNEL unset, the routes use native seg6 encapsulation, and no vendor router was tested. Cisco’s NCS documentation [22] describes a Path MTU Discovery feature for SRv6-encapsulated packets. That is evidence of a documented feature, not evidence that every product reproduces Linux’s result or uses the same defaults.

What a relayed Packet Too Big must identify

The missing operation can be traced without pretending it worked in the tested kernel. The following is a specification-based walkthrough, not a successful relay measurement or a verified configuration. RFC 8754 sections 5.3–5.4 [1] refer encapsulated-packet MTU and error handling to RFC 2473 [23], the IPv6 tunneling specification. The original packet and the tunnel packet have different sources, so reporting a tunnel error to the ingress is only the first half of the process.

Use the same 1400-byte transit link and 96-byte encapsulation. The observed part stops here: h1 sent a 1348-byte IPv6 echo packet, r1 made it 1444 bytes, and p returned Packet Too Big to r1 with MTU 1400. Everything that follows describes the specified processing that would close that boundary.

First consider what information can return. RFC 4443 section 3.2 [24] limits the ICMPv6 error to the IPv6 minimum MTU while including as much of the invoking packet as possible. If the error has no extension headers and fills that 1280-byte budget, the quote holds:

1280-byte ICMPv6 error
  40 bytes  error's IPv6 header, addressed to r1
   8 bytes  ICMPv6 Packet Too Big header, advertised MTU 1400
1232 bytes  quoted invoking packet:
              96 bytes  outer IPv6 + SRH with three SIDs
            1136 bytes  beginning of the original 1348-byte packet

Those 1136 inner bytes include its 40-byte IPv6 header, the eight-byte echo header and 1088 bytes of echo payload. The inner source and true destination are available even though the final 212 payload bytes are absent. This is calculated quote capacity for the stated layout, not a claim that every error contains that much information.

RFC 2473 section 6.7 defines the tunnel MTU as the tunnel path MTU minus its headers. Sections 8.1–8.2 describe learning from the downstream error and relaying it for an IPv6 original packet larger than 1280 bytes. In this example the ingress has enough quoted header information to find the original IPv6 packet after the SRH. It can lower the tunnel MTU to 1400 - 96 = 1304 and construct an error about that original packet:

Field in the specified relay Value in this example
IPv6 source 2001:db8:10::2, r1’s interface toward h1
IPv6 destination 2001:db8:10::1, copied from the original source
ICMPv6 type / code 2 / 0, Packet Too Big
Advertised MTU 1304, the downstream 1400 minus 96 tunnel-header bytes
Quoted packet The available 1136 inner bytes, beginning with the original IPv6 header
Destination inside that quote 2001:db8:15::2, not the active SID

The resulting relay is 40 + 8 + 1136 = 1184 bytes. It doesn’t recreate bytes omitted by the downstream router. Nor does a truncated quote allow verification of the entire original echo payload. RFC 8201 section 4 [25] says nodes should validate that a Packet Too Big concerns transmitted traffic and must reduce the PMTU estimate for the relevant path. Here the quote identifies the actual destination and includes the echo identifiers needed to associate it with the probe.

After such a relay, the largest echo payload fitting this path would be 1304 - 40 - 8 = 1256 bytes. The new inner packet would be 1304 bytes and its encapsulated form exactly 1400 bytes. That is a calculated fit under the specified behavior, not an observed successful retry.

If r1 already knew the 1304-byte tunnel MTU, RFC 2473 section 7.1(a) would apply before injecting another oversized packet into the tunnel: discard an original exceeding both the tunnel MTU and the IPv6 minimum, and send its source Packet Too Big advertising max(tunnel MTU, 1280). With all transport links at 1500, the same subtraction gives 1404. That matches the measured fit boundary; the failed test differed in where its error went.

The minimum-MTU condition prevents turning this into an unrestricted subtraction recipe. Section 7.1(b) handles an original packet of 1280 bytes or less that cannot fit after encapsulation by fragmenting the tunnel packet. Section 8.2 limits the PTB relay to originals larger than the IPv6 minimum. Its subtraction text doesn’t itself state a minimum-MTU clamp. The 1304-byte example stays above that corner rather than inventing a rule for it; RFC 8201 also says a host must discard a PTB reporting less than 1280.

There are attribution limits beyond the arithmetic. More extension headers, a longer SID list or nested encapsulation can leave too little quoted inner header to identify the original traffic. A reduced SRH may omit a SID, or disappear entirely; compressed containers require their own destination-recovery logic, described in RFC 9800 section 9.4 [7]. The ingress cannot assume that a complete original SID list is sitting unchanged in every quote.

RFC 2473 also carries no tenant or VRF identifier. Recovering an inner source address isn’t enough when several tenants use the same address. A deployment might retain service-SID-to-routing-context state, but a remote SID imported by several local VRFs doesn’t identify one unique return context. The tests here used main table 254, without tenant VRFs. This walkthrough therefore establishes neither a general VPN demultiplexer nor a universal vendor default, and it doesn’t apply the tunnel procedure to inline SRH insertion.

A visible header isn’t a visible failure

Packet inspection worked well in these tests. TShark 4.4.19 decoded the SRH fields and nested IPv6 packets. HMAC TLV bytes were read from the packet hex dump. The captures make the destination transitions easy to follow. Describing SRv6 tooling as universally absent would miss the actual problem.

Traceroute exposed a different limitation. Time Exceeded generated inside the domain returned to the encapsulating ingress rather than the original host. Probes from h1 showed asterisks for internal hops. A further test ran traceroute on r1, the router that built the encapsulation. It used one probe per hop, a one-second wait and an eight-hop maximum, first with UDP probes and then with ICMP probes. The policy used End SIDs 5f00:0:2::100 and 5f00:0:3::100, followed by 5f00:0:4::d6 for decapsulation. Both probe types showed asterisks for hops 1–3 and the destination at hop 4. Even this headend’s traceroute failed to associate the returned errors with its probes in the tested configuration.

RFC 9259 [26] specifies SRv6 operations, administration and maintenance (OAM) using ping and traceroute and defines an SRH O-flag. That doesn’t imply that an unmodified customer-side traceroute can explain an encapsulated path, or that every headend’s ordinary traceroute understands its own tunnel quotations. There is a difference between inspecting a successful packet and attributing a quoted failure to the traffic that caused it.

The individual ICMP error-handling proposal for SRv6 VPNs [27] addresses the additional routing-context problem for Time Exceeded and traceroute. Its -01 text focuses on traceroute and Time Exceeded, states that the procedure applies to all ICMPv6 error types, and contains no Packet Too Big or MTU procedure. That distinction matters when reading an error-handling proposal as evidence of a PMTUD solution.

Insertion moves the problem

Linux also offers mode inline, which inserts an SRH into the existing IPv6 packet instead of creating an outer IPv6 header. The original source address remains visible, so internal routers send their ICMP errors toward the original sender. With the same traceroute probe settings, the inline policy exposed every hop, including plain router p.

Insertion and encapsulation are not interchangeable standards choices. RFC 8200 section 4 [28] says extension headers, apart from Hop-by-Hop handling, aren’t processed, inserted or deleted along the delivery path until the packet reaches the node identified by the destination address. RFC 8754 describes an SR source as an originating host or an ingress encapsulating a received packet; RFC 8986 defines encapsulation headend behaviors, not H.Insert.

The SRv6 insertion proposal [29] is an expired individual draft, without IETF endorsement. Its scope is insertion into packets already encapsulated by the domain, including repair scenarios. Products nevertheless implement insertion: Juniper’s documentation [30] describes SRH insertion at transit routers for TI-LFA (Topology-Independent Loop-Free Alternate fast repair), and Cisco documents H-Insert budgets. Published behavior specifications, individual proposals and shipping implementation are different kinds of evidence.

Endpoint removal has its own precise context. RFC 8986 defines the PSP flavor, which removes the SRH at the penultimate segment endpoint. It explains that this operation doesn’t contravene RFC 8200 because the packet’s incoming destination identifies the node executing the behavior. That published rationale for an endpoint operation isn’t blanket approval for arbitrary insertion by a transit router.

The insertion tests here are a narrower case than the insertion draft defends: r1 inserts an SRH directly into host traffic at the domain edge. Linux dropped those packets unless h2 had seg6_enabled=1. With seg6_enabled=0, neither of two test packets arrived; delivery worked after enabling it. That drop is Linux’s SRH receive gate, which runs before the Segments Left == 0 check. RFC 8754 section 4.3.2 [1] says a non-SID local interface with Segments Left equal to zero must ignore the routing header and continue. For the PMTU test, r2 and r3 used End SIDs 5f00:0:2::100 and 5f00:0:3::100. The policy at r1 was:

ip -n r1 -6 route replace 2001:db8:15::/64 \
  encap seg6 mode inline \
  segs 5f00:0:2::100,5f00:0:3::100 dev r

Linux included the original destination as the final segment. The SRH therefore contained the two waypoint SIDs plus 2001:db8:15::2, adding 8 + 3 x 16 = 56 bytes.

At a 1400-byte transit MTU, a 1300-byte echo payload became a 1348-byte original packet and a 1404-byte packet after insertion. Packet Too Big reached h1 this time. But the host recorded MTU 1400 against 5f00:0:3::100, the active SID quoted in the error, rather than against the real destination 2001:db8:15::2. All four probes failed. The host also lacked information that r1 had added 56 bytes after transmission. The decoded error and route-cache observations were:

ICMPv6 Packet Too Big:
  receiver                 h1 (2001:db8:10::1)
  advertised MTU           1400
  quoted packet DA         5f00:0:3::100

h1 route lookup after receiving the errors:
  destination 5f00:0:3::100   cached MTU 1400
  destination 2001:db8:15::2  no learned MTU entry

Echo requests delivered: 0 of 4

In this edge-insertion test, preserving the source fixed error delivery without fixing PMTU attribution. It doesn’t establish that in-domain TI-LFA insertion behaves identically. It does show why making internal hops appear in traceroute isn’t sufficient evidence that the operational boundary is correct.

Support is a platform property

The protocol can describe a list much longer than a deployed forwarding pipeline can impose or process. Maximum SID depth (MSD) is therefore an operational capability, with different limits for different actions. RFC 9352 [8] defines SRv6 capability advertisements including Maximum Segments Left, Maximum End Pop, Maximum H.Encaps and Maximum End D. A single “supports SRv6” checkbox hides those distinctions.

Vendor documentation makes the differences tangible. Cisco’s IOS XR 26.x guide for NCS 5500/5700 [22] describes support for micro-SIDs (uSID) in F3216 format, with a 32-bit block and 16-bit identifiers, a hardware-profile change with a reload, and role-specific budgets: three underlay H-Insert plus three overlay H-Encaps SIDs for a provider-edge (PE) role, compared with six underlay H-Insert SIDs for a transit provider (P) role. Those numbers describe different operations and roles; they aren’t a universal six-entry limit.

Juniper’s network-programming documentation [30] lists six encapsulated SIDs for MX and twelve for ACX7000, with separate insertion and pop limits. The same page lists ICMP and ICMPv6 error handling among unsupported functions in the feature it describes. These are vendor-published feature constraints, not measurements from the Linux lab and not a claim about every Juniper product.

Other implementation choices change the cost even when support exists. Nokia’s 7750 SR documentation [31] describes a port cross-connect forwarding-path extension for SRv6 encapsulation and decapsulation. Cumulus Linux 5.18 documentation [32] limits its SRv6 feature to Spectrum-4 and later, and the default VRF. A chip-family claim can’t substitute for checking the exposed behavior in a specific network operating system (NOS) release and forwarding role.

Compression helps reconcile an instruction-rich architecture with these finite budgets, but counts must stay attached to their units. A 128-bit container can carry several compressed identifiers. “Three SIDs” in a platform table cannot automatically be read as three programmed waypoints, nor converted into an unlimited compressed path. The supported format and operation determine the interpretation.

Independent interoperability evidence supports a mixed assessment. The EANTC 2026 report [33], from an independent laboratory’s vendor-sponsored event, reports successful multi-vendor SRv6 testing while noting that limited support on some devices reduced the scenarios it could validate. That is evidence of functioning interoperability and incomplete coverage at the same time. It isn’t evidence of universal failure.

Production evidence also rules out treating SRv6 as a laboratory curiosity. Rakuten Mobile’s 2025 operator presentation [34] describes SRv6 on 15,000 NCS devices with micro-SIDs (uSID) and flex-algo. Alibaba’s OCP 2025 presentation [35] distinguishes a legacy SRv6 VPN over a 6PE MPLS core from a new SRv6-only eCore without MPLS. Both decks are operator material hosted on a Cisco-run site, so their provenance remains visible. The older deployment-status draft also records Softbank, Iliad and China Telecom/Unicom deployments, though its entries aren’t a current size estimate.

These cases prove deployment, not prevalence. They also show why a network carrying SRv6 service traffic over existing transport and a network programming its transport with SRv6 shouldn’t be collapsed into one deployment claim. The SRV6OPS problem-summary working-group draft [36] still identifies migration, MTU, ICMP and OAM as operational problems. An appendix lists vendor feature inconsistencies among items proposed for future study. Its draft status doesn’t erase those reported concerns, just as a production case study doesn’t settle every operational question.

An instruction address is a trust boundary

The ability to invoke a behavior by destination address makes the security model concrete. RFC 8402 [37] places Segment Routing within a trusted domain by default and requires filtering at its boundaries. SRv6 isn’t premised on arbitrary Internet hosts sending trusted instruction lists across unrelated administrative domains.

RFC 8754 section 5.1 [1] describes filtering external traffic destined for the domain’s SID block and protecting local SIDs against sources outside the infrastructure address block. Filtering only packets carrying an SRH cannot protect a service SID that works without one. Nor can it distinguish every instruction packed into a compressed destination address.

The edge-filter test demonstrates the consequence of a missing boundary. Host h1, treated as external to the SR domain, installed its own encapsulation policy and sent packets to internal SIDs. All three probes were accepted and steered without an edge filter. The experiment then applied this rule set at r1:

 table ip6 srv6edge {
   chain edge {
     type filter hook prerouting priority -300; policy accept;
     iifname "l" ip6 daddr 5f00::/16 counter drop
     iifname "l" rt type 4 counter drop
   }
 }

This is the tested two-rule filter with comments omitted, not a complete production security policy. The destination-prefix rule dropped all three external probes. The Routing Type 4 rule’s counter remained zero because the first rule matched first; that second rule wasn’t independently tested. Traffic encapsulated by r1 after legitimate ingress still delivered three of three probes. The before/after observations were:

Traffic Without edge filter With edge filter
SRv6 imposed by external h1, targeting internal SIDs 3/3 delivered 0/3 delivered
Ordinary host traffic encapsulated by r1 not part of this comparison 3/3 delivered
Destination-prefix drop counter no rule 3 packets
Routing Type 4 drop counter no rule 0 packets

For the injected traffic, h1 selected End SID 5f00:0:2::100 and decapsulation SID 5f00:0:4::d6. The filter’s left-facing interface was the h1–r1 link.

The optional SRH Hash-based Message Authentication Code (HMAC) TLV adds authentication of defined header material, not encryption or an alternative to the domain boundary. RFC 8754 [1] defines the key identifier and covered fields, including the IPv6 source and segment-list addresses. Linux also sets SRH flag 0x08 to mark HMAC presence; RFC 8754 marks every flag bit unused and requires zero on transmission. Linux’s HMAC input omits the two octets after HMAC TLV Length that RFC 8754 includes. A test using HMAC key ID 42 and SHA-256 exposed a provisioning detail: with matching keys on r1 and r2 only, the packet failed at r4. Its default seg6_require_hmac=0 meant validate an HMAC if present; without the key it couldn’t validate it. The headend list selected r2’s End SID and r4’s End.DT6 SID. Adding the matching key at r4 restored delivery. “Optional to require” didn’t mean “ignored when present.”

The dedicated SRv6 security-considerations document [38] was approved and in the RFC Editor queue at the research cutoff, not yet a published RFC. It emphasizes address-range filtering and rejects SRH-presence filtering as a sufficient mitigation. That advice follows directly from the packet formats: the operation can be in the destination address even when no routing header follows it.

What the tests establish

Static routing makes the experiments easy to follow, but it leaves control-plane convergence, BGP service distribution and TI-LFA untested. The kernel lacks VRF and MPLS routing support. Small packet counts establish observed behavior and byte layouts, not loss rates under load. Every namespace shares one kernel; none of these results establishes cross-vendor HMAC interoperability or hardware throughput.

The 1405-byte inner packet fit the host-facing link. It failed only after the network added its instructions, and the error stopped at the router that added them. That is where the architectural elegance runs ahead of the ecosystem: execution can be correct at every endpoint while the original sender remains unable to learn why its packet failed.

Sources

The numbered references identify the external sources cited above. Source types distinguish protocol specifications from proposals, implementation code and deployment evidence. Versions and status reflect the 7 November 2026 source check. The packet observations in the article are experimental evidence from the Linux setup described in the text, not claims drawn from a vendor document.

  1. RFC 8754, “IPv6 Segment Routing Header (SRH)”. Standards Track RFC. Sections 2 (format and HMAC), 3.2 (transit), 4.1 (source), 4.3.2 (local-interface SRH processing), and 5.1, 5.3–5.4 (security, MTU and ICMP).

    https://www.rfc-editor.org/rfc/rfc8754

    https://www.rfc-editor.org/rfc/rfc8754#section-3.2

    https://www.rfc-editor.org/rfc/rfc8754#section-5.3

    https://www.rfc-editor.org/rfc/rfc8754#section-5.4

    https://www.rfc-editor.org/rfc/rfc8754#section-5.1

    https://www.rfc-editor.org/rfc/rfc8754#section-2.1.2

    https://www.rfc-editor.org/rfc/rfc8754#section-4.3.2

  2. RFC 8986, “Segment Routing over IPv6 (SRv6) Network Programming”. Standards Track RFC. Sections 3 (SID structure), 4 (endpoint behaviors), 4.16.1.2 (PSP), and 5.1–5.2 (encapsulation).

    https://www.rfc-editor.org/rfc/rfc8986

    https://www.rfc-editor.org/rfc/rfc8986#section-5.2

  3. RFC 3032, “MPLS Label Stack Encoding”. Standards Track RFC. Section 2.1 (label stack encoding).

    https://www.rfc-editor.org/rfc/rfc3032

  4. RFC 8663, “MPLS Segment Routing over IP”. Standards Track RFC. MPLS-in-IP/UDP transport, distinct from the SRv6 data plane.

    https://www.rfc-editor.org/rfc/rfc8663

  5. RFC 5095, “Deprecation of Type 0 Routing Headers in IPv6”. Standards Track RFC. Deprecation and required handling of RH0.

    https://www.rfc-editor.org/rfc/rfc5095

  6. “SRv6 Implementation and Deployment Status”. Expired individual Internet-Draft; vendor/operator deployment evidence. draft-matsushima-spring-srv6-deployment-status-15, April 2022. Describes reported deployments, not a current independent census.

    https://datatracker.ietf.org/doc/draft-matsushima-spring-srv6-deployment-status/

  7. RFC 9800, “Compressed SRv6 Segment List Encoding”. Standards Track RFC. Sections 4.1–4.2 (NEXT-CSID and REPLACE-CSID), Figure 5 and section 4.2.1 (indexed advancement), 6.2 (encoding), 6.5 (checksums), and 9.1, 9.4 (operations and ICMP).

    https://www.rfc-editor.org/rfc/rfc9800

    https://www.rfc-editor.org/rfc/rfc9800#section-4.2

    https://www.rfc-editor.org/rfc/rfc9800#section-9.4

  8. RFC 9352, “IS-IS Extensions to Support Segment Routing over the IPv6 Data Plane”. Standards Track RFC. Section 4 (SRv6 Maximum SID Depth capability types); SID and locator advertisements.

    https://www.rfc-editor.org/rfc/rfc9352

  9. RFC 9513, “OSPFv3 Extensions for Segment Routing over IPv6 (SRv6)”. Standards Track RFC. SRv6 routing-protocol extensions.

    https://www.rfc-editor.org/rfc/rfc9513

  10. RFC 9252, “BGP Overlay Services Based on Segment Routing over IPv6 (SRv6)”. Standards Track RFC. Section 5 (BGP L3 Service TLV, service SID, next hop, color-based steering and the optional final-transport-SID omission); BGP signaling for L3VPN and EVPN.

    https://www.rfc-editor.org/rfc/rfc9252

    https://www.rfc-editor.org/rfc/rfc9252#section-5

  11. RFC 9256, “Segment Routing Policy Architecture”. Standards Track RFC. Sections 2.9 (active candidate selection), 8.4 (BGP route/color steering), and the policy, candidate-path and segment-list definitions.

    https://www.rfc-editor.org/rfc/rfc9256

    https://www.rfc-editor.org/rfc/rfc9256#section-8.4

    https://www.rfc-editor.org/rfc/rfc9256#section-2.9

  12. RFC 9830, “Advertising Segment Routing Policies in BGP”. Standards Track RFC. BGP SR Policy SAFI; sections 2.4.1 (Preference sub-TLV) and 2.4.4.2.2 (SRv6 SID Type B sub-TLV).

    https://www.rfc-editor.org/rfc/rfc9830

  13. RFC 9603, “Path Computation Element Communication Protocol (PCEP) Extensions for IPv6 Segment Routing”. Standards Track RFC. SRv6 path-computation signaling.

    https://www.rfc-editor.org/rfc/rfc9603

  14. RFC 9350, “IGP Flexible Algorithm”. Standards Track RFC. Constraint-based IGP path computation.

    https://www.rfc-editor.org/rfc/rfc9350

  15. “SRv6 Service Programming”. Working-group Internet-Draft, not a published standard. draft-ietf-spring-srv6-service-programming-00, 6 July 2026. Sections 6.1–6.2 (static and dynamic proxies, interface-associated return processing and cached continuation).

    https://datatracker.ietf.org/doc/draft-ietf-spring-srv6-service-programming/

  16. “SRv6 Plugins”. Open-source implementation documentation. FD.io Vector Packet Processor documentation, v25.10; service-proxy implementations.

    https://s3-docs.fd.io/vpp/25.10/developer/plugins/srv6/index.html

  17. “Leveraging eBPF for programmable network functions with IPv6 Segment Routing”. Research paper. Mathieu Xhonneux, Fabien Duchene and Olivier Bonaventure; CoNEXT 2018; arXiv:1810.10247. Sections 2.1 and 3 cover eBPF and the SRv6 interface.

    https://arxiv.org/abs/1810.10247

  18. Linux net/ipv6/seg6_local.c. Implementation source code, version v6.12. input_action_end_bpf and advance_nextseg establish endpoint/BPF execution order. Code reading is distinct from the Linux 6.12.113+ packet tests.

    https://github.com/torvalds/linux/blob/v6.12/net/ipv6/seg6_local.c

  19. Linux net/core/filter.c and include/uapi/linux/bpf.h. Implementation source code, version v6.12. Documented Flags/Tag/TLV helper contract for bpf_lwt_seg6_store_bytes; lwt_in/lwt_xmit encapsulation-helper variants.

    https://github.com/torvalds/linux/blob/v6.12/include/uapi/linux/bpf.h

    https://github.com/torvalds/linux/blob/v6.12/net/core/filter.c

  20. Cilium bpf/lib/srv6.h. Open-source implementation code. The cited URL targets the moving main branch. Evidence of code, not a product-support or edition guarantee.

    https://github.com/cilium/cilium/blob/main/bpf/lib/srv6.h

  21. “Vinbero”. Open-source project repository and README. Describes its eBPF/XDP and traffic-control SRv6 datapath. The URL is unversioned.

    https://github.com/takehaya/Vinbero

  22. “Configure SRv6 Micro-SID”. Vendor documentation. Cisco NCS 5500/5700, IOS XR 26.x. Platform/profile requirements, SID budgets, and Path MTU Discovery for SRv6 Encapsulated Packets.

    https://www.cisco.com/c/en/us/td/docs/iosxr/ncs5500/segment-routing/26xx/configuration/guide/b-segment-routing-cg-ncs5500-26xx/configure-srv6-micro-sid.html

  23. RFC 2473, “Generic Packet Tunneling in IPv6 Specification”. Standards Track RFC (Proposed Standard). December 1998. Sections 6.7 (tunnel MTU), 7.1 (original IPv6 packet processing), and 8–8.2 (tunnel error handling and PTB relay).

    https://www.rfc-editor.org/rfc/rfc2473

  24. RFC 4443, “Internet Control Message Protocol (ICMPv6) for the Internet Protocol Version 6 (IPv6) Specification”. Internet Standard RFC, STD 89. March 2006; updated by RFC 4884. Section 3.2 (Packet Too Big format, next-hop MTU and invoking-packet quote).

    https://www.rfc-editor.org/rfc/rfc4443#section-3.2

  25. RFC 8201, “Path MTU Discovery for IP version 6”. Internet Standard RFC, STD 87. July 2017. Section 4 (PTB validation, PMTU reduction and IPv6 minimum-MTU rule); section 5.2 (storing PMTU information).

    https://www.rfc-editor.org/rfc/rfc8201#section-4

  26. RFC 9259, “Operations, Administration, and Maintenance (OAM) in Segment Routing over IPv6 (SRv6)”. Standards Track RFC. Section 2.1 (O-flag) and ping/traceroute procedures.

    https://www.rfc-editor.org/rfc/rfc9259

  27. “ICMP Error Handling for VPNs in SRv6 Networks”. Individual Internet-Draft, not a published standard. draft-alivar-6man-icmp-error-srv6-vpn-01, 27 October 2026. Sections 3.2 and 4.1–4.3 concern Time Exceeded/traceroute and VPN routing context. Not WG-adopted; focuses on traceroute and Time Exceeded, states that the procedure applies to all ICMPv6 error types, and contains no PTB or MTU procedure.

    https://datatracker.ietf.org/doc/draft-alivar-6man-icmp-error-srv6-vpn/

  28. RFC 8200, “Internet Protocol, Version 6 (IPv6) Specification”. Standards Track RFC. Section 4 (extension-header processing).

    https://www.rfc-editor.org/rfc/rfc8200#section-4

  29. “SRv6 NET-PGM extension: Insertion”. Expired individual Internet-Draft, not IETF-endorsed. draft-filsfils-spring-srv6-net-pgm-insertion. The cited document record identifies revision -09; the proposal concerns insertion within the SR domain into domain-owned encapsulated packets.

    https://datatracker.ietf.org/doc/draft-filsfils-spring-srv6-net-pgm-insertion/

  30. “SRv6 Network Programming”. Vendor documentation. Junos OS. “TI-LFA for SRv6” and “Supported and Unsupported Features for SRv6 Network Programming in IS-IS,” including the platform-specific SID table.

    https://www.juniper.net/documentation/us/en/software/junos/is-is/topics/topic-map/srv6-network-programming.html

  31. “Segment Routing over IPv6”. Vendor documentation. Nokia Classic CLI Advanced Configuration, ACG up to 26.7.R2; forwarding-path extension and port cross-connect design.

    https://documentation.nokia.com/acg/26-7-2/books/segment-routing-and-pce-classic/c233-seg-rout-ipv6.html

  32. “Segment Routing”. Vendor documentation. NVIDIA Cumulus Linux 5.18. Spectrum-4 and later; default VRF only.

    https://docs.nvidia.com/networking-ethernet-software/cumulus-linux-518/Layer-3/Routing/Segment-Routing/

  33. “EANTC Transport & Cloud Networks Interop Test Report 2026”. Independent laboratory report from a vendor-sponsored interoperability event. SRv6 Results and discussion of limited device support reducing test scope.

    https://eantc.de/wp-content/uploads/2026/03/EANTC-Interop2026-TestReport.pdf

  34. “Bandwidth Guarantee Services with SRv6 policy for deterministic networking”. Operator presentation. Akash Agrawal, Rakuten Mobile, 26 March 2025. Includes the SRv6 uSID transport deployment; hosted on the Cisco-run segment-routing.net site.

    https://www.segment-routing.net/images/MPLS-SRv6-WC-2025/Paris25-D2-1230_Agrawai-Akash.pdf

  35. “eCore: Network Architecture and Whitebox Evolution of Alibaba’s Service-Oriented DCI Network”. Operator/vendor co-presented evidence. OCP Summit 2025; Dennis Cai, presenting on behalf of Roy Jiang, Alibaba Cloud, and Manish Mukherjee, Cisco. Hosted on the Cisco-run segment-routing.net site.

    https://www.segment-routing.net/images/OCP-summit25-eCore_Alibaba.pdf

  36. “SRv6 Deployment and Operation Problem Summary”. Working-group Internet-Draft, not a published standard. draft-ietf-srv6ops-problem-summary-02. Reported deployment and operational problems, including migration, MTU, ICMP and OAM. Vendor feature inconsistencies appear in an appendix of items proposed for future study.

    https://datatracker.ietf.org/doc/draft-ietf-srv6ops-problem-summary/

  37. RFC 8402, “Segment Routing Architecture”. Standards Track RFC. Section 8 (domain trust model) and section 8.2 (SRv6 boundary filtering).

    https://www.rfc-editor.org/rfc/rfc8402

  38. “Segment Routing over IPv6 (SRv6) Security Considerations”. Approved working-group Internet-Draft in the RFC Editor queue at the research cutoff; intended Informational. draft-ietf-spring-srv6-security-19. Domain filtering and security considerations; not yet a published RFC as of the source check.

    https://datatracker.ietf.org/doc/draft-ietf-spring-srv6-security/