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