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