Weekly update

Week of 2026-09-07

Five new capabilities: BFD strict mode, OSPF costs priced from link speed, VRRP groups that track an uplink, an IPv6 Router Advertisement sender, and certificate revocation checking on IKE logins. The biggest fix was in BGP. Ze was rewriting AS numbers out of routes it relayed.

← All weekly updates

🛰️ BGP

Fixed:

  • A route learned from a peer that speaks only 2-byte AS numbers was relayed with 23456 in place of the real AS numbers, and the real ones discarded. Downstream accepted the wrong path and passed it on. Ze now reconciles the two forms once, on receive, and the result is checked against FRR (RFC 6793 Section 4.1).
  • A router with a 4-byte AS number originating a route toward a 2-byte peer lost its own AS number. 23456 went on the wire and the real value was carried nowhere (RFC 6793 Section 4.2.2).
  • A peer that asked for a limited number of paths per prefix received all of them. The capability was negotiated and displayed, and enforced by nothing. capability add-path limit, and the per-family form beside it, now hold.

New:

  • bgp update-delay max-delay holds the first advertisement at startup until the routing table settles, with establish-wait beside it. Off by default.
  • An AS number can be typed in any of the three RFC 5396 notations anywhere in the configuration. bgp as-notation asplain|asdot|asdot+ picks which one Ze prints.
  • update bgp config writes the running peer set back to the configuration file, so a peer added at runtime survives a reload.

🔀 Convergence and forwarding

New:

  • BFD strict mode holds a BGP peer down until its BFD session is up, so a session never establishes across a link BFD cannot cross. The leaves are connection { bfd { strict; hold-down; hold-time; } }, and show bgp peer detail carries bfd-sub-state.
  • OSPF prices an interface that has no cost from its link speed. ospf { reference-bandwidth <Mbps>; } sets the divisor, default 100000, and it reaches the OSPFv3 address families too.
  • A VRRP group lowers the priority it advertises while a tracked interface is down: track { interface <name> { priority-decrement <1..254>; } }. accept-mode lets a non-owner answer on the virtual address (RFC 9568 Section 6.4.3).
  • rib { fib-withhold [ bgp isis ]; } names the protocols whose winning routes Ze keeps out of the forwarding table. The route still wins its prefix, still appears in show rib, and still redistributes.
  • Ze sends IPv6 Router Advertisements on an interface unit, the job radvd does elsewhere: prefixes, flags and resolvers under router-advertisement { ... } (RFC 4861).

Fixed:

  • OSPF and a BGP peer to the same neighbor over the same link opened two BFD sessions instead of sharing one (RFC 5882 Section 4.4).
  • Changing an interface cost, or the reference bandwidth, restarted the interface and dropped the adjacency.
  • A policy route naming more than one ingress interface matched no packets at all. It asked for a packet whose input interface was both names at once.

🔒 Security

New:

  • EAP-TLS checks the certificate chain against a revocation list. The crl leaf-list sits under the ca block, and certificate-status-request asks the authenticator to staple its own status, which today needs a chain with no intermediate. Revocation is checked again after the login, while the tunnel is up (RFC 9190 Section 5.4).
  • The EAP-TLS peer identifies itself as anonymous@realm, so the configured username stays off the wire in clear (RFC 9190 Section 2.1.8).
  • session-resumption gives a TLS 1.3 login a ticket the next exchange redeems. Revocation is re-checked on the resumed handshake.
  • show vpn ipsec dataplane sa, policy and drift read Ze's security associations back out of the kernel and report where the two disagree. show vpn ipsec sa now carries real kernel byte and packet counters.
  • max-sessions-per-mac bounds what one MAC address can make the PPPoE server allocate, default 8. A replayed PADR could exhaust the server's file descriptors and orphan sessions.

Fixed:

  • A local DDoS mitigation that never cleared left its drop rule installed for the life of the daemon, blackholing the victim prefix. max-mitigation-duration was parsed, validated and enforced by nothing. Four other ways a drop rule outlived what created it are closed with it.
  • A rate handed to Ze by RADIUS could overflow when it was scaled, so a subscriber was shaped at a rate nobody configured.
  • Two denial of service defects in a dependency were reachable from Ze. One of them is reachable through the daemon's own SSH server, where a peer could deadlock the channel an operator reaches the CLI through.

📚 Standards programme

Ze is being checked against every RFC it implements, one MUST at a time. The work continues.

Of 5,468 requirements, 3,596 are MUST-level and 3,319 are checked. 237 still owe a test, against 313 last week, and 59 of the 184 documents have been read end to end, against 57.

The owing number fell this week because the fixing caught up with the reading. One shape kept turning up: a setting an operator can type, that Ze accepts, validates and displays, and that reaches no code at all. The paths limit and the mitigation cap were both that, and so was the plugin respawn leaf, which no producer had ever read.

A green run proves everything on the list. It does not yet prove the list is complete, which is what reading each document end to end is for: https://ze-software.net/quality/rfc-compliance/

🔭 Coming up

44 pieces of work were finished and closed this week, and 81 were opened.

Reading continues, and the 125 documents not yet read end to end are the queue. Closing what the reading finds comes first, then the 237 MUSTs owing a test. SHOULD-level work waits behind all of it.