2 * bgp_bfd.c: BGP BFD handling routines
4 * @copyright Copyright (C) 2015 Cumulus Networks, Inc.
6 * This file is part of GNU Zebra.
8 * GNU Zebra is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2, or (at your option) any
13 * GNU Zebra is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
18 * You should have received a copy of the GNU General Public License along
19 * with this program; see the file COPYING; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
38 #include "bgpd/bgpd.h"
40 #include "bgpd/bgp_bfd.h"
41 #include "bgpd/bgp_debug.h"
42 #include "bgpd/bgp_vty.h"
43 #include "bgpd/bgp_packet.h"
45 DEFINE_MTYPE_STATIC(BGPD
, BFD_CONFIG
, "BFD configuration data");
47 extern struct zclient
*zclient
;
49 static void bfd_session_status_update(struct bfd_session_params
*bsp
,
50 const struct bfd_session_status
*bss
,
53 struct peer
*peer
= arg
;
55 if (BGP_DEBUG(bfd
, BFD_LIB
))
56 zlog_debug("%s: neighbor %s vrf %s(%u) bfd state %s -> %s",
57 __func__
, peer
->conf_if
? peer
->conf_if
: peer
->host
,
58 bfd_sess_vrf(bsp
), bfd_sess_vrf_id(bsp
),
59 bfd_get_status_str(bss
->previous_state
),
60 bfd_get_status_str(bss
->state
));
62 if (bss
->state
== BSS_DOWN
&& bss
->previous_state
== BSS_UP
) {
63 if (CHECK_FLAG(peer
->sflags
, PEER_STATUS_NSF_MODE
)
64 && bfd_sess_cbit(bsp
) && !bss
->remote_cbit
) {
65 if (BGP_DEBUG(bfd
, BFD_LIB
))
67 "%s BFD DOWN message ignored in the process of graceful restart when C bit is cleared",
71 peer
->last_reset
= PEER_DOWN_BFD_DOWN
;
73 /* draft-ietf-idr-bfd-subcode */
74 if (BGP_IS_VALID_STATE_FOR_NOTIF(peer
->status
))
75 bgp_notify_send(peer
, BGP_NOTIFY_CEASE
,
76 BGP_NOTIFY_CEASE_BFD_DOWN
);
78 BGP_EVENT_ADD(peer
, BGP_Stop
);
81 if (bss
->state
== BSS_UP
&& bss
->previous_state
!= BSS_UP
82 && !peer_established(peer
)) {
83 if (!BGP_PEER_START_SUPPRESSED(peer
)) {
84 bgp_fsm_nht_update(peer
, true);
85 BGP_EVENT_ADD(peer
, BGP_Start
);
90 void bgp_peer_config_apply(struct peer
*p
, struct peer_group
*pg
)
96 /* When called on a group, apply to all peers. */
97 if (CHECK_FLAG(p
->sflags
, PEER_STATUS_GROUP
)) {
98 for (ALL_LIST_ELEMENTS_RO(p
->group
->peer
, n
, pn
))
99 bgp_peer_config_apply(pn
, pg
);
103 /* No group, just use current configuration. */
104 if (pg
== NULL
|| pg
->conf
->bfd_config
== NULL
) {
105 bfd_sess_set_timers(p
->bfd_config
->session
,
106 p
->bfd_config
->detection_multiplier
,
107 p
->bfd_config
->min_rx
,
108 p
->bfd_config
->min_tx
);
109 bfd_sess_set_cbit(p
->bfd_config
->session
, p
->bfd_config
->cbit
);
110 bfd_sess_set_profile(p
->bfd_config
->session
,
111 p
->bfd_config
->profile
);
112 bfd_sess_install(p
->bfd_config
->session
);
117 * Check if the group configuration was overwritten or apply group
123 * If using default control plane independent configuration,
124 * then prefer group's (e.g. it means it wasn't manually configured).
126 if (!p
->bfd_config
->cbit
)
127 bfd_sess_set_cbit(p
->bfd_config
->session
,
128 gconfig
->bfd_config
->cbit
);
130 bfd_sess_set_cbit(p
->bfd_config
->session
, p
->bfd_config
->cbit
);
132 /* If no profile was specified in peer, then use the group profile. */
133 if (p
->bfd_config
->profile
[0] == 0)
134 bfd_sess_set_profile(p
->bfd_config
->session
,
135 gconfig
->bfd_config
->profile
);
137 bfd_sess_set_profile(p
->bfd_config
->session
,
138 p
->bfd_config
->profile
);
140 /* If no specific timers were configured, then use the group timers. */
141 if (p
->bfd_config
->detection_multiplier
== BFD_DEF_DETECT_MULT
142 || p
->bfd_config
->min_rx
== BFD_DEF_MIN_RX
143 || p
->bfd_config
->min_tx
== BFD_DEF_MIN_TX
)
144 bfd_sess_set_timers(p
->bfd_config
->session
,
145 gconfig
->bfd_config
->detection_multiplier
,
146 gconfig
->bfd_config
->min_rx
,
147 gconfig
->bfd_config
->min_tx
);
149 bfd_sess_set_timers(p
->bfd_config
->session
,
150 p
->bfd_config
->detection_multiplier
,
151 p
->bfd_config
->min_rx
,
152 p
->bfd_config
->min_tx
);
154 bfd_sess_install(p
->bfd_config
->session
);
157 void bgp_peer_bfd_update_source(struct peer
*p
)
159 struct bfd_session_params
*session
= p
->bfd_config
->session
;
160 const union sockunion
*source
;
161 bool changed
= false;
168 /* Nothing to do for groups. */
169 if (CHECK_FLAG(p
->sflags
, PEER_STATUS_GROUP
))
172 /* Figure out the correct source to use. */
173 if (CHECK_FLAG(p
->flags
, PEER_FLAG_UPDATE_SOURCE
) && p
->update_source
)
174 source
= p
->update_source
;
176 source
= p
->su_local
;
178 /* Update peer's source/destination addresses. */
179 bfd_sess_addresses(session
, &family
, &src
.v6
, &dst
.v6
);
180 if (family
== AF_INET
) {
181 if ((source
&& source
->sin
.sin_addr
.s_addr
!= src
.v4
.s_addr
)
182 || p
->su
.sin
.sin_addr
.s_addr
!= dst
.v4
.s_addr
) {
183 if (BGP_DEBUG(bfd
, BFD_LIB
))
185 "%s: address [%pI4->%pI4] to [%pI4->%pI4]",
186 __func__
, &src
.v4
, &dst
.v4
,
187 source
? &source
->sin
.sin_addr
189 &p
->su
.sin
.sin_addr
);
191 bfd_sess_set_ipv4_addrs(
192 session
, source
? &source
->sin
.sin_addr
: NULL
,
193 &p
->su
.sin
.sin_addr
);
197 if ((source
&& memcmp(&source
->sin6
, &src
.v6
, sizeof(src
.v6
)))
198 || memcmp(&p
->su
.sin6
, &dst
.v6
, sizeof(dst
.v6
))) {
199 if (BGP_DEBUG(bfd
, BFD_LIB
))
201 "%s: address [%pI6->%pI6] to [%pI6->%pI6]",
202 __func__
, &src
.v6
, &dst
.v6
,
203 source
? &source
->sin6
.sin6_addr
205 &p
->su
.sin6
.sin6_addr
);
207 bfd_sess_set_ipv6_addrs(session
,
208 source
? &source
->sin6
.sin6_addr
210 &p
->su
.sin6
.sin6_addr
);
215 /* Update interface. */
216 if (p
->nexthop
.ifp
&& bfd_sess_interface(session
) == NULL
) {
217 if (BGP_DEBUG(bfd
, BFD_LIB
))
218 zlog_debug("%s: interface none to %s", __func__
,
219 p
->nexthop
.ifp
->name
);
221 bfd_sess_set_interface(session
, p
->nexthop
.ifp
->name
);
229 * - We detected that the peer is a hop away from us (remove multi hop).
230 * (this happens when `p->shared_network` is set to `true`)
231 * - eBGP multi hop / TTL security changed.
233 if (!PEER_IS_MULTIHOP(p
) && bfd_sess_hop_count(session
) > 1) {
234 if (BGP_DEBUG(bfd
, BFD_LIB
))
235 zlog_debug("%s: TTL %d to 1", __func__
,
236 bfd_sess_hop_count(session
));
238 bfd_sess_set_hop_count(session
, 1);
241 if (PEER_IS_MULTIHOP(p
) && p
->ttl
!= bfd_sess_hop_count(session
)) {
242 if (BGP_DEBUG(bfd
, BFD_LIB
))
243 zlog_debug("%s: TTL %d to %d", __func__
,
244 bfd_sess_hop_count(session
), p
->ttl
);
246 bfd_sess_set_hop_count(session
, p
->ttl
);
251 if (bfd_sess_vrf_id(session
) != p
->bgp
->vrf_id
) {
252 if (BGP_DEBUG(bfd
, BFD_LIB
))
254 "%s: VRF %s(%d) to %s(%d)", __func__
,
255 bfd_sess_vrf(session
), bfd_sess_vrf_id(session
),
256 vrf_id_to_name(p
->bgp
->vrf_id
), p
->bgp
->vrf_id
);
258 bfd_sess_set_vrf(session
, p
->bgp
->vrf_id
);
263 bfd_sess_install(session
);
267 * Reset BFD configuration data structure to its defaults settings.
269 static void bgp_peer_bfd_reset(struct peer
*p
)
272 p
->bfd_config
->detection_multiplier
= BFD_DEF_DETECT_MULT
;
273 p
->bfd_config
->min_rx
= BFD_DEF_MIN_RX
;
274 p
->bfd_config
->min_tx
= BFD_DEF_MIN_TX
;
275 p
->bfd_config
->cbit
= false;
276 p
->bfd_config
->profile
[0] = 0;
279 void bgp_peer_configure_bfd(struct peer
*p
, bool manual
)
281 /* Groups should not call this. */
282 assert(!CHECK_FLAG(p
->sflags
, PEER_STATUS_GROUP
));
284 /* Already configured, skip it. */
286 /* If manually active update flag. */
287 if (!p
->bfd_config
->manual
)
288 p
->bfd_config
->manual
= manual
;
293 /* Allocate memory for configuration overrides. */
294 p
->bfd_config
= XCALLOC(MTYPE_BFD_CONFIG
, sizeof(*p
->bfd_config
));
295 p
->bfd_config
->manual
= manual
;
297 /* Create new session and assign callback. */
298 p
->bfd_config
->session
= bfd_sess_new(bfd_session_status_update
, p
);
299 bgp_peer_bfd_reset(p
);
301 /* Configure session with basic BGP peer data. */
302 if (p
->su
.sa
.sa_family
== AF_INET
)
303 bfd_sess_set_ipv4_addrs(p
->bfd_config
->session
,
304 p
->su_local
? &p
->su_local
->sin
.sin_addr
306 &p
->su
.sin
.sin_addr
);
308 bfd_sess_set_ipv6_addrs(
309 p
->bfd_config
->session
,
310 p
->su_local
? &p
->su_local
->sin6
.sin6_addr
: NULL
,
311 &p
->su
.sin6
.sin6_addr
);
313 bfd_sess_set_vrf(p
->bfd_config
->session
, p
->bgp
->vrf_id
);
314 bfd_sess_set_hop_count(p
->bfd_config
->session
,
315 PEER_IS_MULTIHOP(p
) ? p
->ttl
: 1);
318 bfd_sess_set_interface(p
->bfd_config
->session
,
319 p
->nexthop
.ifp
->name
);
322 static void bgp_peer_remove_bfd(struct peer
*p
)
324 /* Groups should not call this. */
325 assert(!CHECK_FLAG(p
->sflags
, PEER_STATUS_GROUP
));
328 * Peer configuration was removed, however we must check if there
329 * is still a group configuration to keep this running.
331 if (p
->group
&& p
->group
->conf
->bfd_config
) {
332 p
->bfd_config
->manual
= false;
333 bgp_peer_bfd_reset(p
);
334 bgp_peer_config_apply(p
, p
->group
);
339 bfd_sess_free(&p
->bfd_config
->session
);
341 XFREE(MTYPE_BFD_CONFIG
, p
->bfd_config
);
344 static void bgp_group_configure_bfd(struct peer
*p
)
349 /* Peers should not call this. */
350 assert(CHECK_FLAG(p
->sflags
, PEER_STATUS_GROUP
));
352 /* Already allocated: do nothing. */
356 p
->bfd_config
= XCALLOC(MTYPE_BFD_CONFIG
, sizeof(*p
->bfd_config
));
359 p
->bfd_config
->detection_multiplier
= BFD_DEF_DETECT_MULT
;
360 p
->bfd_config
->min_rx
= BFD_DEF_MIN_RX
;
361 p
->bfd_config
->min_tx
= BFD_DEF_MIN_TX
;
363 for (ALL_LIST_ELEMENTS_RO(p
->group
->peer
, n
, pn
))
364 bgp_peer_configure_bfd(pn
, false);
367 static void bgp_group_remove_bfd(struct peer
*p
)
372 /* Peers should not call this. */
373 assert(CHECK_FLAG(p
->sflags
, PEER_STATUS_GROUP
));
375 /* Already freed: do nothing. */
376 if (p
->bfd_config
== NULL
)
379 /* Free configuration and point to `NULL`. */
380 XFREE(MTYPE_BFD_CONFIG
, p
->bfd_config
);
382 /* Now that it is `NULL` recalculate configuration for all peers. */
383 for (ALL_LIST_ELEMENTS_RO(p
->group
->peer
, n
, pn
)) {
384 if (pn
->bfd_config
->manual
)
385 bgp_peer_config_apply(pn
, NULL
);
387 bgp_peer_remove_bfd(pn
);
391 void bgp_peer_remove_bfd_config(struct peer
*p
)
393 if (CHECK_FLAG(p
->sflags
, PEER_STATUS_GROUP
))
394 bgp_group_remove_bfd(p
);
396 bgp_peer_remove_bfd(p
);
400 * bgp_bfd_peer_config_write - Write the peer BFD configuration.
402 void bgp_bfd_peer_config_write(struct vty
*vty
, const struct peer
*peer
,
406 * Always show group BFD configuration, but peer only when explicitly
409 if ((!CHECK_FLAG(peer
->sflags
, PEER_STATUS_GROUP
)
410 && peer
->bfd_config
->manual
)
411 || CHECK_FLAG(peer
->sflags
, PEER_STATUS_GROUP
)) {
413 vty_out(vty
, " neighbor %s bfd\n", addr
);
415 vty_out(vty
, " neighbor %s bfd %d %d %d\n", addr
,
416 peer
->bfd_config
->detection_multiplier
,
417 peer
->bfd_config
->min_rx
, peer
->bfd_config
->min_tx
);
418 #endif /* HAVE_BFDD */
421 if (peer
->bfd_config
->profile
[0])
422 vty_out(vty
, " neighbor %s bfd profile %s\n", addr
,
423 peer
->bfd_config
->profile
);
425 if (peer
->bfd_config
->cbit
)
426 vty_out(vty
, " neighbor %s bfd check-control-plane-failure\n",
431 * bgp_bfd_show_info - Show the peer BFD information.
433 void bgp_bfd_show_info(struct vty
*vty
, const struct peer
*peer
,
434 json_object
*json_neigh
)
436 bfd_sess_show(vty
, json_neigh
, peer
->bfd_config
->session
);
441 "neighbor <A.B.C.D|X:X::X:X|WORD> bfd",
444 "Enables BFD support\n")
449 peer
= peer_and_group_lookup_vty(vty
, argv
[idx_peer
]->arg
);
451 return CMD_WARNING_CONFIG_FAILED
;
453 if (CHECK_FLAG(peer
->sflags
, PEER_STATUS_GROUP
))
454 bgp_group_configure_bfd(peer
);
456 bgp_peer_configure_bfd(peer
, true);
458 bgp_peer_config_apply(peer
, peer
->group
);
467 #endif /* HAVE_BFDD */
469 neighbor_bfd_param_cmd
,
470 "neighbor <A.B.C.D|X:X::X:X|WORD> bfd (2-255) (50-60000) (50-60000)",
473 "Enables BFD support\n"
474 "Detect Multiplier\n"
475 "Required min receive interval\n"
476 "Desired min transmit interval\n")
479 int idx_number_1
= 3;
480 int idx_number_2
= 4;
481 int idx_number_3
= 5;
482 long detection_multiplier
, min_rx
, min_tx
;
485 peer
= peer_and_group_lookup_vty(vty
, argv
[idx_peer
]->arg
);
487 return CMD_WARNING_CONFIG_FAILED
;
489 detection_multiplier
= strtol(argv
[idx_number_1
]->arg
, NULL
, 10);
490 min_rx
= strtol(argv
[idx_number_2
]->arg
, NULL
, 10);
491 min_tx
= strtol(argv
[idx_number_3
]->arg
, NULL
, 10);
493 if (CHECK_FLAG(peer
->sflags
, PEER_STATUS_GROUP
))
494 bgp_group_configure_bfd(peer
);
496 bgp_peer_configure_bfd(peer
, true);
498 peer
->bfd_config
->detection_multiplier
= detection_multiplier
;
499 peer
->bfd_config
->min_rx
= min_rx
;
500 peer
->bfd_config
->min_tx
= min_tx
;
501 bgp_peer_config_apply(peer
, peer
->group
);
506 DEFUN (neighbor_bfd_check_controlplane_failure
,
507 neighbor_bfd_check_controlplane_failure_cmd
,
508 "[no] neighbor <A.B.C.D|X:X::X:X|WORD> bfd check-control-plane-failure",
513 "Link dataplane status with BGP controlplane\n")
515 const char *no
= strmatch(argv
[0]->text
, "no") ? "no" : NULL
;
523 peer
= peer_and_group_lookup_vty(vty
, argv
[idx_peer
]->arg
);
525 vty_out(vty
, "%% Specify remote-as or peer-group commands first\n");
526 return CMD_WARNING_CONFIG_FAILED
;
529 if (CHECK_FLAG(peer
->sflags
, PEER_STATUS_GROUP
))
530 bgp_group_configure_bfd(peer
);
532 bgp_peer_configure_bfd(peer
, true);
534 peer
->bfd_config
->cbit
= no
== NULL
;
535 bgp_peer_config_apply(peer
, peer
->group
);
540 DEFUN (no_neighbor_bfd
,
543 "no neighbor <A.B.C.D|X:X::X:X|WORD> bfd",
545 "no neighbor <A.B.C.D|X:X::X:X|WORD> bfd [(2-255) (50-60000) (50-60000)]",
546 #endif /* HAVE_BFDD */
550 "Disables BFD support\n"
552 "Detect Multiplier\n"
553 "Required min receive interval\n"
554 "Desired min transmit interval\n"
555 #endif /* !HAVE_BFDD */
561 peer
= peer_and_group_lookup_vty(vty
, argv
[idx_peer
]->arg
);
563 return CMD_WARNING_CONFIG_FAILED
;
565 if (CHECK_FLAG(peer
->sflags
, PEER_STATUS_GROUP
))
566 bgp_group_remove_bfd(peer
);
568 bgp_peer_remove_bfd(peer
);
574 DEFUN(neighbor_bfd_profile
, neighbor_bfd_profile_cmd
,
575 "neighbor <A.B.C.D|X:X::X:X|WORD> bfd profile BFDPROF",
580 BFD_PROFILE_NAME_STR
)
582 int idx_peer
= 1, idx_prof
= 4;
585 peer
= peer_and_group_lookup_vty(vty
, argv
[idx_peer
]->arg
);
587 return CMD_WARNING_CONFIG_FAILED
;
589 if (CHECK_FLAG(peer
->sflags
, PEER_STATUS_GROUP
))
590 bgp_group_configure_bfd(peer
);
592 bgp_peer_configure_bfd(peer
, true);
594 strlcpy(peer
->bfd_config
->profile
, argv
[idx_prof
]->arg
,
595 sizeof(peer
->bfd_config
->profile
));
596 bgp_peer_config_apply(peer
, peer
->group
);
601 DEFUN(no_neighbor_bfd_profile
, no_neighbor_bfd_profile_cmd
,
602 "no neighbor <A.B.C.D|X:X::X:X|WORD> bfd profile [BFDPROF]",
608 BFD_PROFILE_NAME_STR
)
613 peer
= peer_and_group_lookup_vty(vty
, argv
[idx_peer
]->arg
);
615 return CMD_WARNING_CONFIG_FAILED
;
617 if (CHECK_FLAG(peer
->sflags
, PEER_STATUS_GROUP
))
618 bgp_group_configure_bfd(peer
);
620 bgp_peer_configure_bfd(peer
, true);
622 peer
->bfd_config
->profile
[0] = 0;
623 bgp_peer_config_apply(peer
, peer
->group
);
627 #endif /* HAVE_BFDD */
629 void bgp_bfd_init(struct thread_master
*tm
)
631 /* Initialize BFD client functions */
632 bfd_protocol_integration_init(zclient
, tm
);
634 /* "neighbor bfd" commands. */
635 install_element(BGP_NODE
, &neighbor_bfd_cmd
);
636 install_element(BGP_NODE
, &neighbor_bfd_param_cmd
);
637 install_element(BGP_NODE
, &neighbor_bfd_check_controlplane_failure_cmd
);
638 install_element(BGP_NODE
, &no_neighbor_bfd_cmd
);
641 install_element(BGP_NODE
, &neighbor_bfd_profile_cmd
);
642 install_element(BGP_NODE
, &no_neighbor_bfd_profile_cmd
);
643 #endif /* HAVE_BFDD */