]> git.proxmox.com Git - mirror_frr.git/blob - lib/bfd.c
Merge pull request #8543 from volta-networks/fix_bgp_invalid_nexthop
[mirror_frr.git] / lib / bfd.c
1 /**
2 * bfd.c: BFD handling routines
3 *
4 * @copyright Copyright (C) 2015 Cumulus Networks, Inc.
5 *
6 * This file is part of GNU Zebra.
7 *
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
11 * later version.
12 *
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.
17 *
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
21 */
22
23 #include <zebra.h>
24
25 #include "command.h"
26 #include "memory.h"
27 #include "prefix.h"
28 #include "thread.h"
29 #include "stream.h"
30 #include "vrf.h"
31 #include "zclient.h"
32 #include "table.h"
33 #include "vty.h"
34 #include "bfd.h"
35
36 DEFINE_MTYPE_STATIC(LIB, BFD_INFO, "BFD info");
37
38 static int bfd_debug = 0;
39 static struct bfd_gbl bfd_gbl;
40
41 /*
42 * bfd_gbl_init - Initialize the BFD global structure
43 */
44 void bfd_gbl_init(void)
45 {
46 memset(&bfd_gbl, 0, sizeof(struct bfd_gbl));
47 }
48
49 /*
50 * bfd_gbl_exit - Called when daemon exits
51 */
52 void bfd_gbl_exit(void)
53 {
54 SET_FLAG(bfd_gbl.flags, BFD_GBL_FLAG_IN_SHUTDOWN);
55 }
56
57 /*
58 * bfd_info_create - Allocate the BFD information
59 */
60 struct bfd_info *bfd_info_create(void)
61 {
62 struct bfd_info *bfd_info;
63
64 bfd_info = XCALLOC(MTYPE_BFD_INFO, sizeof(struct bfd_info));
65 assert(bfd_info);
66
67 bfd_info->status = BFD_STATUS_UNKNOWN;
68 bfd_info->type = BFD_TYPE_NOT_CONFIGURED;
69 bfd_info->last_update = 0;
70 return bfd_info;
71 }
72
73 /*
74 * bfd_info_free - Free the BFD information.
75 */
76 void bfd_info_free(struct bfd_info **bfd_info)
77 {
78 XFREE(MTYPE_BFD_INFO, *bfd_info);
79 }
80
81 /*
82 * bfd_validate_param - Validate the BFD paramter information.
83 */
84 int bfd_validate_param(struct vty *vty, const char *dm_str, const char *rx_str,
85 const char *tx_str, uint8_t *dm_val, uint32_t *rx_val,
86 uint32_t *tx_val)
87 {
88 *dm_val = strtoul(dm_str, NULL, 10);
89 *rx_val = strtoul(rx_str, NULL, 10);
90 *tx_val = strtoul(tx_str, NULL, 10);
91 return CMD_SUCCESS;
92 }
93
94 /*
95 * bfd_set_param - Set the configured BFD paramter values
96 */
97 void bfd_set_param(struct bfd_info **bfd_info, uint32_t min_rx, uint32_t min_tx,
98 uint8_t detect_mult, const char *profile, int defaults,
99 int *command)
100 {
101 if (!*bfd_info) {
102 *bfd_info = bfd_info_create();
103 *command = ZEBRA_BFD_DEST_REGISTER;
104 } else {
105 if (((*bfd_info)->required_min_rx != min_rx)
106 || ((*bfd_info)->desired_min_tx != min_tx)
107 || ((*bfd_info)->detect_mult != detect_mult)
108 || ((*bfd_info)->profile[0] == 0 && profile)
109 || ((*bfd_info)->profile[0] && profile == NULL)
110 || (profile && (*bfd_info)->profile[0]
111 && strcmp((*bfd_info)->profile, profile)))
112 *command = ZEBRA_BFD_DEST_UPDATE;
113 }
114
115 if (*command) {
116 (*bfd_info)->required_min_rx = min_rx;
117 (*bfd_info)->desired_min_tx = min_tx;
118 (*bfd_info)->detect_mult = detect_mult;
119 if (profile)
120 strlcpy((*bfd_info)->profile, profile,
121 BFD_PROFILE_NAME_LEN);
122 else
123 (*bfd_info)->profile[0] = '\0';
124 }
125
126 if (!defaults)
127 SET_FLAG((*bfd_info)->flags, BFD_FLAG_PARAM_CFG);
128 else
129 UNSET_FLAG((*bfd_info)->flags, BFD_FLAG_PARAM_CFG);
130 }
131
132 /*
133 * bfd_peer_sendmsg - Format and send a peer register/Unregister
134 * command to Zebra to be forwarded to BFD
135 *
136 * DEPRECATED: use zclient_bfd_command instead
137 */
138 void bfd_peer_sendmsg(struct zclient *zclient, struct bfd_info *bfd_info,
139 int family, void *dst_ip, void *src_ip, char *if_name,
140 int ttl, int multihop, int cbit, int command,
141 int set_flag, vrf_id_t vrf_id)
142 {
143 struct bfd_session_arg args = {};
144 size_t addrlen;
145
146 /* Individual reg/dereg messages are suppressed during shutdown. */
147 if (CHECK_FLAG(bfd_gbl.flags, BFD_GBL_FLAG_IN_SHUTDOWN)) {
148 if (bfd_debug)
149 zlog_debug(
150 "%s: Suppressing BFD peer reg/dereg messages",
151 __func__);
152 return;
153 }
154
155 /* Check socket. */
156 if (!zclient || zclient->sock < 0) {
157 if (bfd_debug)
158 zlog_debug(
159 "%s: Can't send BFD peer register, Zebra client not established",
160 __func__);
161 return;
162 }
163
164 /* Fill in all arguments. */
165 args.ttl = ttl;
166 args.cbit = cbit;
167 args.family = family;
168 args.mhop = multihop;
169 args.vrf_id = vrf_id;
170 args.command = command;
171 args.set_flag = set_flag;
172 args.bfd_info = bfd_info;
173 if (args.bfd_info) {
174 args.min_rx = bfd_info->required_min_rx;
175 args.min_tx = bfd_info->desired_min_tx;
176 args.detection_multiplier = bfd_info->detect_mult;
177 if (bfd_info->profile[0]) {
178 args.profilelen = strlen(bfd_info->profile);
179 strlcpy(args.profile, bfd_info->profile,
180 sizeof(args.profile));
181 }
182 }
183
184 addrlen = family == AF_INET ? sizeof(struct in_addr)
185 : sizeof(struct in6_addr);
186 memcpy(&args.dst, dst_ip, addrlen);
187 if (src_ip)
188 memcpy(&args.src, src_ip, addrlen);
189
190 if (if_name)
191 args.ifnamelen =
192 strlcpy(args.ifname, if_name, sizeof(args.ifname));
193
194 zclient_bfd_command(zclient, &args);
195 }
196
197 /*
198 * bfd_get_command_dbg_str - Convert command to a debug string.
199 */
200 const char *bfd_get_command_dbg_str(int command)
201 {
202 switch (command) {
203 case ZEBRA_BFD_DEST_REGISTER:
204 return "Register";
205 case ZEBRA_BFD_DEST_DEREGISTER:
206 return "Deregister";
207 case ZEBRA_BFD_DEST_UPDATE:
208 return "Update";
209 default:
210 return "Unknown";
211 }
212 }
213
214 /*
215 * bfd_get_peer_info - Extract the Peer information for which the BFD session
216 * went down from the message sent from Zebra to clients.
217 */
218 struct interface *bfd_get_peer_info(struct stream *s, struct prefix *dp,
219 struct prefix *sp, int *status,
220 int *remote_cbit,
221 vrf_id_t vrf_id)
222 {
223 unsigned int ifindex;
224 struct interface *ifp = NULL;
225 int plen;
226 int local_remote_cbit;
227
228 /*
229 * If the ifindex lookup fails the
230 * rest of the data in the stream is
231 * not read. All examples of this function
232 * call immediately use the dp->family which
233 * is not good. Ensure we are not using
234 * random data
235 */
236 memset(dp, 0, sizeof(*dp));
237 memset(sp, 0, sizeof(*sp));
238
239 /* Get interface index. */
240 STREAM_GETL(s, ifindex);
241
242 /* Lookup index. */
243 if (ifindex != 0) {
244 ifp = if_lookup_by_index(ifindex, vrf_id);
245 if (ifp == NULL) {
246 if (bfd_debug)
247 zlog_debug(
248 "zebra_interface_bfd_read: Can't find interface by ifindex: %d ",
249 ifindex);
250 return NULL;
251 }
252 }
253
254 /* Fetch destination address. */
255 STREAM_GETC(s, dp->family);
256
257 plen = prefix_blen(dp);
258 STREAM_GET(&dp->u.prefix, s, plen);
259 STREAM_GETC(s, dp->prefixlen);
260
261 /* Get BFD status. */
262 STREAM_GETL(s, (*status));
263
264 STREAM_GETC(s, sp->family);
265
266 plen = prefix_blen(sp);
267 STREAM_GET(&sp->u.prefix, s, plen);
268 STREAM_GETC(s, sp->prefixlen);
269
270 STREAM_GETC(s, local_remote_cbit);
271 if (remote_cbit)
272 *remote_cbit = local_remote_cbit;
273 return ifp;
274
275 stream_failure:
276 return NULL;
277 }
278
279 /*
280 * bfd_get_status_str - Convert BFD status to a display string.
281 */
282 const char *bfd_get_status_str(int status)
283 {
284 switch (status) {
285 case BFD_STATUS_DOWN:
286 return "Down";
287 case BFD_STATUS_UP:
288 return "Up";
289 case BFD_STATUS_ADMIN_DOWN:
290 return "Admin Down";
291 case BFD_STATUS_UNKNOWN:
292 default:
293 return "Unknown";
294 }
295 }
296
297 /*
298 * bfd_last_update - Calculate the last BFD update time and convert it
299 * into a dd:hh:mm:ss display format.
300 */
301 static void bfd_last_update(time_t last_update, char *buf, size_t len)
302 {
303 time_t curr;
304 time_t diff;
305 struct tm tm;
306 struct timeval tv;
307
308 /* If no BFD satatus update has ever been received, print `never'. */
309 if (last_update == 0) {
310 snprintf(buf, len, "never");
311 return;
312 }
313
314 /* Get current time. */
315 monotime(&tv);
316 curr = tv.tv_sec;
317 diff = curr - last_update;
318 gmtime_r(&diff, &tm);
319
320 snprintf(buf, len, "%d:%02d:%02d:%02d", tm.tm_yday, tm.tm_hour,
321 tm.tm_min, tm.tm_sec);
322 }
323
324 /*
325 * bfd_show_param - Show the BFD parameter information.
326 */
327 void bfd_show_param(struct vty *vty, struct bfd_info *bfd_info, int bfd_tag,
328 int extra_space, bool use_json, json_object *json_obj)
329 {
330 json_object *json_bfd = NULL;
331
332 if (!bfd_info)
333 return;
334
335 if (use_json) {
336 if (bfd_tag)
337 json_bfd = json_object_new_object();
338 else
339 json_bfd = json_obj;
340
341 json_object_int_add(json_bfd, "detectMultiplier",
342 bfd_info->detect_mult);
343 json_object_int_add(json_bfd, "rxMinInterval",
344 bfd_info->required_min_rx);
345 json_object_int_add(json_bfd, "txMinInterval",
346 bfd_info->desired_min_tx);
347 if (bfd_tag)
348 json_object_object_add(json_obj, "peerBfdInfo",
349 json_bfd);
350 } else {
351 vty_out(vty,
352 " %s%sDetect Multiplier: %d, Min Rx interval: %d, Min Tx interval: %d\n",
353 (extra_space) ? " " : "", (bfd_tag) ? "BFD: " : " ",
354 bfd_info->detect_mult, bfd_info->required_min_rx,
355 bfd_info->desired_min_tx);
356 }
357 }
358
359 /*
360 * bfd_show_status - Show the BFD status information.
361 */
362 static void bfd_show_status(struct vty *vty, struct bfd_info *bfd_info,
363 int bfd_tag, int extra_space, bool use_json,
364 json_object *json_bfd)
365 {
366 char time_buf[32];
367
368 if (!bfd_info)
369 return;
370
371 bfd_last_update(bfd_info->last_update, time_buf, 32);
372 if (use_json) {
373 json_object_string_add(json_bfd, "status",
374 bfd_get_status_str(bfd_info->status));
375 json_object_string_add(json_bfd, "lastUpdate", time_buf);
376 } else {
377 vty_out(vty, " %s%sStatus: %s, Last update: %s\n",
378 (extra_space) ? " " : "", (bfd_tag) ? "BFD: " : " ",
379 bfd_get_status_str(bfd_info->status), time_buf);
380 }
381 }
382
383 /*
384 * bfd_show_info - Show the BFD information.
385 */
386 void bfd_show_info(struct vty *vty, struct bfd_info *bfd_info, int multihop,
387 int extra_space, bool use_json, json_object *json_obj)
388 {
389 json_object *json_bfd = NULL;
390
391 if (!bfd_info)
392 return;
393
394 if (use_json) {
395 json_bfd = json_object_new_object();
396 if (multihop)
397 json_object_string_add(json_bfd, "type", "multi hop");
398 else
399 json_object_string_add(json_bfd, "type", "single hop");
400 } else {
401 vty_out(vty, " %sBFD: Type: %s\n", (extra_space) ? " " : "",
402 (multihop) ? "multi hop" : "single hop");
403 }
404
405 bfd_show_param(vty, bfd_info, 0, extra_space, use_json, json_bfd);
406 bfd_show_status(vty, bfd_info, 0, extra_space, use_json, json_bfd);
407
408 if (use_json)
409 json_object_object_add(json_obj, "peerBfdInfo", json_bfd);
410 else
411 vty_out(vty, "\n");
412 }
413
414 /*
415 * bfd_client_sendmsg - Format and send a client register
416 * command to Zebra to be forwarded to BFD
417 */
418 void bfd_client_sendmsg(struct zclient *zclient, int command,
419 vrf_id_t vrf_id)
420 {
421 struct stream *s;
422 enum zclient_send_status ret;
423
424 /* Check socket. */
425 if (!zclient || zclient->sock < 0) {
426 if (bfd_debug)
427 zlog_debug(
428 "%s: Can't send BFD client register, Zebra client not established",
429 __func__);
430 return;
431 }
432
433 s = zclient->obuf;
434 stream_reset(s);
435 zclient_create_header(s, command, vrf_id);
436
437 stream_putl(s, getpid());
438
439 stream_putw_at(s, 0, stream_get_endp(s));
440
441 ret = zclient_send_message(zclient);
442
443 if (ret == ZCLIENT_SEND_FAILURE) {
444 if (bfd_debug)
445 zlog_debug(
446 "bfd_client_sendmsg %ld: zclient_send_message() failed",
447 (long)getpid());
448 return;
449 }
450
451 return;
452 }
453
454 int zclient_bfd_command(struct zclient *zc, struct bfd_session_arg *args)
455 {
456 struct stream *s;
457 size_t addrlen;
458
459 /* Individual reg/dereg messages are suppressed during shutdown. */
460 if (CHECK_FLAG(bfd_gbl.flags, BFD_GBL_FLAG_IN_SHUTDOWN)) {
461 if (bfd_debug)
462 zlog_debug(
463 "%s: Suppressing BFD peer reg/dereg messages",
464 __func__);
465 return -1;
466 }
467
468 /* Check socket. */
469 if (!zc || zc->sock < 0) {
470 if (bfd_debug)
471 zlog_debug("%s: zclient unavailable", __func__);
472 return -1;
473 }
474
475 s = zc->obuf;
476 stream_reset(s);
477
478 /* Create new message. */
479 zclient_create_header(s, args->command, args->vrf_id);
480 stream_putl(s, getpid());
481
482 /* Encode destination address. */
483 stream_putw(s, args->family);
484 addrlen = (args->family == AF_INET) ? sizeof(struct in_addr)
485 : sizeof(struct in6_addr);
486 stream_put(s, &args->dst, addrlen);
487
488 /*
489 * For more BFD integration protocol details, see function
490 * `_ptm_msg_read` in `bfdd/ptm_adapter.c`.
491 */
492 #if HAVE_BFDD > 0
493 /* Session timers. */
494 stream_putl(s, args->min_rx);
495 stream_putl(s, args->min_tx);
496 stream_putc(s, args->detection_multiplier);
497
498 /* Is multi hop? */
499 stream_putc(s, args->mhop != 0);
500
501 /* Source address. */
502 stream_putw(s, args->family);
503 stream_put(s, &args->src, addrlen);
504
505 /* Send the expected TTL. */
506 stream_putc(s, args->ttl);
507
508 /* Send interface name if any. */
509 stream_putc(s, args->ifnamelen);
510 if (args->ifnamelen)
511 stream_put(s, args->ifname, args->ifnamelen);
512
513 /* Send the C bit indicator. */
514 stream_putc(s, args->cbit);
515
516 /* Send profile name if any. */
517 stream_putc(s, args->profilelen);
518 if (args->profilelen)
519 stream_put(s, args->profile, args->profilelen);
520 #else /* PTM BFD */
521 /* Encode timers if this is a registration message. */
522 if (args->command != ZEBRA_BFD_DEST_DEREGISTER) {
523 stream_putl(s, args->min_rx);
524 stream_putl(s, args->min_tx);
525 stream_putc(s, args->detection_multiplier);
526 }
527
528 if (args->mhop) {
529 /* Multi hop indicator. */
530 stream_putc(s, 1);
531
532 /* Multi hop always sends the source address. */
533 stream_putw(s, args->family);
534 stream_put(s, &args->src, addrlen);
535
536 /* Send the expected TTL. */
537 stream_putc(s, args->ttl);
538 } else {
539 /* Multi hop indicator. */
540 stream_putc(s, 0);
541
542 /* Single hop only sends the source address when IPv6. */
543 if (args->family == AF_INET6) {
544 stream_putw(s, args->family);
545 stream_put(s, &args->src, addrlen);
546 }
547
548 /* Send interface name if any. */
549 stream_putc(s, args->ifnamelen);
550 if (args->ifnamelen)
551 stream_put(s, args->ifname, args->ifnamelen);
552 }
553 #endif /* HAVE_BFDD */
554
555 /* Finish the message by writing the size. */
556 stream_putw_at(s, 0, stream_get_endp(s));
557
558 /* Send message to zebra. */
559 if (zclient_send_message(zc) == ZCLIENT_SEND_FAILURE) {
560 if (bfd_debug)
561 zlog_debug("%s: zclient_send_message failed", __func__);
562 return -1;
563 }
564
565 /* Write registration indicator into data structure. */
566 if (args->bfd_info && args->set_flag) {
567 if (args->command == ZEBRA_BFD_DEST_REGISTER)
568 SET_FLAG(args->bfd_info->flags, BFD_FLAG_BFD_REG);
569 else if (args->command == ZEBRA_BFD_DEST_DEREGISTER)
570 UNSET_FLAG(args->bfd_info->flags, BFD_FLAG_BFD_REG);
571 }
572
573 return 0;
574 }
575
576 /**
577 * BFD protocol integration configuration.
578 */
579
580 /** Events definitions. */
581 enum bfd_session_event {
582 /** Remove the BFD session configuration. */
583 BSE_UNINSTALL,
584 /** Install the BFD session configuration. */
585 BSE_INSTALL,
586 };
587
588 /**
589 * Data structure to do the necessary tricks to hide the BFD protocol
590 * integration internals.
591 */
592 struct bfd_session_params {
593 /** Contains the session parameters and more. */
594 struct bfd_session_arg args;
595 /** Contains the session state. */
596 struct bfd_session_status bss;
597 /** Protocol implementation status update callback. */
598 bsp_status_update updatecb;
599 /** Protocol implementation custom data pointer. */
600 void *arg;
601
602 /**
603 * Next event.
604 *
605 * This variable controls what action to execute when the command batch
606 * finishes. Normally we'd use `thread_add_event` value, however since
607 * that function is going to be called multiple times and the value
608 * might be different we'll use this variable to keep track of it.
609 */
610 enum bfd_session_event lastev;
611 /**
612 * BFD session configuration event.
613 *
614 * Multiple actions might be asked during a command batch (either via
615 * configuration load or northbound batch), so we'll use this to
616 * install/uninstall the BFD session parameters only once.
617 */
618 struct thread *installev;
619
620 /** BFD session installation state. */
621 bool installed;
622
623 /** Global BFD paramaters list. */
624 TAILQ_ENTRY(bfd_session_params) entry;
625 };
626
627 struct bfd_sessions_global {
628 /**
629 * Global BFD session parameters list for (re)installation and update
630 * without code duplication among daemons.
631 */
632 TAILQ_HEAD(bsplist, bfd_session_params) bsplist;
633
634 /** Pointer to FRR's event manager. */
635 struct thread_master *tm;
636 /** Pointer to zebra client data structure. */
637 struct zclient *zc;
638
639 /** Debugging state. */
640 bool debugging;
641 /** Is shutting down? */
642 bool shutting_down;
643 };
644
645 /** Global configuration variable. */
646 static struct bfd_sessions_global bsglobal;
647
648 /** Global empty address for IPv4/IPv6. */
649 static const struct in6_addr i6a_zero;
650
651 struct bfd_session_params *bfd_sess_new(bsp_status_update updatecb, void *arg)
652 {
653 struct bfd_session_params *bsp;
654
655 bsp = XCALLOC(MTYPE_BFD_INFO, sizeof(*bsp));
656
657 /* Save application data. */
658 bsp->updatecb = updatecb;
659 bsp->arg = arg;
660
661 /* Set defaults. */
662 bsp->args.detection_multiplier = BFD_DEF_DETECT_MULT;
663 bsp->args.ttl = BFD_SINGLE_HOP_TTL;
664 bsp->args.min_rx = BFD_DEF_MIN_RX;
665 bsp->args.min_tx = BFD_DEF_MIN_TX;
666 bsp->args.vrf_id = VRF_DEFAULT;
667
668 /* Register in global list. */
669 TAILQ_INSERT_TAIL(&bsglobal.bsplist, bsp, entry);
670
671 return bsp;
672 }
673
674 static bool _bfd_sess_valid(const struct bfd_session_params *bsp)
675 {
676 /* Peer/local address not configured. */
677 if (bsp->args.family == 0)
678 return false;
679
680 /* Address configured but invalid. */
681 if (bsp->args.family != AF_INET && bsp->args.family != AF_INET6) {
682 if (bsglobal.debugging)
683 zlog_debug("%s: invalid session family: %d", __func__,
684 bsp->args.family);
685 return false;
686 }
687
688 /* Invalid address. */
689 if (memcmp(&bsp->args.dst, &i6a_zero, sizeof(i6a_zero)) == 0) {
690 if (bsglobal.debugging) {
691 if (bsp->args.family == AF_INET)
692 zlog_debug("%s: invalid address: %pI4",
693 __func__,
694 (struct in_addr *)&bsp->args.dst);
695 else
696 zlog_debug("%s: invalid address: %pI6",
697 __func__, &bsp->args.dst);
698 }
699 return false;
700 }
701
702 /* Multi hop requires local address. */
703 if (bsp->args.mhop
704 && memcmp(&i6a_zero, &bsp->args.src, sizeof(i6a_zero)) == 0) {
705 if (bsglobal.debugging)
706 zlog_debug(
707 "%s: multi hop but no local address provided",
708 __func__);
709 return false;
710 }
711
712 /* Check VRF ID. */
713 if (bsp->args.vrf_id == VRF_UNKNOWN) {
714 if (bsglobal.debugging)
715 zlog_debug("%s: asked for unknown VRF", __func__);
716 return false;
717 }
718
719 return true;
720 }
721
722 static int _bfd_sess_send(struct thread *t)
723 {
724 struct bfd_session_params *bsp = THREAD_ARG(t);
725 int rv;
726
727 /* Validate configuration before trying to send bogus data. */
728 if (!_bfd_sess_valid(bsp))
729 return 0;
730
731 if (bsp->lastev == BSE_INSTALL) {
732 bsp->args.command = bsp->installed ? ZEBRA_BFD_DEST_UPDATE
733 : ZEBRA_BFD_DEST_REGISTER;
734 } else
735 bsp->args.command = ZEBRA_BFD_DEST_DEREGISTER;
736
737 /* If not installed and asked for uninstall, do nothing. */
738 if (!bsp->installed && bsp->args.command == ZEBRA_BFD_DEST_DEREGISTER)
739 return 0;
740
741 rv = zclient_bfd_command(bsglobal.zc, &bsp->args);
742 /* Command was sent successfully. */
743 if (rv == 0) {
744 /* Update installation status. */
745 if (bsp->args.command == ZEBRA_BFD_DEST_DEREGISTER)
746 bsp->installed = false;
747 else if (bsp->args.command == ZEBRA_BFD_DEST_REGISTER)
748 bsp->installed = true;
749 } else {
750 struct ipaddr src, dst;
751
752 src.ipa_type = bsp->args.family;
753 src.ipaddr_v6 = bsp->args.src;
754 dst.ipa_type = bsp->args.family;
755 dst.ipaddr_v6 = bsp->args.dst;
756
757 zlog_err(
758 "%s: BFD session %pIA -> %pIA interface %s VRF %s(%u) was not %s",
759 __func__, &src, &dst,
760 bsp->args.ifnamelen ? bsp->args.ifname : "*",
761 vrf_id_to_name(bsp->args.vrf_id), bsp->args.vrf_id,
762 bsp->lastev == BSE_INSTALL ? "installed"
763 : "uninstalled");
764 }
765
766 return 0;
767 }
768
769 static void _bfd_sess_remove(struct bfd_session_params *bsp)
770 {
771 /* Not installed, nothing to do. */
772 if (!bsp->installed)
773 return;
774
775 /* Cancel any pending installation request. */
776 THREAD_OFF(bsp->installev);
777
778 /* Send request to remove any session. */
779 bsp->lastev = BSE_UNINSTALL;
780 thread_execute(bsglobal.tm, _bfd_sess_send, bsp, 0);
781 }
782
783 void bfd_sess_free(struct bfd_session_params **bsp)
784 {
785 if (*bsp == NULL)
786 return;
787
788 /* Remove any installed session. */
789 _bfd_sess_remove(*bsp);
790
791 /* Remove from global list. */
792 TAILQ_REMOVE(&bsglobal.bsplist, (*bsp), entry);
793
794 /* Free the memory and point to NULL. */
795 XFREE(MTYPE_BFD_INFO, (*bsp));
796 }
797
798 static bool bfd_sess_address_changed(const struct bfd_session_params *bsp,
799 uint32_t family,
800 const struct in6_addr *src,
801 const struct in6_addr *dst)
802 {
803 size_t addrlen;
804
805 if (bsp->args.family != family)
806 return true;
807
808 addrlen = (family == AF_INET) ? sizeof(struct in_addr)
809 : sizeof(struct in6_addr);
810 if ((src == NULL && memcmp(&bsp->args.src, &i6a_zero, addrlen))
811 || (src && memcmp(src, &bsp->args.src, addrlen))
812 || memcmp(dst, &bsp->args.dst, addrlen))
813 return true;
814
815 return false;
816 }
817
818 void bfd_sess_set_ipv4_addrs(struct bfd_session_params *bsp,
819 struct in_addr *src, struct in_addr *dst)
820 {
821 if (!bfd_sess_address_changed(bsp, AF_INET, (struct in6_addr *)src,
822 (struct in6_addr *)dst))
823 return;
824
825 /* If already installed, remove the old setting. */
826 _bfd_sess_remove(bsp);
827
828 bsp->args.family = AF_INET;
829
830 /* Clean memory, set zero value and avoid static analyser warnings. */
831 memset(&bsp->args.src, 0, sizeof(bsp->args.src));
832 memset(&bsp->args.dst, 0, sizeof(bsp->args.dst));
833
834 /* Copy the equivalent of IPv4 to arguments structure. */
835 if (src)
836 memcpy(&bsp->args.src, src, sizeof(struct in_addr));
837
838 assert(dst);
839 memcpy(&bsp->args.dst, dst, sizeof(struct in_addr));
840 }
841
842 void bfd_sess_set_ipv6_addrs(struct bfd_session_params *bsp,
843 struct in6_addr *src, struct in6_addr *dst)
844 {
845 if (!bfd_sess_address_changed(bsp, AF_INET, (struct in6_addr *)src,
846 (struct in6_addr *)dst))
847 return;
848
849 /* If already installed, remove the old setting. */
850 _bfd_sess_remove(bsp);
851
852 bsp->args.family = AF_INET6;
853
854 /* Clean memory, set zero value and avoid static analyser warnings. */
855 memset(&bsp->args.src, 0, sizeof(bsp->args.src));
856
857 if (src)
858 bsp->args.src = *src;
859
860 assert(dst);
861 bsp->args.dst = *dst;
862 }
863
864 void bfd_sess_set_interface(struct bfd_session_params *bsp, const char *ifname)
865 {
866 if ((ifname == NULL && bsp->args.ifnamelen == 0)
867 || (ifname && strcmp(bsp->args.ifname, ifname) == 0))
868 return;
869
870 /* If already installed, remove the old setting. */
871 _bfd_sess_remove(bsp);
872
873 if (ifname == NULL) {
874 bsp->args.ifname[0] = 0;
875 bsp->args.ifnamelen = 0;
876 return;
877 }
878
879 if (strlcpy(bsp->args.ifname, ifname, sizeof(bsp->args.ifname))
880 > sizeof(bsp->args.ifname))
881 zlog_warn("%s: interface name truncated: %s", __func__, ifname);
882
883 bsp->args.ifnamelen = strlen(bsp->args.ifname);
884 }
885
886 void bfd_sess_set_profile(struct bfd_session_params *bsp, const char *profile)
887 {
888 if (profile == NULL) {
889 bsp->args.profile[0] = 0;
890 bsp->args.profilelen = 0;
891 return;
892 }
893
894 if (strlcpy(bsp->args.profile, profile, sizeof(bsp->args.profile))
895 > sizeof(bsp->args.profile))
896 zlog_warn("%s: profile name truncated: %s", __func__, profile);
897
898 bsp->args.profilelen = strlen(bsp->args.profile);
899 }
900
901 void bfd_sess_set_vrf(struct bfd_session_params *bsp, vrf_id_t vrf_id)
902 {
903 if (bsp->args.vrf_id == vrf_id)
904 return;
905
906 /* If already installed, remove the old setting. */
907 _bfd_sess_remove(bsp);
908
909 bsp->args.vrf_id = vrf_id;
910 }
911
912 void bfd_sess_set_mininum_ttl(struct bfd_session_params *bsp, uint8_t min_ttl)
913 {
914 assert(min_ttl != 0);
915
916 if (bsp->args.ttl == ((BFD_SINGLE_HOP_TTL + 1) - min_ttl))
917 return;
918
919 /* If already installed, remove the old setting. */
920 _bfd_sess_remove(bsp);
921
922 /* Invert TTL value: protocol expects number of hops. */
923 min_ttl = (BFD_SINGLE_HOP_TTL + 1) - min_ttl;
924 bsp->args.ttl = min_ttl;
925 bsp->args.mhop = (min_ttl > 1);
926 }
927
928 void bfd_sess_set_hop_count(struct bfd_session_params *bsp, uint8_t min_ttl)
929 {
930 if (bsp->args.ttl == min_ttl)
931 return;
932
933 /* If already installed, remove the old setting. */
934 _bfd_sess_remove(bsp);
935
936 bsp->args.ttl = min_ttl;
937 bsp->args.mhop = (min_ttl > 1);
938 }
939
940
941 void bfd_sess_set_cbit(struct bfd_session_params *bsp, bool enable)
942 {
943 bsp->args.cbit = enable;
944 }
945
946 void bfd_sess_set_timers(struct bfd_session_params *bsp,
947 uint8_t detection_multiplier, uint32_t min_rx,
948 uint32_t min_tx)
949 {
950 bsp->args.detection_multiplier = detection_multiplier;
951 bsp->args.min_rx = min_rx;
952 bsp->args.min_tx = min_tx;
953 }
954
955 void bfd_sess_install(struct bfd_session_params *bsp)
956 {
957 bsp->lastev = BSE_INSTALL;
958 thread_add_event(bsglobal.tm, _bfd_sess_send, bsp, 0, &bsp->installev);
959 }
960
961 void bfd_sess_uninstall(struct bfd_session_params *bsp)
962 {
963 bsp->lastev = BSE_UNINSTALL;
964 thread_add_event(bsglobal.tm, _bfd_sess_send, bsp, 0, &bsp->installev);
965 }
966
967 enum bfd_session_state bfd_sess_status(const struct bfd_session_params *bsp)
968 {
969 return bsp->bss.state;
970 }
971
972 uint8_t bfd_sess_minimum_ttl(const struct bfd_session_params *bsp)
973 {
974 return ((BFD_SINGLE_HOP_TTL + 1) - bsp->args.ttl);
975 }
976
977 uint8_t bfd_sess_hop_count(const struct bfd_session_params *bsp)
978 {
979 return bsp->args.ttl;
980 }
981
982 const char *bfd_sess_profile(const struct bfd_session_params *bsp)
983 {
984 return bsp->args.profilelen ? bsp->args.profile : NULL;
985 }
986
987 void bfd_sess_addresses(const struct bfd_session_params *bsp, int *family,
988 struct in6_addr *src, struct in6_addr *dst)
989 {
990 *family = bsp->args.family;
991 if (src)
992 *src = bsp->args.src;
993 if (dst)
994 *dst = bsp->args.dst;
995 }
996
997 const char *bfd_sess_interface(const struct bfd_session_params *bsp)
998 {
999 if (bsp->args.ifnamelen)
1000 return bsp->args.ifname;
1001
1002 return NULL;
1003 }
1004
1005 const char *bfd_sess_vrf(const struct bfd_session_params *bsp)
1006 {
1007 return vrf_id_to_name(bsp->args.vrf_id);
1008 }
1009
1010 vrf_id_t bfd_sess_vrf_id(const struct bfd_session_params *bsp)
1011 {
1012 return bsp->args.vrf_id;
1013 }
1014
1015 bool bfd_sess_cbit(const struct bfd_session_params *bsp)
1016 {
1017 return bsp->args.cbit;
1018 }
1019
1020 void bfd_sess_timers(const struct bfd_session_params *bsp,
1021 uint8_t *detection_multiplier, uint32_t *min_rx,
1022 uint32_t *min_tx)
1023 {
1024 *detection_multiplier = bsp->args.detection_multiplier;
1025 *min_rx = bsp->args.min_rx;
1026 *min_tx = bsp->args.min_tx;
1027 }
1028
1029 void bfd_sess_show(struct vty *vty, struct json_object *json,
1030 struct bfd_session_params *bsp)
1031 {
1032 json_object *json_bfd = NULL;
1033 char time_buf[64];
1034
1035 if (!bsp)
1036 return;
1037
1038 /* Show type. */
1039 if (json) {
1040 json_bfd = json_object_new_object();
1041 if (bsp->args.mhop)
1042 json_object_string_add(json_bfd, "type", "multi hop");
1043 else
1044 json_object_string_add(json_bfd, "type", "single hop");
1045 } else
1046 vty_out(vty, " BFD: Type: %s\n",
1047 bsp->args.mhop ? "multi hop" : "single hop");
1048
1049 /* Show configuration. */
1050 if (json) {
1051 json_object_int_add(json_bfd, "detectMultiplier",
1052 bsp->args.detection_multiplier);
1053 json_object_int_add(json_bfd, "rxMinInterval",
1054 bsp->args.min_rx);
1055 json_object_int_add(json_bfd, "txMinInterval",
1056 bsp->args.min_tx);
1057 } else {
1058 vty_out(vty,
1059 " Detect Multiplier: %d, Min Rx interval: %d, Min Tx interval: %d\n",
1060 bsp->args.detection_multiplier, bsp->args.min_rx,
1061 bsp->args.min_tx);
1062 }
1063
1064 bfd_last_update(bsp->bss.last_event, time_buf, sizeof(time_buf));
1065 if (json) {
1066 json_object_string_add(json_bfd, "status",
1067 bfd_get_status_str(bsp->bss.state));
1068 json_object_string_add(json_bfd, "lastUpdate", time_buf);
1069 } else
1070 vty_out(vty, " Status: %s, Last update: %s\n",
1071 bfd_get_status_str(bsp->bss.state), time_buf);
1072
1073 if (json)
1074 json_object_object_add(json, "peerBfdInfo", json_bfd);
1075 else
1076 vty_out(vty, "\n");
1077 }
1078
1079 /*
1080 * Zebra communication related.
1081 */
1082
1083 /**
1084 * Callback for reinstallation of all registered BFD sessions.
1085 *
1086 * Use this as `zclient` `bfd_dest_replay` callback.
1087 */
1088 static int zclient_bfd_session_reply(ZAPI_CALLBACK_ARGS)
1089 {
1090 struct bfd_session_params *bsp;
1091
1092 /* Do nothing when shutting down. */
1093 if (bsglobal.shutting_down)
1094 return 0;
1095
1096 if (bsglobal.debugging)
1097 zlog_debug("%s: sending all sessions registered", __func__);
1098
1099 /* Send the client registration */
1100 bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, vrf_id);
1101
1102 /* Replay all activated peers. */
1103 TAILQ_FOREACH (bsp, &bsglobal.bsplist, entry) {
1104 /* Skip not installed sessions. */
1105 if (!bsp->installed)
1106 continue;
1107
1108 /* We are reconnecting, so we must send installation. */
1109 bsp->installed = false;
1110
1111 /* Cancel any pending installation request. */
1112 THREAD_OFF(bsp->installev);
1113
1114 /* Ask for installation. */
1115 bsp->lastev = BSE_INSTALL;
1116 thread_execute(bsglobal.tm, _bfd_sess_send, bsp, 0);
1117 }
1118
1119 return 0;
1120 }
1121
1122 static int zclient_bfd_session_update(ZAPI_CALLBACK_ARGS)
1123 {
1124 struct bfd_session_params *bsp, *bspn;
1125 size_t sessions_updated = 0;
1126 struct interface *ifp;
1127 int remote_cbit = false;
1128 int state = BFD_STATUS_UNKNOWN;
1129 time_t now;
1130 size_t addrlen;
1131 struct prefix dp;
1132 struct prefix sp;
1133 char ifstr[128], cbitstr[32];
1134
1135 /* Do nothing when shutting down. */
1136 if (bsglobal.shutting_down)
1137 return 0;
1138
1139 ifp = bfd_get_peer_info(zclient->ibuf, &dp, &sp, &state, &remote_cbit,
1140 vrf_id);
1141 /*
1142 * When interface lookup fails or an invalid stream is read, we must
1143 * not proceed otherwise it will trigger an assertion while checking
1144 * family type below.
1145 */
1146 if (dp.family == 0 || sp.family == 0)
1147 return 0;
1148
1149 if (bsglobal.debugging) {
1150 ifstr[0] = 0;
1151 if (ifp)
1152 snprintf(ifstr, sizeof(ifstr), " (interface %s)",
1153 ifp->name);
1154
1155 snprintf(cbitstr, sizeof(cbitstr), " (CPI bit %s)",
1156 remote_cbit ? "yes" : "no");
1157
1158 zlog_debug("%s: %pFX -> %pFX%s VRF %s(%u)%s: %s", __func__, &sp,
1159 &dp, ifstr, vrf_id_to_name(vrf_id), vrf_id, cbitstr,
1160 bfd_get_status_str(state));
1161 }
1162
1163 switch (dp.family) {
1164 case AF_INET:
1165 addrlen = sizeof(struct in_addr);
1166 break;
1167 case AF_INET6:
1168 addrlen = sizeof(struct in6_addr);
1169 break;
1170
1171 default:
1172 /* Unexpected value. */
1173 assert(0);
1174 break;
1175 }
1176
1177 /* Cache current time to avoid multiple monotime clock calls. */
1178 now = monotime(NULL);
1179
1180 /* Notify all matching sessions about update. */
1181 TAILQ_FOREACH_SAFE (bsp, &bsglobal.bsplist, entry, bspn) {
1182 /* Skip not installed entries. */
1183 if (!bsp->installed)
1184 continue;
1185 /* Skip different VRFs. */
1186 if (bsp->args.vrf_id != vrf_id)
1187 continue;
1188 /* Skip different families. */
1189 if (bsp->args.family != dp.family)
1190 continue;
1191 /* Skip different interface. */
1192 if (bsp->args.ifnamelen && ifp
1193 && strcmp(bsp->args.ifname, ifp->name) != 0)
1194 continue;
1195 /* Skip non matching destination addresses. */
1196 if (memcmp(&bsp->args.dst, &dp.u, addrlen) != 0)
1197 continue;
1198 /*
1199 * Source comparison test:
1200 * We will only compare source if BFD daemon provided the
1201 * source address and the protocol set a source address in
1202 * the configuration otherwise we'll just skip it.
1203 */
1204 if (sp.family && memcmp(&bsp->args.src, &i6a_zero, addrlen) != 0
1205 && memcmp(&sp.u, &i6a_zero, addrlen) != 0
1206 && memcmp(&bsp->args.src, &sp.u, addrlen) != 0)
1207 continue;
1208 /* No session state change. */
1209 if ((int)bsp->bss.state == state)
1210 continue;
1211
1212 bsp->bss.last_event = now;
1213 bsp->bss.previous_state = bsp->bss.state;
1214 bsp->bss.state = state;
1215 bsp->bss.remote_cbit = remote_cbit;
1216 bsp->updatecb(bsp, &bsp->bss, bsp->arg);
1217 sessions_updated++;
1218 }
1219
1220 if (bsglobal.debugging)
1221 zlog_debug("%s: sessions updated: %zu", __func__,
1222 sessions_updated);
1223
1224 return 0;
1225 }
1226
1227 void bfd_protocol_integration_init(struct zclient *zc, struct thread_master *tm)
1228 {
1229 /* Initialize data structure. */
1230 TAILQ_INIT(&bsglobal.bsplist);
1231
1232 /* Copy pointers. */
1233 bsglobal.zc = zc;
1234 bsglobal.tm = tm;
1235
1236 /* Install our callbacks. */
1237 zc->interface_bfd_dest_update = zclient_bfd_session_update;
1238 zc->bfd_dest_replay = zclient_bfd_session_reply;
1239
1240 /* Send the client registration */
1241 bfd_client_sendmsg(zc, ZEBRA_BFD_CLIENT_REGISTER, VRF_DEFAULT);
1242 }
1243
1244 void bfd_protocol_integration_set_debug(bool enable)
1245 {
1246 bsglobal.debugging = enable;
1247 }
1248
1249 void bfd_protocol_integration_set_shutdown(bool enable)
1250 {
1251 bsglobal.shutting_down = enable;
1252 }
1253
1254 bool bfd_protocol_integration_debug(void)
1255 {
1256 return bsglobal.debugging;
1257 }
1258
1259 bool bfd_protocol_integration_shutting_down(void)
1260 {
1261 return bsglobal.shutting_down;
1262 }