Repository navigation
RFC conformance
"Full BFD support" is not a single target: the BFD RFC family covers transports and deployments that are different protocols in practice. The honest deliverable is this matrix, with every deviation named. What this project aims to be complete for is everything bfdd can offload over the bffdp data-plane protocol, tested, with deviations justified. Today that is 5880 / 5881 / 5883 plus echo, demand mode and authentication with key rollover. Reviewed against the code on 2026-10-02.
| RFC | Title | Status | Notes |
|---|---|---|---|
| 5880 | BFD base | implemented, four deviations, four notes | Async mode, full state machine, poll/final, demand mode (with the s6.6 verification poll the RFC leaves optional), echo. Passive role, AdminDown and diag codes present. Authentication: simple password and keyed / meticulous keyed SHA1, with every key bfdd sends and its send and accept lifetimes, so a rollover happens in the data plane (s6.7.1); the replay window is forgotten after twice the detection time (s6.7). Deviation 1: keyed SHA1 computes an HMAC as bfdd does, not the RFC's plain SHA1 with the embedded key (FRR issue 23274). Deviation 2: keyed MD5 (types 2, 3) not implemented; bfdd maps no keychain to them. Deviation 4: a Final answers every Poll within a budget. Deviation 5: authenticated packets past a verify budget are dropped before the digest. Notes: fast-path replies go at the peer's pace; packets faster than the peer may send are dropped; echo is reflected within a budget per peer; demultiplexing on Your Discriminator alone is budgeted. See below. |
| 5881 | Single-hop IPv4/IPv6 | implemented, one deviation | GTSM on both planes, the control ports bound to the session type, one source port per session in 49152-65535 on both planes, demultiplexing on Your Discriminator once it is known, echo on 3785. Every control and echo packet is marked CS6, which no RFC requires and bfdd also does, so a switch that queues by DSCP keeps it out of a congested queue. Deviation 3: echo is sourced from the session's own address. See below. |
| 5882 | Generic application of BFD | n/a | Guidance for BFD clients; that is bfdd's side. |
| 5883 | Multihop | implemented | Port 4784, per-session minimum TTL, TTL restored on the reflected reply. |
| 5884 | BFD for MPLS LSPs | not implemented | Different encapsulation (MPLS echo bootstrap); not in bfdd's data plane. |
| 5885 | BFD for VCCV / pseudowires | not implemented | Same. |
| 7130 | micro-BFD on LAG members | not implemented | Would need per-member sessions on one address pair; the map key is address-only. Feasible later by keying on ifindex too. |
| 7419 | Common interval support | partial | The engine honours any interval; shipping the RFC's recommended set as presets is a configuration item, not an engine one. |
| 7880 / 7881 / 7882 | Seamless BFD (S-BFD) | not implemented | Reflector/initiator model with fixed discriminators. The XDP reflector is a natural fit for the S-BFD reflector role and is the first extension worth considering once bfdd's data plane can carry it. |
| 8562 / 8563 | Multipoint BFD | not implemented, rejected on the wire | The M bit is dropped (s6.8.6), multipoint being a different protocol. |
| 8971 | BFD for VXLAN | not implemented | Encapsulation. |
| 9127 | BFD YANG | n/a | Management; bfdd's. |
| 9468 | Unsolicited BFD | not implemented | Would need "accept a session from an unknown peer", which is exactly what the unknown-session drop removes by default. If wanted it is a per-interface allow rule, never a default. |
| 6428 | MPLS-TP CC/CV/RDI | not implemented | Different profile. |
| 9355 | OSPF BFD strict mode | n/a | A client behaviour (OSPF waits for BFD Up); ospfd's and bfdd's side. |
| 9521 | BFD for Geneve | not implemented | Encapsulation, as VXLAN. |
| 9747 | Unaffiliated BFD echo | not implemented | Echo with no control session behind it. The reflector answers echo only for a peer of an echo-active session (declined otherwise), which is what keeps it from being an open reflector; unaffiliated echo would need an explicit allow list, never a default. |
| 9764 | BFD encapsulated in large packets | not implemented | Padding a session's packets to probe path MTU. Padded packets are not tested here; the fast path's reply is the control packet's own length. |
RFC 5880 s6.7.4 specifies keyed SHA1 as SHA1(packet with the key bytes embedded in the auth section). bfdd instead computes an HMAC-SHA1 over the
packet with the auth digest zeroed. This engine matches bfdd, because
bfdd is the control plane it serves and interop with it is the requirement
that matters; a packet this engine accepts is exactly one bfdd would. The
divergence from the letter of the RFC is bfdd's, and is filed upstream as
FRR issue 23274.
Types 2 (keyed MD5) and 3 (meticulous keyed MD5) exist in the RFC and in bfdd's enum, but bfdd maps no keychain algorithm to them, so nothing can drive them over the data plane. They are not implemented here. Simple password (type 1) and keyed / meticulous keyed SHA1 (types 4, 5) are.
RFC 5880 s6.7 has every authenticated packet verified. The program
verifies at most one per half the session's Required Min RX, twice the rate
the peer may legally send, with a burst of two so a Final that closely
follows a regular packet still gets through. Past that, a packet is dropped
before the digest and counted verify-limited, not counted for liveness.
The budget reads only arrival time, never Poll or Final, which are not yet
authenticated, and a failed verify gives its token back. A real peer never
meets it: on the testbed, 2.76M authenticated packets in five minutes moved
it by zero. It exists because the failure budget counts only bad digests, so
without it a neighbour holding the key could make the program run a full
HMAC per packet (see Security model).
The XDP program answers each control packet as it arrives, so it transmits at the peer's pace rather than on a timer of its own. RFC 5880 s6.8.7 bounds our rate by max(our Desired Min TX, the peer's Required Min RX), so the engine arms the fast path only while the peer's pace, max(its Desired Min TX, our Required Min RX), is no faster than that bound. Otherwise userspace transmits on its own jittered timer. The replies carry the peer's jitter rather than ours.
RFC 5881 s4 says the source of an echo packet MUST NOT be on the subnet of the interface it leaves by, so the neighbour's forwarding does not answer with a redirect. The engine sends a self-addressed echo from the session's local address, as bfdd does. Echo is sent only while the peer advertises a nonzero Required Min Echo RX (RFC 5880 s6.8.9), and never on multihop sessions.
RFC 5880 s6.8.7 says a Final answers a Poll "without respect to the transmission timer or any other transmission limitations". The program answers every Poll, however closely they follow each other, up to 32 per session per 100ms; past that it drops them. A real Poll sequence is a handful of packets at the peer's interval, so this only ever meets a forger, who could otherwise have us transmit one Final per frame of a flood.
RFC 5880 s6.8.7 bars the peer from sending faster than our Required Min RX.
The program takes a session's packets at most twice that fast: a packet
closer than half the interval to the last one taken still counts for
liveness, but is neither answered nor passed up, and counts as too-fast.
The peer's jitter can shorten an interval by a quarter at most, so a real
peer never meets this. Poll and Final packets are exempt, within the
budget above. On an authenticated session the verify budget (Deviation 5)
comes first, so there a packet that fast is dropped before the digest and
not counted for liveness, Poll and Final included past the burst of two.
RFC 5880 s6.8.9 bars a peer from sending echo faster than our Required Min
Echo RX. The program reflects a known peer's echo up to four times that rate
plus 16 per 100ms, and drops the rest (echo-ratelimited), so a forger
cannot overflow the transmit ring through the reflector. UDP/3785 from
anyone not a peer of an echo-active session is never reflected
(declined).
RFC 5880 s6.3 has a packet from an unknown address pair matched to its
session by Your Discriminator, for a peer whose address changed. The program
hands such packets to the engine at most 256 per 100ms per CPU
(moved-ratelimited); past that, a forger who has seen a discriminator
could otherwise run the engine flat. A real address move is a handful of
packets.
Running it
Reference
Development