TTR Networks

Intermediate · BGP on loopbacks vs interface addresses · section 6 of 8

Static routes need BFD

✓ Lab-tested 17 October 2026 on VyOS 2026.10.16-0024-rolling

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.

Link A broken, without BFD (real output)
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.
R1: BFD on each static next hop
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.
BFD up on both links (real output)
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.
Link A breaks, with BFD running (real output)
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.
Both routers: BFD for OSPF and BGP (on the section 4 setup)
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.
Three BFD sessions (real output)
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.
Log adjacency changes, to see the timing
set protocols ospf log-adjacency-changes detail
  • Line 1: Every OSPF state change goes to the log, with a timestamp.
Link A breaks, with BFD on OSPF (real output)
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