]> git.proxmox.com Git - mirror_frr.git/blob - bgpd/bgp_bfd.c
Merge pull request #13649 from donaldsharp/unlock_the_node_or_else
[mirror_frr.git] / bgpd / bgp_bfd.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /**
3 * bgp_bfd.c: BGP BFD handling routines
4 *
5 * @copyright Copyright (C) 2015 Cumulus Networks, Inc.
6 */
7
8 #include <zebra.h>
9
10 #include "command.h"
11 #include "linklist.h"
12 #include "memory.h"
13 #include "prefix.h"
14 #include "frrevent.h"
15 #include "buffer.h"
16 #include "stream.h"
17 #include "vrf.h"
18 #include "zclient.h"
19 #include "bfd.h"
20 #include "lib/json.h"
21 #include "filter.h"
22
23 #include "bgpd/bgpd.h"
24 #include "bgp_fsm.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"
29
30 DEFINE_MTYPE_STATIC(BGPD, BFD_CONFIG, "BFD configuration data");
31
32 extern struct zclient *zclient;
33
34 static void bfd_session_status_update(struct bfd_session_params *bsp,
35 const struct bfd_session_status *bss,
36 void *arg)
37 {
38 struct peer *peer = arg;
39
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));
46
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))
51 zlog_debug(
52 "%s BFD DOWN message ignored in the process of graceful restart when C bit is cleared",
53 peer->host);
54 return;
55 }
56 peer->last_reset = PEER_DOWN_BFD_DOWN;
57
58 /* rfc9384 */
59 if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status))
60 bgp_notify_send(peer, BGP_NOTIFY_CEASE,
61 BGP_NOTIFY_CEASE_BFD_DOWN);
62
63 BGP_EVENT_ADD(peer, BGP_Stop);
64 }
65
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);
71 }
72 }
73 }
74
75 void bgp_peer_config_apply(struct peer *p, struct peer_group *pg)
76 {
77 struct listnode *n;
78 struct peer *pn;
79 struct peer *gconfig;
80
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);
85 return;
86 }
87
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);
98 return;
99 }
100
101 /*
102 * Check if the group configuration was overwritten or apply group
103 * configuration.
104 */
105 gconfig = pg->conf;
106
107 /*
108 * If using default control plane independent configuration,
109 * then prefer group's (e.g. it means it wasn't manually configured).
110 */
111 if (!p->bfd_config->cbit)
112 bfd_sess_set_cbit(p->bfd_config->session,
113 gconfig->bfd_config->cbit);
114 else
115 bfd_sess_set_cbit(p->bfd_config->session, p->bfd_config->cbit);
116
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);
121 else
122 bfd_sess_set_profile(p->bfd_config->session,
123 p->bfd_config->profile);
124
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);
133 else
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);
138
139 bfd_sess_install(p->bfd_config->session);
140 }
141
142 void bgp_peer_bfd_update_source(struct peer *p)
143 {
144 struct bfd_session_params *session = p->bfd_config->session;
145 const union sockunion *source;
146 bool changed = false;
147 int family;
148 union {
149 struct in_addr v4;
150 struct in6_addr v6;
151 } src, dst;
152
153 /* Nothing to do for groups. */
154 if (CHECK_FLAG(p->sflags, PEER_STATUS_GROUP))
155 return;
156
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;
160 else
161 source = p->su_local;
162
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))
169 zlog_debug(
170 "%s: address [%pI4->%pI4] to [%pI4->%pI4]",
171 __func__, &src.v4, &dst.v4,
172 source ? &source->sin.sin_addr
173 : &src.v4,
174 &p->su.sin.sin_addr);
175
176 bfd_sess_set_ipv4_addrs(
177 session, source ? &source->sin.sin_addr : NULL,
178 &p->su.sin.sin_addr);
179 changed = true;
180 }
181 } else {
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))
185 zlog_debug(
186 "%s: address [%pI6->%pI6] to [%pI6->%pI6]",
187 __func__, &src.v6, &dst.v6,
188 source ? &source->sin6.sin6_addr
189 : &src.v6,
190 &p->su.sin6.sin6_addr);
191
192 bfd_sess_set_ipv6_addrs(session,
193 source ? &source->sin6.sin6_addr
194 : NULL,
195 &p->su.sin6.sin6_addr);
196 changed = true;
197 }
198 }
199
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);
205
206 bfd_sess_set_interface(session, p->nexthop.ifp->name);
207 changed = true;
208 }
209
210 /*
211 * Update TTL.
212 *
213 * Two cases:
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.
217 */
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));
222
223 bfd_sess_set_hop_count(session, 1);
224 changed = true;
225 }
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);
230
231 bfd_sess_set_hop_count(session, p->ttl);
232 changed = true;
233 }
234
235 /* Update VRF. */
236 if (bfd_sess_vrf_id(session) != p->bgp->vrf_id) {
237 if (BGP_DEBUG(bfd, BFD_LIB))
238 zlog_debug(
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);
242
243 bfd_sess_set_vrf(session, p->bgp->vrf_id);
244 changed = true;
245 }
246
247 if (changed)
248 bfd_sess_install(session);
249 }
250
251 /**
252 * Reset BFD configuration data structure to its defaults settings.
253 */
254 static void bgp_peer_bfd_reset(struct peer *p)
255 {
256 /* Set defaults. */
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;
262 }
263
264 void bgp_peer_configure_bfd(struct peer *p, bool manual)
265 {
266 /* Groups should not call this. */
267 assert(!CHECK_FLAG(p->sflags, PEER_STATUS_GROUP));
268
269 /* Already configured, skip it. */
270 if (p->bfd_config) {
271 /* If manually active update flag. */
272 if (!p->bfd_config->manual)
273 p->bfd_config->manual = manual;
274
275 return;
276 }
277
278 /* Allocate memory for configuration overrides. */
279 p->bfd_config = XCALLOC(MTYPE_BFD_CONFIG, sizeof(*p->bfd_config));
280 p->bfd_config->manual = manual;
281
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);
285
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
290 : NULL,
291 &p->su.sin.sin_addr);
292 else
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);
297
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);
301
302 if (p->nexthop.ifp)
303 bfd_sess_set_interface(p->bfd_config->session,
304 p->nexthop.ifp->name);
305 }
306
307 static void bgp_peer_remove_bfd(struct peer *p)
308 {
309 /* Groups should not call this. */
310 assert(!CHECK_FLAG(p->sflags, PEER_STATUS_GROUP));
311
312 /*
313 * Peer configuration was removed, however we must check if there
314 * is still a group configuration to keep this running.
315 */
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);
320 return;
321 }
322
323 if (p->bfd_config)
324 bfd_sess_free(&p->bfd_config->session);
325
326 XFREE(MTYPE_BFD_CONFIG, p->bfd_config);
327 }
328
329 static void bgp_group_configure_bfd(struct peer *p)
330 {
331 struct listnode *n;
332 struct peer *pn;
333
334 /* Peers should not call this. */
335 assert(CHECK_FLAG(p->sflags, PEER_STATUS_GROUP));
336
337 /* Already allocated: do nothing. */
338 if (p->bfd_config)
339 return;
340
341 p->bfd_config = XCALLOC(MTYPE_BFD_CONFIG, sizeof(*p->bfd_config));
342
343 /* Set defaults. */
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;
347
348 for (ALL_LIST_ELEMENTS_RO(p->group->peer, n, pn))
349 bgp_peer_configure_bfd(pn, false);
350 }
351
352 static void bgp_group_remove_bfd(struct peer *p)
353 {
354 struct listnode *n;
355 struct peer *pn;
356
357 /* Peers should not call this. */
358 assert(CHECK_FLAG(p->sflags, PEER_STATUS_GROUP));
359
360 /* Already freed: do nothing. */
361 if (p->bfd_config == NULL)
362 return;
363
364 /* Free configuration and point to `NULL`. */
365 XFREE(MTYPE_BFD_CONFIG, p->bfd_config);
366
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);
371 else
372 bgp_peer_remove_bfd(pn);
373 }
374 }
375
376 void bgp_peer_remove_bfd_config(struct peer *p)
377 {
378 if (CHECK_FLAG(p->sflags, PEER_STATUS_GROUP))
379 bgp_group_remove_bfd(p);
380 else
381 bgp_peer_remove_bfd(p);
382 }
383
384 /*
385 * bgp_bfd_peer_config_write - Write the peer BFD configuration.
386 */
387 void bgp_bfd_peer_config_write(struct vty *vty, const struct peer *peer,
388 const char *addr)
389 {
390 /*
391 * Always show group BFD configuration, but peer only when explicitly
392 * configured.
393 */
394 if ((!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)
395 && peer->bfd_config->manual)
396 || CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
397 #if HAVE_BFDD > 0
398 vty_out(vty, " neighbor %s bfd\n", addr);
399 #else
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 */
404 }
405
406 if (peer->bfd_config->profile[0])
407 vty_out(vty, " neighbor %s bfd profile %s\n", addr,
408 peer->bfd_config->profile);
409
410 if (peer->bfd_config->cbit)
411 vty_out(vty, " neighbor %s bfd check-control-plane-failure\n",
412 addr);
413 }
414
415 /*
416 * bgp_bfd_show_info - Show the peer BFD information.
417 */
418 void bgp_bfd_show_info(struct vty *vty, const struct peer *peer,
419 json_object *json_neigh)
420 {
421 bfd_sess_show(vty, json_neigh, peer->bfd_config->session);
422 }
423
424 DEFUN (neighbor_bfd,
425 neighbor_bfd_cmd,
426 "neighbor <A.B.C.D|X:X::X:X|WORD> bfd",
427 NEIGHBOR_STR
428 NEIGHBOR_ADDR_STR2
429 "Enables BFD support\n")
430 {
431 int idx_peer = 1;
432 struct peer *peer;
433
434 peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg);
435 if (!peer)
436 return CMD_WARNING_CONFIG_FAILED;
437
438 if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP))
439 bgp_group_configure_bfd(peer);
440 else
441 bgp_peer_configure_bfd(peer, true);
442
443 bgp_peer_config_apply(peer, peer->group);
444
445 return CMD_SUCCESS;
446 }
447
448 #if HAVE_BFDD > 0
449 DEFUN_HIDDEN(
450 #else
451 DEFUN(
452 #endif /* HAVE_BFDD */
453 neighbor_bfd_param,
454 neighbor_bfd_param_cmd,
455 "neighbor <A.B.C.D|X:X::X:X|WORD> bfd (2-255) (50-60000) (50-60000)",
456 NEIGHBOR_STR
457 NEIGHBOR_ADDR_STR2
458 "Enables BFD support\n"
459 "Detect Multiplier\n"
460 "Required min receive interval\n"
461 "Desired min transmit interval\n")
462 {
463 int idx_peer = 1;
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;
468 struct peer *peer;
469
470 peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg);
471 if (!peer)
472 return CMD_WARNING_CONFIG_FAILED;
473
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);
477
478 if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP))
479 bgp_group_configure_bfd(peer);
480 else
481 bgp_peer_configure_bfd(peer, true);
482
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);
487
488 return CMD_SUCCESS;
489 }
490
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",
494 NO_STR
495 NEIGHBOR_STR
496 NEIGHBOR_ADDR_STR2
497 "BFD support\n"
498 "Link dataplane status with BGP controlplane\n")
499 {
500 const char *no = strmatch(argv[0]->text, "no") ? "no" : NULL;
501 int idx_peer = 0;
502 struct peer *peer;
503
504 if (no)
505 idx_peer = 2;
506 else
507 idx_peer = 1;
508 peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg);
509 if (!peer)
510 return CMD_WARNING_CONFIG_FAILED;
511
512 if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP))
513 bgp_group_configure_bfd(peer);
514 else
515 bgp_peer_configure_bfd(peer, true);
516
517 peer->bfd_config->cbit = no == NULL;
518 bgp_peer_config_apply(peer, peer->group);
519
520 return CMD_SUCCESS;
521 }
522
523 DEFUN (no_neighbor_bfd,
524 no_neighbor_bfd_cmd,
525 #if HAVE_BFDD > 0
526 "no neighbor <A.B.C.D|X:X::X:X|WORD> bfd",
527 #else
528 "no neighbor <A.B.C.D|X:X::X:X|WORD> bfd [(2-255) (50-60000) (50-60000)]",
529 #endif /* HAVE_BFDD */
530 NO_STR
531 NEIGHBOR_STR
532 NEIGHBOR_ADDR_STR2
533 "Disables BFD support\n"
534 #if HAVE_BFDD == 0
535 "Detect Multiplier\n"
536 "Required min receive interval\n"
537 "Desired min transmit interval\n"
538 #endif /* !HAVE_BFDD */
539 )
540 {
541 int idx_peer = 2;
542 struct peer *peer;
543
544 peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg);
545 if (!peer)
546 return CMD_WARNING_CONFIG_FAILED;
547
548 if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP))
549 bgp_group_remove_bfd(peer);
550 else
551 bgp_peer_remove_bfd(peer);
552
553 return CMD_SUCCESS;
554 }
555
556 #if HAVE_BFDD > 0
557 DEFUN(neighbor_bfd_profile, neighbor_bfd_profile_cmd,
558 "neighbor <A.B.C.D|X:X::X:X|WORD> bfd profile BFDPROF",
559 NEIGHBOR_STR
560 NEIGHBOR_ADDR_STR2
561 "BFD integration\n"
562 BFD_PROFILE_STR
563 BFD_PROFILE_NAME_STR)
564 {
565 int idx_peer = 1, idx_prof = 4;
566 struct peer *peer;
567
568 peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg);
569 if (!peer)
570 return CMD_WARNING_CONFIG_FAILED;
571
572 if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP))
573 bgp_group_configure_bfd(peer);
574 else
575 bgp_peer_configure_bfd(peer, true);
576
577 strlcpy(peer->bfd_config->profile, argv[idx_prof]->arg,
578 sizeof(peer->bfd_config->profile));
579 bgp_peer_config_apply(peer, peer->group);
580
581 return CMD_SUCCESS;
582 }
583
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]",
586 NO_STR
587 NEIGHBOR_STR
588 NEIGHBOR_ADDR_STR2
589 "BFD integration\n"
590 BFD_PROFILE_STR
591 BFD_PROFILE_NAME_STR)
592 {
593 int idx_peer = 2;
594 struct peer *peer;
595
596 peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg);
597 if (!peer)
598 return CMD_WARNING_CONFIG_FAILED;
599
600 if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP))
601 bgp_group_configure_bfd(peer);
602 else
603 bgp_peer_configure_bfd(peer, true);
604
605 peer->bfd_config->profile[0] = 0;
606 bgp_peer_config_apply(peer, peer->group);
607
608 return CMD_SUCCESS;
609 }
610 #endif /* HAVE_BFDD */
611
612 void bgp_bfd_init(struct event_loop *tm)
613 {
614 /* Initialize BFD client functions */
615 bfd_protocol_integration_init(zclient, tm);
616
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);
622
623 #if HAVE_BFDD > 0
624 install_element(BGP_NODE, &neighbor_bfd_profile_cmd);
625 install_element(BGP_NODE, &no_neighbor_bfd_profile_cmd);
626 #endif /* HAVE_BFDD */
627 }