Guide

OSPF

Ze includes an experimental native OSPF engine under the ospf config root. The same engine drives OSPFv2 for IPv4 and OSPFv3 for the address-family ipv6 subsection: interface and neighbor state, LSDB flooding, SPF, ABR/NSSA policy, redistribution, and route installation are shared; the wire codec, raw transport, and prefix strategy are address-family specific.

SPF builds one graph per area from Router-LSAs and Network-LSAs, enforces the RFC 2328 two-way check, derives next-hops from Router-LSA link data, merges equal-cost next-hops, and inserts one locrib.Path per next-hop with OSPF admin distance 110. The kernel FIB path is Loc-RIB -> sysrib -> fibkernel, not redistribution events.

ABR support treats a router as area-border only when it is active in the backbone and at least one other area. It originates Type 3 Summary-Network LSAs and Type 4 Summary-ASBR LSAs, applies configured area ranges with optional cost or not-advertise, withdraws stale self-originated summaries, accepts backbone summaries when calculating on an ABR, and computes inter-area costs as cost-to-advertising-ABR plus the summary metric.

Neighbor changes queue self-LSA origination on the engine's maintenance worker. Hello processing can therefore continue while that worker reads live interface addresses and link speeds. Changes during an active pass queue another pass; MinLSInterval still limits publication, and the maintenance timer retries it.

Interface network types

Each interface selects an RFC 2328 / RFC 5340 network type with network-type, in both families (the IPv6 address-family ipv6 interface omits loopback):

Configured NBMA neighbor and poll state appears in show ospf interface. The ze_ospf_nbma_neighbors{interface,af,state}, ze_ospf_nbma_polls_total{interface,af}, and ze_ospf_ptmp_host_routes{interface,af} metrics track NBMA/PtMP operation.

Interface cost

The cost an interface advertises in its Router-LSA link is the cost leaf when the interface sets one. An interface that sets none is priced by the auto-cost convention: reference-bandwidth divided by the speed of the link, both in Mbit/s. Under the default reference bandwidth of 100000 a 10 Gbit/s link costs 10, a 1 Gbit/s link costs 100 and a 100 Mbit/s link costs 1000, so a faster path wins with no per-interface configuration. Raise reference-bandwidth to spread the costs of a fast network further apart; lower it to compress them.

ospf {
    router-id 10.0.0.1
    reference-bandwidth 400000
    interfaces {
        interface eth0 {
            area 0.0.0.0
        }
        interface eth1 {
            area 0.0.0.0
            cost 5
        }
    }
}

The quotient is clamped to the range a Router-LSA link metric carries, 1 to 65535: RFC 2328 Appendix C.3 states that the interface output cost "must always be greater than 0", and the wire field is two octets wide. A link at or above the reference bandwidth therefore costs 1, and a link slow enough to exceed 65535 is capped there.

The speed comes from the kernel, and which devices answer is the kernel's decision: a loopback, a dummy and a tunnel report none, a bridge and any link with no carrier report a negative speed, and a veth reports 10000 because its driver declares 10 Gbit/s. An interface with no reported speed costs 1, which is what Ze advertised for every unconfigured interface before auto-cost existed. Set cost on it to price it deliberately. The speed is read again on each origination pass rather than cached, so a link that renegotiates re-prices itself on the next pass. The same cost is what show ospf interface reports, what an LDP-IGP sync hold-down raises to 65535, and what the RFC 3630 TE metric falls back to when te-metric is unset.

Changing reference-bandwidth, or adding, changing, or removing an interface's cost leaf, preserves established adjacencies in every address family. Removing cost restores auto-cost from the current reference bandwidth and link speed. Ze re-prices each interface in place and requests self-LSA origination at the commit. If MinLSInterval prevents publication, the maintenance timer retries after the interval, including on passive-only and loopback-only routers. A new router ID or an area-type change that changes the E-bit or N-bit still restarts the affected interfaces. Other restart boundaries inside an interface block, including area, network type, timers, and authentication, remain unchanged.

reference-bandwidth is one router-wide number. Every OSPFv3 address family under address-family prices a link exactly as the IPv4 family does, and no address family carries a reference bandwidth of its own, so one physical link never advertises two costs.

Three conditions report no speed for any interface, not only for one device. A VPP interface has no /sys/class/net entry, so the VPP backend answers 0 for every interface. A host that is not Linux runs the stub backend, which answers 0 for every interface. Ze itself answers 0 when no interface backend is loaded. Auto-cost prices nothing under these three conditions, and every interface with no cost leaf costs 1. Set cost on each interface you must price there.

Multiple address families (RFC 5838)

The OSPFv3 (IPv6) family carries several address families over one link, each as a separate OSPFv3 instance distinguished by its Instance ID range (RFC 5838 §2.1): IPv6-unicast 0-31, IPv6-multicast 32-63, IPv4-unicast 64-95, IPv4-multicast 96-127. Configure each under ospf { address-family { <af> { instance-id N; areas {...} interfaces {...} } } }, where <af> is ipv6-unicast (or the bare ipv6), ipv6-multicast, ipv4-unicast, or ipv4-multicast. The Instance ID must fall inside the chosen AF's range or the config is rejected. Each configured AF runs its own engine instance with a private LSDB, neighbor table, SPF, and Loc-RIB install family (IPv6-unicast routes install into family.IPv6Unicast, IPv4-unicast into family.IPv4Unicast, etc.).

A router running more than one address family sets the AF-bit (RFC 5838 §2.4) in its Hello and DD Options. A non-default AF requires the AF-bit to form an adjacency (§2.5); the default IPv6-unicast AF ignores a missing AF-bit for backward interoperability (§2.6), so a lone IPv6-unicast instance keeps the same on-wire bytes as before and peers with a legacy FRR ospf6d. IPv4-over-OSPFv3 (Instance ID 64-95) carries a 0-32-bit IPv4 prefix in a single 32-bit word (§2.7) and derives its next-hop from the IPv6 link-local adjacency. show ospf ipv6 lists each running AF instance with its address family and Instance ID.

AS-External routes and redistribution

A router becomes an AS Boundary Router (ASBR) when it originates external LSAs, either by redistributing routes from another protocol or through default-information originate. OSPFv2 uses Type 5 AS-External-LSAs; OSPFv3 uses AS-External-LSAs (0x4005) in normal areas and NSSA-LSAs (0x2007) in attached NSSA areas. The ASBR sets the E-bit in its Router-LSA; the ABR and ASBR roles are independent. AS-scoped Type 5 / 0x4005 LSAs live in the AS-wide store, separate from per-area LSDBs.

Redistribution uses the shared redistribute orchestrator. redistribute { destination ospf { import connected static bgp } } injects each accepted route as an external LSA in the configured address family; redistribute { destination bgp { import ospf } } exports OSPF-selected routes back out. OSPF self-import is a runtime no-op (loop prevention: the import source ospf equals the importing protocol). The per-source metric, metric-type (E1/E2), and route tag come from the ospf container's redistribute list; an unenrolled source falls back to metric 20, type-2. A redistributed route that carries its own tag overrides that per-source tag: the tag on the route is the more specific value, and it becomes the External Route Tag of the Type 5 (or Type 7) LSA. A route with no tag of its own takes the per-source value. RFC 2328 Appendix A.4.5 defines the field and states that OSPF itself does not read it, so the precedence is Ze's choice rather than the RFC's.

The redistribute block is the whole configuration. It needs no plugin block and no attach process block. import bgp names the daemon's own Loc-RIB, and each peer's delivery to it is derived from the rule. See Route Filters and Redistribution, "Route Redistribution".

Received external LSAs are resolved by the external SPF stage, which runs after the intra-area and inter-area route tables are built. Each external is resolved against its ASBR (or a non-zero forwarding address, re-resolved through the route table; unreachable externals are skipped). Type 1 (E1) cost is the distance to the forwarding target plus the advertised metric; type 2 (E2) cost is the advertised metric only, tie-broken by the forwarding distance. A type 1 external always wins over a type 2 regardless of cost, and any external ranks below an intra-area or inter-area route for the same prefix. The winning path installs as one locrib.Path with admin distance 110.

default-information originate advertises a default route: 0.0.0.0/0 as an OSPFv2 Type 5 AS-External-LSA, and ::/0 as an OSPFv3 AS-External-LSA, whose LS Type is 0x4005 rather than 0x0005 (RFC 5340 Section 4.4.3.6). The two numbers are not interchangeable and the wrong one fails quietly: RFC 5340 Appendix A.4.2.1 reads 0x0005 as U=0 with S2S1=00, which is link-local scope, so a v2-numbered default floods to the directly attached routers and no further. With always it originates unconditionally; otherwise it originates only while a non-OSPF default route exists in the Loc-RIB (OSPF's own default does not satisfy the condition). The engine re-evaluates the condition at config-apply and live on Loc-RIB default-route changes, withdrawing the LSA when the condition lapses.

Stub and NSSA areas

A stub area (area-type stub) carries no AS-External information: the E-bit is clear in every Hello and Router-LSA originated within it (a Hello whose E-bit does not match is dropped and no adjacency forms), Type 4 ASBR-Summary and Type 5 AS-External LSAs are neither accepted from nor flooded into the area, and each attached ABR injects a single Type 3 default (0.0.0.0/0; OSPFv3 injects the equivalent ::/0 as an Inter-Area-Prefix-LSA) at the configured default-cost. A totally-stubby area (no-summary true) additionally suppresses every inter-area Type 3 summary except that default, so a spoke holds only intra-area routes plus one default.

An NSSA (area-type nssa, RFC 3101) is stub-like but permits local external redistribution. The N-bit must match between neighbors. The E-bit stays clear. An NSSA ASBR originates Type 7 LSAs for redistributed routes.

Per-source redistribution controls the Type-7 P-bit with nssa-propagate true; without it the P-bit is clear (RFC 3101 Appendix D). A router also originating a Type-5 copy clears the Type-7 P-bit. A requested P-set advertisement requires a usable forwarding address; losing that address withdraws it. Interface down/up and maintenance reconciliation replay retained import intent with live interface state, preserving the prefix identity, metric and tag. Withdrawn imports are not replayed.

Every NSSA border router originates a default into each directly attached NSSA, in both address families and with no operator leaf to enable it (RFC 3101 §2.4). A regular NSSA gets a P-clear Type 7 default: an OSPFv2 Type 7 LSA, or the OSPFv3 NSSA-LSA (0x2007) that carries the P-bit in its prefix options rather than in the LSA header. A no-summary NSSA gets the default through the summary path instead, as an OSPFv2 Type 3 summary-LSA or the OSPFv3 ::/0 Inter-Area-Prefix-LSA, and gets no Type 7 default at all (RFC 3101 §2.7). In both address families the border router rejects received Type 7 defaults when the P-bit is clear or summary import is disabled; a router that is not an NSSA border router installs them.

An internal NSSA router can originate a P-set default with nssa { default-originate true } when it has a usable forwarding address. The leaf is an internal-router setting: a border router originates its default anyway, so the leaf changes nothing there, and it is inert in a no-summary NSSA because RFC 3101 §1.3 makes an internal router's Type 7 default and the no-summary option mutually exclusive.

Among the ABRs attached to an NSSA, the eligible translator-candidate with the highest Router ID is elected (RFC 3101 §3.5). The role is configurable per area (nssa { translate-role candidate|always|never }), with a stability-interval for handover. A never ABR does not stand for election. The translator re-originates a P-set, nonzero-forwarding-address Type 7 as a Type 5, preserving the forwarding address, metric and tag. External route selection compares E1/E2 type and cost first. Only equivalent advertisements sharing a nonzero forwarding address use the §2.5(6) tie-break: P-set Type 7, then Type 5, then the higher advertising Router ID.

Type-7 routes require the advertising ASBR to be reachable inside the originating NSSA, even when a forwarding address is present. Their nonzero forwarding address must resolve through an intra-area route in that NSSA. Type-5 ASBR and forwarding-address resolution use normal areas, not paths through an NSSA. SPF retains per-area candidates until these checks complete.

Selected role (2026-09-21): ordinary OSPF, including CE operation. Ze does not select RFC 4577's native VPN-PE role, VRF/domain VPN import/export or optional sham links. Section 1 says: "No special procedures are needed in the CE router though; CE routers just run whatever OSPF implementations they may have." Ordinary routing, authentication and backbone attachment remain required. When deploying Ze as a CE with a third-party PE, configure the backbone attachment required by RFC 4577 §4.1.4, directly or by virtual link. The plain-prefix redistribution metric/tag settings do not perform VPN-domain comparison or generate VPN-IPv4 metadata.

A virtual link (RFC 2328 §15 for IPv4, RFC 5340 §4.2 for IPv6) is a logical unnumbered point-to-point link that belongs to the backbone but runs through a non-backbone, non-stub (for IPv6 also non-NSSA) transit area between two area-border routers. It repairs a partitioned backbone or attaches a remote area to Area 0 when a router has no physical backbone interface. Configure it under the transit area on both endpoints, keyed by the far endpoint's Router ID:

ospf {
    router-id 10.0.0.1
    areas {
        area 0.0.0.1 {
            virtual-link 10.0.0.2 {
                hello-interval 10
                dead-interval 40
                retransmit-interval 5
                transmit-delay 1
            }
        }
    }
}

The transit area must be declared and must not be the backbone 0.0.0.0, a stub, or (for IPv6) an NSSA; the router must be an area-border router; and remote-router-id must not be this router's own Router ID. These are rejected at config validation. The virtual link's cost is never configured: it is the transit area's intra-area SPF path cost to the neighbour, recomputed every SPF run. When the neighbour is reachable in the transit area and the adjacency reaches Full, the endpoint originates the virtual link into its backbone Router-LSA (an OSPFv2 Type-4 link record whose Link ID is the neighbour and Link Data is the local transit address; an OSPFv3 RouterLinkTypeVirtual record) and sets the Router-LSA V-bit in the transit area's Router-LSA (RFC 2328 §A.4.2 / §16.3 TransitCapability), which drives the far ABR's transit-area Summary-LSA pass. Virtual-link packets are routed across the transit area (IPv4 TTL > 1; IPv6 global source with hop limit > 1), not link-local. Inspect them with show ospf virtual-links. The IPv6 (OSPFv3) family carries its own list under address-family ipv6.

An OSPFv3 virtual link requires global endpoint addresses. Each endpoint advertises one local /128 with the LA bit in the transit area's Intra-Area-Prefix-LSA. Ze waits until both address advertisements are present and withdraws the virtual interface if either disappears. A subnet-only advertisement cannot supply a host address. Address changes are reconsidered after every SPF run; unchanged addresses and transit costs preserve the adjacency.

Removing a virtual-link configuration stops its synthetic interface and removes its neighbor state. Changing the Hello or dead timer recreates that interface; a transit cost change updates the existing interface without restarting it.

OSPFv3 Link-LSAs (0x0008) are stored in a link-scoped LSDB keyed by the receiving interface, not in the area or AS-wide stores. Ze originates one self Link-LSA per active OSPFv3 interface, carrying the interface link-local address and routable IPv6 prefixes, floods it only on that link, and releases the link store when the interface is removed. During database exchange, link-scoped LSAs participate in DD summaries and LS Request lookup using the interface context.

Opaque LSAs (RFC 5250)

Ze implements the OSPFv2 opaque-LSA carrier framework (RFC 5250, which obsoletes RFC 2370) for Traffic Engineering, Router Information, Segment Routing and Grace-LSAs. The carrier provides scope-correct storage and flooding, the Opaque Type / Opaque ID split, O-bit capability negotiation and a consumer registry. Unknown application bodies remain uninterpreted; understood applications can reject malformed LSAs before storage, acknowledgement or flooding.

The three opaque scopes map onto three stores: a Type 9 link-local opaque LSA lives in the per-interface link store and floods only on its arrival link; a Type 10 area opaque LSA lives in the per-area store; a Type 11 AS-wide opaque LSA lives in a dedicated AS-wide opaque store, parallel to the Type 5 AS-External store, and floods AS-wide but never into a stub or NSSA area. The 32-bit Link State ID splits into an 8-bit Opaque Type (an IANA registry selector) and a 24-bit Opaque ID (an application instance identifier). An opaque LSA never becomes an SPF vertex.

Opaque capability is negotiated with the O-bit in the OSPF Options field. Enable it with ospf { opaque true }: Ze sets the O-bit in Database Description packets and floods opaque LSAs only to neighbours that also set it in their DD. The O-bit is a DD-only signal, not part of the Hello E/N option match. Received opaque LSAs are stored and re-flooded per scope regardless of the opaque leaf, subject to registered application validation. Disabling the leaf stops advertising opaque capability and originating opaque LSAs. A virtual neighbour's DD omits both Type-5 and Type-11 headers. A received Type-11 opaque LSA is usable only while its originating router is reachable (RFC 5250 §5). Opaque activity is counted by ze_ospf_opaque_lsas{scope,opaque_type}, ze_ospf_opaque_originations_total{opaque_type}, ze_ospf_opaque_received_total{opaque_type,registered}, ze_ospf_opaque_consumer_errors_total{opaque_type}, and ze_ospf_opaque_capable_neighbors{interface}.

Traffic Engineering (RFC 3630, RFC 5392)

Ze implements the OSPFv2 Traffic Engineering LSA as a consumer of the opaque carrier: it registers Opaque type 1 (RFC 3630 intra-area TE) and Opaque type 6 (RFC 5392 Inter-AS-TE-v2) and codes the TE LSA body (the Router-Address TLV, the Link TLV, and the link sub-TLVs). It requires opaque true. TE is not a routing change: the carrier floods the LSAs by scope, and the parsed topology feeds a passive Traffic Engineering Database (TED) that never triggers SPF (RFC 3630 §1).

Enable TE per interface with a traffic-engineering block plus a stable TE router-address:

ospf {
    router-id 10.0.0.1
    opaque true
    router-address 10.0.0.1
    interfaces {
        interface eth0 {
            area 0.0.0.0
            network-type point-to-point
            cost 10
            traffic-engineering {
                enable true
                te-metric 100
                max-bandwidth 1250000000            # bytes/second
                max-reservable-bandwidth 1000000000 # bytes/second
                admin-group 5                        # 32-bit mask, LSB = group 0
            }
        }
    }
}

Each enabled interface with a usable Link ID (a Full neighbour on point-to-point, a known DR on multi-access) originates one Link TE LSA at a distinct Instance; the Router-Address TLV is advertised once per router at Instance 0. The TE metric is independent of the OSPF cost and defaults to it when unset (RFC 3630 §2.5.5). Bandwidths are carried on the wire as 32-bit IEEE-754 bytes/second and stored as float64 in the TED. A link that goes down or is de-configured is MaxAge-withdrawn, and its TED entry is removed.

An inter-AS TE link (RFC 5392) is a proxied advertisement with no OSPF adjacency on the link. Configure it with an inter-as sub-block; it originates an Opaque-type-6 LSA carrying the Remote AS Number (required) and at least one Remote ASBR ID (IPv4 sub-TLV 22 or IPv6 sub-TLV 24), omitting the Link ID sub-TLV (RFC 5392 §3.2.1). The scope selects the flooding scope per RFC 5392 §3.1.1: area (Type 10) or as (Type 11, AS-wide). A Type-11 inter-AS entry is held usable only while its originator is reachable (RFC 5250 §5).

traffic-engineering {
    enable true
    inter-as {
        remote-as 65001
        remote-asbr-ipv4 203.0.113.9
        scope as        # Type 11 (AS-wide); "area" for Type 10
    }
}

An interface carrying an inter-as block is passive whatever its passive leaf says: Ze sends no Hello and forms no adjacency over an inter-AS link (RFC 5392 §4). The interface stays in area state, so its TE advertisement is originated.

show ospf te-database renders the TED: router addresses and links with their Link ID, local/remote address, link type, TE metric, bandwidths, admin group, and (for inter-AS links) remote AS and ASBR. show ospf database opaque-area and opaque-as decode any TE LSA body inline. TE activity is counted by ze_ospf_te_lsas{scope,kind}, ze_ospf_te_database_links{area}, ze_ospf_te_originations_total{kind}, ze_ospf_te_received_total{kind,usable}, ze_ospf_te_parse_errors_total{opaque_type}, and ze_ospf_te_unreachable_originators. The read-only TED snapshot is available for a future RSVP-TE admission consumer; CSPF and RSVP-TE signalling are out of scope.

Router Information (RFC 7770)

Ze advertises this router's optional capabilities in the Router Information (RI) LSA (RFC 7770, which obsoletes RFC 4970). The RI LSA carries two things this router controls: the Router Informational Capabilities TLV (type 1) and a consumer-neutral hook so a later extension (Segment Routing) can inject its own TLVs into the same LSA. RI is informational only: it never feeds SPF or the route table, so a received RI LSA changes no routing.

The two address families carry the RI LSA differently but share one body. For OSPFv2 the RI LSA is an Opaque LSA with Opaque type 4 (RI rides the opaque carrier above and requires opaque true); the 24-bit Opaque ID is the RI LSA Instance ID (Instance 0 is LS ID 4.0.0.0). For OSPFv3 the RI LSA is a native LSA with function code 12 and the U-bit set (0x800C link, 0xA00C area, 0xC00C AS), so a router that does not understand it still floods it (RFC 5340). Both carriages build the same TLV stream, so the advertised bytes are identical across the two families.

Enable it with a router-information container. It applies to both address families (a top-level container drives the OSPFv3 family unless that family sets its own):

ospf {
    opaque true
    router-information {
        enabled true
        scope area
        scope as
    }
}

The scope leaf-list selects the flooding scope(s) per RFC 7770 §2.7 (link / area / as); when enabled with no scope listed it defaults to area + AS, matching the common Segment-Routing deployment. When AS scope is chosen and an NSSA is attached, Ze also advertises an area-scoped RI LSA into that NSSA (RFC 7770 §2.7 SHOULD).

The Router Informational Capabilities TLV is always the first TLV in Instance 0 (RFC 7770 §2.4 MUST) and reflects live engine state, so it cannot advertise a capability the router lacks: bit 0/1 (graceful restart capable / helper) track the GR configuration, bit 2 (stub router) is set when max-metric is configured (RFC 6987), and bit 3 (traffic engineering) is set when TE is configured (RFC 3630). An empty Functional Capabilities TLV (type 2) is carried as a placeholder (the RFC functional registry is initially empty). A downstream consumer registers additional TLVs; the originator emits them after the type-1 TLV in ascending TLV-type order, and if they overflow one LSA they spill into subsequent Instance IDs (a receiver uses the smallest Instance ID for an unspecified-multi-instance TLV, RFC 7770 §3). A registered builder that panics is isolated and counted, and the RI LSA is still emitted.

show ospf database router-information decodes the stored RI LSAs for both address families into the named capability bits and the TLV list. RI activity is counted by ze_ospf_ri_lsas{af,scope}, ze_ospf_ri_originations_total{af,scope}, ze_ospf_ri_received_total{af}, ze_ospf_ri_tlv_builder_errors_total, and ze_ospf_ri_capability_bits{bit}.

Ze carries OSPFv2 prefix and link attributes in the Extended Prefix Opaque LSA (Opaque type 7) and Extended Link Opaque LSA (Opaque type 8), as defined by RFC 7684. Their TLV containers hold the prefix or link identity and application sub-TLVs, including Segment Routing attributes when configured. These LSAs do not themselves install IP routes or enter SPF. Origination requires opaque true.

Origination is off by default and gated per type: ospf { extended-prefix true } originates Extended Prefix Opaque LSAs, ospf { extended-link true } originates Extended Link Opaque LSAs. Reception (decode, store, show) is always on once the plugin is built, regardless of these leaves. The advertised set is derived from the router's authoritative base LSAs, so the Route Type and Link identity correlate with what a peer already sees: each Extended Prefix TLV's Route Type comes from the originating LSA (intra-area from a Router-LSA stub link, inter-area from a self Type-3 summary, AS-external from a self Type-5), and each Extended Link TLV's Link Type / Link ID / Link Data are copied from the matching Router-LSA point-to-point or transit link (RFC 2328 §A.4.2). An Extended Prefix Opaque LSA is flooded at the scope its prefixes require: area (Type 10) for intra/inter-area prefixes, AS (Type 11) for AS-external prefixes; an Extended Link Opaque LSA is always area-scoped (RFC 7684 §3) and carries exactly one Extended Link TLV (§3.1 SHALL: one link per LSA, for fine-grained change handling). An ABR sets the A-Flag (0x80) on an inter-area prefix locally connected in another area, and preserves the N-Flag (0x40) when a host prefix is propagated between areas (§2.1). A prefix or link that disappears is MaxAge-withdrawn.

On receipt, Ze validates top-level TLVs and nested sub-TLV framing before any LSDB processing. An overrun or trailing fragment shorter than a TLV header makes that LSA malformed: Ze counts and discards it without storage, acknowledgement or reflooding, including self-originated and MaxAge input and when origination is disabled (§5). Valid companion LSAs in the same update are still processed. Unknown opaque applications remain uninterpreted. For valid extended LSAs, the N-Flag is ignored on a non-host prefix (§2.1). When the same prefix appears in multiple Extended Prefix LSAs from one router, the lowest Opaque ID wins (§2); duplicate prefixes within one LSA use the first instance. Applications register sub-TLV codecs through registerPrefixSubTLV / registerLinkSubTLV; unknown sub-TLVs are skipped by Length, and a registered codec panic is recovered and counted.

show ospf database opaque-area / opaque-as decode the stored Extended Prefix/Link bodies inline (Route Type, prefix, A/N flags, Link Type/ID/Data, and each sub-TLV as type/length/hex or a registered codec's string), not as raw opaque hex. Extended Prefix/Link activity is counted by ze_ospf_ext_prefix_lsas{scope}, ze_ospf_ext_link_lsas, ze_ospf_ext_originations_total{opaque_type}, ze_ospf_ext_malformed_total{opaque_type}, and ze_ospf_ext_subtlv_errors_total{registry}.

The OSPF engines publish native LSDB snapshots to the BGP-LS exporter. The snapshots include router and network-pseudonode identities, links and prefixes, with decoded TE and Segment Routing attributes where those LSAs provide them. OSPFv2 and OSPFv3 keep their protocol, instance and area identities separate. The exporter consumes completed SPF reachability, including routers that advertise no selected IP prefix, and receives an updated snapshot after an LSDB change or SPF run. Removing an area or stopping an engine withdraws its previous snapshot.

The BGP-LS architecture describes export configuration and how those snapshots become BGP Link-State NLRIs.

Segment Routing (RFC 8665, RFC 8666)

Ze implements OSPF Segment Routing over the MPLS data plane as one feature spanning both address families, exactly as OSPF itself is one engine across IPv4 and IPv6. The control plane is shared and written once: the SRGB (Segment Routing Global Block) and SRLB (Segment Routing Local Block) label ranges, the multi-range index-to-label arithmetic (label = SRGB-base + index, resolved across the originator's ranges in advertised order), the NP/E/M push/swap/PHP truth table, and the install through the existing mpls-fib bus. Only the wire carriage differs by address family: the IPv4 family (OSPFv2, RFC 8665) rides SR TLVs in the Router Information (RFC 7770) and Extended Prefix/Link (RFC 7684) opaque LSAs, while the IPv6 family (OSPFv3, RFC 8666) rides them in the OSPFv3 RI Opaque LSA and the RFC 8362 Extended LSAs. The RFC 8666 sub-TLV type codes (Prefix-SID 4, Adj-SID 5, LAN-Adj-SID 6, SID/Label 7, Extended Prefix Range 9) are the OSPFv3 Extended-LSA registry values and are deliberately different from the OSPFv2 RFC 8665 numbers.

SR is off by default. Enable it per address family:

ospf {
    router-id 10.0.0.1;
    opaque true;
    segment-routing {
        enable true;
        srgb { lower-bound 16000; upper-bound 23999; }
        srlb { lower-bound 40000; upper-bound 40999; }
        prefix-sid 10.0.0.1/32 { index 1; node-sid true; }
    }
    address-family {
        ipv6 {
            segment-routing {
                enable true;
                srgb { lower-bound 16000; upper-bound 23999; }
            }
        }
    }
}

When enabled, the RI LSA advertises SR-Algorithm 0 (SPF) plus the SRGB, SRLB, and optional SR Mapping-Server preference TLVs (area-scoped); the Extended Prefix LSA carries a Prefix-SID sub-TLV for each configured node prefix; and the Extended Link LSA carries an Adjacency-SID (and a LAN-Adjacency-SID on broadcast/NBMA) allocated from the SRLB for each Full adjacency. The same registered RI capability builders emit into both the OSPFv2 and the OSPFv3 RI LSA, so the SR-Algorithm/SRGB/SRLB advertisement is shared across the two families (RFC 8666 §4). On reception Ze records each remote router's SR-Algorithm and ordered SRGB, computes the outgoing MPLS label for a reachable Prefix-SID, and applies the next-hop router's NP/E/M flags: NP=0 means penultimate-hop popping (forward as plain IP), NP=1/E=0 keeps the label, NP=1/E=1 uses the Explicit NULL label (0 for IPv4, 2 for IPv6), and the M-Flag makes NP and E ignored. A Prefix-SID for an algorithm the originator did not advertise, or a duplicate Prefix-SID for the same prefix/topology/algorithm, is recorded but not installed. When an adjacency leaves Full, its Adjacency-SID advertisement and forwarding entry are withdrawn before the SRLB label is freed for reuse. Only V=0/L=0 (4-octet index) and V=1/L=1 (3-octet local label) are valid SID encodings; any other combination causes the SID advertisement to be ignored. An SR TLV or sub-TLV whose length is invalid makes the whole LSA that carries it malformed (RFC 8665 §9, RFC 8666 §10), and the LSA is counted and ignored: a Router Information LSA applies none of its SR capabilities, an Extended Prefix LSA (or OSPFv3 Extended-LSA) installs none of its Prefix-SIDs and contributes none of its prefixes to the Extended Prefix attributes Ze resolves (a previous instance's prefixes are withdrawn), an Extended Link LSA lends none of its Adjacency-SIDs to a TI-LFA repair stack, and BGP-LS exports none of it. A Prefix-SID or Adjacency-SID length is exact and follows the V-Flag: 7 octets when it is set, 8 when it is clear (11 and 12 for a LAN Adjacency-SID). The length is judged before the V/L-Flags, so a sub-TLV whose length and flags are both invalid condemns the LSA, while one whose flags alone are invalid is ignored by itself. Prefix-SIDs are judged per prefix, topology (MT-ID) and algorithm. Within that scope, a router that advertises more than one Prefix-SID in an area has all of them ignored, even when they bind the same SID (RFC 8665 §5, RFC 8666 §6); a router advertising one Prefix-SID for Algorithm 0 and another for a Flexible Algorithm or another topology keeps its Algorithm 0 SID. Across routers the RFCs say nothing, and Ze applies its own policy: different routers binding the same SID to a prefix (an anycast prefix, or ABRs propagating one Prefix-SID) are one binding and it is used, while different routers binding different SIDs leave no binding to prefer, so all of them are ignored. A range TLV of valid length whose content is rejected, such as a range starting at a reserved label, is ignored alone and counted, and the rest of the LSA still applies.

MPLS forwarding is programmed through the shared mpls-fib bus with two SR source tags (OSPF-SR for IPv4, OSPFv3-SR for IPv6), the third producer alongside RSVP-TE and LDP; fib-kernel remains the single owner of kernel forwarding state. Prefix-SIDs install a push (ingress) or swap (transit) entry toward the SPF next-hop; Adjacency-SIDs install a pop/forward entry keyed by the local SRLB label. SR reads the shared SPF route table (read-only) through a post-run hook that fires after the IP-route Installer has applied, so an SR label push always rides an already-installed IP route. SR LSAs are never SPF vertices and never change the route table directly.

show ospf segment-routing and show ospf ipv6 segment-routing render the configured SRGB/SRLB, the advertised algorithm, this node's Prefix-SIDs, and the allocated Adjacency-SIDs. The ze doctor check doctor-ospf-segment-routing-overlap reports an SRGB/SRLB overlap or other unsound range before runtime install. SR activity is exposed by the ze_ospf_sr_* metric series, all carrying an af label: ze_ospf_sr_enabled, ze_ospf_sr_prefix_sids{direction}, ze_ospf_sr_adj_sids, ze_ospf_sr_labels_installed{op}, ze_ospf_sr_label_compute_errors_total{reason}, ze_ospf_sr_srlb_labels_in_use, and ze_ospf_sr_malformed_tlvs_total{tlv}.

Fast reroute: LFA and TI-LFA (RFC 5286)

Fast reroute pre-computes, alongside each primary next-hop, a loop-free backup next-hop so a single local link or node failure is repaired locally the instant it is detected, before the IGP reconverges. The backup is programmed into the FIB next to the primary; on primary-down the forwarding plane swings to the backup with no control-plane recompute. It is a compute-and-install feature: it changes no OSPF packet on the wire (the only wire signal is the Adj-SID Backup flag advertised by Segment Routing), lives entirely in the SPF / Loc-RIB / FIB seams, and is off by default.

Two tiers run. Base LFA (RFC 5286) runs one extra shortest-path tree rooted at each directly-connected neighbour to get D_opt(N,*), then selects, per primary next-hop, the neighbour that best satisfies the loop-free inequality D_opt(N,D) < D_opt(N,S) + D_opt(S,D) (strict), preferring node-protecting over link-protecting alternates (§3.6), honouring the §3.5 cost/reverse-cost gate and the §3.3 broadcast pseudo-node rule, and classifying downstream alternates against D_opt(S,D) (Errata 2323). TI-LFA (mode ti-lfa) adds a fallback where no directly-connected LFA exists: it computes the post-convergence SPF (the protected resource removed from a graph clone), derives the P-space/Q-space, and builds a Segment Routing repair label stack (a P-node Prefix-SID resolved through the P-node's SRGB, plus an Adj-SID across the protected resource where needed), giving 100% single-failure coverage on the post-convergence path. TI-LFA repair labels require Segment Routing (RFC 8665) and are IPv4-only; OSPFv3 gets base-LFA next-hop selection through the same address-family seam. In OSPF multi-area topologies where the local-area SPF cannot see the real path (an inter-area prefix reachable via more than one ABR, an external prefix via more than one ASBR, or the backbone with virtual links), the LFA is suppressed (the prefix is left unprotected) rather than installed wrong (RFC 5286 §6.3).

ospf {
    fast-reroute {
        enable true
        mode ti-lfa          # lfa (default) or ti-lfa
        node-protection true # prefer node-protecting alternates (RFC 5286 §3.6)
    }
}

show ospf route fast-reroute lists each prefix's primary next-hops with their backup next-hop, protection class (node/link/downstream), and TI-LFA repair label stack; unprotected primaries are shown as unprotected. Fast-reroute activity is exposed by the ze_ospf_fast_reroute_* metric series: ze_ospf_fast_reroute_protected_prefixes{area,class}, ze_ospf_fast_reroute_unprotected_prefixes{area,reason}, ze_ospf_fast_reroute_backups_installed{kind}, ze_ospf_fast_reroute_compute_seconds{area}, and ze_ospf_fast_reroute_ti_lfa_repair_labels{area}. On Linux the backup is programmed as a link-down/backup multipath next-hop carrying the repair MPLS encap, so the kernel forwards to it only when the primary link is down.

Graceful Restart (RFC 3623, RFC 5187)

Graceful Restart lets Ze restart or reload its OSPF control software while staying on the forwarding path ("non-stop forwarding"). It is one feature with one control plane spanning both address families: the restarter and helper state machines, the FIB-retention coupling, the non-volatile restart fact, and the config / CLI / metrics surface are shared, because the OSPF engine's FSM, flooding, DR election, SPF, and LSDB sequencing are address-family-neutral. Only the Grace-LSA wire object differs: the IPv4 family (OSPFv2, RFC 3623) carries it as a link-local Type-9 Opaque LSA (Opaque Type 3, Opaque ID 0, three TLVs) through the opaque carrier, and the IPv6 family (OSPFv3, RFC 5187) carries it as a native link-scope LSA (LS Type 0x000B, function code 11, two TLVs, Link State ID = the originating interface's Interface ID). Both encode the grace period as the Grace-LSA's LS age against the Grace Period TLV: LS age starts at 0, is never reset on retransmit, and DoNotAge is never set, so the grace clock cannot be extended indefinitely.

It is configured under a single family-neutral graceful-restart container (a top-level container drives both families; the OSPFv3 family inherits it unless it sets its own). The restarter sub-container sets support (disabled default, planned, or planned-and-unplanned) and restart-interval (1..1800 s, default 120); the helper sub-container sets support (default true) and strict-lsa-checking (default true). With GR disabled (the default) Ze originates no Grace-LSA, never enters helper mode, and restarts exactly as a router without the feature.

As the restarting router (RFC 3623 §2), Ze ensures the FIB is current, originates one Grace-LSA per interface, persists the restart fact (grace deadline, reason, and the OSPFv3 §3.1 LSA-ID -> prefix and §3.2 Interface-ID preservation maps) to the ZeFS non-volatile store, and stops without withdrawing routes. On resume it re-reads the fact; if the grace window is still open it enters in-restart mode: self-LSA origination is suppressed at the shared originateSelfLSAs chokepoint (both families), route install is suppressed at the SPF Installer (SPF still computes, e.g. for virtual-link restore, but neither installs nor withdraws), and the pre-restart RTPROT_ZE kernel routes are retained by the fib-kernel stale-mark-then-sweep. Ze exits on the earliest of the three RFC 3623 §2.2 triggers (all pre-restart adjacencies re-reach Full, an inconsistent LSA is received, or the grace period expires), then re-originates its self-LSAs (OSPFv3 with the preserved Interface IDs / LSA-IDs), re-installs routes (refreshing the retained kernel routes before the sweep deadline), flushes stale self-LSAs, and flushes its own Grace-LSAs. A stale restart fact whose grace window already closed is ignored, so an unrelated cold boot starts normally. The unplanned-outage path (RFC 3623 §5) is off by default and, when enabled, originates Grace-LSAs before any Hello with the reason restricted to unknown or switch-to-redundant-control-processor.

As a helper (RFC 3623 §3), on receiving a Grace-LSA that passes every §3.1 check (a Full adjacency with the restarting router X, the LSDB unchanged since X restarted, the grace period not expired, the helper policy permits, and this router not itself restarting) Ze enters helper mode: it keeps advertising the adjacency to X in its Router-LSA (and Network-LSA if DR) regardless of the neighbour's synchronisation state, and keeps X as DR if X was DR. It exits when the Grace-LSA is flushed, the grace period expires, or (with strict LSA checking) an installed LSA with changed content would have flooded to X, with the §3.2 stub-area exception (a changed AS-external LSA in a stub/NSSA area, which would never flood to X, does not terminate helping). A re-received Grace-LSA while already helping just updates the grace period.

show ospf graceful-restart and show ospf ipv6 graceful-restart report the per-family restarter state (in-restart or not, grace deadline, reason) and the active helper sessions (which neighbours are helped and their remaining grace). Graceful Restart activity is counted by ze_ospf_gr_restarter_active{family}, ze_ospf_gr_restarter_exits_total{family,reason}, ze_ospf_gr_helper_sessions{family,interface}, ze_ospf_gr_helper_exits_total{family,reason}, and ze_ospf_gr_grace_lsas{family,direction}. A ze doctor check (doctor-ospf-graceful-restart-nvs) warns when the restarter is enabled but the non-volatile restart-fact store is unwritable.

OSPF uses the daemon-owned store through state RPCs. It does not open a second store or require a separate ze.config.dir setting. Runtime initialization durably increments the boot count and reads restart state before packet processing. A failed state request stops initialization; there is no clock-derived fallback. Restart writes wait for the daemon's durable acknowledgement. Reads distinguish absent state from unavailable or corrupt state.

Multi-Instance (RFC 6549)

Several independent OSPFv2 instances can coexist on one physical interface / subnet. RFC 6549 carves an 8-bit Instance ID out of the OSPFv2 common header by splitting the former 16-bit Authentication Type field into an Instance ID (header offset 14) and an 8-bit AuType (offset 15); the header size is unchanged. Ze runs one full OSPFv2 engine per configured Instance ID (its own LSDB, SPF, and neighbour table), each demultiplexed by the Instance ID: a received packet whose Instance ID does not match the receiving engine's is discarded before any handler runs, so neighbours only form within a matching instance.

An interface joins one or more instances with the per-interface instance-id leaf-list; absent, it belongs to the base instance 0, which is bit-for-bit identical to base OSPFv2 (a Ze router with no multi-instance config interoperates unchanged with a legacy peer such as FRR). A non-zero Instance ID is transmitted in every packet's header and, on a legacy peer, is read as a mismatched 16-bit AuType and dropped at authentication -- the intended isolation. Listing two Instance IDs on one interface enrols it in two coexisting instances.

ospf {
	interfaces {
		interface eth0 {
			area 0.0.0.0
			instance-id 0
			instance-id 5
		}
	}
}

show ospf instance lists each configured instance with its Instance ID and the size of its isolated area/interface/neighbour/database state. The demux exposes ze_ospf_instances (gauge) and ze_ospf_instance_mismatch_drops_total{interface} (counter, packets dropped because their Instance ID matched no configured instance). The Instance ID has local subnet significance only: it lives only in the packet header, is never carried in an LSA, and is never compared across links. Ze does not compute per-topology routes (Multi-Topology Routing / RFC 4915 is out of scope), so the reserved Instance IDs 0/1/2 carry no special semantics.

Authentication

OSPFv2 packets can be authenticated per interface with a key chain (RFC 2328 Appendix D, RFC 5709, RFC 7474). Four schemes are supported, selected by the key algorithm and the chain's extended-sequence flag: AuType 0 (none), AuType 1 (simple 8-byte cleartext password), AuType 2 (md5 keyed-MD5 or HMAC-SHA-1/256/384/512), and AuType 3 (the same HMAC-SHA algorithms with a 64-bit extended cryptographic sequence number). Every outgoing packet is signed and every incoming packet is verified before it reaches the ISM/NSM/LSDB; a packet that fails is dropped before any protocol processing and increments ze_ospf_auth_failures_total{interface,reason}.

For cryptographic auth the OSPF common-header Checksum is zero, and the OSPF Packet Length covers only the header and body. The digest, preceded by a 64-bit sequence number for AuType 3, follows the body and counts only in the IP length. Digest and password comparisons are constant-time. Receive sequence checks are per neighbour, key-id and packet type. AuType 3 binds the IP source address into Apad (RFC 7474 §5) and uses a boot-count high word durably reserved in the daemon-owned state store before packet processing. Low-word wrap reserves another durable boot count before transmitting the wrapped sequence. If that reservation fails or cannot advance, packets are suppressed until it succeeds; no clock-derived or memory-only fallback is used.

After replacing the router or losing the non-volatile boot-count state, replace the OSPF authentication keys before resuming adjacencies (RFC 7474 §8). Stop affected OSPF instances, provision new shared secrets on this router and its peers, and retire the old keys from their accept windows before resuming. Restarting with an empty store cannot distinguish a new installation from lost state and does not make an old key safe to reuse.

Keys are organised as named chains for hitless rotation: a chain holds multiple keys, the key whose send-lifetime covers the current time signs, and every key whose accept-lifetime covers the current time is accepted on receive. RFC 7474 section 4 requires that window to include the current time for a key used on reception, so a packet made with a key outside its window is dropped and counted as accept-lifetime in ze_ospf_auth_failures_total. Configure the two windows to overlap and a key rolls over without dropping the adjacency; close an accept-lifetime and the key is retired without editing the chain. A key with no accept-lifetime is accepted at every time. A chain is bound per interface, or an interface set to authentication { mode inherit } uses the area-level default chain. Secrets are stored $9$-encoded and never appear in plaintext in show configuration or backups.

OSPFv3 IPsec (RFC 4552)

OSPFv3 has no authentication field of its own, so an IPv6-family interface is protected by a manually keyed IPsec Security Association instead. The ipsec block installs one transport-mode kernel SA and three protocol-89 XFRM policies (out, in and forward) scoped to that interface. AH gives integrity only; ESP gives integrity, and confidentiality when an encryption-algorithm and encryption-key are set. This path is Linux-only, and it is mutually exclusive with a RFC 7166 authentication key chain on the same interface.

ospf {
    address-family {
        ipv6 {
            interfaces {
                interface eth1 {
                    area 0.0.0.0
                    ipsec {
                        protocol ah
                        spi 256
                        algorithm sha256
                        key <64 hex characters>
                        replay-window 64
                    }
                }
            }
        }
    }
}

Each interface needs its own SPI: the kernel identifies the shared wildcard state by destination, SPI and protocol, so two interfaces sharing an SPI collide. spi is 256 or above, because RFC 4303 §2.1 reserves 0 to 255. The key length must match algorithm (sha1 40, sha256 64, sha384 96, sha512 128 hex characters).

replay-window is the anti-replay window in packets, for AH and ESP alike. RFC 4302 §3.4.3 and RFC 4303 §3.4.3 require every implementation to support the anti-replay service and leave its use to the receiver, per SA. Ze accepts 0, which disables it, or 32 to 255; 32 is the minimum window those sections make mandatory to support, and 64 is the size they prefer. The kernel performs the per-packet check.

The default is 0, and leaving it there is the RFC's own advice for this path. RFC 4302 §5: "If the key used to compute an ICV is manually distributed, correct provision of the anti-replay service would require correct maintenance of the counter state at the sender, until the key is replaced ... Thus, a compliant implementation SHOULD NOT provide this service in conjunction with SAs that are manually keyed." Nothing rekeys these SAs, and the sequence counter lives in kernel state that a restart clears. So a peer that restarts begins again at sequence 1, and a receiver holding a window from before the restart drops those packets until its own SA is reinstalled. Enable the window only where a peer restart is an event you handle, because recovery needs the receiving interface to bounce.

Operational Commands

Operational state is available through the show ospf command tree:

show ospf
show ospf instance
show ospf neighbor
show ospf interface
show ospf database [router|network|summary|asbr-summary|external|nssa-external]
show ospf database [opaque-link|opaque-area|opaque-as]
show ospf database router-information
show ospf te-database
show ospf route
show ospf spf
show ospf border-routers

show ospf database opaque-link|opaque-area|opaque-as filter the LSDB to the RFC 5250 opaque LSAs of Type 9 (link), Type 10 (area), and Type 11 (AS) respectively. show ospf database router-information decodes the RFC 7770 Router Information LSAs of both address families (see Router Information above). show ospf te-database renders the RFC 3630 / RFC 5392 Traffic Engineering Database (see Traffic Engineering above).

show ospf is the process summary (router-id, ABR/ASBR status, areas, and the active stub-router / max-metric state). show ospf database lists every LSA; the per-type subviews filter to one LS Type (1/2/3/4/5/7). show ospf route reports area, prefix, metric, route type, origin router, and next-hop set. show ospf spf reports per-area last run, duration, node count, pending state, and current throttle delay. show ospf border-routers reports reachable ABRs and ASBRs with their area, metric, and next-hop set.

The runtime can be reset without reconfiguring via clear ospf process (tear down adjacencies and re-run SPF), clear ospf neighbor (re-form adjacencies), and clear ospf counters (reset the SPF-run log). The neighbor and database views are also available in the web UI at /ospf and /ospf/database, with live updates over SSE.

Replayable Ze terminal lab

Diagnose a missing OSPF route

Inspect the active OSPF configuration, query the running control plane with Ze's CLI, trace a Full neighbor through the LSDB, and confirm the expected route.

Reproducible
Ze 26.08.3150 secondsLinux namespace labTerminalZe recorder Plain-text transcript
Read the demonstration transcript
An operator is investigating why 10.255.0.3/32 is missing.

$ ze config show demos/terminal/ospf-adjacency/ze.conf ospf
The daemon configuration shows the OSPF process, area, interface, and router ID used by the recording.

$ ze cli -c 'show ospf neighbor detail'
The live FRR neighbor at 172.31.0.3 is Full.

$ ze cli -c 'show ospf database router'
The live link-state database contains FRR's Router-LSA.

$ ze cli -c 'show ospf route'
The FRR loopback 10.255.0.3/32 is an intra-area route through 172.31.0.3.

The recording uses `ze cli` directly. No output wrapper or synthetic summary sits between the operator and the running control plane.