]> git.proxmox.com Git - mirror_frr.git/blob - lib/bfd.c
lib: new BFD integration API
[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 ifindex = stream_getl(s);
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 dp->family = stream_getc(s);
256
257 plen = prefix_blen(dp);
258 stream_get(&dp->u.prefix, s, plen);
259 dp->prefixlen = stream_getc(s);
260
261 /* Get BFD status. */
262 *status = stream_getl(s);
263
264 sp->family = stream_getc(s);
265
266 plen = prefix_blen(sp);
267 stream_get(&sp->u.prefix, s, plen);
268 sp->prefixlen = stream_getc(s);
269
270 local_remote_cbit = stream_getc(s);
271 if (remote_cbit)
272 *remote_cbit = local_remote_cbit;
273 return ifp;
274 }
275
276 /*
277 * bfd_get_status_str - Convert BFD status to a display string.
278 */
279 const char *bfd_get_status_str(int status)
280 {
281 switch (status) {
282 case BFD_STATUS_DOWN:
283 return "Down";
284 case BFD_STATUS_UP:
285 return "Up";
286 case BFD_STATUS_ADMIN_DOWN:
287 return "Admin Down";
288 case BFD_STATUS_UNKNOWN:
289 default:
290 return "Unknown";
291 }
292 }
293
294 /*
295 * bfd_last_update - Calculate the last BFD update time and convert it
296 * into a dd:hh:mm:ss display format.
297 */
298 static void bfd_last_update(time_t last_update, char *buf, size_t len)
299 {
300 time_t curr;
301 time_t diff;
302 struct tm tm;
303 struct timeval tv;
304
305 /* If no BFD satatus update has ever been received, print `never'. */
306 if (last_update == 0) {
307 snprintf(buf, len, "never");
308 return;
309 }
310
311 /* Get current time. */
312 monotime(&tv);
313 curr = tv.tv_sec;
314 diff = curr - last_update;
315 gmtime_r(&diff, &tm);
316
317 snprintf(buf, len, "%d:%02d:%02d:%02d", tm.tm_yday, tm.tm_hour,
318 tm.tm_min, tm.tm_sec);
319 }
320
321 /*
322 * bfd_show_param - Show the BFD parameter information.
323 */
324 void bfd_show_param(struct vty *vty, struct bfd_info *bfd_info, int bfd_tag,
325 int extra_space, bool use_json, json_object *json_obj)
326 {
327 json_object *json_bfd = NULL;
328
329 if (!bfd_info)
330 return;
331
332 if (use_json) {
333 if (bfd_tag)
334 json_bfd = json_object_new_object();
335 else
336 json_bfd = json_obj;
337
338 json_object_int_add(json_bfd, "detectMultiplier",
339 bfd_info->detect_mult);
340 json_object_int_add(json_bfd, "rxMinInterval",
341 bfd_info->required_min_rx);
342 json_object_int_add(json_bfd, "txMinInterval",
343 bfd_info->desired_min_tx);
344 if (bfd_tag)
345 json_object_object_add(json_obj, "peerBfdInfo",
346 json_bfd);
347 } else {
348 vty_out(vty,
349 " %s%sDetect Multiplier: %d, Min Rx interval: %d, Min Tx interval: %d\n",
350 (extra_space) ? " " : "", (bfd_tag) ? "BFD: " : " ",
351 bfd_info->detect_mult, bfd_info->required_min_rx,
352 bfd_info->desired_min_tx);
353 }
354 }
355
356 /*
357 * bfd_show_status - Show the BFD status information.
358 */
359 static void bfd_show_status(struct vty *vty, struct bfd_info *bfd_info,
360 int bfd_tag, int extra_space, bool use_json,
361 json_object *json_bfd)
362 {
363 char time_buf[32];
364
365 if (!bfd_info)
366 return;
367
368 bfd_last_update(bfd_info->last_update, time_buf, 32);
369 if (use_json) {
370 json_object_string_add(json_bfd, "status",
371 bfd_get_status_str(bfd_info->status));
372 json_object_string_add(json_bfd, "lastUpdate", time_buf);
373 } else {
374 vty_out(vty, " %s%sStatus: %s, Last update: %s\n",
375 (extra_space) ? " " : "", (bfd_tag) ? "BFD: " : " ",
376 bfd_get_status_str(bfd_info->status), time_buf);
377 }
378 }
379
380 /*
381 * bfd_show_info - Show the BFD information.
382 */
383 void bfd_show_info(struct vty *vty, struct bfd_info *bfd_info, int multihop,
384 int extra_space, bool use_json, json_object *json_obj)
385 {
386 json_object *json_bfd = NULL;
387
388 if (!bfd_info)
389 return;
390
391 if (use_json) {
392 json_bfd = json_object_new_object();
393 if (multihop)
394 json_object_string_add(json_bfd, "type", "multi hop");
395 else
396 json_object_string_add(json_bfd, "type", "single hop");
397 } else {
398 vty_out(vty, " %sBFD: Type: %s\n", (extra_space) ? " " : "",
399 (multihop) ? "multi hop" : "single hop");
400 }
401
402 bfd_show_param(vty, bfd_info, 0, extra_space, use_json, json_bfd);
403 bfd_show_status(vty, bfd_info, 0, extra_space, use_json, json_bfd);
404
405 if (use_json)
406 json_object_object_add(json_obj, "peerBfdInfo", json_bfd);
407 else
408 vty_out(vty, "\n");
409 }
410
411 /*
412 * bfd_client_sendmsg - Format and send a client register
413 * command to Zebra to be forwarded to BFD
414 */
415 void bfd_client_sendmsg(struct zclient *zclient, int command,
416 vrf_id_t vrf_id)
417 {
418 struct stream *s;
419 enum zclient_send_status ret;
420
421 /* Check socket. */
422 if (!zclient || zclient->sock < 0) {
423 if (bfd_debug)
424 zlog_debug(
425 "%s: Can't send BFD client register, Zebra client not established",
426 __func__);
427 return;
428 }
429
430 s = zclient->obuf;
431 stream_reset(s);
432 zclient_create_header(s, command, vrf_id);
433
434 stream_putl(s, getpid());
435
436 stream_putw_at(s, 0, stream_get_endp(s));
437
438 ret = zclient_send_message(zclient);
439
440 if (ret == ZCLIENT_SEND_FAILURE) {
441 if (bfd_debug)
442 zlog_debug(
443 "bfd_client_sendmsg %ld: zclient_send_message() failed",
444 (long)getpid());
445 return;
446 }
447
448 return;
449 }
450
451 int zclient_bfd_command(struct zclient *zc, struct bfd_session_arg *args)
452 {
453 struct stream *s;
454 size_t addrlen;
455
456 /* Individual reg/dereg messages are suppressed during shutdown. */
457 if (CHECK_FLAG(bfd_gbl.flags, BFD_GBL_FLAG_IN_SHUTDOWN)) {
458 if (bfd_debug)
459 zlog_debug(
460 "%s: Suppressing BFD peer reg/dereg messages",
461 __func__);
462 return -1;
463 }
464
465 /* Check socket. */
466 if (!zc || zc->sock < 0) {
467 if (bfd_debug)
468 zlog_debug("%s: zclient unavailable", __func__);
469 return -1;
470 }
471
472 s = zc->obuf;
473 stream_reset(s);
474
475 /* Create new message. */
476 zclient_create_header(s, args->command, args->vrf_id);
477 stream_putl(s, getpid());
478
479 /* Encode destination address. */
480 stream_putw(s, args->family);
481 addrlen = (args->family == AF_INET) ? sizeof(struct in_addr)
482 : sizeof(struct in6_addr);
483 stream_put(s, &args->dst, addrlen);
484
485 /*
486 * For more BFD integration protocol details, see function
487 * `_ptm_msg_read` in `bfdd/ptm_adapter.c`.
488 */
489 #if HAVE_BFDD > 0
490 /* Session timers. */
491 stream_putl(s, args->min_rx);
492 stream_putl(s, args->min_tx);
493 stream_putc(s, args->detection_multiplier);
494
495 /* Is multi hop? */
496 stream_putc(s, args->mhop != 0);
497
498 /* Source address. */
499 stream_putw(s, args->family);
500 stream_put(s, &args->src, addrlen);
501
502 /* Send the expected TTL. */
503 stream_putc(s, args->ttl);
504
505 /* Send interface name if any. */
506 stream_putc(s, args->ifnamelen);
507 if (args->ifnamelen)
508 stream_put(s, args->ifname, args->ifnamelen);
509
510 /* Send the C bit indicator. */
511 stream_putc(s, args->cbit);
512
513 /* Send profile name if any. */
514 stream_putc(s, args->profilelen);
515 if (args->profilelen)
516 stream_put(s, args->profile, args->profilelen);
517 #else /* PTM BFD */
518 /* Encode timers if this is a registration message. */
519 if (args->command != ZEBRA_BFD_DEST_DEREGISTER) {
520 stream_putl(s, args->min_rx);
521 stream_putl(s, args->min_tx);
522 stream_putc(s, args->detection_multiplier);
523 }
524
525 if (args->mhop) {
526 /* Multi hop indicator. */
527 stream_putc(s, 1);
528
529 /* Multi hop always sends the source address. */
530 stream_putw(s, args->family);
531 stream_put(s, &args->src, addrlen);
532
533 /* Send the expected TTL. */
534 stream_putc(s, args->ttl);
535 } else {
536 /* Multi hop indicator. */
537 stream_putc(s, 0);
538
539 /* Single hop only sends the source address when IPv6. */
540 if (args->family == AF_INET6) {
541 stream_putw(s, args->family);
542 stream_put(s, &args->src, addrlen);
543 }
544
545 /* Send interface name if any. */
546 stream_putc(s, args->ifnamelen);
547 if (args->ifnamelen)
548 stream_put(s, args->ifname, args->ifnamelen);
549 }
550 #endif /* HAVE_BFDD */
551
552 /* Finish the message by writing the size. */
553 stream_putw_at(s, 0, stream_get_endp(s));
554
555 /* Send message to zebra. */
556 if (zclient_send_message(zc) == ZCLIENT_SEND_FAILURE) {
557 if (bfd_debug)
558 zlog_debug("%s: zclient_send_message failed", __func__);
559 return -1;
560 }
561
562 /* Write registration indicator into data structure. */
563 if (args->bfd_info && args->set_flag) {
564 if (args->command == ZEBRA_BFD_DEST_REGISTER)
565 SET_FLAG(args->bfd_info->flags, BFD_FLAG_BFD_REG);
566 else if (args->command == ZEBRA_BFD_DEST_DEREGISTER)
567 UNSET_FLAG(args->bfd_info->flags, BFD_FLAG_BFD_REG);
568 }
569
570 return 0;
571 }
572
573 /**
574 * BFD protocol integration configuration.
575 */
576
577 /** Events definitions. */
578 enum bfd_session_event {
579 /** Remove the BFD session configuration. */
580 BSE_UNINSTALL,
581 /** Install the BFD session configuration. */
582 BSE_INSTALL,
583 };
584
585 /**
586 * Data structure to do the necessary tricks to hide the BFD protocol
587 * integration internals.
588 */
589 struct bfd_session_params {
590 /** Contains the session parameters and more. */
591 struct bfd_session_arg args;
592 /** Contains the session state. */
593 struct bfd_session_status bss;
594 /** Protocol implementation status update callback. */
595 bsp_status_update updatecb;
596 /** Protocol implementation custom data pointer. */
597 void *arg;
598
599 /**
600 * Next event.
601 *
602 * This variable controls what action to execute when the command batch
603 * finishes. Normally we'd use `thread_add_event` value, however since
604 * that function is going to be called multiple times and the value
605 * might be different we'll use this variable to keep track of it.
606 */
607 enum bfd_session_event lastev;
608 /**
609 * BFD session configuration event.
610 *
611 * Multiple actions might be asked during a command batch (either via
612 * configuration load or northbound batch), so we'll use this to
613 * install/uninstall the BFD session parameters only once.
614 */
615 struct thread *installev;
616
617 /** BFD session installation state. */
618 bool installed;
619 /** BFD session enabled. */
620 bool enabled;
621
622 /** Global BFD paramaters list. */
623 TAILQ_ENTRY(bfd_session_params) entry;
624 };
625
626 struct bfd_sessions_global {
627 /**
628 * Global BFD session parameters list for (re)installation and update
629 * without code duplication among daemons.
630 */
631 TAILQ_HEAD(bsplist, bfd_session_params) bsplist;
632
633 /** Pointer to FRR's event manager. */
634 struct thread_master *tm;
635 /** Pointer to zebra client data structure. */
636 struct zclient *zc;
637
638 /** Debugging state. */
639 bool debugging;
640 /** Is shutting down? */
641 bool shutting_down;
642 };
643
644 /** Global configuration variable. */
645 static struct bfd_sessions_global bsglobal;
646
647 /** Global empty address for IPv4/IPv6. */
648 static const struct in6_addr i6a_zero;
649
650 struct bfd_session_params *bfd_sess_new(bsp_status_update updatecb, void *arg)
651 {
652 struct bfd_session_params *bsp;
653
654 bsp = XCALLOC(MTYPE_BFD_INFO, sizeof(*bsp));
655
656 /* Save application data. */
657 bsp->updatecb = updatecb;
658 bsp->arg = arg;
659
660 /* Set defaults. */
661 bsp->args.detection_multiplier = BFD_DEF_DETECT_MULT;
662 bsp->args.ttl = BFD_SINGLE_HOP_TTL;
663 bsp->args.min_rx = BFD_DEF_MIN_RX;
664 bsp->args.min_tx = BFD_DEF_MIN_TX;
665 bsp->args.vrf_id = VRF_DEFAULT;
666
667 /* Register in global list. */
668 TAILQ_INSERT_TAIL(&bsglobal.bsplist, bsp, entry);
669
670 return bsp;
671 }
672
673 static bool _bfd_sess_valid(const struct bfd_session_params *bsp)
674 {
675 /* Peer/local address not configured. */
676 if (bsp->args.family == 0)
677 return false;
678
679 /* Address configured but invalid. */
680 if (bsp->args.family != AF_INET && bsp->args.family != AF_INET6) {
681 if (bsglobal.debugging)
682 zlog_debug("%s: invalid session family: %d", __func__,
683 bsp->args.family);
684 return false;
685 }
686
687 /* Invalid address. */
688 if (memcmp(&bsp->args.dst, &i6a_zero, sizeof(i6a_zero)) == 0) {
689 if (bsglobal.debugging) {
690 if (bsp->args.family == AF_INET)
691 zlog_debug("%s: invalid address: %pI4",
692 __func__,
693 (struct in_addr *)&bsp->args.dst);
694 else
695 zlog_debug("%s: invalid address: %pI6",
696 __func__, &bsp->args.dst);
697 }
698 return false;
699 }
700
701 /* Multi hop requires local address. */
702 if (bsp->args.mhop
703 && memcmp(&i6a_zero, &bsp->args.src, sizeof(i6a_zero)) == 0) {
704 if (bsglobal.debugging)
705 zlog_debug(
706 "%s: multi hop but no local address provided",
707 __func__);
708 return false;
709 }
710
711 /* Check VRF ID. */
712 if (bsp->args.vrf_id == VRF_UNKNOWN) {
713 if (bsglobal.debugging)
714 zlog_debug("%s: asked for unknown VRF", __func__);
715 return false;
716 }
717
718 return true;
719 }
720
721 static int _bfd_sess_send(struct thread *t)
722 {
723 struct bfd_session_params *bsp = THREAD_ARG(t);
724 int rv;
725
726 /* Validate configuration before trying to send bogus data. */
727 if (!_bfd_sess_valid(bsp))
728 return 0;
729
730 if (bsp->lastev == BSE_INSTALL) {
731 bsp->args.command = bsp->installed ? ZEBRA_BFD_DEST_UPDATE
732 : ZEBRA_BFD_DEST_REGISTER;
733 } else
734 bsp->args.command = ZEBRA_BFD_DEST_DEREGISTER;
735
736 /* If not installed and asked for uninstall, do nothing. */
737 if (!bsp->installed && bsp->args.command == ZEBRA_BFD_DEST_DEREGISTER)
738 return 0;
739
740 rv = zclient_bfd_command(bsglobal.zc, &bsp->args);
741 /* Command was sent successfully. */
742 if (rv == 0) {
743 /* Update installation status. */
744 if (bsp->args.command == ZEBRA_BFD_DEST_DEREGISTER)
745 bsp->installed = false;
746 else if (bsp->args.command == ZEBRA_BFD_DEST_REGISTER)
747 bsp->installed = true;
748 }
749
750 return 0;
751 }
752
753 static void _bfd_sess_remove(struct bfd_session_params *bsp)
754 {
755 /* Not installed, nothing to do. */
756 if (!bsp->installed)
757 return;
758
759 /* Cancel any pending installation request. */
760 THREAD_OFF(bsp->installev);
761
762 /* Send request to remove any session. */
763 bsp->lastev = BSE_UNINSTALL;
764 thread_execute(bsglobal.tm, _bfd_sess_send, bsp, 0);
765 }
766
767 void bfd_sess_free(struct bfd_session_params **bsp)
768 {
769 if (*bsp == NULL)
770 return;
771
772 /* Remove any installed session. */
773 _bfd_sess_remove(*bsp);
774
775 /* Remove from global list. */
776 TAILQ_REMOVE(&bsglobal.bsplist, (*bsp), entry);
777
778 /* Free the memory and point to NULL. */
779 XFREE(MTYPE_BFD_INFO, (*bsp));
780 }
781
782 void bfd_sess_enable(struct bfd_session_params *bsp, bool enable)
783 {
784 /* Remove the session when disabling. */
785 if (!enable)
786 _bfd_sess_remove(bsp);
787
788 bsp->enabled = enable;
789 }
790
791 void bfd_sess_set_ipv4_addrs(struct bfd_session_params *bsp,
792 struct in_addr *src, struct in_addr *dst)
793 {
794 /* If already installed, remove the old setting. */
795 _bfd_sess_remove(bsp);
796
797 bsp->args.family = AF_INET;
798
799 /* Clean memory, set zero value and avoid static analyser warnings. */
800 memset(&bsp->args.src, 0, sizeof(bsp->args.src));
801 memset(&bsp->args.dst, 0, sizeof(bsp->args.dst));
802
803 /* Copy the equivalent of IPv4 to arguments structure. */
804 if (src)
805 memcpy(&bsp->args.src, src, sizeof(struct in_addr));
806
807 assert(dst);
808 memcpy(&bsp->args.dst, dst, sizeof(struct in_addr));
809 }
810
811 void bfd_sess_set_ipv6_addrs(struct bfd_session_params *bsp,
812 struct in6_addr *src, struct in6_addr *dst)
813 {
814 /* If already installed, remove the old setting. */
815 _bfd_sess_remove(bsp);
816
817 bsp->args.family = AF_INET6;
818
819 /* Clean memory, set zero value and avoid static analyser warnings. */
820 memset(&bsp->args.src, 0, sizeof(bsp->args.src));
821
822 if (src)
823 bsp->args.src = *src;
824
825 assert(dst);
826 bsp->args.dst = *dst;
827 }
828
829 void bfd_sess_set_interface(struct bfd_session_params *bsp, const char *ifname)
830 {
831 /* If already installed, remove the old setting. */
832 _bfd_sess_remove(bsp);
833
834 if (ifname == NULL) {
835 bsp->args.ifname[0] = 0;
836 bsp->args.ifnamelen = 0;
837 return;
838 }
839
840 if (strlcpy(bsp->args.ifname, ifname, sizeof(bsp->args.ifname))
841 > sizeof(bsp->args.ifname))
842 zlog_warn("%s: interface name truncated: %s", __func__, ifname);
843
844 bsp->args.ifnamelen = strlen(bsp->args.ifname);
845 }
846
847 void bfd_sess_set_profile(struct bfd_session_params *bsp, const char *profile)
848 {
849 if (profile == NULL) {
850 bsp->args.profile[0] = 0;
851 bsp->args.profilelen = 0;
852 return;
853 }
854
855 if (strlcpy(bsp->args.profile, profile, sizeof(bsp->args.profile))
856 > sizeof(bsp->args.profile))
857 zlog_warn("%s: profile name truncated: %s", __func__, profile);
858
859 bsp->args.profilelen = strlen(bsp->args.profile);
860 }
861
862 void bfd_sess_set_vrf(struct bfd_session_params *bsp, vrf_id_t vrf_id)
863 {
864 /* If already installed, remove the old setting. */
865 _bfd_sess_remove(bsp);
866
867 bsp->args.vrf_id = vrf_id;
868 }
869
870 void bfd_sess_set_mininum_ttl(struct bfd_session_params *bsp, uint8_t min_ttl)
871 {
872 assert(min_ttl != 0);
873
874 /* If already installed, remove the old setting. */
875 _bfd_sess_remove(bsp);
876
877 /* Invert TTL value: protocol expects number of hops. */
878 min_ttl = (BFD_SINGLE_HOP_TTL + 1) - min_ttl;
879 bsp->args.ttl = min_ttl;
880 bsp->args.mhop = (min_ttl > 1);
881 }
882
883 void bfd_sess_set_hop_count(struct bfd_session_params *bsp, uint8_t min_ttl)
884 {
885 /* If already installed, remove the old setting. */
886 _bfd_sess_remove(bsp);
887
888 bsp->args.ttl = min_ttl;
889 bsp->args.mhop = (min_ttl > 1);
890 }
891
892
893 void bfd_sess_set_cbit(struct bfd_session_params *bsp, bool enable)
894 {
895 bsp->args.cbit = enable;
896 }
897
898 void bfd_sess_set_timers(struct bfd_session_params *bsp,
899 uint8_t detection_multiplier, uint32_t min_rx,
900 uint32_t min_tx)
901 {
902 bsp->args.detection_multiplier = detection_multiplier;
903 bsp->args.min_rx = min_rx;
904 bsp->args.min_tx = min_tx;
905 }
906
907 void bfd_sess_install(struct bfd_session_params *bsp)
908 {
909 /* Don't attempt to install/update when disabled. */
910 if (!bsp->enabled)
911 return;
912
913 bsp->lastev = BSE_INSTALL;
914 thread_add_event(bsglobal.tm, _bfd_sess_send, bsp, 0, &bsp->installev);
915 }
916
917 void bfd_sess_uninstall(struct bfd_session_params *bsp)
918 {
919 bsp->lastev = BSE_UNINSTALL;
920 thread_add_event(bsglobal.tm, _bfd_sess_send, bsp, 0, &bsp->installev);
921 }
922
923 enum bfd_session_state bfd_sess_status(const struct bfd_session_params *bsp)
924 {
925 return bsp->bss.state;
926 }
927
928 uint8_t bfd_sess_minimum_ttl(const struct bfd_session_params *bsp)
929 {
930 return ((BFD_SINGLE_HOP_TTL + 1) - bsp->args.ttl);
931 }
932
933 uint8_t bfd_sess_hop_count(const struct bfd_session_params *bsp)
934 {
935 return bsp->args.ttl;
936 }
937
938 const char *bfd_sess_profile(const struct bfd_session_params *bsp)
939 {
940 return bsp->args.profilelen ? bsp->args.profile : NULL;
941 }
942
943 void bfd_sess_addresses(const struct bfd_session_params *bsp, int *family,
944 struct in6_addr *src, struct in6_addr *dst)
945 {
946 *family = bsp->args.family;
947 if (src)
948 *src = bsp->args.src;
949 if (dst)
950 *dst = bsp->args.dst;
951 }
952
953 const char *bfd_sess_interface(const struct bfd_session_params *bsp)
954 {
955 if (bsp->args.ifnamelen)
956 return bsp->args.ifname;
957
958 return NULL;
959 }
960
961 const char *bfd_sess_vrf(const struct bfd_session_params *bsp)
962 {
963 return vrf_id_to_name(bsp->args.vrf_id);
964 }
965
966 vrf_id_t bfd_sess_vrf_id(const struct bfd_session_params *bsp)
967 {
968 return bsp->args.vrf_id;
969 }
970
971 bool bfd_sess_cbit(const struct bfd_session_params *bsp)
972 {
973 return bsp->args.cbit;
974 }
975
976 void bfd_sess_timers(const struct bfd_session_params *bsp,
977 uint8_t *detection_multiplier, uint32_t *min_rx,
978 uint32_t *min_tx)
979 {
980 *detection_multiplier = bsp->args.detection_multiplier;
981 *min_rx = bsp->args.min_rx;
982 *min_tx = bsp->args.min_tx;
983 }
984
985 void bfd_sess_show(struct vty *vty, struct json_object *json,
986 struct bfd_session_params *bsp)
987 {
988 json_object *json_bfd = NULL;
989 char time_buf[64];
990
991 /* Show type. */
992 if (json) {
993 json_bfd = json_object_new_object();
994 if (bsp->args.mhop)
995 json_object_string_add(json_bfd, "type", "multi hop");
996 else
997 json_object_string_add(json_bfd, "type", "single hop");
998 } else
999 vty_out(vty, " BFD: Type: %s\n",
1000 bsp->args.mhop ? "multi hop" : "single hop");
1001
1002 /* Show configuration. */
1003 if (json) {
1004 json_object_int_add(json_bfd, "detectMultiplier",
1005 bsp->args.detection_multiplier);
1006 json_object_int_add(json_bfd, "rxMinInterval",
1007 bsp->args.min_rx);
1008 json_object_int_add(json_bfd, "txMinInterval",
1009 bsp->args.min_tx);
1010 } else {
1011 vty_out(vty,
1012 " Detect Multiplier: %d, Min Rx interval: %d, Min Tx interval: %d\n",
1013 bsp->args.detection_multiplier, bsp->args.min_rx,
1014 bsp->args.min_tx);
1015 }
1016
1017 bfd_last_update(bsp->bss.last_event, time_buf, sizeof(time_buf));
1018 if (json) {
1019 json_object_string_add(json_bfd, "status",
1020 bfd_get_status_str(bsp->bss.state));
1021 json_object_string_add(json_bfd, "lastUpdate", time_buf);
1022 } else
1023 vty_out(vty, " Status: %s, Last update: %s\n",
1024 bfd_get_status_str(bsp->bss.state), time_buf);
1025
1026 if (json)
1027 json_object_object_add(json, "peerBfdInfo", json_bfd);
1028 else
1029 vty_out(vty, "\n");
1030 }
1031
1032 /*
1033 * Zebra communication related.
1034 */
1035
1036 /**
1037 * Callback for reinstallation of all registered BFD sessions.
1038 *
1039 * Use this as `zclient` `bfd_dest_replay` callback.
1040 */
1041 static int zclient_bfd_session_reply(ZAPI_CALLBACK_ARGS)
1042 {
1043 struct bfd_session_params *bsp;
1044
1045 /* Do nothing when shutting down. */
1046 if (bsglobal.shutting_down)
1047 return 0;
1048
1049 if (bsglobal.debugging)
1050 zlog_debug("%s: sending all sessions registered", __func__);
1051
1052 /* Send the client registration */
1053 bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, vrf_id);
1054
1055 /* Replay all activated peers. */
1056 TAILQ_FOREACH (bsp, &bsglobal.bsplist, entry) {
1057 /* Skip disabled sessions. */
1058 if (!bsp->enabled)
1059 continue;
1060
1061 /* We are reconnecting, so we must send installation. */
1062 bsp->installed = false;
1063
1064 /* Cancel any pending installation request. */
1065 THREAD_OFF(bsp->installev);
1066
1067 /* Ask for installation. */
1068 bsp->lastev = BSE_INSTALL;
1069 thread_execute(bsglobal.tm, _bfd_sess_send, bsp, 0);
1070 }
1071
1072 return 0;
1073 }
1074
1075 static int zclient_bfd_session_update(ZAPI_CALLBACK_ARGS)
1076 {
1077 struct bfd_session_params *bsp;
1078 size_t sessions_updated = 0;
1079 struct interface *ifp;
1080 int remote_cbit = false;
1081 int state = BFD_STATUS_UNKNOWN;
1082 size_t addrlen;
1083 struct prefix dp;
1084 struct prefix sp;
1085 char ifstr[128], cbitstr[32];
1086
1087 /* Do nothing when shutting down. */
1088 if (bsglobal.shutting_down)
1089 return 0;
1090
1091 ifp = bfd_get_peer_info(zclient->ibuf, &dp, &sp, &state, &remote_cbit,
1092 vrf_id);
1093
1094 if (bsglobal.debugging) {
1095 ifstr[0] = 0;
1096 if (ifp)
1097 snprintf(ifstr, sizeof(ifstr), " (interface %s)",
1098 ifp->name);
1099
1100 snprintf(cbitstr, sizeof(cbitstr), " (CPI bit %s)",
1101 remote_cbit ? "yes" : "no");
1102
1103 zlog_debug("%s: %pFX -> %pFX%s VRF %s(%u)%s: %s", __func__, &sp,
1104 &dp, ifstr, vrf_id_to_name(vrf_id), vrf_id, cbitstr,
1105 bfd_get_status_str(state));
1106 }
1107
1108 switch (dp.family) {
1109 case AF_INET:
1110 addrlen = sizeof(struct in_addr);
1111 break;
1112 case AF_INET6:
1113 addrlen = sizeof(struct in6_addr);
1114 break;
1115
1116 default:
1117 /* Unexpected value. */
1118 assert(0);
1119 break;
1120 }
1121
1122 /* Notify all matching sessions about update. */
1123 TAILQ_FOREACH (bsp, &bsglobal.bsplist, entry) {
1124 /* Skip disabled or not installed entries. */
1125 if (!bsp->enabled || !bsp->installed)
1126 continue;
1127 /* Skip different VRFs. */
1128 if (bsp->args.vrf_id != vrf_id)
1129 continue;
1130 /* Skip different families. */
1131 if (bsp->args.family != dp.family)
1132 continue;
1133 /* Skip different interface. */
1134 if (bsp->args.ifnamelen && ifp
1135 && strcmp(bsp->args.ifname, ifp->name) != 0)
1136 continue;
1137 /* Skip non matching destination addresses. */
1138 if (memcmp(&bsp->args.dst, &dp.u, addrlen) != 0)
1139 continue;
1140 /*
1141 * Source comparison test:
1142 * We will only compare source if BFD daemon provided the
1143 * source address and the protocol set a source address in
1144 * the configuration otherwise we'll just skip it.
1145 */
1146 if (sp.family && memcmp(&bsp->args.src, &i6a_zero, addrlen) != 0
1147 && memcmp(&sp.u, &i6a_zero, addrlen) != 0
1148 && memcmp(&bsp->args.src, &sp.u, addrlen) != 0)
1149 continue;
1150 /* No session state change. */
1151 if ((int)bsp->bss.state == state)
1152 continue;
1153
1154 bsp->bss.last_event = monotime(NULL);
1155 bsp->bss.previous_state = bsp->bss.state;
1156 bsp->bss.state = state;
1157 bsp->bss.remote_cbit = remote_cbit;
1158 bsp->updatecb(bsp, &bsp->bss, bsp->arg);
1159 sessions_updated++;
1160 }
1161
1162 if (bsglobal.debugging)
1163 zlog_debug("%s: sessions updated: %zu", __func__,
1164 sessions_updated);
1165
1166 return 0;
1167 }
1168
1169 void bfd_protocol_integration_init(struct zclient *zc, struct thread_master *tm)
1170 {
1171 /* Initialize data structure. */
1172 TAILQ_INIT(&bsglobal.bsplist);
1173
1174 /* Copy pointers. */
1175 bsglobal.zc = zc;
1176 bsglobal.tm = tm;
1177
1178 /* Install our callbacks. */
1179 zc->interface_bfd_dest_update = zclient_bfd_session_update;
1180 zc->bfd_dest_replay = zclient_bfd_session_reply;
1181
1182 /* Send the client registration */
1183 bfd_client_sendmsg(zc, ZEBRA_BFD_CLIENT_REGISTER, VRF_DEFAULT);
1184 }
1185
1186 void bfd_protocol_integration_set_debug(bool enable)
1187 {
1188 bsglobal.debugging = enable;
1189 }
1190
1191 void bfd_protocol_integration_set_shutdown(bool enable)
1192 {
1193 bsglobal.shutting_down = enable;
1194 }
1195
1196 bool bfd_protocol_integration_debug(void)
1197 {
1198 return bsglobal.debugging;
1199 }
1200
1201 bool bfd_protocol_integration_shutting_down(void)
1202 {
1203 return bsglobal.shutting_down;
1204 }