RFC 8362: Extended LSA Support for FRR ospf6d
This is a story about six months of work that didn’t ship, and what I’m still figuring out about why.
The surface story is technical: I implemented RFC 8362 Extended LSA support for FRR’s ospf6d. The code works. The tests pass. It wasn’t accepted upstream.
The deeper story is about what happens when you bring architectural instincts from one context into another, and they don’t translate. When your experience says one thing and the environment says another. When you’re left wondering whether your expertise is an asset or a liability in unfamiliar territory.
Why OSPFv3 Needed Extended LSAs
OSPFv3’s original extensibility mechanism was defining new LSA types—and this worked. TLV-based LSA types were added for Traffic Engineering (RFC 5329) and other features. But for the core LSA types (Router, Network, Inter-Area-Prefix, etc.), the format remained fixed C structs mapped directly to wire format.
This has limitations:
- 64KB size limit — Large topologies with many prefixes can exceed this
- No sub-structure in core LSAs — Metadata like Segment Routing SIDs can’t be cleanly attached to prefixes or adjacencies
RFC 8362 addresses this with Extended LSA versions of the core types, using TLV encoding:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Type | Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Value... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
E-LSA is foundational for:
- Segment Routing (RFC 8666, RFC 9513) — SID/Label sub-TLVs attach to prefixes and adjacencies
- Flex-Algorithm (RFC 9350) — Algorithm-specific metrics and constraints via sub-TLVs
- Multi-Topology Routing (draft-ietf-ospf-mt-ospfv3) — Multiple independent topologies over shared infrastructure
If you’re deploying OSPFv3 with Segment Routing, you need E-LSA support.
Two Wire Formats, One Codebase
Here’s where it gets interesting—architecturally.
FRR’s ospf6d operates directly on wire-format structs. Algorithms cast the LSA body to the appropriate C struct and access fields directly. This is efficient: no parsing overhead, no intermediate representation, no allocation churn. For a single wire format, it’s a reasonable design.
But RFC 8362 introduces a second wire format for the same semantic data. Now you have two representations of “inter-area prefix”: the legacy fixed struct and the TLV-encoded E-LSA version. Every algorithm that touches LSA contents—SPF calculation, ABR summarization, ASBR external routes, flooding, show commands—needs to handle both.
The obvious approaches:
Duplicate everything. Write parallel code paths for legacy and E-LSA. Every function that processes Inter-Area-Prefix-LSA gets a sibling that processes E-Inter-Area-Prefix-LSA. This keeps the code style consistent but roughly doubles the surface area. Every bug fix, every enhancement, applied twice. Semantic drift over time as the paths evolve independently.
Abstract over the difference. Introduce an iterator or handler pattern that walks LSA contents uniformly, with format-specific parsing isolated behind it. Algorithms see one interface regardless of wire format. Less duplication, but new patterns the codebase doesn’t use elsewhere.
My instincts, shaped by years of systems work in other contexts, said: abstract. Don’t duplicate business logic. Isolate the format-specific stuff.
But it wasn’t just about this feature. The duplication approach would work, but it would leave the codebase harder to maintain than I found it. The next person adding TLV-based functionality faces the same problem, plus more code to navigate. The abstraction, if accepted, makes future work easier. Leave the place better than you found it—that’s the instinct.
There’s also consistency to consider. ospf6d already has TLV-based LSAs—Grace-LSA for graceful restart uses the same TLV header structure. The iterator pattern aligns E-LSA handling with existing TLV-based functionality rather than introducing something foreign.
Iterator Pattern Across 9 LSA Types
We implemented all 9 E-LSA types from RFC 8362, using an iterator pattern. Commit b919740 introduces the mechanism—the foreach_lsdesc iterator and handler registration. Commit 589795c shows it applied to existing code, replacing direct struct access with handler callbacks.
The same iterator walks legacy LSA descriptors or E-LSA TLVs depending on LSA type. Format-specific parsing stays in handlers; algorithms see a uniform interface.
The scope of the work:
| Metric | Value |
|---|---|
| Commits | 97 |
| Development time | 6.5 months |
| Lines added | 27,293 |
| Lines removed | 1,786 |
| Net change | +25,507 lines |
| Files modified | 277 |
| Test code | 3,448 lines |
| Test topologies | 7 |
The tests cover multi-area flooding, mixed legacy/E-LSA interoperability, E-LSA-only networks, broadcast DR/BDR election, and graceful restart. The code works. The approach handles the complexity.
“Not How Things Are Done Here”
The implementation went through three PR iterations:
The feedback: callback-based iteration is wrong for this codebase. Callbacks add indirection. They make code harder to follow, harder to debug. This isn’t how things are done here. The alternative: separate code paths for legacy and E-LSA.
I understood the concern. I’ve worked in codebases where callbacks created impenetrable spaghetti. The objection wasn’t unreasonable.
But I also saw the duplication path leading somewhere bad—a lot of nearly-identical code to maintain in sync forever. I asked for more specific guidance. What would an acceptable middle ground look like? The answer was conceptual rather than concrete: not callbacks, but no specific alternative I could implement.
I tried variations—inlining some callbacks, reducing indirection, restructuring the handler registration. None landed. Eventually the discussion ended without resolution.
Was I Wrong, or Just Unwelcome?
Here’s what I’m still sitting with.
The code works. The tests pass. The iterator pattern handles both formats correctly across all 9 LSA types, SPF, ABR, ASBR, flooding, and graceful restart. We had confidence in the technical approach because we had working software proving it.
What I don’t know is whether my instincts were right for this context.
Maybe the duplication approach would have been fine—maybe the maintenance burden I imagined wouldn’t materialize, or would be acceptable to people who know this codebase better than I do. Maybe there’s a third approach I didn’t see because I was anchored on the patterns I knew. Maybe my years of “abstraction over duplication” instincts are a bias, not wisdom, in environments where those patterns aren’t established.
Or maybe I was right, and I just couldn’t communicate it effectively. Or maybe there’s no clean answer and the feature is just genuinely hard to add to this architecture. I don’t know.
What I do know is that experience from one context doesn’t transfer automatically to another. The instincts that made me effective elsewhere made me confident in an approach that wasn’t accepted here. Whether that’s because I was wrong, or because I failed to build consensus, or because the environment has different values—I genuinely can’t tell.
I’ve written a detailed analysis comparing approaches, trying to work through the trade-offs. I still don’t have a clear answer.
A Working Branch, An Open Question
The branch works. It passes tests. It’s a complete reference implementation of RFC 8362 for ospf6d, if anyone wants it.
If you need E-LSA support in production today, Holo Routing is a Rust-based routing suite with RFC 8362 support and active development. For FRR, the feature remains unimplemented.
This is one of those experiences that shaped me. I spent months pushing against a door that wasn’t going to open—not because the work was wrong, but because it wasn’t wanted in that form, in that place, at that time. The conclusion I came to: put your efforts where they’re welcomed, or go your own way.
Acknowledgments
Thanks to Jake Lodge, who found and fixed countless bugs in this code. This implementation wouldn’t exist without his work and support.