SIDs as instructions: LOC:FUNCT:ARG and the behavior zoo
S1·E3The config that says the kernel is broken · Dell lab, Round Rock, day two of a three-day PoC bring-up
Builds on: Source routing, segments, and the two data planes, The SRH on the wire
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
- Split a SID into LOC:FUNCT:ARG at a given block, node and function length and say why the leftover bits must be zero.
- Distinguish a function from a behavior and use the right word when reading the IANA registry.
- Select the correct RFC 8986 behavior for a stated job and name its IANA codepoint and signalling protocol.
- Explain why PSP is chosen by the source node and appears as a separate SID rather than a flag.
Episode 3 — The config that says the kernel is broken
The Dell SE has retyped the incumbent’s config onto the Linux headend line by line, and the box refuses exactly one statement: End.X flavors psp comes back with Unsupported Flavor operation(s).[6] He is drafting a bug report against the kernel. The network lead is on video with the notebook, three pages fuller than in the briefing room, waiting on the sign-off that releases the purchase order Thursday. The PoC has no day to spare for a kernel bug that is not one.
SRv6 works at all because an address can be an instruction. RFC 8986 defines a SID as LOC:FUNCT:ARG - a locator, then function bits, then arguments, with any leftover bits required to be zero - and it separates two words people use interchangeably: “The term ‘function’ refers to the bit string in the SRv6 SID. The term ‘behavior’ identifies the behavior bound to the SID.”[1] The registry lists behaviors, and the behaviors are the zoo: End moves the packet along the path, End.X cross-connects to a named neighbor, the End.DT family decapsulates into a table.[1]
Flavors are the trap in front of him. PSP is “deterministically controlled by the SR source node”, advertised as a separate SID rather than a knob on the endpoint, and Linux implements only PSP of the three flavors - not on End.X.[1][6][5]
A behavior is what a node does. A flavor is what the headend asked for, and it asks by choosing a different SID.
So you take the config apart the way you take apart every customer config: locator, behavior, flavor.
1A SID has structure, and the leftover bits are zero
RFC 8986, February 2021, is the document that turns an address into an instruction.[1] Its central definition, verbatim: “This document defines an SRv6 SID as consisting of LOC:FUNCT:ARG, where a locator (LOC) is encoded in the L most significant bits of the SID, followed by F bits of function (FUNCT) and A bits of arguments (ARG). L, the locator length, is flexible, and an operator is free to use the locator length of their choice. F and A may be any value as long as L+F+A <= 128.”[1] And the constraint that makes the whole thing tidy: “When L+F+A is less than 128, then the remaining bits of the SID MUST be zero.”[1]
The locator itself has structure too: “A locator may be represented as B:N where B is the SRv6 SID block (IPv6 prefix allocated for SRv6 SIDs by the operator) and N is the identifier of the parent node instantiating the SID.”[1] B and N are not abstractions - they are literally the block-len and node-len knobs you type into FRR and NVUE.[9][10] The reference allocation pattern in section 3.2 is equally concrete: assign B::/48 to the SR domain, then a unique B:N::/64 block to each SRv6-enabled node.[1]
The argument field has one rule worth quoting to anyone doing load-balancing work: “The ARG value of a routed SID SHOULD remain constant among packets in a given flow. Varying ARG values among packets in a flow may result in different ECMP hashing and cause reordering.”[1]
Implementations disagree about defaults, and you should know that before a bake-off. FRR’s zebra uses block-len 24 with node-len = prefix-len - 24 and 16 function bits by default.[9] Cumulus Linux defaults to block-length 16, node-length 16, func-length 0.[10] Community SONiC’s 2021 SRV6_MY_SID_TABLE schema used 40/24/16[13] while its 2025 static-configuration design uses 32/16/16 with a zero argument length.[14] None of these are wrong; they are different defaults for the same three numbers, and a SID plan has to state them explicitly.
End
IANA codepoint 1RFC 8986 / 9800: supported"The most basic behavior. It is the instantiation of a Prefix-SID." The packet keeps its SRH; the endpoint advances it by one segment and re-forwards on the same FIB table.
B:N:FUNCT:: — "Assign block B::/48 to the SR domain", "Assign a unique B:N::/64 block to each SRv6-enabled node"
"L, the locator length, is flexible… F and A may be any value as long as L+F+A <= 128." §3.2 fixes only B (48) and N (16); F and A are the operator's choice, drawn here as F=16 with the rest as ARG. "When L+F+A is less than 128, then the remaining bits of the SID MUST be zero."
Codepoint: 1 End · 2 +PSP · 3 +USP · 4 +PSP&USP · 28 +USD · 29 +PSP&USD (RFC 8986 §10.2.2). Note the gap at 13 — never compute a codepoint, look it up.
Control plane: IGP and BGP-LS only (RFC 8986 §8.4, Table 3).
Flavor: no flavor — The unflavored behavior, exactly as its section defines it. (RFC 8986 §4)
2Function is a number; behavior is what happens
This is a vocabulary lesson with real consequences. RFC 8986: “The term ‘function’ refers to the bit string in the SRv6 SID. The term ‘behavior’ identifies the behavior bound to the SID.”[1] People use the two words interchangeably and then cannot read the IANA registry, because the registry is called “SRv6 Endpoint Behaviors” and lists behaviors, not functions.[1] A node picks the function bits freely; the behavior is what it programs against them.
The base behavior is End, “the most basic behavior. It is the instantiation of a Prefix-SID [RFC8402].”[1][2] Its pseudocode is short enough to hold in your head: check that Segments Left is not zero, check the Hop Limit, bounds-check Last Entry and Segments Left, then S12. Decrement IPv6 Hop Limit by 1, S13. Decrement Segments Left by 1, S14. Update IPv6 DA with Segment List[Segments Left], S15. Submit the packet to the egress IPv6 FIB lookup.[1] That last line matters: “The End behavior operates on the same FIB table (i.e., identified by VRF or L3 relay ID) associated to the packet.”[1] End does not change lookup context; End.T is the behavior that does.[1]
One more End detail earns its place because it looks like a failure and is not. If the packet’s upper-layer header is not permitted on that SID, the node answers with “ICMP Parameter Problem … with Code 4 (SR Upper-layer Header Error)”, and operators “may enable local configuration to allow Upper-Layer header type 58 (ICMPv6)” so that SIDs can be pinged.[1] Code 4 back from a ping means the SID exists and is programmed; ICMPv6 is simply not on its permitted list.[1]
3The zoo, sorted by what job it does
RFC 8986 defines a lot of behaviors. Sorting them by job makes them memorable.
Move the packet along the path. End (section 4.1) is the Prefix-SID. End.X (4.2) is an L3 cross-connect to a specific neighbor - the Adjacency-SID. End.T (4.3) is End with the next lookup in a named IPv6 table.[1]
Deliver the packet to a service. End.DX6 and End.DX4 decapsulate and cross-connect to a specified next hop. End.DT6, End.DT4 and End.DT46 decapsulate and look up in a specific IPv6, IPv4 or dual-stack table - End.DT46 is the per-VRF dual-stack service SID you will meet in every L3VPN design.[1] The L2 family is End.DX2 (L2 cross-connect), End.DX2V (VLAN L2 table lookup), End.DT2U (unicast MAC lookup) and End.DT2M (L2 flooding for BUM traffic).[1]
Bind to another policy. End.B6.Encaps (4.13) is the SRv6 Binding SID - “Endpoint Bound to an SRv6 Policy with Encapsulation” - with End.B6.Encaps.Red as its reduced variant, and End.BM (4.15) binds to an SR-MPLS policy, which is how an SRv6 island stitches into an existing MPLS core.[1]
Enter the domain. The headend behaviors in section 5 are H.Encaps, H.Encaps.Red, H.Encaps.L2 and H.Encaps.L2.Red.[1] H.Encaps pushes an IPv6 header with its own SRH, sets the outer source address to the encapsulating node and the outer destination to the first SID, and decrements the inner IPv6 Hop Limit or IPv4 TTL.[1] H.Encaps.Red leaves the first SID out of the SRH because it is already in the destination address, and both may omit the SRH entirely “when the SRv6 Policy only contains one segment and there is no need to use any flag, tag, or TLV.”[1]
Implementations ship subsets. Linux implements End, End.X, End.T, End.DX2, End.DX6, End.DX4, End.DT4, End.DT6, End.DT46, End.B6, End.B6.Encaps and End.BPF - while the uAPI enum also declares End.BM, End.S, End.AS and End.AM which are not in the kernel action table and simply fail to configure.[6][7] FRR’s static-sids node offers only the compressed set uN, uA, uDT4, uDT6, uDT46.[8] Cumulus Linux documents uN and uA and nothing else.[10]
End.DT46
IANA codepoint 20Linux seg6local: 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`.
4Flavors, and why PSP is not a switch you flip
Section 4.16 defines flavors as “variants of the End, End.X, and End.T behaviors. The End, End.X, and End.T behaviors can support these flavors either individually or in combinations.”[1]
PSP, Penultimate Segment Pop, is the one that gets misunderstood. Mechanically it is simple: after the destination address update, if Segments Left has reached zero, the node copies the SRH’s Next Header up into the preceding header, decreases the payload length by 8*(Hdr Ext Len+1), and removes the SRH.[1] The spec is emphatic that this “only takes place at a penultimate SR Segment Endpoint Node and does not happen at any transit node.”[1]
The part that surprises people is who decides. Verbatim: “The PSP operation is deterministically controlled by the SR source node … A PSP-flavored SID is used by the SR source node when it needs to instruct the penultimate SR Segment Endpoint Node listed in the SRH to remove the SRH.”[1] And the control-plane consequence: “An SR Segment Endpoint Node that offers both PSP- and non-PSP-flavored behavior advertises them as two different SIDs.”[1] So PSP is not a checkbox on the penultimate router; it is a different SID that the headend chooses to put in the list.
Why bother? The stated benefit is at the egress PE, which “is required to parse and remove fewer bytes” and whose “header is more likely to be within the memory accessible to the lookup engine in the forwarding ASIC.”[1] And a reassurance for the MTU conversation: “The usage of PSP does not increase the MTU of the IPv6 packet and hence does not have any impact on the Path MTU (PMTU) discovery mechanism.”[1]
USP is Ultimate Segment Pop: the last endpoint removes the SRH before processing the next header. USD is Ultimate Segment Decapsulation.[1]
The implementation reality is narrower than the spec. In Linux, of the three RFC 8986 flavors only PSP is implemented, and the per-behavior masks allow psp and next-csid on End but only next-csid on End.X - asking for End.X flavors psp returns the extack string Unsupported Flavor operation(s).[6] The man page agrees, listing usp and usd as “not yet supported in kernel.”[5] On the SONiC side the uSID design maps end, end.x, end.t, un and ua all to a PSP-and-USD flavor combination in SAI, which is a different set again.[11]
5Codepoints and who is allowed to advertise them
Two reference tables close this lesson, and both are things you look up rather than derive.
The IANA “SRv6 Endpoint Behaviors” codepoints from section 10.2.2 are what you actually read off an IS-IS LSP or a BGP update: 1 End, 2 End with PSP, 3 End with USP, 4 End with PSP and USP, 5 End.X, 9 End.T, 14 End.B6.Encaps, 15 End.BM, 16 End.DX6, 17 End.DX4, 18 End.DT6, 19 End.DT4, 20 End.DT46, 21 End.DX2, 22 End.DX2V, 23 End.DT2U, 24 End.DT2M, 27 End.B6.Encaps.Red, 28 End with USD, 29 End with PSP and USD.[1] Note the gaps - 13 is unused in the initial registry, and so are 25 and 26.[1] Behaviors are not contiguous, so never compute a codepoint from a neighbour.
The signalling matrix in section 8.4 table 3 is the paper check that catches a whole class of design error. End, End.X and End.T with their flavors are signalled by IGP and BGP-LS only. The decap family End.DX6, End.DX4, End.DT6, End.DT4 and End.DT46 adds BGP IP, VPN and EVPN routes. The L2 family End.DX2, End.DX2V, End.DT2U and End.DT2M is BGP-LS and BGP service routes only, never IGP. End.B6.Encaps, End.B6.Encaps.Red and End.B6.BM are BGP-LS only.[1] When somebody asks why their EVPN service SID never showed up in the IGP, that table is the answer, and RFC 9252 is where the BGP encoding lives.[12]
One last framing for the field. In SR-MPLS a VPN route carries a 20-bit label that means something only inside the egress router’s label table. In SRv6 it carries a 128-bit address that is the instruction and is routable on its own.[1][12] That is why the first check on a non-forwarding SRv6 VPN is not the behavior but the reachability of the service SID from the ingress PE.[12]
Given: locator fcbb:bbbb:1::/48 configured with block-len 32, node-len 16, func-bits 16, and the SID fcbb:bbbb:1:fe00:: bound to a per-VRF dual-stack decapsulation.
- Split the address. Block is the top 32 bits:
fcbb:bbbb. Node is the next 16:0001. Function is the next 16:fe00. Argument length is 0, so all remaining bits must be zero - and they are, since the address ends in::.[1] - Check the sum.
32 + 16 + 16 + 0 = 64, which is no greater than 128, so the SID is legal, and the trailing 64 bits are zero as required.[1] - Check it against the tool. FRR requires
block-len + node-len = locator-lenandblock-len + node-len + func-bits <= 128; here 32 + 16 = 48 = the locator prefix length, so the locator is accepted.[9] - Name the behavior. Decapsulate and look up in a specific table for both IPv4 and IPv6 is
End.DT46.[1] - Name the codepoint.
End.DT46is 20 in the IANA registry.[1] - Name the signalling. The decap family is signalled by IGP, BGP-LS and BGP IP, VPN and EVPN routes - in practice, by BGP as a service SID.[1][12]
- Name the Linux equivalent.
ip -6 route add fcbb:bbbb:1:fe00::/64 encap seg6local action End.DT46 vrftable 100 dev vrf100, which requires the VRF device to exist.[5]
Given: locator fcbb:bbbb:2::/48 with the same 32/16/16 structure, and the SID fcbb:bbbb:2:e000:: bound to a cross-connect toward one named neighbor on eth1.
- Block = ____ , Node = ____ , Function = ____
L + F + A= ____ , legal because ____- Behavior name = ____
- IANA codepoint = ____
- Signalling protocols allowed for this behavior = ____
- Linux command:
ip -6 route add ____ encap seg6local action ____ nh6 ____ dev eth1[5] - Would
flavors pspbe accepted on this behavior in Linux? ____ , because ____[6]
A customer sends you a packet capture in which the destination address is fcbb:bbbb:3:fe03:: and an IS-IS LSP that advertises endpoint behavior codepoint 20 for that SID, with a SID structure of block 32, node 16, function 16, argument 0.
Produce: (a) the locator, node identifier and function value; (b) the behavior name and what the node will do with the packet; (c) whether this SID could legally have been learned from the IGP alone, citing the section 8.4 table; (d) the single most likely reason a packet sent to it from a remote PE does not forward, and the one command you would ask for to confirm it.
Acceptance criteria: your answer names End.DT46, states that the decap family is signalled by BGP service routes rather than being an IGP-only behavior, and identifies service-SID reachability from the ingress PE as the first check rather than proposing a behavior change.[1][12]
Episode 3 closes — Thursday, with the PoC passing
You split the SID by the block, node and function lengths the platform uses, name the behavior instead of the vendor keyword, and drop the flavor the headend was never going to select - a source node either advertises a PSP-flavored SID or it does not, and this one did not.[1] The morning’s second failure has the same shape: a service SID the customer expected from the IGP, when the decap family is signalled by BGP service routes.[1][12]
What you say to the SE: “The kernel told you the truth. That line was a flavor, and flavors are chosen by the headend, not typed on the endpoint.”[6][1]
The sign-off holds. Then the incumbent’s architect prices that path at 40 bytes against your 96, and procurement wants one number by tomorrow.
Lab
Goal: use the BlueField-3 Arm side as a second, real SRv6 endpoint. Mutating at the OS level only - VRF creation and sysctls, each with a rollback below. No firmware writes, no mode changes.
- Pre-flight inventory on the Arm side before touching anything:
Expected:uname -r zgrep -E 'CONFIG_IPV6_SEG6|CONFIG_NET_L3_MASTER_DEV' /boot/config-$(uname -r) ip -6 addr showCONFIG_IPV6_SEG6_LWTUNNELandCONFIG_NET_L3_MASTER_DEVpresent. IfCONFIG_NET_L3_MASTER_DEVis missing, stop and record it - End.DT4 and End.DT46 have no input handler without it, and that fact is the deliverable.[6] - Create the VRF and enable strict mode. Rollback for both lines is written before you run them:
Rollback:ip link add vrf100 type vrf table 100 && ip link set vrf100 up sysctl -w net.vrf.strict_mode=1sysctl -w net.vrf.strict_mode=0thenip link del vrf100.[5] - Program an End.DT46 SID on the Arm and confirm it installs:
Expected: the route installs and theip -6 route add fcbb:bbbb:9:fe00::/64 encap seg6local action End.DT46 vrftable 100 count dev vrf100 ip -s -6 route show | grep -A3 seg6localcountattribute exposes packets, bytes and errors.[5] Rollback:ip -6 route del fcbb:bbbb:9:fe00::/64. - Drive traffic from the x86 host through the DPU and out the ConnectX port to a containerlab node, then read the counters again. Expected: the packet counter moves and the error counter does not. If errors climb instead, the SRH arriving at the Arm has a non-zero Segments Left - the decap family only accepts packets with Segments Left zero or no SRH at all.[5]
- Tear down in reverse order and confirm the host is as you found it: delete the route, delete the VRF, reset
net.vrf.strict_mode, resetseg6_enabled. - Optional, customer lab only: on a Spectrum-4 running Cumulus Linux, run
nv show router segment-routing srv6 sid <sid>for a SID you configured asuAand note that the output printsEnd.X- NVUE shows the RFC 8986 base behavior, not the flavored name.[10] Read-only.
Goal: instantiate behaviors on a Linux node and collect the exact failure strings, so that you can tell a customer the difference between “the RFC defines it” and “this kernel implements it”. Read-only in the hardware sense: routes and sysctls only, each with a rollback.
- Enable seg6 on the node as in lesson 2, then install the simplest behavior:
Expected: the route appears withip -6 route add fcbb:bbbb:1::/48 encap seg6local action End dev eth1 ip -6 route show | grep seg6localencap seg6local action End.[5] Rollback:ip -6 route del fcbb:bbbb:1::/48. - Add the behaviors that need parameters, one at a time, and note which attribute each one demands:
Expected: End.X requires a next hop, End.T requires a table - note that End.T is absent from the man page’s action list even though the kernel implements it, so readip -6 route add fcbb:bbbb:1:e000::/64 encap seg6local action End.X nh6 2001:db8::2 dev eth1 ip -6 route add fcbb:bbbb:1:e100::/64 encap seg6local action End.T table 100 dev eth1 ip -6 route add fcbb:bbbb:1:fe00::/64 encap seg6local action End.DT46 vrftable 100 dev vrf100seg6_local.cwhen the docs come up empty - and End.DT46 requires a VRF table and a real VRF device withnet.vrf.strict_mode=1.[5][6] Rollback: delete each route, thensysctl -w net.vrf.strict_mode=0if you set it. - Break End.DT6 on purpose by giving it neither table nor vrftable and capture the extack:
Expected, verbatim:ip -6 route add fcbb:bbbb:1:fe01::/64 encap seg6local action End.DT6 dev eth1table or vrftable must be specified.[6][5] If you get a different string, record it - kernels differ and your customer’s kernel is the one that matters. - Ask for two things the kernel does not implement and record both rejections:
Expected: End.BM is a uAPI enum value with no entry in the kernel action table, and the flavor request returnsip -6 route add fcbb:bbbb:1:fe02::/64 encap seg6local action End.BM dev eth1 ip -6 route add fcbb:bbbb:1:e001::/64 encap seg6local action End.X flavors psp nh6 2001:db8::2 dev eth1Unsupported Flavor operation(s)because End.X supports next-csid only.[6][7] - For every behavior you got working, write down its IANA codepoint from RFC 8986 section 10.2.2 and confirm the gap at 13 with your own eyes.[1] Deliverable: a two-column table - RFC behavior against this kernel’s actual result, with the verbatim error string in the failure rows. That table is the artefact a customer keeps.
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, why an SRv6 service route carries a 128-bit address where an MPLS VPN route carried a 20-bit label, and what that address actually tells the receiving router to do.
Sources
Facts in this lesson were checked against RFC 8986 sections 3.1, 4.1, 4.16, 5.2, 8.4 and 10.2.2 re-fetched 2026-09-07; Linux seg6_local.c and ip-route.8.in read 2026-09-07. Dates are when each page was fetched.
- RFC 8986 - SRv6 Network Programming · fetched 2026-09-07
- RFC 8402 - Segment Routing Architecture · fetched 2026-09-07
- RFC 8754 - IPv6 Segment Routing Header (SRH) · fetched 2026-09-07
- RFC 9800 - Compressed SRv6 Segment List Encoding · fetched 2026-09-07
- iproute2 man source - ip-route.8.in · fetched 2026-09-07
- Linux kernel source - net/ipv6/seg6_local.c · fetched 2026-09-07
- Linux kernel uAPI - include/uapi/linux/seg6_local.h · fetched 2026-09-07
- FRR documentation - Static (staticd SRv6 static-sids) · fetched 2026-09-07
- FRR documentation - Zebra (SRv6 locators) · fetched 2026-09-07
- Segment Routing | Cumulus Linux 5.18 · fetched 2026-09-07
- SONiC HLD - SRv6 uSID · fetched 2026-09-07
- RFC 9252 - BGP Overlay Services Based on SRv6 · fetched 2026-09-07
- SONiC HLD - SRv6 · fetched 2026-09-07
- SONiC HLD - Static Configuration of SRv6 in SONiC · fetched 2026-09-07
The same idea elsewhere
Other lessons that cover this ground, sometimes from another course's angle.