1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * bgp_bfd.c: BGP BFD handling routines
5 * @copyright Copyright (C) 2015 Cumulus Networks, Inc.
23 #include "bgpd/bgpd.h"
25 #include "bgpd/bgp_bfd.h"
26 #include "bgpd/bgp_debug.h"
27 #include "bgpd/bgp_vty.h"
28 #include "bgpd/bgp_packet.h"
30 DEFINE_MTYPE_STATIC(BGPD
, BFD_CONFIG
, "BFD configuration data");
32 extern struct zclient
*zclient
;
34 static void bfd_session_status_update(struct bfd_session_params
*bsp
,
35 const struct bfd_session_status
*bss
,
38 struct peer
*peer
= arg
;
40 if (BGP_DEBUG(bfd
, BFD_LIB
))
41 zlog_debug("%s: neighbor %s vrf %s(%u) bfd state %s -> %s",
42 __func__
, peer
->conf_if
? peer
->conf_if
: peer
->host
,
43 bfd_sess_vrf(bsp
), bfd_sess_vrf_id(bsp
),
44 bfd_get_status_str(bss
->previous_state
),
45 bfd_get_status_str(bss
->state
));
47 if (bss
->state
== BSS_DOWN
&& bss
->previous_state
== BSS_UP
) {
48 if (CHECK_FLAG(peer
->sflags
, PEER_STATUS_NSF_MODE
)
49 && bfd_sess_cbit(bsp
) && !bss
->remote_cbit
) {
50 if (BGP_DEBUG(bfd
, BFD_LIB
))
52 "%s BFD DOWN message ignored in the process of graceful restart when C bit is cleared",
56 peer
->last_reset
= PEER_DOWN_BFD_DOWN
;
58 /* draft-ietf-idr-bfd-subcode */
59 if (BGP_IS_VALID_STATE_FOR_NOTIF(peer
->status
))
60 bgp_notify_send(peer
, BGP_NOTIFY_CEASE
,
61 BGP_NOTIFY_CEASE_BFD_DOWN
);
63 BGP_EVENT_ADD(peer
, BGP_Stop
);
66 if (bss
->state
== BSS_UP
&& bss
->previous_state
!= BSS_UP
67 && !peer_established(peer
)) {
68 if (!BGP_PEER_START_SUPPRESSED(peer
)) {
69 bgp_fsm_nht_update(peer
, true);
70 BGP_EVENT_ADD(peer
, BGP_Start
);
75 void bgp_peer_config_apply(struct peer
*p
, struct peer_group
*pg
)
81 /* When called on a group, apply to all peers. */
82 if (CHECK_FLAG(p
->sflags
, PEER_STATUS_GROUP
)) {
83 for (ALL_LIST_ELEMENTS_RO(p
->group
->peer
, n
, pn
))
84 bgp_peer_config_apply(pn
, pg
);
88 /* No group, just use current configuration. */
89 if (pg
== NULL
|| pg
->conf
->bfd_config
== NULL
) {
90 bfd_sess_set_timers(p
->bfd_config
->session
,
91 p
->bfd_config
->detection_multiplier
,
92 p
->bfd_config
->min_rx
,
93 p
->bfd_config
->min_tx
);
94 bfd_sess_set_cbit(p
->bfd_config
->session
, p
->bfd_config
->cbit
);
95 bfd_sess_set_profile(p
->bfd_config
->session
,
96 p
->bfd_config
->profile
);
97 bfd_sess_install(p
->bfd_config
->session
);
102 * Check if the group configuration was overwritten or apply group
108 * If using default control plane independent configuration,
109 * then prefer group's (e.g. it means it wasn't manually configured).
111 if (!p
->bfd_config
->cbit
)
112 bfd_sess_set_cbit(p
->bfd_config
->session
,
113 gconfig
->bfd_config
->cbit
);
115 bfd_sess_set_cbit(p
->bfd_config
->session
, p
->bfd_config
->cbit
);
117 /* If no profile was specified in peer, then use the group profile. */
118 if (p
->bfd_config
->profile
[0] == 0)
119 bfd_sess_set_profile(p
->bfd_config
->session
,
120 gconfig
->bfd_config
->profile
);
122 bfd_sess_set_profile(p
->bfd_config
->session
,
123 p
->bfd_config
->profile
);
125 /* If no specific timers were configured, then use the group timers. */
126 if (p
->bfd_config
->detection_multiplier
== BFD_DEF_DETECT_MULT
127 || p
->bfd_config
->min_rx
== BFD_DEF_MIN_RX
128 || p
->bfd_config
->min_tx
== BFD_DEF_MIN_TX
)
129 bfd_sess_set_timers(p
->bfd_config
->session
,
130 gconfig
->bfd_config
->detection_multiplier
,
131 gconfig
->bfd_config
->min_rx
,
132 gconfig
->bfd_config
->min_tx
);
134 bfd_sess_set_timers(p
->bfd_config
->session
,
135 p
->bfd_config
->detection_multiplier
,
136 p
->bfd_config
->min_rx
,
137 p
->bfd_config
->min_tx
);
139 bfd_sess_install(p
->bfd_config
->session
);
142 void bgp_peer_bfd_update_source(struct peer
*p
)
144 struct bfd_session_params
*session
= p
->bfd_config
->session
;
145 const union sockunion
*source
;
146 bool changed
= false;
153 /* Nothing to do for groups. */
154 if (CHECK_FLAG(p
->sflags
, PEER_STATUS_GROUP
))
157 /* Figure out the correct source to use. */
158 if (CHECK_FLAG(p
->flags
, PEER_FLAG_UPDATE_SOURCE
) && p
->update_source
)
159 source
= p
->update_source
;
161 source
= p
->su_local
;
163 /* Update peer's source/destination addresses. */
164 bfd_sess_addresses(session
, &family
, &src
.v6
, &dst
.v6
);
165 if (family
== AF_INET
) {
166 if ((source
&& source
->sin
.sin_addr
.s_addr
!= src
.v4
.s_addr
)
167 || p
->su
.sin
.sin_addr
.s_addr
!= dst
.v4
.s_addr
) {
168 if (BGP_DEBUG(bfd
, BFD_LIB
))
170 "%s: address [%pI4->%pI4] to [%pI4->%pI4]",
171 __func__
, &src
.v4
, &dst
.v4
,
172 source
? &source
->sin
.sin_addr
174 &p
->su
.sin
.sin_addr
);
176 bfd_sess_set_ipv4_addrs(
177 session
, source
? &source
->sin
.sin_addr
: NULL
,
178 &p
->su
.sin
.sin_addr
);
182 if ((source
&& memcmp(&source
->sin6
, &src
.v6
, sizeof(src
.v6
)))
183 || memcmp(&p
->su
.sin6
, &dst
.v6
, sizeof(dst
.v6
))) {
184 if (BGP_DEBUG(bfd
, BFD_LIB
))
186 "%s: address [%pI6->%pI6] to [%pI6->%pI6]",
187 __func__
, &src
.v6
, &dst
.v6
,
188 source
? &source
->sin6
.sin6_addr
190 &p
->su
.sin6
.sin6_addr
);
192 bfd_sess_set_ipv6_addrs(session
,
193 source
? &source
->sin6
.sin6_addr
195 &p
->su
.sin6
.sin6_addr
);
200 /* Update interface. */
201 if (p
->nexthop
.ifp
&& bfd_sess_interface(session
) == NULL
) {
202 if (BGP_DEBUG(bfd
, BFD_LIB
))
203 zlog_debug("%s: interface none to %s", __func__
,
204 p
->nexthop
.ifp
->name
);
206 bfd_sess_set_interface(session
, p
->nexthop
.ifp
->name
);
214 * - We detected that the peer is a hop away from us (remove multi hop).
215 * (this happens when `p->shared_network` is set to `true`)
216 * - eBGP multi hop / TTL security changed.
218 if (!PEER_IS_MULTIHOP(p
) && bfd_sess_hop_count(session
) > 1) {
219 if (BGP_DEBUG(bfd
, BFD_LIB
))
220 zlog_debug("%s: TTL %d to 1", __func__
,
221 bfd_sess_hop_count(session
));
223 bfd_sess_set_hop_count(session
, 1);
226 if (PEER_IS_MULTIHOP(p
) && p
->ttl
!= bfd_sess_hop_count(session
)) {
227 if (BGP_DEBUG(bfd
, BFD_LIB
))
228 zlog_debug("%s: TTL %d to %d", __func__
,
229 bfd_sess_hop_count(session
), p
->ttl
);
231 bfd_sess_set_hop_count(session
, p
->ttl
);
236 if (bfd_sess_vrf_id(session
) != p
->bgp
->vrf_id
) {
237 if (BGP_DEBUG(bfd
, BFD_LIB
))
239 "%s: VRF %s(%d) to %s(%d)", __func__
,
240 bfd_sess_vrf(session
), bfd_sess_vrf_id(session
),
241 vrf_id_to_name(p
->bgp
->vrf_id
), p
->bgp
->vrf_id
);
243 bfd_sess_set_vrf(session
, p
->bgp
->vrf_id
);
248 bfd_sess_install(session
);
252 * Reset BFD configuration data structure to its defaults settings.
254 static void bgp_peer_bfd_reset(struct peer
*p
)
257 p
->bfd_config
->detection_multiplier
= BFD_DEF_DETECT_MULT
;
258 p
->bfd_config
->min_rx
= BFD_DEF_MIN_RX
;
259 p
->bfd_config
->min_tx
= BFD_DEF_MIN_TX
;
260 p
->bfd_config
->cbit
= false;
261 p
->bfd_config
->profile
[0] = 0;
264 void bgp_peer_configure_bfd(struct peer
*p
, bool manual
)
266 /* Groups should not call this. */
267 assert(!CHECK_FLAG(p
->sflags
, PEER_STATUS_GROUP
));
269 /* Already configured, skip it. */
271 /* If manually active update flag. */
272 if (!p
->bfd_config
->manual
)
273 p
->bfd_config
->manual
= manual
;
278 /* Allocate memory for configuration overrides. */
279 p
->bfd_config
= XCALLOC(MTYPE_BFD_CONFIG
, sizeof(*p
->bfd_config
));
280 p
->bfd_config
->manual
= manual
;
282 /* Create new session and assign callback. */
283 p
->bfd_config
->session
= bfd_sess_new(bfd_session_status_update
, p
);
284 bgp_peer_bfd_reset(p
);
286 /* Configure session with basic BGP peer data. */
287 if (p
->su
.sa
.sa_family
== AF_INET
)
288 bfd_sess_set_ipv4_addrs(p
->bfd_config
->session
,
289 p
->su_local
? &p
->su_local
->sin
.sin_addr
291 &p
->su
.sin
.sin_addr
);
293 bfd_sess_set_ipv6_addrs(
294 p
->bfd_config
->session
,
295 p
->su_local
? &p
->su_local
->sin6
.sin6_addr
: NULL
,
296 &p
->su
.sin6
.sin6_addr
);
298 bfd_sess_set_vrf(p
->bfd_config
->session
, p
->bgp
->vrf_id
);
299 bfd_sess_set_hop_count(p
->bfd_config
->session
,
300 PEER_IS_MULTIHOP(p
) ? p
->ttl
: 1);
303 bfd_sess_set_interface(p
->bfd_config
->session
,
304 p
->nexthop
.ifp
->name
);
307 static void bgp_peer_remove_bfd(struct peer
*p
)
309 /* Groups should not call this. */
310 assert(!CHECK_FLAG(p
->sflags
, PEER_STATUS_GROUP
));
313 * Peer configuration was removed, however we must check if there
314 * is still a group configuration to keep this running.
316 if (p
->group
&& p
->group
->conf
->bfd_config
) {
317 p
->bfd_config
->manual
= false;
318 bgp_peer_bfd_reset(p
);
319 bgp_peer_config_apply(p
, p
->group
);
324 bfd_sess_free(&p
->bfd_config
->session
);
326 XFREE(MTYPE_BFD_CONFIG
, p
->bfd_config
);
329 static void bgp_group_configure_bfd(struct peer
*p
)
334 /* Peers should not call this. */
335 assert(CHECK_FLAG(p
->sflags
, PEER_STATUS_GROUP
));
337 /* Already allocated: do nothing. */
341 p
->bfd_config
= XCALLOC(MTYPE_BFD_CONFIG
, sizeof(*p
->bfd_config
));
344 p
->bfd_config
->detection_multiplier
= BFD_DEF_DETECT_MULT
;
345 p
->bfd_config
->min_rx
= BFD_DEF_MIN_RX
;
346 p
->bfd_config
->min_tx
= BFD_DEF_MIN_TX
;
348 for (ALL_LIST_ELEMENTS_RO(p
->group
->peer
, n
, pn
))
349 bgp_peer_configure_bfd(pn
, false);
352 static void bgp_group_remove_bfd(struct peer
*p
)
357 /* Peers should not call this. */
358 assert(CHECK_FLAG(p
->sflags
, PEER_STATUS_GROUP
));
360 /* Already freed: do nothing. */
361 if (p
->bfd_config
== NULL
)
364 /* Free configuration and point to `NULL`. */
365 XFREE(MTYPE_BFD_CONFIG
, p
->bfd_config
);
367 /* Now that it is `NULL` recalculate configuration for all peers. */
368 for (ALL_LIST_ELEMENTS_RO(p
->group
->peer
, n
, pn
)) {
369 if (pn
->bfd_config
->manual
)
370 bgp_peer_config_apply(pn
, NULL
);
372 bgp_peer_remove_bfd(pn
);
376 void bgp_peer_remove_bfd_config(struct peer
*p
)
378 if (CHECK_FLAG(p
->sflags
, PEER_STATUS_GROUP
))
379 bgp_group_remove_bfd(p
);
381 bgp_peer_remove_bfd(p
);
385 * bgp_bfd_peer_config_write - Write the peer BFD configuration.
387 void bgp_bfd_peer_config_write(struct vty
*vty
, const struct peer
*peer
,
391 * Always show group BFD configuration, but peer only when explicitly
394 if ((!CHECK_FLAG(peer
->sflags
, PEER_STATUS_GROUP
)
395 && peer
->bfd_config
->manual
)
396 || CHECK_FLAG(peer
->sflags
, PEER_STATUS_GROUP
)) {
398 vty_out(vty
, " neighbor %s bfd\n", addr
);
400 vty_out(vty
, " neighbor %s bfd %d %d %d\n", addr
,
401 peer
->bfd_config
->detection_multiplier
,
402 peer
->bfd_config
->min_rx
, peer
->bfd_config
->min_tx
);
403 #endif /* HAVE_BFDD */
406 if (peer
->bfd_config
->profile
[0])
407 vty_out(vty
, " neighbor %s bfd profile %s\n", addr
,
408 peer
->bfd_config
->profile
);
410 if (peer
->bfd_config
->cbit
)
411 vty_out(vty
, " neighbor %s bfd check-control-plane-failure\n",
416 * bgp_bfd_show_info - Show the peer BFD information.
418 void bgp_bfd_show_info(struct vty
*vty
, const struct peer
*peer
,
419 json_object
*json_neigh
)
421 bfd_sess_show(vty
, json_neigh
, peer
->bfd_config
->session
);
426 "neighbor <A.B.C.D|X:X::X:X|WORD> bfd",
429 "Enables BFD support\n")
434 peer
= peer_and_group_lookup_vty(vty
, argv
[idx_peer
]->arg
);
436 return CMD_WARNING_CONFIG_FAILED
;
438 if (CHECK_FLAG(peer
->sflags
, PEER_STATUS_GROUP
))
439 bgp_group_configure_bfd(peer
);
441 bgp_peer_configure_bfd(peer
, true);
443 bgp_peer_config_apply(peer
, peer
->group
);
452 #endif /* HAVE_BFDD */
454 neighbor_bfd_param_cmd
,
455 "neighbor <A.B.C.D|X:X::X:X|WORD> bfd (2-255) (50-60000) (50-60000)",
458 "Enables BFD support\n"
459 "Detect Multiplier\n"
460 "Required min receive interval\n"
461 "Desired min transmit interval\n")
464 int idx_number_1
= 3;
465 int idx_number_2
= 4;
466 int idx_number_3
= 5;
467 long detection_multiplier
, min_rx
, min_tx
;
470 peer
= peer_and_group_lookup_vty(vty
, argv
[idx_peer
]->arg
);
472 return CMD_WARNING_CONFIG_FAILED
;
474 detection_multiplier
= strtol(argv
[idx_number_1
]->arg
, NULL
, 10);
475 min_rx
= strtol(argv
[idx_number_2
]->arg
, NULL
, 10);
476 min_tx
= strtol(argv
[idx_number_3
]->arg
, NULL
, 10);
478 if (CHECK_FLAG(peer
->sflags
, PEER_STATUS_GROUP
))
479 bgp_group_configure_bfd(peer
);
481 bgp_peer_configure_bfd(peer
, true);
483 peer
->bfd_config
->detection_multiplier
= detection_multiplier
;
484 peer
->bfd_config
->min_rx
= min_rx
;
485 peer
->bfd_config
->min_tx
= min_tx
;
486 bgp_peer_config_apply(peer
, peer
->group
);
491 DEFUN (neighbor_bfd_check_controlplane_failure
,
492 neighbor_bfd_check_controlplane_failure_cmd
,
493 "[no] neighbor <A.B.C.D|X:X::X:X|WORD> bfd check-control-plane-failure",
498 "Link dataplane status with BGP controlplane\n")
500 const char *no
= strmatch(argv
[0]->text
, "no") ? "no" : NULL
;
508 peer
= peer_and_group_lookup_vty(vty
, argv
[idx_peer
]->arg
);
510 return CMD_WARNING_CONFIG_FAILED
;
512 if (CHECK_FLAG(peer
->sflags
, PEER_STATUS_GROUP
))
513 bgp_group_configure_bfd(peer
);
515 bgp_peer_configure_bfd(peer
, true);
517 peer
->bfd_config
->cbit
= no
== NULL
;
518 bgp_peer_config_apply(peer
, peer
->group
);
523 DEFUN (no_neighbor_bfd
,
526 "no neighbor <A.B.C.D|X:X::X:X|WORD> bfd",
528 "no neighbor <A.B.C.D|X:X::X:X|WORD> bfd [(2-255) (50-60000) (50-60000)]",
529 #endif /* HAVE_BFDD */
533 "Disables BFD support\n"
535 "Detect Multiplier\n"
536 "Required min receive interval\n"
537 "Desired min transmit interval\n"
538 #endif /* !HAVE_BFDD */
544 peer
= peer_and_group_lookup_vty(vty
, argv
[idx_peer
]->arg
);
546 return CMD_WARNING_CONFIG_FAILED
;
548 if (CHECK_FLAG(peer
->sflags
, PEER_STATUS_GROUP
))
549 bgp_group_remove_bfd(peer
);
551 bgp_peer_remove_bfd(peer
);
557 DEFUN(neighbor_bfd_profile
, neighbor_bfd_profile_cmd
,
558 "neighbor <A.B.C.D|X:X::X:X|WORD> bfd profile BFDPROF",
563 BFD_PROFILE_NAME_STR
)
565 int idx_peer
= 1, idx_prof
= 4;
568 peer
= peer_and_group_lookup_vty(vty
, argv
[idx_peer
]->arg
);
570 return CMD_WARNING_CONFIG_FAILED
;
572 if (CHECK_FLAG(peer
->sflags
, PEER_STATUS_GROUP
))
573 bgp_group_configure_bfd(peer
);
575 bgp_peer_configure_bfd(peer
, true);
577 strlcpy(peer
->bfd_config
->profile
, argv
[idx_prof
]->arg
,
578 sizeof(peer
->bfd_config
->profile
));
579 bgp_peer_config_apply(peer
, peer
->group
);
584 DEFUN(no_neighbor_bfd_profile
, no_neighbor_bfd_profile_cmd
,
585 "no neighbor <A.B.C.D|X:X::X:X|WORD> bfd profile [BFDPROF]",
591 BFD_PROFILE_NAME_STR
)
596 peer
= peer_and_group_lookup_vty(vty
, argv
[idx_peer
]->arg
);
598 return CMD_WARNING_CONFIG_FAILED
;
600 if (CHECK_FLAG(peer
->sflags
, PEER_STATUS_GROUP
))
601 bgp_group_configure_bfd(peer
);
603 bgp_peer_configure_bfd(peer
, true);
605 peer
->bfd_config
->profile
[0] = 0;
606 bgp_peer_config_apply(peer
, peer
->group
);
610 #endif /* HAVE_BFDD */
612 void bgp_bfd_init(struct thread_master
*tm
)
614 /* Initialize BFD client functions */
615 bfd_protocol_integration_init(zclient
, tm
);
617 /* "neighbor bfd" commands. */
618 install_element(BGP_NODE
, &neighbor_bfd_cmd
);
619 install_element(BGP_NODE
, &neighbor_bfd_param_cmd
);
620 install_element(BGP_NODE
, &neighbor_bfd_check_controlplane_failure_cmd
);
621 install_element(BGP_NODE
, &no_neighbor_bfd_cmd
);
624 install_element(BGP_NODE
, &neighbor_bfd_profile_cmd
);
625 install_element(BGP_NODE
, &no_neighbor_bfd_profile_cmd
);
626 #endif /* HAVE_BFDD */