Static routes need BFD
Static routes have a weakness: they don't know whether the next hop is alive. A static route stays installed as long as the local interface is up. Break link A once more: R1's route to R2's loopback still lists both links, and any traffic that hashes onto link A is silently lost. (Here, both test pings did.) Unlike OSPF, nothing will ever time out and fix it.
Bidirectional Forwarding Detection (BFD) fixes that. Each router sends small hello packets to the other on every link, several times a second. If enough of them in a row go missing, BFD declares that path down and tells whatever is using it: here, the static routes. Configure a BFD peer for each next hop, and add bfd to each static next hop, on both routers.
With link A still broken, the BFD session for 192.0.2.1 is down and the static route has dropped that next hop. Restore the link, and both come back. Break it again with BFD already running, and within seconds the route is down to link B, with the eBGP session's uptime unaffected.
The defaults on VyOS are a 300 ms interval and a multiplier of 3, so a failed path is detected in under a second. (The output shows a 3000 ms detection timeout because it was taken after the failure: while a BFD session is down, it slows its packets to one per second, as the BFD standard requires.)
BFD for OSPF and BGP
BFD isn't only for static routes. To try it with the routing protocols, go back to the iBGP-over-OSPF setup from section 4 and add bfd to both OSPF interfaces and to the BGP neighbor, on both routers.
R1 then has three BFD sessions: one single-hop session per link for OSPF, and one multi-hop session between the loopbacks for BGP. FRR chose multi-hop for BGP by itself, because the loopbacks aren't on a shared link.
Break link A again, with log-adjacency-changes turned on so R1 logs exactly when it reacts. The adjacency reached Full at 12:59:35 and was deleted at 12:59:49, only 14 seconds later. OSPF's own dead interval needs 40 seconds of silence, so this can only have been BFD. (FRR records a BFD-triggered teardown with the InactivityTimer reason, the same as a dead-interval expiry.) Meanwhile the BGP session's multi-hop BFD session stayed up over link B, and so did the BGP session itself.
Use BFD wherever your timers are too slow, and wherever a failure might not take your own interface down.
vyos@R1:~$ show ip route 198.51.100.2
Routing entry for 198.51.100.2/32
Known via "static", distance 1, metric 0, best
Last update 00:01:54 ago
Flags: Recursion Selected RR Distance
Status: Installed
* 192.0.2.1, via eth0, weight 1
* 192.0.2.3, via eth1, weight 1
vyos@R1:~$ ping 203.0.113.1 count 3
PING 203.0.113.1 (203.0.113.1) 56(84) bytes of data.
--- 203.0.113.1 ping statistics ---
3 packets transmitted, 0 received, 100% packet loss, time 2052ms
vyos@R1:~$ ping 203.0.113.1 source-address 198.51.100.1 count 3
PING 203.0.113.1 (203.0.113.1) from 198.51.100.1 : 56(84) bytes of data.
--- 203.0.113.1 ping statistics ---
3 packets transmitted, 0 received, 100% packet loss, time 2049ms- Lines 7–8: The static route still lists link A: R1's eth0 is up, so the route stays.
- Line 14: Traffic hashed onto link A is lost.
- Line 20: This flow hashed onto link A as well.
set protocols bfd peer 192.0.2.1
set protocols bfd peer 192.0.2.3
set protocols static route 198.51.100.2/32 next-hop 192.0.2.1 bfd
set protocols static route 198.51.100.2/32 next-hop 192.0.2.3 bfd- Lines 1–2: A BFD session to R2's address on each link.
- Lines 3–4: Tie each static next hop to its BFD session. R2 has the mirror image.
vyos@R1:~$ show bfd peers brief
Session count: 2
SessionId LocalAddress PeerAddress Status Profile
========= ============ =========== ====== =======
800492982 192.0.2.0 192.0.2.1 up -
1172276849 192.0.2.2 192.0.2.3 up -
vyos@R1:~$ show ip route 198.51.100.2
Routing entry for 198.51.100.2/32
Known via "static", distance 1, metric 0, best
Last update 00:00:04 ago
Flags: Recursion Selected RR Distance
Status: Installed
* 192.0.2.1, via eth0, weight 1
* 192.0.2.3, via eth1, weight 1- Lines 5–6: One BFD session per link, both up.
- Lines 13–14: Both next hops installed.
vyos@R1:~$ show bfd peer 192.0.2.1
peer 192.0.2.1 vrf default
ID: 800492982
Remote ID: 0
Active mode
Status: down
Downtime: 5 second(s)
Diagnostics: control detection time expired
Remote diagnostics: ok
Peer Type: configured
RTT min/avg/max: 0/0/0 usec
Local timers:
Detect-multiplier: 3
Receive interval: 300ms
Transmission interval: 300ms
Transmission interval (actual with jitter): 970ms
Detection timeout: 3000ms
Echo receive interval: 50ms
Echo transmission interval: disabled
Remote timers:
Detect-multiplier: 3
Receive interval: 300ms
Transmission interval: 300ms
Echo receive interval: 50ms
vyos@R1:~$ show ip route 198.51.100.2
Routing entry for 198.51.100.2/32
Known via "static", distance 1, metric 0, best
Last update 00:00:06 ago
Flags: Recursion Selected RR Distance
Status: Installed
* 192.0.2.3, via eth1, weight 1
vyos@R1:~$ show ip bgp summary
IPv4 Unicast Summary:
BGP router identifier 198.51.100.1, local AS number 64500 VRF default vrf-id 0
BGP table version 13
RIB entries 1, using 160 bytes of memory
Peers 1, using 30 KiB of memory
Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd PfxSnt Desc
198.51.100.2 4 64501 20 25 13 0 0 00:02:29 1 1 R2 AS64501 loopback
Total number of neighbors 1- Lines 6–8: Down within seconds of the failure…
- Lines 13–15: 3 × 300 ms while the session is up: under a second to detect a failure.
- Line 17: Shown after the failure: a down session slows to one packet per second.
- Line 32: Link A's next hop is gone; only link B remains.
- Line 43: The eBGP session didn't notice.
set protocols ospf interface eth0 bfd
set protocols ospf interface eth1 bfd
set protocols bgp neighbor 198.51.100.2 bfd- Lines 1–2: A single-hop BFD session on each OSPF link.
- Line 3: A BFD session for the BGP neighbor. R2 has the same, pointing at 198.51.100.1.
vyos@R1:~$ show bfd peers brief
Session count: 3
SessionId LocalAddress PeerAddress Status Profile
========= ============ =========== ====== =======
3945685501 192.0.2.2 192.0.2.3 up -
108409544 198.51.100.1 198.51.100.2 up -
755790311 192.0.2.0 192.0.2.1 up -
vyos@R1:~$ show ip ospf neighbor detail | match "Neighbor 198|interface address|BFD"
Neighbor 198.51.100.2, interface address 192.0.2.1
BFD: Type: single hop
Neighbor 198.51.100.2, interface address 192.0.2.3
BFD: Type: single hop
vyos@R1:~$ show bgp neighbors 198.51.100.2 | match "BFD|BGP state"
BGP state = Established, up for 00:00:50
BFD Hold Time (interval 30) timer expires in 0 seconds
BFD: Type: multi hop- Line 5: Link B, for OSPF.
- Line 6: Loopback to loopback, for BGP.
- Line 7: Link A, for OSPF.
- Lines 10–12: OSPF uses single-hop BFD on each link.
- Line 17: BGP between loopbacks gets a multi-hop session.
set protocols ospf log-adjacency-changes detail- Line 1: Every OSPF state change goes to the log, with a timestamp.
vyos@R1:~$ show log | match "ospfd\[" | tail -4
Oct 17 12:59:35 ospfd[1279]: AdjChg: Nbr 198.51.100.2, NbrIP 192.0.2.1 (default) on eth0:192.0.2.0: Init -> ExStart (2-WayReceived)
Oct 17 12:59:35 ospfd[1279]: AdjChg: Nbr 198.51.100.2, NbrIP 192.0.2.1 (default) on eth0:192.0.2.0: ExStart -> Exchange (NegotiationDone)
Oct 17 12:59:35 ospfd[1279]: AdjChg: Nbr 198.51.100.2, NbrIP 192.0.2.1 (default) on eth0:192.0.2.0: Exchange -> Full (ExchangeDone)
Oct 17 12:59:49 ospfd[1279]: AdjChg: Nbr 198.51.100.2, NbrIP 192.0.2.1 (default) on eth0:192.0.2.0: Full -> Deleted (InactivityTimer)- Line 4: The adjacency comes up on link A…
- Line 5: …and is torn down 14 seconds later, well inside the 40-second dead interval: BFD noticed first.
Check your understanding
Why does a static route keep using a dead next hop without BFD?
- Static routes only check whether the local interface is up
- Because ECMP hides failures
- Because the BGP session is still up
- Static routes time out after 180 seconds