Ping the SID: OAM and the five failures that actually happen
S3·E2The SID that answered with an error · Customer NOC, 23:40, the night after the bridge call
Builds on: L3VPN and EVPN services over SRv6, SRv6 in the Linux kernel: seg6 and seg6local
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
- Interpret the three outcomes of pinging a SID and explain why ICMP Parameter Problem Code 4 is a success signal.
- Locate the O-flag in the SRH and state what a node does and does not do with it.
- Diagnose the five recurring SRv6 failures from their evidence rather than from symptoms.
- Map per-SID counters across Linux seg6local, Cumulus Linux and community SONiC to the same three questions.
Episode 2 — The SID that answered with an error
Ninety minutes of window left and a junior engineer has typed “SRv6 is broken” into the incident channel. His evidence: he pinged a segment identifier on the endpoint and got an ICMP Parameter Problem back instead of an echo reply. The Dell SE watches the clock; everything in his promise spreadsheet is downstream of this window. The night-shift operator waits with the label maker.
You ask for the code number. It is 4. That is the best news all night, because Code 4 is SR Upper-layer Header Error, generated by the target node itself after it matched the destination address to a locally programmed SID and ran the behavior far enough to inspect the upper-layer header.[2] The SID exists. ICMPv6 is simply not a permitted upper-layer header on it, and the same section notes that operators may enable local configuration to allow it so SIDs can be pinged.[2] “Show me the counter,” says the network lead, unmoved, notebook already open.
SRv6 OAM exists so that nobody has to deploy a new protocol to answer that question. RFC 9259 describes how the existing IPv6 ping and traceroute mechanisms are used in an SRv6 network, and states plainly that ping can be used to check SID reachability and to validate that a SID is locally programmed at the target node.[1] A segment is an address, so the tools that test addresses test segments.
An ICMP error from the far end is evidence; silence is the absence of it.
The next SID in their list answers nothing at all. That is segment 1.
1Pinging a SID: three outcomes, one of which looks like a failure
RFC 9259 describes “how the existing IPv6 mechanisms for ping and traceroute can be used in an SRv6 network” and specifies the OAM flag for “controllable and predictable flow sampling from segment endpoints.”[1] Its Appendix A is the practical part: pinging an IPv6 address via a segment list, pinging a SID, traceroute via a segment list, traceroute to a SID, hybrid OAM using the O-flag, and monitoring of SRv6 paths.[1]
The sentence to internalise is that “the ping mechanism described above can also be used to perform SID reachability checks and to validate that the SID is locally programmed at the target node.”[1] That only works when the endpoint’s configuration permits ICMPv6 as an upper-layer header on that SID.[2]
Which produces three outcomes, not two:
| Outcome | What it proves | What to do next |
|---|---|---|
| Echo reply | The SID is programmed and ICMPv6 is permitted on it | Move to the next SID in the list |
| ICMP Parameter Problem, Code 4 | The SID is programmed; ICMPv6 is not a permitted upper-layer header | Nothing. This is a pass. Record it and move on |
| Silence | No evidence the SID exists at that node | Start diagnosing here |
Code 4 is “SR Upper-layer Header Error” from RFC 8986 section 4.1.1, and the same section notes that operators “may enable local configuration to allow Upper-Layer header type 58 (ICMPv6)” precisely so SIDs can be pinged.[2] A customer who reads Code 4 as a failure will spend an afternoon fixing a working endpoint.
1. PE1 · Linux + FRR
Encapsulate: outer Destination Address = the first segment, fcbb:bbbb:2::1. The SRH carries 3 entries in reverse — Segment List[0] = fcbb:bbbb:4::100, the LAST segment of the policy.
The SR source node encapsulates: it pushes an outer IPv6 header whose Destination Address is the FIRST segment, and (for the uncompressed encodings) an SRH holding the rest. RFC 8402 calls this PUSH. The inner packet's Hop Limit is decremented once, here.
fcbb:bbbb:2::12Last Entry 2 · Hdr Ext Len 6 · Routing Type 496 BFAE angle: "SR supports per-flow explicit routing while maintaining per-flow state only at the ingress nodes to the SR domain" (RFC 8402 §1). The fabric holds no policy state — that is the whole scaling argument against RSVP-TE.
sysctl -w net.ipv6.conf.all.seg6_enabled=1 net.ipv6.conf.eth1.seg6_enabled=1 ip -6 route add 2001:db8:cafe::/64 encap seg6 mode encap \ segs fcbb:bbbb:2::1,fcbb:bbbb:3::1,fcbb:bbbb:4::100 dev eth1 # mode encap.red for the reduced SRH. segs are written in SR-policy order; # the kernel reverses them into the Segment List.
H.Encaps / ip -6 route … mode encap. All n segments ride in the SRH, stored in reverse: Segment List[0] holds the LAST segment of the policy. Segments Left = n-1, Last Entry = n-1, Hdr Ext Len = 2n. Overhead 40 + 8 + 16n.
Same path, three encodings: full SRH 96 B · reduced 80 B · uSID 40 B. The IPv6 floor is 1280 octets, so this is the number the MTU conversation turns on.
Four FRR nodes around a vrnetlab/dell_sonic:4.5.1 node that is pure IPv6 transit. Three segments.
2The O-flag, and how to write up a negative result
The O-flag sits at bit 2 of the SRH Flags octet, in a field RFC 8754 itself declares entirely unused and reserved.[1][3] Its semantics are narrow and worth quoting: it is “used as a marking bit in user packets to trigger telemetry data collection and export at the segment endpoints”, and processing inserts one step into the endpoint pseudocode — “(a) Make a copy of the packet. (b) Send the copied packet, along with a timestamp, to the OAM process for telemetry data collection and export.”[1]
Two properties make it safe to enable and useless to guess about. First, “timestamp and any other metadata are not carried in the packet forwarded to the next hop”, so the forwarded packet is byte-identical to one without OAM processing.[1] Second, “implementation of the O-flag is OPTIONAL. If a node does not support the O-flag, then it simply ignores it upon reception. If a node supports the O-flag, it can optionally advertise its potential via control plane protocol(s)” — and that advertisement is the O-flag bit in the IS-IS SRv6 Capabilities sub-TLV type 25, whose 2 octets of flags currently define nothing else.[1][4]
The RFC assumes IPFIX as the export protocol “without the loss of generality.”[1]
So when you craft an SRH with the O-flag in scapy, aim it at a Linux endpoint and nothing happens, the correct write-up is not “bug”. It is: the specification makes implementation optional and unsupporting nodes ignore the flag, this node produced no OAM output, therefore this node does not implement the O-flag. That sentence is publishable. “SRv6 OAM is broken on Linux” is not.
3The five failures that actually happen
Symptoms in an SRv6 domain are almost always the same — “it pings the underlay but the service does not work” — so triage has to run on evidence, not on symptoms. These are the five, each with the command that produces its evidence.
1. MSD advertised as zero. RFC 9352 registers four MSD types: 41 SRH Max Segments Left, 42 SRH Max End Pop, 44 SRH Max H.encaps and 45 SRH Max End D.[4] Each has the same brutal default: “if no value is advertised, the supported value is 0”, and for type 44 specifically, a zero means “the headend can apply an SR Policy that only contains one segment without inserting any SRH header.”[4] A headend computing a six-segment path against a receiver that advertised nothing produces drops at the endpoint and nothing useful in a log. Evidence: dump the IS-IS LSPs and confirm all four types are present and non-zero on every node in the path.[4]
2. MTU, with no PMTUD relief inside the domain. The arithmetic is 40 bytes of outer IPv6 plus 8 bytes of SRH base plus 16 bytes per segment, so an uncompressed three-segment encapsulation costs 96 bytes; the same path as a single uSID carrier costs 40 with no SRH at all.[3][11] IPv6 guarantees only that “every link in the Internet have an MTU of 1280 octets or greater”, which is a floor and not a budget.[9] Evidence: sweep the inner payload size until it passes, and compare against the smallest interface MTU on the path. The classic report — “it broke when I added a fourth segment” — is this, almost every time.
3. An unroutable service SID. RFC 9252 says the SRv6 Service SID SHOULD be routable within the AS of the egress PE, and it is that reachability, not the advertisement, that the ingress PE depends on.[12] FRR turns the SHOULD into a hard local rule: “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”, so the path is present and the prefix still does not install.[13] Evidence: the nexthop view on the ingress PE, covered in the previous lesson.
4. seg6_enabled left at 0. The per-interface sysctl “accept or drop SR-enabled IPv6 packets on this interface. Relevant packets are those with SRH present and DA = local”, and it defaults to 0.[6] A lab node with a perfect seg6local route silently drops everything until it is set. The practical enable line is sysctl -w net.ipv6.conf.all.seg6_enabled=1 net.ipv6.conf.default.seg6_enabled=1 net.ipv6.conf.eth1.seg6_enabled=1; whether the all knob alone is sufficient is [UNVERIFIED] against a kernel document, so set the specific interface too and test it yourself.[6] Evidence: read the sysctl on the receiving interface, not on all.
5. An unresolved uA or End.X next hop. In Linux, End.X requires SEG6_LOCAL_NH6, so a missing next hop is a configuration rejection.[10] In community SONiC it is worse because it looks healthy: “when IP NextHop doesn’t exist, SRV6_MY_SID_TABLE objects are programmed with Drop action and notify NeighOrch to resolve IP NextHop. When that NextHop is resolved, SRV6Orchagent updates SRV6_MY_SID_TABLE with valid IP NextHop handle and Forward action.”[8] The entry exists, the SID is programmed, and the action is Drop. On Cumulus, uA next-hop learning is by router advertisement, and “if the adjacent device is a non-Spectrum switch, you need to enable router advertisements on the adjacent device on the connected interface to ensure proper next hop discovery.”[7] That last one is a real interop footgun in a mixed Dell and NVIDIA rack.
Ping to a SID does not answer
4Counters as evidence, in three dialects
Every implementation answers the same three questions — how many packets hit this SID, how many bytes, how many failed — with different words.
Linux. Add count to the seg6local route. That enables “1) packets correctly processed; 2) bytes correctly processed; 3) packets that cause a processing error (i.e., missing SID List, wrong SID List, etc). To retrieve the counters … use the -s flag in the show command.”[5] The kernel names are SEG6_LOCAL_CNT_PACKETS, SEG6_LOCAL_CNT_BYTES and SEG6_LOCAL_CNT_ERRORS, where errors count “packets that have NOT been properly processed by an SRv6 Behavior instance.”[10]
Cumulus Linux. nv show router segment-routing srv6 stats gives per-SID Hit Counters as SID and Packets, plus a Drop Counters section with “Total no-sid-dropped packets”; there are also stats sid <sid> and stats no-sid-drops forms, and nv action clear router segment-routing srv6 stats to zero them.[7] Two caveats: there are no byte counters in the documented output, and “when you enable packet trimming with segment routing, Cumulus Linux counts the trimmed packet twice in the SRv6 statistics.”[7]
Community SONiC. MySID counters use the flex-counter infrastructure with a default polling interval of 10 seconds, gated on the SAI attribute capability, and the CLI is counterpoll srv6 {enable|disable|interval}, sonic-clear srv6counters, show srv6 stats and show srv6 stats <MySID>, with columns MySID, Packets and Bytes.[8]
The mapping is the useful artefact:
| Question | Linux | Cumulus Linux | Community SONiC |
|---|---|---|---|
| Packets that hit this SID | count + ip -s -6 route show |
nv show ... srv6 stats Packets column |
show srv6 stats Packets column |
| Bytes | Yes, SEG6_LOCAL_CNT_BYTES |
Not in the documented output | Yes, Bytes column |
| Packets that failed the behavior | SEG6_LOCAL_CNT_ERRORS |
No per-SID error counter documented | Not documented in the base HLD |
| Packets for a SID that does not exist | Not exposed per-SID | no-sid-drops total |
Not documented in the base HLD |
Note what the table does not say. A blank cell means the documentation fetched on 2026-09-07 does not describe that counter, not that the platform lacks it.
5Produce all three outcomes on purpose
Setup. Three Linux endpoints in containerlab, one segment list, one host generating traffic.
Outcome 1, a clean reply. On endpoint B, program a SID and permit ICMPv6 on it, then ping the SID address from A. Expected: normal echo replies. This proves the SID is programmed and that the local policy allows ICMPv6 as an upper-layer header.[1][2]
Outcome 2, Parameter Problem Code 4. On endpoint C, program a SID with the default upper-layer policy, so ICMPv6 is not permitted on it. Ping it from A. Expected: ICMPv6 parameter problem with code 4 rather than an echo reply. Read it as: the SID exists and is programmed, ICMP is not allowed on it, nothing to fix.[2]
Outcome 3, silence. Ping an address inside the SID block that no node instantiates. Expected: no answer at all. This is the only outcome that starts an investigation.
Traceroute through the list. Run a traceroute through the three-segment list and account for each hop’s TTL source. Transit nodes are ordinary IPv6 forwarders under RFC 8754 section 3.2 and do nothing SR-specific; only the endpoints run the behavior, decrement Segments Left and rewrite the destination address.[3]
The O-flag experiment. Craft an SRH with bit 2 of the Flags octet set and send it at endpoint B.[1] Expected on Linux: the packet is processed exactly as if the flag were clear, with no OAM copy anywhere, because implementation is OPTIONAL and an unsupporting node ignores it.[1] Write that down as a dated negative result naming the kernel version.
Counters. Add count to every seg6local route, re-run each ping, and build a per-SID table from ip -s -6 route show.[5] Expected: the Code-4 SID shows non-zero packets, which is the numeric confirmation that Code 4 means processed.
- Endpoint B: program a SID and permit ______ as an upper-layer header on it. Ping from A. Expected outcome: ____________.
- Endpoint C: program a SID with the default policy. Ping from A. Expected outcome: ICMP ____________ ____________ Code ___. Meaning: ____________________.
- Ping an address in the SID block that nobody instantiates. Expected outcome: ____________. This is the only outcome that ____________________.
- Traceroute through the three-segment list. The nodes that appear as hops but do nothing SR-specific are called ______ nodes, defined in RFC 8754 section ______.
- Set bit ___ of the SRH ______ octet with scapy and send it at a Linux endpoint. Expected: ____________________, because implementation of that flag is ____________.
- Add ______ to every seg6local route and read the counters with
ip ___ -6 route show. The three counters are ______, ______ and ______.
Build the evidence table. On your containerlab fabric, produce all three ping outcomes deliberately, then produce each of the five failures from segment 3 that your lab can reach — at minimum: seg6_enabled at 0, an unresolved End.X next hop, and an MTU failure found by payload sweep.
Acceptance criteria. For each of the eight rows you record the exact command, the exact observed output, and the one sentence you would say to a customer. The seg6_enabled row states which interface you set and whether all alone was sufficient on your kernel — that specific point is marked unverified in this course, so your result either confirms or refutes it and must be dated.[6] The MTU row records two numbers: the largest inner payload that survives a three-segment uncompressed path, and the largest that survives the same path as one uSID carrier.[11] The O-flag row is written as a negative result with the kernel version, not as a bug.[1]
Ninety minutes, and the fifth failure
The silent SID was seg6_enabled left at 0 on the receiving interface, which defaults to 0 and drops SR-enabled packets with nothing in any log to say so.[6] The operator labels that port before anyone can argue with him. The last twenty minutes go to dumping IS-IS LSPs and confirming all four MSD types are present and non-zero, because a receiver that advertised nothing is read as zero and neither end reports anything.[4] What you say to the network lead: “Nothing here was broken. One sysctl was off, and the error your engineer escalated was proof the other endpoint works.” At 07:20 the Dell SE finds you in the hotel lobby. The customer wants one word by nine: does Dell do SRv6.
Lab
Pre-flight inventory (read-only). On the Dell-lab x86 host and the BlueField-3 Arm: uname -r, ip -br addr, ip -6 route show | grep seg6local, sysctl net.ipv6.conf.all.seg6_enabled, and the MTU of every interface on the intended path (ip -br link). Write the MTU list down; you are about to change one of them.
- Ping ladder across real hardware. Reproduce all three outcomes between the x86 host and an Arm-side endpoint. Expected: identical semantics to the containerlab run.[1][2] If not: compare
seg6_enabledon the Arm’s receiving interface against the host’s.[6] Rollback:ip -6 route del <sid>/128for every seg6local route you added on the Arm and on the host, restore any upper-layer/ICMPv6 permission you changed on a SID, re-apply theseg6_enabledvalues from the pre-flight capture, then diffip -6 route showagainst that capture — it must match. - Set an intermediate MTU. Force a 1500-byte MTU on one interface in the path with
ip link set <ifname> mtu 1500. Expected: large encapsulated packets start failing. Rollback:ip link set <ifname> mtu <original>from the pre-flight list, and confirm withip -br link. - Sweep, uncompressed. Find the largest inner payload that survives a three-segment uncompressed path. Expected: the failure point sits about 96 bytes below the plain-IPv6 limit — 40 outer plus 8 SRH base plus 48 for three segments.[3]
- Sweep, uSID. Repeat with the same path expressed as one uSID carrier. Expected: the failure point moves up by roughly the SRH and the extra segments you removed.[11] Record both numbers; that comparison is the strongest single argument for uSID you can hand a customer.
- Restore. Put the MTU back, re-run one large-payload test, and confirm it passes again.
- Optional, customer lab only. On a Spectrum-4 running Cumulus Linux 5.14 or later, drive traffic at a SID that does not exist and read
nv show router segment-routing srv6 stats no-sid-drops.[7] Read-only. Note the packet-trimming double-count caveat in your write-up if trimming is enabled on that switch.[7]
Goal. Produce all three ping-the-SID outcomes on purpose, then read counters in three dialects.
- Pre-flight.
sysctl net.ipv6.conf.all.seg6_enabled net.ipv6.conf.default.seg6_enabledand, per node,ip -6 route show | grep seg6local. Record the starting values — you will change them. Rollback for the whole lab: re-apply the recorded sysctl values and delete any route you added.[6] - Outcome 1, reply. Program a SID on endpoint B, permit ICMPv6 on it, ping it. Expected: echo replies.[1] If not: check
seg6_enabledon B’s receiving interface before anything else.[6] - Outcome 2, Code 4. Program a SID on endpoint C with the default upper-layer policy and ping it. Expected: ICMPv6 Parameter Problem Code 4.[2] Record it as a pass.
- Outcome 3, silence. Ping an uninstantiated address in the SID block. Expected: no reply.
- Traceroute. Traceroute through a three-segment list and write one line per hop saying whether it is an SR source node, a transit node or a segment endpoint node.[3]
- O-flag. Craft an SRH with bit 2 of the Flags octet set using scapy and send it at a Linux endpoint.[1] Expected: no OAM behaviour, because Linux does not implement the O-flag. Record the kernel version and the negative result. If not: capture what did happen before concluding anything.
- Counters. Add
countto everyseg6localroute, regenerate traffic, and build a per-SID table fromip -s -6 route show.[5] Rollback: replace the routes withoutcountif you want the original state, or leave them —countchanges accounting only, not forwarding. - Read the other two dialects. From the captured Cumulus and SONiC transcripts, map each column to its Linux equivalent using the table in segment 4.[7][8] Expected: you can name, for each platform, which of the four questions it cannot answer from documented output.
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 ICMP error can be the best news you get when troubleshooting an SRv6 path.
Sources
Facts in this lesson were checked against RFC 9259, RFC 8986, RFC 8754, RFC 9352 and RFC 8200 (rfc-editor) re-checked 2026-09-07; iproute2 ip-route.8.in and Linux seg6 sysctl docs 2026-09-07; Cumulus Linux 5.18 Segment Routing page and SONiC SRv6 HLD 2026-09-07. Dates are when each page was fetched.
- RFC 9259 — OAM in Segment Routing over IPv6 (SRv6) · fetched 2026-09-07
- RFC 8986 — SRv6 Network Programming · fetched 2026-09-07
- RFC 8754 — IPv6 Segment Routing Header (SRH) · fetched 2026-09-07
- RFC 9352 — IS-IS Extensions to Support Segment Routing over the IPv6 Data Plane · fetched 2026-09-07
- iproute2 man source: ip-route.8.in (main) · fetched 2026-09-07
- Linux kernel doc: Seg6 Sysfs variables · fetched 2026-09-07
- Segment Routing | Cumulus Linux 5.18 · fetched 2026-09-07
- SONiC HLD: Segment Routing over IPv6 (SRv6) · fetched 2026-09-07
- RFC 8200 — Internet Protocol Version 6 (IPv6) Specification · fetched 2026-09-07
- Linux kernel source: net/ipv6/seg6_local.c (master) · fetched 2026-09-07
- RFC 9800 — Compressed SRv6 Segment List Encoding · fetched 2026-09-07
- RFC 9252 — BGP Overlay Services Based on Segment Routing over IPv6 (SRv6) · fetched 2026-09-07
- FRR documentation — BGP (latest) · fetched 2026-09-07
The same idea elsewhere
Other lessons that cover this ground, sometimes from another course's angle.
- FRR: locators, the SID Manager, IS-IS SRv6 and TI-LFAElsewhere in this course · Same ground: msd, isis and sid
- NVIDIA switching: uN and uA on Spectrum-4Elsewhere in this course · Same ground: no-sid-drops, cumulus and hw_counters
- The switch half: Cumulus NVUE and Dell Enterprise SONiCRoCE course · Same ground: sonic, cumulus and misconception