]> git.proxmox.com Git - mirror_frr.git/blob - bfdd/bfd.c
Merge pull request #10632 from donaldsharp/thread_return_null
[mirror_frr.git] / bfdd / bfd.c
1 /*********************************************************************
2 * Copyright 2013 Cumulus Networks, LLC. All rights reserved.
3 * Copyright 2014,2015,2016,2017 Cumulus Networks, Inc. All rights reserved.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the Free
7 * Software Foundation; either version 2 of the License, or (at your option)
8 * any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 * more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; see the file COPYING; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 *
19 * bfd.c: implements the BFD protocol.
20 *
21 * Authors
22 * -------
23 * Shrijeet Mukherjee [shm@cumulusnetworks.com]
24 * Kanna Rajagopal [kanna@cumulusnetworks.com]
25 * Radhika Mahankali [Radhika@cumulusnetworks.com]
26 */
27
28 #include <zebra.h>
29
30 #include "lib/jhash.h"
31 #include "lib/network.h"
32
33 #include "bfd.h"
34
35 DEFINE_MTYPE_STATIC(BFDD, BFDD_CONFIG, "long-lived configuration memory");
36 DEFINE_MTYPE_STATIC(BFDD, BFDD_PROFILE, "long-lived profile memory");
37 DEFINE_MTYPE_STATIC(BFDD, BFDD_SESSION_OBSERVER, "Session observer");
38 DEFINE_MTYPE_STATIC(BFDD, BFDD_VRF, "BFD VRF");
39
40 /*
41 * Prototypes
42 */
43 static uint32_t ptm_bfd_gen_ID(void);
44 static void ptm_bfd_echo_xmt_TO(struct bfd_session *bfd);
45 static struct bfd_session *bfd_find_disc(struct sockaddr_any *sa,
46 uint32_t ldisc);
47 static int bfd_session_update(struct bfd_session *bs, struct bfd_peer_cfg *bpc);
48 static const char *get_diag_str(int diag);
49
50 static void bs_admin_down_handler(struct bfd_session *bs, int nstate);
51 static void bs_down_handler(struct bfd_session *bs, int nstate);
52 static void bs_init_handler(struct bfd_session *bs, int nstate);
53 static void bs_up_handler(struct bfd_session *bs, int nstate);
54
55 /**
56 * Remove BFD profile from all BFD sessions so we don't leave dangling
57 * pointers.
58 */
59 static void bfd_profile_detach(struct bfd_profile *bp);
60
61 /* Zeroed array with the size of an IPv6 address. */
62 struct in6_addr zero_addr;
63
64 /** BFD profiles list. */
65 struct bfdproflist bplist;
66
67 /*
68 * Functions
69 */
70 struct bfd_profile *bfd_profile_lookup(const char *name)
71 {
72 struct bfd_profile *bp;
73
74 TAILQ_FOREACH (bp, &bplist, entry) {
75 if (strcmp(name, bp->name))
76 continue;
77
78 return bp;
79 }
80
81 return NULL;
82 }
83
84 static void bfd_profile_set_default(struct bfd_profile *bp)
85 {
86 bp->admin_shutdown = false;
87 bp->detection_multiplier = BFD_DEFDETECTMULT;
88 bp->echo_mode = false;
89 bp->passive = false;
90 bp->minimum_ttl = BFD_DEF_MHOP_TTL;
91 bp->min_echo_rx = BFD_DEF_REQ_MIN_ECHO_RX;
92 bp->min_echo_tx = BFD_DEF_DES_MIN_ECHO_TX;
93 bp->min_rx = BFD_DEFREQUIREDMINRX;
94 bp->min_tx = BFD_DEFDESIREDMINTX;
95 }
96
97 struct bfd_profile *bfd_profile_new(const char *name)
98 {
99 struct bfd_profile *bp;
100
101 /* Search for duplicates. */
102 if (bfd_profile_lookup(name) != NULL)
103 return NULL;
104
105 /* Allocate, name it and put into list. */
106 bp = XCALLOC(MTYPE_BFDD_PROFILE, sizeof(*bp));
107 strlcpy(bp->name, name, sizeof(bp->name));
108 TAILQ_INSERT_TAIL(&bplist, bp, entry);
109
110 /* Set default values. */
111 bfd_profile_set_default(bp);
112
113 return bp;
114 }
115
116 void bfd_profile_free(struct bfd_profile *bp)
117 {
118 /* Detach from any session. */
119 if (bglobal.bg_shutdown == false)
120 bfd_profile_detach(bp);
121
122 /* Remove from global list. */
123 TAILQ_REMOVE(&bplist, bp, entry);
124
125 XFREE(MTYPE_BFDD_PROFILE, bp);
126 }
127
128 void bfd_profile_apply(const char *profname, struct bfd_session *bs)
129 {
130 struct bfd_profile *bp;
131
132 /* Remove previous profile if any. */
133 if (bs->profile_name) {
134 /* We are changing profiles. */
135 if (strcmp(bs->profile_name, profname)) {
136 XFREE(MTYPE_BFDD_PROFILE, bs->profile_name);
137 bs->profile_name =
138 XSTRDUP(MTYPE_BFDD_PROFILE, profname);
139 }
140 } else /* Save the current profile name (in case it doesn't exist). */
141 bs->profile_name = XSTRDUP(MTYPE_BFDD_PROFILE, profname);
142
143 /* Look up new profile to apply. */
144 bp = bfd_profile_lookup(profname);
145
146 /* Point to profile if it exists. */
147 bs->profile = bp;
148
149 /* Apply configuration. */
150 bfd_session_apply(bs);
151 }
152
153 void bfd_session_apply(struct bfd_session *bs)
154 {
155 struct bfd_profile *bp;
156 uint32_t min_tx = bs->timers.desired_min_tx;
157 uint32_t min_rx = bs->timers.required_min_rx;
158
159 /* Pick the source of configuration. */
160 bp = bs->profile ? bs->profile : &bs->peer_profile;
161
162 /* Set multiplier if not the default. */
163 if (bs->peer_profile.detection_multiplier == BFD_DEFDETECTMULT)
164 bs->detect_mult = bp->detection_multiplier;
165 else
166 bs->detect_mult = bs->peer_profile.detection_multiplier;
167
168 /* Set timers if not the default. */
169 if (bs->peer_profile.min_tx == BFD_DEFDESIREDMINTX)
170 bs->timers.desired_min_tx = bp->min_tx;
171 else
172 bs->timers.desired_min_tx = bs->peer_profile.min_tx;
173
174 if (bs->peer_profile.min_rx == BFD_DEFREQUIREDMINRX)
175 bs->timers.required_min_rx = bp->min_rx;
176 else
177 bs->timers.required_min_rx = bs->peer_profile.min_rx;
178
179 /* We can only apply echo options on single hop sessions. */
180 if (!CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)) {
181 /* Configure echo timers if they were default. */
182 if (bs->peer_profile.min_echo_rx == BFD_DEF_REQ_MIN_ECHO_RX)
183 bs->timers.required_min_echo_rx = bp->min_echo_rx;
184 else
185 bs->timers.required_min_echo_rx =
186 bs->peer_profile.min_echo_rx;
187
188 if (bs->peer_profile.min_echo_tx == BFD_DEF_DES_MIN_ECHO_TX)
189 bs->timers.desired_min_echo_tx = bp->min_echo_tx;
190 else
191 bs->timers.desired_min_echo_tx =
192 bs->peer_profile.min_echo_tx;
193
194 /* Toggle echo if default value. */
195 if (bs->peer_profile.echo_mode == false)
196 bfd_set_echo(bs, bp->echo_mode);
197 else
198 bfd_set_echo(bs, bs->peer_profile.echo_mode);
199 } else {
200 /* Configure the TTL packet filter. */
201 if (bs->peer_profile.minimum_ttl == BFD_DEF_MHOP_TTL)
202 bs->mh_ttl = bp->minimum_ttl;
203 else
204 bs->mh_ttl = bs->peer_profile.minimum_ttl;
205 }
206
207 /* Toggle 'passive-mode' if default value. */
208 if (bs->peer_profile.passive == false)
209 bfd_set_passive_mode(bs, bp->passive);
210 else
211 bfd_set_passive_mode(bs, bs->peer_profile.passive);
212
213 /* Toggle 'no shutdown' if default value. */
214 if (bs->peer_profile.admin_shutdown == false)
215 bfd_set_shutdown(bs, bp->admin_shutdown);
216 else
217 bfd_set_shutdown(bs, bs->peer_profile.admin_shutdown);
218
219 /* If session interval changed negotiate new timers. */
220 if (bs->ses_state == PTM_BFD_UP
221 && (bs->timers.desired_min_tx != min_tx
222 || bs->timers.required_min_rx != min_rx))
223 bfd_set_polling(bs);
224
225 /* Send updated information to data plane. */
226 bfd_dplane_update_session(bs);
227 }
228
229 void bfd_profile_remove(struct bfd_session *bs)
230 {
231 /* Remove any previous set profile name. */
232 XFREE(MTYPE_BFDD_PROFILE, bs->profile_name);
233 bs->profile = NULL;
234
235 bfd_session_apply(bs);
236 }
237
238 void gen_bfd_key(struct bfd_key *key, struct sockaddr_any *peer,
239 struct sockaddr_any *local, bool mhop, const char *ifname,
240 const char *vrfname)
241 {
242 memset(key, 0, sizeof(*key));
243
244 switch (peer->sa_sin.sin_family) {
245 case AF_INET:
246 key->family = AF_INET;
247 memcpy(&key->peer, &peer->sa_sin.sin_addr,
248 sizeof(peer->sa_sin.sin_addr));
249 memcpy(&key->local, &local->sa_sin.sin_addr,
250 sizeof(local->sa_sin.sin_addr));
251 break;
252 case AF_INET6:
253 key->family = AF_INET6;
254 memcpy(&key->peer, &peer->sa_sin6.sin6_addr,
255 sizeof(peer->sa_sin6.sin6_addr));
256 memcpy(&key->local, &local->sa_sin6.sin6_addr,
257 sizeof(local->sa_sin6.sin6_addr));
258 break;
259 }
260
261 key->mhop = mhop;
262 if (ifname && ifname[0])
263 strlcpy(key->ifname, ifname, sizeof(key->ifname));
264 if (vrfname && vrfname[0])
265 strlcpy(key->vrfname, vrfname, sizeof(key->vrfname));
266 else
267 strlcpy(key->vrfname, VRF_DEFAULT_NAME, sizeof(key->vrfname));
268 }
269
270 struct bfd_session *bs_peer_find(struct bfd_peer_cfg *bpc)
271 {
272 struct bfd_session *bs;
273 struct peer_label *pl;
274 struct bfd_key key;
275
276 /* Try to find label first. */
277 if (bpc->bpc_has_label) {
278 pl = pl_find(bpc->bpc_label);
279 if (pl != NULL) {
280 bs = pl->pl_bs;
281 return bs;
282 }
283 }
284
285 /* Otherwise fallback to peer/local hash lookup. */
286 gen_bfd_key(&key, &bpc->bpc_peer, &bpc->bpc_local, bpc->bpc_mhop,
287 bpc->bpc_localif, bpc->bpc_vrfname);
288
289 return bfd_key_lookup(key);
290 }
291
292 /*
293 * Starts a disabled BFD session.
294 *
295 * A session is disabled when the specified interface/VRF doesn't exist
296 * yet. It might happen on FRR boot or with virtual interfaces.
297 */
298 int bfd_session_enable(struct bfd_session *bs)
299 {
300 struct interface *ifp = NULL;
301 struct vrf *vrf = NULL;
302 int psock;
303
304 /* We are using data plane, we don't need software. */
305 if (bs->bdc)
306 return 0;
307
308 /*
309 * If the interface or VRF doesn't exist, then we must register
310 * the session but delay its start.
311 */
312 if (bs->key.vrfname[0]) {
313 vrf = vrf_lookup_by_name(bs->key.vrfname);
314 if (vrf == NULL) {
315 zlog_err(
316 "session-enable: specified VRF %s doesn't exists.",
317 bs->key.vrfname);
318 return 0;
319 }
320 } else {
321 vrf = vrf_lookup_by_id(VRF_DEFAULT);
322 }
323
324 assert(vrf);
325
326 if (bs->key.ifname[0]) {
327 ifp = if_lookup_by_name(bs->key.ifname, vrf->vrf_id);
328 if (ifp == NULL) {
329 zlog_err(
330 "session-enable: specified interface %s (VRF %s) doesn't exist.",
331 bs->key.ifname, vrf->name);
332 return 0;
333 }
334 }
335
336 /* Assign interface/VRF pointers. */
337 bs->vrf = vrf;
338
339 /* Assign interface pointer (if any). */
340 bs->ifp = ifp;
341
342 /* Attempt to use data plane. */
343 if (bglobal.bg_use_dplane && bfd_dplane_add_session(bs) == 0) {
344 control_notify_config(BCM_NOTIFY_CONFIG_ADD, bs);
345 return 0;
346 }
347
348 /* Sanity check: don't leak open sockets. */
349 if (bs->sock != -1) {
350 if (bglobal.debug_peer_event)
351 zlog_debug("session-enable: previous socket open");
352
353 close(bs->sock);
354 bs->sock = -1;
355 }
356
357 /*
358 * Get socket for transmitting control packets. Note that if we
359 * could use the destination port (3784) for the source
360 * port we wouldn't need a socket per session.
361 */
362 if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_IPV6) == 0) {
363 psock = bp_peer_socket(bs);
364 if (psock == -1)
365 return 0;
366 } else {
367 psock = bp_peer_socketv6(bs);
368 if (psock == -1)
369 return 0;
370 }
371
372 /*
373 * We've got a valid socket, lets start the timers and the
374 * protocol.
375 */
376 bs->sock = psock;
377
378 /* Only start timers if we are using active mode. */
379 if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_PASSIVE) == 0) {
380 bfd_recvtimer_update(bs);
381 ptm_bfd_start_xmt_timer(bs, false);
382 }
383
384 return 0;
385 }
386
387 /*
388 * Disabled a running BFD session.
389 *
390 * A session is disabled when the specified interface/VRF gets removed
391 * (e.g. virtual interfaces).
392 */
393 void bfd_session_disable(struct bfd_session *bs)
394 {
395 /* We are using data plane, we don't need software. */
396 if (bs->bdc)
397 return;
398
399 /* Free up socket resources. */
400 if (bs->sock != -1) {
401 close(bs->sock);
402 bs->sock = -1;
403 }
404
405 /* Disable all timers. */
406 bfd_recvtimer_delete(bs);
407 bfd_xmttimer_delete(bs);
408 ptm_bfd_echo_stop(bs);
409
410 /* Set session down so it doesn't report UP and disabled. */
411 ptm_bfd_sess_dn(bs, BD_PATH_DOWN);
412 }
413
414 static uint32_t ptm_bfd_gen_ID(void)
415 {
416 uint32_t session_id;
417
418 /*
419 * RFC 5880, Section 6.8.1. recommends that we should generate
420 * random session identification numbers.
421 */
422 do {
423 session_id = ((frr_weak_random() << 16) & 0xFFFF0000)
424 | (frr_weak_random() & 0x0000FFFF);
425 } while (session_id == 0 || bfd_id_lookup(session_id) != NULL);
426
427 return session_id;
428 }
429
430 void ptm_bfd_start_xmt_timer(struct bfd_session *bfd, bool is_echo)
431 {
432 uint64_t jitter, xmt_TO;
433 int maxpercent;
434
435 xmt_TO = is_echo ? bfd->echo_xmt_TO : bfd->xmt_TO;
436
437 /*
438 * From section 6.5.2: trasmit interval should be randomly jittered
439 * between
440 * 75% and 100% of nominal value, unless detect_mult is 1, then should
441 * be
442 * between 75% and 90%.
443 */
444 maxpercent = (bfd->detect_mult == 1) ? 16 : 26;
445 jitter = (xmt_TO * (75 + (frr_weak_random() % maxpercent))) / 100;
446 /* XXX remove that division above */
447
448 if (is_echo)
449 bfd_echo_xmttimer_update(bfd, jitter);
450 else
451 bfd_xmttimer_update(bfd, jitter);
452 }
453
454 static void ptm_bfd_echo_xmt_TO(struct bfd_session *bfd)
455 {
456 /* Send the scheduled echo packet */
457 ptm_bfd_echo_snd(bfd);
458
459 /* Restart the timer for next time */
460 ptm_bfd_start_xmt_timer(bfd, true);
461 }
462
463 void ptm_bfd_xmt_TO(struct bfd_session *bfd, int fbit)
464 {
465 /* Send the scheduled control packet */
466 ptm_bfd_snd(bfd, fbit);
467
468 /* Restart the timer for next time */
469 ptm_bfd_start_xmt_timer(bfd, false);
470 }
471
472 void ptm_bfd_echo_stop(struct bfd_session *bfd)
473 {
474 bfd->echo_xmt_TO = 0;
475 bfd->echo_detect_TO = 0;
476 UNSET_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE);
477
478 bfd_echo_xmttimer_delete(bfd);
479 bfd_echo_recvtimer_delete(bfd);
480 }
481
482 void ptm_bfd_echo_start(struct bfd_session *bfd)
483 {
484 bfd->echo_detect_TO = (bfd->remote_detect_mult * bfd->echo_xmt_TO);
485 if (bfd->echo_detect_TO > 0) {
486 bfd_echo_recvtimer_update(bfd);
487 ptm_bfd_echo_xmt_TO(bfd);
488 }
489 }
490
491 void ptm_bfd_sess_up(struct bfd_session *bfd)
492 {
493 int old_state = bfd->ses_state;
494
495 bfd->local_diag = 0;
496 bfd->ses_state = PTM_BFD_UP;
497 monotime(&bfd->uptime);
498
499 /* Connection is up, lets negotiate timers. */
500 bfd_set_polling(bfd);
501
502 /* Start sending control packets with poll bit immediately. */
503 ptm_bfd_snd(bfd, 0);
504
505 control_notify(bfd, bfd->ses_state);
506
507 if (old_state != bfd->ses_state) {
508 bfd->stats.session_up++;
509 if (bglobal.debug_peer_event)
510 zlog_debug("state-change: [%s] %s -> %s",
511 bs_to_string(bfd), state_list[old_state].str,
512 state_list[bfd->ses_state].str);
513 }
514 }
515
516 void ptm_bfd_sess_dn(struct bfd_session *bfd, uint8_t diag)
517 {
518 int old_state = bfd->ses_state;
519
520 bfd->local_diag = diag;
521 bfd->discrs.remote_discr = 0;
522 bfd->ses_state = PTM_BFD_DOWN;
523 bfd->polling = 0;
524 bfd->demand_mode = 0;
525 monotime(&bfd->downtime);
526
527 /*
528 * Only attempt to send if we have a valid socket:
529 * this function might be called by session disablers and in
530 * this case we won't have a valid socket (i.e. interface was
531 * removed or VRF doesn't exist anymore).
532 */
533 if (bfd->sock != -1)
534 ptm_bfd_snd(bfd, 0);
535
536 /* Slow down the control packets, the connection is down. */
537 bs_set_slow_timers(bfd);
538
539 /* only signal clients when going from up->down state */
540 if (old_state == PTM_BFD_UP)
541 control_notify(bfd, PTM_BFD_DOWN);
542
543 /* Stop echo packet transmission if they are active */
544 if (CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE))
545 ptm_bfd_echo_stop(bfd);
546
547 /* Stop attempting to transmit or expect control packets if passive. */
548 if (CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_PASSIVE)) {
549 bfd_recvtimer_delete(bfd);
550 bfd_xmttimer_delete(bfd);
551 }
552
553 if (old_state != bfd->ses_state) {
554 bfd->stats.session_down++;
555 if (bglobal.debug_peer_event)
556 zlog_debug("state-change: [%s] %s -> %s reason:%s",
557 bs_to_string(bfd), state_list[old_state].str,
558 state_list[bfd->ses_state].str,
559 get_diag_str(bfd->local_diag));
560 }
561 }
562
563 static struct bfd_session *bfd_find_disc(struct sockaddr_any *sa,
564 uint32_t ldisc)
565 {
566 struct bfd_session *bs;
567
568 bs = bfd_id_lookup(ldisc);
569 if (bs == NULL)
570 return NULL;
571
572 switch (bs->key.family) {
573 case AF_INET:
574 if (memcmp(&sa->sa_sin.sin_addr, &bs->key.peer,
575 sizeof(sa->sa_sin.sin_addr)))
576 return NULL;
577 break;
578 case AF_INET6:
579 if (memcmp(&sa->sa_sin6.sin6_addr, &bs->key.peer,
580 sizeof(sa->sa_sin6.sin6_addr)))
581 return NULL;
582 break;
583 }
584
585 return bs;
586 }
587
588 struct bfd_session *ptm_bfd_sess_find(struct bfd_pkt *cp,
589 struct sockaddr_any *peer,
590 struct sockaddr_any *local,
591 struct interface *ifp,
592 vrf_id_t vrfid,
593 bool is_mhop)
594 {
595 struct vrf *vrf;
596 struct bfd_key key;
597
598 /* Find our session using the ID signaled by the remote end. */
599 if (cp->discrs.remote_discr)
600 return bfd_find_disc(peer, ntohl(cp->discrs.remote_discr));
601
602 /* Search for session without using discriminator. */
603 vrf = vrf_lookup_by_id(vrfid);
604
605 gen_bfd_key(&key, peer, local, is_mhop, ifp ? ifp->name : NULL,
606 vrf ? vrf->name : VRF_DEFAULT_NAME);
607
608 /* XXX maybe remoteDiscr should be checked for remoteHeard cases. */
609 return bfd_key_lookup(key);
610 }
611
612 void bfd_xmt_cb(struct thread *t)
613 {
614 struct bfd_session *bs = THREAD_ARG(t);
615
616 ptm_bfd_xmt_TO(bs, 0);
617 }
618
619 void bfd_echo_xmt_cb(struct thread *t)
620 {
621 struct bfd_session *bs = THREAD_ARG(t);
622
623 if (bs->echo_xmt_TO > 0)
624 ptm_bfd_echo_xmt_TO(bs);
625 }
626
627 /* Was ptm_bfd_detect_TO() */
628 void bfd_recvtimer_cb(struct thread *t)
629 {
630 struct bfd_session *bs = THREAD_ARG(t);
631
632 switch (bs->ses_state) {
633 case PTM_BFD_INIT:
634 case PTM_BFD_UP:
635 ptm_bfd_sess_dn(bs, BD_CONTROL_EXPIRED);
636 break;
637 }
638 }
639
640 /* Was ptm_bfd_echo_detect_TO() */
641 void bfd_echo_recvtimer_cb(struct thread *t)
642 {
643 struct bfd_session *bs = THREAD_ARG(t);
644
645 switch (bs->ses_state) {
646 case PTM_BFD_INIT:
647 case PTM_BFD_UP:
648 ptm_bfd_sess_dn(bs, BD_ECHO_FAILED);
649 break;
650 }
651 }
652
653 struct bfd_session *bfd_session_new(void)
654 {
655 struct bfd_session *bs;
656
657 bs = XCALLOC(MTYPE_BFDD_CONFIG, sizeof(*bs));
658
659 /* Set peer session defaults. */
660 bfd_profile_set_default(&bs->peer_profile);
661
662 bs->timers.desired_min_tx = BFD_DEFDESIREDMINTX;
663 bs->timers.required_min_rx = BFD_DEFREQUIREDMINRX;
664 bs->timers.required_min_echo_rx = BFD_DEF_REQ_MIN_ECHO_RX;
665 bs->timers.desired_min_echo_tx = BFD_DEF_DES_MIN_ECHO_TX;
666 bs->detect_mult = BFD_DEFDETECTMULT;
667 bs->mh_ttl = BFD_DEF_MHOP_TTL;
668 bs->ses_state = PTM_BFD_DOWN;
669
670 /* Initiate connection with slow timers. */
671 bs_set_slow_timers(bs);
672
673 /* Initiate remote settings as well. */
674 bs->remote_timers = bs->cur_timers;
675 bs->remote_detect_mult = BFD_DEFDETECTMULT;
676
677 bs->sock = -1;
678 monotime(&bs->uptime);
679 bs->downtime = bs->uptime;
680
681 return bs;
682 }
683
684 int bfd_session_update_label(struct bfd_session *bs, const char *nlabel)
685 {
686 /* New label treatment:
687 * - Check if the label is taken;
688 * - Try to allocate the memory for it and register;
689 */
690 if (bs->pl == NULL) {
691 if (pl_find(nlabel) != NULL) {
692 /* Someone is already using it. */
693 return -1;
694 }
695
696 pl_new(nlabel, bs);
697
698 return 0;
699 }
700
701 /*
702 * Test label change consistency:
703 * - Do nothing if it's the same label;
704 * - Check if the future label is already taken;
705 * - Change label;
706 */
707 if (strcmp(nlabel, bs->pl->pl_label) == 0)
708 return -1;
709 if (pl_find(nlabel) != NULL)
710 return -1;
711
712 strlcpy(bs->pl->pl_label, nlabel, sizeof(bs->pl->pl_label));
713 return 0;
714 }
715
716 static void _bfd_session_update(struct bfd_session *bs,
717 struct bfd_peer_cfg *bpc)
718 {
719 if (bpc->bpc_has_txinterval) {
720 bs->timers.desired_min_tx = bpc->bpc_txinterval * 1000;
721 bs->peer_profile.min_tx = bs->timers.desired_min_tx;
722 }
723
724 if (bpc->bpc_has_recvinterval) {
725 bs->timers.required_min_rx = bpc->bpc_recvinterval * 1000;
726 bs->peer_profile.min_rx = bs->timers.required_min_rx;
727 }
728
729 if (bpc->bpc_has_detectmultiplier) {
730 bs->detect_mult = bpc->bpc_detectmultiplier;
731 bs->peer_profile.detection_multiplier = bs->detect_mult;
732 }
733
734 if (bpc->bpc_has_echorecvinterval) {
735 bs->timers.required_min_echo_rx = bpc->bpc_echorecvinterval * 1000;
736 bs->peer_profile.min_echo_rx = bs->timers.required_min_echo_rx;
737 }
738
739 if (bpc->bpc_has_echotxinterval) {
740 bs->timers.desired_min_echo_tx = bpc->bpc_echotxinterval * 1000;
741 bs->peer_profile.min_echo_tx = bs->timers.desired_min_echo_tx;
742 }
743
744 if (bpc->bpc_has_label)
745 bfd_session_update_label(bs, bpc->bpc_label);
746
747 if (bpc->bpc_cbit)
748 SET_FLAG(bs->flags, BFD_SESS_FLAG_CBIT);
749 else
750 UNSET_FLAG(bs->flags, BFD_SESS_FLAG_CBIT);
751
752 if (bpc->bpc_has_minimum_ttl) {
753 bs->mh_ttl = bpc->bpc_minimum_ttl;
754 bs->peer_profile.minimum_ttl = bpc->bpc_minimum_ttl;
755 }
756
757 bs->peer_profile.echo_mode = bpc->bpc_echo;
758 bfd_set_echo(bs, bpc->bpc_echo);
759
760 /*
761 * Shutdown needs to be the last in order to avoid timers enable when
762 * the session is disabled.
763 */
764 bs->peer_profile.admin_shutdown = bpc->bpc_shutdown;
765 bfd_set_passive_mode(bs, bpc->bpc_passive);
766 bfd_set_shutdown(bs, bpc->bpc_shutdown);
767
768 /*
769 * Apply profile last: it also calls `bfd_set_shutdown`.
770 *
771 * There is no problem calling `shutdown` twice if the value doesn't
772 * change or if it is overriden by peer specific configuration.
773 */
774 if (bpc->bpc_has_profile)
775 bfd_profile_apply(bpc->bpc_profile, bs);
776 }
777
778 static int bfd_session_update(struct bfd_session *bs, struct bfd_peer_cfg *bpc)
779 {
780 /* User didn't want to update, return failure. */
781 if (bpc->bpc_createonly)
782 return -1;
783
784 _bfd_session_update(bs, bpc);
785
786 control_notify_config(BCM_NOTIFY_CONFIG_UPDATE, bs);
787
788 return 0;
789 }
790
791 void bfd_session_free(struct bfd_session *bs)
792 {
793 struct bfd_session_observer *bso;
794
795 bfd_session_disable(bs);
796
797 /* Remove session from data plane if any. */
798 bfd_dplane_delete_session(bs);
799
800 bfd_key_delete(bs->key);
801 bfd_id_delete(bs->discrs.my_discr);
802
803 /* Remove observer if any. */
804 TAILQ_FOREACH(bso, &bglobal.bg_obslist, bso_entry) {
805 if (bso->bso_bs != bs)
806 continue;
807
808 break;
809 }
810 if (bso != NULL)
811 bs_observer_del(bso);
812
813 pl_free(bs->pl);
814
815 XFREE(MTYPE_BFDD_PROFILE, bs->profile_name);
816 XFREE(MTYPE_BFDD_CONFIG, bs);
817 }
818
819 struct bfd_session *ptm_bfd_sess_new(struct bfd_peer_cfg *bpc)
820 {
821 struct bfd_session *bfd, *l_bfd;
822
823 /* check to see if this needs a new session */
824 l_bfd = bs_peer_find(bpc);
825 if (l_bfd) {
826 /* Requesting a duplicated peer means update configuration. */
827 if (bfd_session_update(l_bfd, bpc) == 0)
828 return l_bfd;
829 else
830 return NULL;
831 }
832
833 /* Get BFD session storage with its defaults. */
834 bfd = bfd_session_new();
835
836 /*
837 * Store interface/VRF name in case we need to delay session
838 * start. See `bfd_session_enable` for more information.
839 */
840 if (bpc->bpc_has_localif)
841 strlcpy(bfd->key.ifname, bpc->bpc_localif,
842 sizeof(bfd->key.ifname));
843
844 if (bpc->bpc_has_vrfname)
845 strlcpy(bfd->key.vrfname, bpc->bpc_vrfname,
846 sizeof(bfd->key.vrfname));
847 else
848 strlcpy(bfd->key.vrfname, VRF_DEFAULT_NAME,
849 sizeof(bfd->key.vrfname));
850
851 /* Copy remaining data. */
852 if (bpc->bpc_ipv4 == false)
853 SET_FLAG(bfd->flags, BFD_SESS_FLAG_IPV6);
854
855 bfd->key.family = (bpc->bpc_ipv4) ? AF_INET : AF_INET6;
856 switch (bfd->key.family) {
857 case AF_INET:
858 memcpy(&bfd->key.peer, &bpc->bpc_peer.sa_sin.sin_addr,
859 sizeof(bpc->bpc_peer.sa_sin.sin_addr));
860 memcpy(&bfd->key.local, &bpc->bpc_local.sa_sin.sin_addr,
861 sizeof(bpc->bpc_local.sa_sin.sin_addr));
862 break;
863
864 case AF_INET6:
865 memcpy(&bfd->key.peer, &bpc->bpc_peer.sa_sin6.sin6_addr,
866 sizeof(bpc->bpc_peer.sa_sin6.sin6_addr));
867 memcpy(&bfd->key.local, &bpc->bpc_local.sa_sin6.sin6_addr,
868 sizeof(bpc->bpc_local.sa_sin6.sin6_addr));
869 break;
870
871 default:
872 assert(1);
873 break;
874 }
875
876 if (bpc->bpc_mhop)
877 SET_FLAG(bfd->flags, BFD_SESS_FLAG_MH);
878
879 bfd->key.mhop = bpc->bpc_mhop;
880
881 if (bs_registrate(bfd) == NULL)
882 return NULL;
883
884 /* Apply other configurations. */
885 _bfd_session_update(bfd, bpc);
886
887 return bfd;
888 }
889
890 struct bfd_session *bs_registrate(struct bfd_session *bfd)
891 {
892 /* Registrate session into data structures. */
893 bfd_key_insert(bfd);
894 bfd->discrs.my_discr = ptm_bfd_gen_ID();
895 bfd_id_insert(bfd);
896
897 /* Try to enable session and schedule for packet receive/send. */
898 if (bfd_session_enable(bfd) == -1) {
899 /* Unrecoverable failure, remove the session/peer. */
900 bfd_session_free(bfd);
901 return NULL;
902 }
903
904 /* Add observer if we have moving parts. */
905 if (bfd->key.ifname[0] || bfd->key.vrfname[0] || bfd->sock == -1)
906 bs_observer_add(bfd);
907
908 if (bglobal.debug_peer_event)
909 zlog_debug("session-new: %s", bs_to_string(bfd));
910
911 control_notify_config(BCM_NOTIFY_CONFIG_ADD, bfd);
912
913 return bfd;
914 }
915
916 int ptm_bfd_sess_del(struct bfd_peer_cfg *bpc)
917 {
918 struct bfd_session *bs;
919
920 /* Find session and call free(). */
921 bs = bs_peer_find(bpc);
922 if (bs == NULL)
923 return -1;
924
925 /* This pointer is being referenced, don't let it be deleted. */
926 if (bs->refcount > 0) {
927 zlog_err("session-delete: refcount failure: %" PRIu64" references",
928 bs->refcount);
929 return -1;
930 }
931
932 if (bglobal.debug_peer_event)
933 zlog_debug("session-delete: %s", bs_to_string(bs));
934
935 control_notify_config(BCM_NOTIFY_CONFIG_DELETE, bs);
936
937 bfd_session_free(bs);
938
939 return 0;
940 }
941
942 void bfd_set_polling(struct bfd_session *bs)
943 {
944 /*
945 * Start polling procedure: the only timers that require polling
946 * to change value without losing connection are:
947 *
948 * - Desired minimum transmission interval;
949 * - Required minimum receive interval;
950 *
951 * RFC 5880, Section 6.8.3.
952 */
953 bs->polling = 1;
954 }
955
956 /*
957 * bs_<state>_handler() functions implement the BFD state machine
958 * transition mechanism. `<state>` is the current session state and
959 * the parameter `nstate` is the peer new state.
960 */
961 static void bs_admin_down_handler(struct bfd_session *bs
962 __attribute__((__unused__)),
963 int nstate __attribute__((__unused__)))
964 {
965 /*
966 * We are administratively down, there is no state machine
967 * handling.
968 */
969 }
970
971 static void bs_down_handler(struct bfd_session *bs, int nstate)
972 {
973 switch (nstate) {
974 case PTM_BFD_ADM_DOWN:
975 /*
976 * Remote peer doesn't want to talk, so lets keep the
977 * connection down.
978 */
979 case PTM_BFD_UP:
980 /* Peer can't be up yet, wait it go to 'init' or 'down'. */
981 break;
982
983 case PTM_BFD_DOWN:
984 /*
985 * Remote peer agreed that the path is down, lets try to
986 * bring it up.
987 */
988 bs->ses_state = PTM_BFD_INIT;
989
990 /*
991 * RFC 5880, Section 6.1.
992 * A system taking the Passive role MUST NOT begin
993 * sending BFD packets for a particular session until
994 * it has received a BFD packet for that session, and thus
995 * has learned the remote system's discriminator value.
996 *
997 * Now we can start transmission timer in passive mode.
998 */
999 if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_PASSIVE))
1000 ptm_bfd_xmt_TO(bs, 0);
1001
1002 break;
1003
1004 case PTM_BFD_INIT:
1005 /*
1006 * Remote peer told us his path is up, lets turn
1007 * activate the session.
1008 */
1009 ptm_bfd_sess_up(bs);
1010 break;
1011
1012 default:
1013 if (bglobal.debug_peer_event)
1014 zlog_debug("state-change: unhandled neighbor state: %d",
1015 nstate);
1016 break;
1017 }
1018 }
1019
1020 static void bs_init_handler(struct bfd_session *bs, int nstate)
1021 {
1022 switch (nstate) {
1023 case PTM_BFD_ADM_DOWN:
1024 /*
1025 * Remote peer doesn't want to talk, so lets make the
1026 * connection down.
1027 */
1028 ptm_bfd_sess_dn(bs, BD_NEIGHBOR_DOWN);
1029 break;
1030
1031 case PTM_BFD_DOWN:
1032 /* Remote peer hasn't moved to first stage yet. */
1033 break;
1034
1035 case PTM_BFD_INIT:
1036 case PTM_BFD_UP:
1037 /* We agreed on the settings and the path is up. */
1038 ptm_bfd_sess_up(bs);
1039 break;
1040
1041 default:
1042 if (bglobal.debug_peer_event)
1043 zlog_debug("state-change: unhandled neighbor state: %d",
1044 nstate);
1045 break;
1046 }
1047 }
1048
1049 static void bs_up_handler(struct bfd_session *bs, int nstate)
1050 {
1051 switch (nstate) {
1052 case PTM_BFD_ADM_DOWN:
1053 case PTM_BFD_DOWN:
1054 /* Peer lost or asked to shutdown connection. */
1055 ptm_bfd_sess_dn(bs, BD_NEIGHBOR_DOWN);
1056 break;
1057
1058 case PTM_BFD_INIT:
1059 case PTM_BFD_UP:
1060 /* Path is up and working. */
1061 break;
1062
1063 default:
1064 if (bglobal.debug_peer_event)
1065 zlog_debug("state-change: unhandled neighbor state: %d",
1066 nstate);
1067 break;
1068 }
1069 }
1070
1071 void bs_state_handler(struct bfd_session *bs, int nstate)
1072 {
1073 switch (bs->ses_state) {
1074 case PTM_BFD_ADM_DOWN:
1075 bs_admin_down_handler(bs, nstate);
1076 break;
1077 case PTM_BFD_DOWN:
1078 bs_down_handler(bs, nstate);
1079 break;
1080 case PTM_BFD_INIT:
1081 bs_init_handler(bs, nstate);
1082 break;
1083 case PTM_BFD_UP:
1084 bs_up_handler(bs, nstate);
1085 break;
1086
1087 default:
1088 if (bglobal.debug_peer_event)
1089 zlog_debug("state-change: [%s] is in invalid state: %d",
1090 bs_to_string(bs), nstate);
1091 break;
1092 }
1093 }
1094
1095 /*
1096 * Handles echo timer manipulation after updating timer.
1097 */
1098 void bs_echo_timer_handler(struct bfd_session *bs)
1099 {
1100 uint32_t old_timer;
1101
1102 /*
1103 * Before doing any echo handling, check if it is possible to
1104 * use it.
1105 *
1106 * - Check for `echo-mode` configuration.
1107 * - Check that we are not using multi hop (RFC 5883,
1108 * Section 3).
1109 * - Check that we are already at the up state.
1110 */
1111 if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO) == 0
1112 || CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)
1113 || bs->ses_state != PTM_BFD_UP)
1114 return;
1115
1116 /* Remote peer asked to stop echo. */
1117 if (bs->remote_timers.required_min_echo == 0) {
1118 if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO_ACTIVE))
1119 ptm_bfd_echo_stop(bs);
1120
1121 return;
1122 }
1123
1124 /*
1125 * Calculate the echo transmission timer: we must not send
1126 * echo packets faster than the minimum required time
1127 * announced by the remote system.
1128 *
1129 * RFC 5880, Section 6.8.9.
1130 */
1131 old_timer = bs->echo_xmt_TO;
1132 if (bs->remote_timers.required_min_echo > bs->timers.desired_min_echo_tx)
1133 bs->echo_xmt_TO = bs->remote_timers.required_min_echo;
1134 else
1135 bs->echo_xmt_TO = bs->timers.desired_min_echo_tx;
1136
1137 if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO_ACTIVE) == 0
1138 || old_timer != bs->echo_xmt_TO)
1139 ptm_bfd_echo_start(bs);
1140 }
1141
1142 /*
1143 * RFC 5880 Section 6.5.
1144 *
1145 * When a BFD control packet with the final bit is received, we must
1146 * update the session parameters.
1147 */
1148 void bs_final_handler(struct bfd_session *bs)
1149 {
1150 /* Start using our new timers. */
1151 bs->cur_timers.desired_min_tx = bs->timers.desired_min_tx;
1152 bs->cur_timers.required_min_rx = bs->timers.required_min_rx;
1153
1154 /*
1155 * TODO: demand mode. See RFC 5880 Section 6.1.
1156 *
1157 * When using demand mode we must disable the detection timer
1158 * for lost control packets.
1159 */
1160 if (bs->demand_mode) {
1161 /* Notify watchers about changed timers. */
1162 control_notify_config(BCM_NOTIFY_CONFIG_UPDATE, bs);
1163 return;
1164 }
1165
1166 /*
1167 * Calculate transmission time based on new timers.
1168 *
1169 * Transmission calculation:
1170 * Unless specified by exceptions at the end of Section 6.8.7, the
1171 * transmission time will be determined by the system with the
1172 * slowest rate.
1173 *
1174 * RFC 5880, Section 6.8.7.
1175 */
1176 if (bs->timers.desired_min_tx > bs->remote_timers.required_min_rx)
1177 bs->xmt_TO = bs->timers.desired_min_tx;
1178 else
1179 bs->xmt_TO = bs->remote_timers.required_min_rx;
1180
1181 /* Apply new transmission timer immediately. */
1182 ptm_bfd_start_xmt_timer(bs, false);
1183
1184 /* Notify watchers about changed timers. */
1185 control_notify_config(BCM_NOTIFY_CONFIG_UPDATE, bs);
1186 }
1187
1188 void bs_set_slow_timers(struct bfd_session *bs)
1189 {
1190 /*
1191 * BFD connection must use slow timers before going up or after
1192 * losing connectivity to avoid wasting bandwidth.
1193 *
1194 * RFC 5880, Section 6.8.3.
1195 */
1196 bs->cur_timers.desired_min_tx = BFD_DEF_SLOWTX;
1197 bs->cur_timers.required_min_rx = BFD_DEF_SLOWTX;
1198 bs->cur_timers.required_min_echo = 0;
1199
1200 /* Set the appropriated timeouts for slow connection. */
1201 bs->detect_TO = (BFD_DEFDETECTMULT * BFD_DEF_SLOWTX);
1202 bs->xmt_TO = BFD_DEF_SLOWTX;
1203 }
1204
1205 void bfd_set_echo(struct bfd_session *bs, bool echo)
1206 {
1207 if (echo) {
1208 /* Check if echo mode is already active. */
1209 if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO))
1210 return;
1211
1212 SET_FLAG(bs->flags, BFD_SESS_FLAG_ECHO);
1213
1214 /* Activate/update echo receive timeout timer. */
1215 if (bs->bdc == NULL)
1216 bs_echo_timer_handler(bs);
1217 } else {
1218 /* Check if echo mode is already disabled. */
1219 if (!CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO))
1220 return;
1221
1222 UNSET_FLAG(bs->flags, BFD_SESS_FLAG_ECHO);
1223
1224 /* Deactivate timeout timer. */
1225 if (bs->bdc == NULL)
1226 ptm_bfd_echo_stop(bs);
1227 }
1228 }
1229
1230 void bfd_set_shutdown(struct bfd_session *bs, bool shutdown)
1231 {
1232 bool is_shutdown;
1233
1234 /*
1235 * Special case: we are batching changes and the previous state was
1236 * not shutdown. Instead of potentially disconnect a running peer,
1237 * we'll get the current status to validate we were really down.
1238 */
1239 if (bs->ses_state == PTM_BFD_UP)
1240 is_shutdown = false;
1241 else
1242 is_shutdown = CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN);
1243
1244 if (shutdown) {
1245 /* Already shutdown. */
1246 if (is_shutdown)
1247 return;
1248
1249 SET_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN);
1250
1251 /* Handle data plane shutdown case. */
1252 if (bs->bdc) {
1253 bs->ses_state = PTM_BFD_ADM_DOWN;
1254 bfd_dplane_update_session(bs);
1255 control_notify(bs, bs->ses_state);
1256 return;
1257 }
1258
1259 /* Disable all events. */
1260 bfd_recvtimer_delete(bs);
1261 bfd_echo_recvtimer_delete(bs);
1262 bfd_xmttimer_delete(bs);
1263 bfd_echo_xmttimer_delete(bs);
1264
1265 /* Change and notify state change. */
1266 bs->ses_state = PTM_BFD_ADM_DOWN;
1267 control_notify(bs, bs->ses_state);
1268
1269 /* Don't try to send packets with a disabled session. */
1270 if (bs->sock != -1)
1271 ptm_bfd_snd(bs, 0);
1272 } else {
1273 /* Already working. */
1274 if (!is_shutdown)
1275 return;
1276
1277 UNSET_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN);
1278
1279 /* Handle data plane shutdown case. */
1280 if (bs->bdc) {
1281 bs->ses_state = PTM_BFD_DOWN;
1282 bfd_dplane_update_session(bs);
1283 control_notify(bs, bs->ses_state);
1284 return;
1285 }
1286
1287 /* Change and notify state change. */
1288 bs->ses_state = PTM_BFD_DOWN;
1289 control_notify(bs, bs->ses_state);
1290
1291 /* Enable timers if non passive, otherwise stop them. */
1292 if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_PASSIVE)) {
1293 bfd_recvtimer_delete(bs);
1294 bfd_xmttimer_delete(bs);
1295 } else {
1296 bfd_recvtimer_update(bs);
1297 bfd_xmttimer_update(bs, bs->xmt_TO);
1298 }
1299 }
1300 }
1301
1302 void bfd_set_passive_mode(struct bfd_session *bs, bool passive)
1303 {
1304 if (passive) {
1305 SET_FLAG(bs->flags, BFD_SESS_FLAG_PASSIVE);
1306
1307 /* Session is already up and running, nothing to do now. */
1308 if (bs->ses_state != PTM_BFD_DOWN)
1309 return;
1310
1311 /* Lets disable the timers since we are now passive. */
1312 bfd_recvtimer_delete(bs);
1313 bfd_xmttimer_delete(bs);
1314 } else {
1315 UNSET_FLAG(bs->flags, BFD_SESS_FLAG_PASSIVE);
1316
1317 /* Session is already up and running, nothing to do now. */
1318 if (bs->ses_state != PTM_BFD_DOWN)
1319 return;
1320
1321 /* Session is down, let it attempt to start the connection. */
1322 bfd_xmttimer_update(bs, bs->xmt_TO);
1323 bfd_recvtimer_update(bs);
1324 }
1325 }
1326
1327 /*
1328 * Helper functions.
1329 */
1330 static const char *get_diag_str(int diag)
1331 {
1332 for (int i = 0; diag_list[i].str; i++) {
1333 if (diag_list[i].type == diag)
1334 return diag_list[i].str;
1335 }
1336 return "N/A";
1337 }
1338
1339 const char *satostr(const struct sockaddr_any *sa)
1340 {
1341 #define INETSTR_BUFCOUNT 8
1342 static char buf[INETSTR_BUFCOUNT][INET6_ADDRSTRLEN];
1343 static int bufidx;
1344 const struct sockaddr_in *sin = &sa->sa_sin;
1345 const struct sockaddr_in6 *sin6 = &sa->sa_sin6;
1346
1347 bufidx += (bufidx + 1) % INETSTR_BUFCOUNT;
1348 buf[bufidx][0] = 0;
1349
1350 switch (sin->sin_family) {
1351 case AF_INET:
1352 inet_ntop(AF_INET, &sin->sin_addr, buf[bufidx],
1353 sizeof(buf[bufidx]));
1354 break;
1355 case AF_INET6:
1356 inet_ntop(AF_INET6, &sin6->sin6_addr, buf[bufidx],
1357 sizeof(buf[bufidx]));
1358 break;
1359
1360 default:
1361 strlcpy(buf[bufidx], "unknown", sizeof(buf[bufidx]));
1362 break;
1363 }
1364
1365 return buf[bufidx];
1366 }
1367
1368 const char *diag2str(uint8_t diag)
1369 {
1370 switch (diag) {
1371 case 0:
1372 return "ok";
1373 case 1:
1374 return "control detection time expired";
1375 case 2:
1376 return "echo function failed";
1377 case 3:
1378 return "neighbor signaled session down";
1379 case 4:
1380 return "forwarding plane reset";
1381 case 5:
1382 return "path down";
1383 case 6:
1384 return "concatenated path down";
1385 case 7:
1386 return "administratively down";
1387 case 8:
1388 return "reverse concatenated path down";
1389 default:
1390 return "unknown";
1391 }
1392 }
1393
1394 int strtosa(const char *addr, struct sockaddr_any *sa)
1395 {
1396 memset(sa, 0, sizeof(*sa));
1397
1398 if (inet_pton(AF_INET, addr, &sa->sa_sin.sin_addr) == 1) {
1399 sa->sa_sin.sin_family = AF_INET;
1400 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
1401 sa->sa_sin.sin_len = sizeof(sa->sa_sin);
1402 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
1403 return 0;
1404 }
1405
1406 if (inet_pton(AF_INET6, addr, &sa->sa_sin6.sin6_addr) == 1) {
1407 sa->sa_sin6.sin6_family = AF_INET6;
1408 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
1409 sa->sa_sin6.sin6_len = sizeof(sa->sa_sin6);
1410 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
1411 return 0;
1412 }
1413
1414 return -1;
1415 }
1416
1417 void integer2timestr(uint64_t time, char *buf, size_t buflen)
1418 {
1419 uint64_t year, month, day, hour, minute, second;
1420 int rv;
1421
1422 #define MINUTES (60)
1423 #define HOURS (60 * MINUTES)
1424 #define DAYS (24 * HOURS)
1425 #define MONTHS (30 * DAYS)
1426 #define YEARS (12 * MONTHS)
1427 if (time >= YEARS) {
1428 year = time / YEARS;
1429 time -= year * YEARS;
1430
1431 rv = snprintfrr(buf, buflen, "%" PRIu64 " year(s), ", year);
1432 buf += rv;
1433 buflen -= rv;
1434 }
1435 if (time >= MONTHS) {
1436 month = time / MONTHS;
1437 time -= month * MONTHS;
1438
1439 rv = snprintfrr(buf, buflen, "%" PRIu64 " month(s), ", month);
1440 buf += rv;
1441 buflen -= rv;
1442 }
1443 if (time >= DAYS) {
1444 day = time / DAYS;
1445 time -= day * DAYS;
1446
1447 rv = snprintfrr(buf, buflen, "%" PRIu64 " day(s), ", day);
1448 buf += rv;
1449 buflen -= rv;
1450 }
1451 if (time >= HOURS) {
1452 hour = time / HOURS;
1453 time -= hour * HOURS;
1454
1455 rv = snprintfrr(buf, buflen, "%" PRIu64 " hour(s), ", hour);
1456 buf += rv;
1457 buflen -= rv;
1458 }
1459 if (time >= MINUTES) {
1460 minute = time / MINUTES;
1461 time -= minute * MINUTES;
1462
1463 rv = snprintfrr(buf, buflen, "%" PRIu64 " minute(s), ", minute);
1464 buf += rv;
1465 buflen -= rv;
1466 }
1467 second = time % MINUTES;
1468 snprintfrr(buf, buflen, "%" PRIu64 " second(s)", second);
1469 }
1470
1471 const char *bs_to_string(const struct bfd_session *bs)
1472 {
1473 static char buf[256];
1474 char addr_buf[INET6_ADDRSTRLEN];
1475 int pos;
1476 bool is_mhop = CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH);
1477
1478 pos = snprintf(buf, sizeof(buf), "mhop:%s", is_mhop ? "yes" : "no");
1479 pos += snprintf(buf + pos, sizeof(buf) - pos, " peer:%s",
1480 inet_ntop(bs->key.family, &bs->key.peer, addr_buf,
1481 sizeof(addr_buf)));
1482 pos += snprintf(buf + pos, sizeof(buf) - pos, " local:%s",
1483 inet_ntop(bs->key.family, &bs->key.local, addr_buf,
1484 sizeof(addr_buf)));
1485 if (bs->key.vrfname[0])
1486 pos += snprintf(buf + pos, sizeof(buf) - pos, " vrf:%s",
1487 bs->key.vrfname);
1488 if (bs->key.ifname[0])
1489 pos += snprintf(buf + pos, sizeof(buf) - pos, " ifname:%s",
1490 bs->key.ifname);
1491
1492 (void)pos;
1493
1494 return buf;
1495 }
1496
1497 int bs_observer_add(struct bfd_session *bs)
1498 {
1499 struct bfd_session_observer *bso;
1500
1501 bso = XCALLOC(MTYPE_BFDD_SESSION_OBSERVER, sizeof(*bso));
1502 bso->bso_bs = bs;
1503 bso->bso_addr.family = bs->key.family;
1504 memcpy(&bso->bso_addr.u.prefix, &bs->key.local,
1505 sizeof(bs->key.local));
1506
1507 TAILQ_INSERT_TAIL(&bglobal.bg_obslist, bso, bso_entry);
1508
1509 return 0;
1510 }
1511
1512 void bs_observer_del(struct bfd_session_observer *bso)
1513 {
1514 TAILQ_REMOVE(&bglobal.bg_obslist, bso, bso_entry);
1515 XFREE(MTYPE_BFDD_SESSION_OBSERVER, bso);
1516 }
1517
1518 void bs_to_bpc(struct bfd_session *bs, struct bfd_peer_cfg *bpc)
1519 {
1520 memset(bpc, 0, sizeof(*bpc));
1521
1522 bpc->bpc_ipv4 = (bs->key.family == AF_INET);
1523 bpc->bpc_mhop = bs->key.mhop;
1524
1525 switch (bs->key.family) {
1526 case AF_INET:
1527 bpc->bpc_peer.sa_sin.sin_family = AF_INET;
1528 memcpy(&bpc->bpc_peer.sa_sin.sin_addr, &bs->key.peer,
1529 sizeof(bpc->bpc_peer.sa_sin.sin_addr));
1530
1531 if (memcmp(&bs->key.local, &zero_addr, sizeof(bs->key.local))) {
1532 bpc->bpc_local.sa_sin.sin_family = AF_INET6;
1533 memcpy(&bpc->bpc_local.sa_sin.sin_addr, &bs->key.local,
1534 sizeof(bpc->bpc_local.sa_sin.sin_addr));
1535 }
1536 break;
1537
1538 case AF_INET6:
1539 bpc->bpc_peer.sa_sin.sin_family = AF_INET6;
1540 memcpy(&bpc->bpc_peer.sa_sin6.sin6_addr, &bs->key.peer,
1541 sizeof(bpc->bpc_peer.sa_sin6.sin6_addr));
1542
1543 bpc->bpc_local.sa_sin6.sin6_family = AF_INET6;
1544 memcpy(&bpc->bpc_local.sa_sin6.sin6_addr, &bs->key.local,
1545 sizeof(bpc->bpc_local.sa_sin6.sin6_addr));
1546 break;
1547 }
1548
1549 if (bs->key.ifname[0]) {
1550 bpc->bpc_has_localif = true;
1551 strlcpy(bpc->bpc_localif, bs->key.ifname,
1552 sizeof(bpc->bpc_localif));
1553 }
1554
1555 if (bs->key.vrfname[0]) {
1556 bpc->bpc_has_vrfname = true;
1557 strlcpy(bpc->bpc_vrfname, bs->key.vrfname,
1558 sizeof(bpc->bpc_vrfname));
1559 }
1560 }
1561
1562
1563 /*
1564 * BFD hash data structures to find sessions.
1565 */
1566 static struct hash *bfd_id_hash;
1567 static struct hash *bfd_key_hash;
1568
1569 static unsigned int bfd_id_hash_do(const void *p);
1570 static unsigned int bfd_key_hash_do(const void *p);
1571
1572 static void _bfd_free(struct hash_bucket *hb,
1573 void *arg __attribute__((__unused__)));
1574
1575 /* BFD hash for our discriminator. */
1576 static unsigned int bfd_id_hash_do(const void *p)
1577 {
1578 const struct bfd_session *bs = p;
1579
1580 return jhash_1word(bs->discrs.my_discr, 0);
1581 }
1582
1583 static bool bfd_id_hash_cmp(const void *n1, const void *n2)
1584 {
1585 const struct bfd_session *bs1 = n1, *bs2 = n2;
1586
1587 return bs1->discrs.my_discr == bs2->discrs.my_discr;
1588 }
1589
1590 /* BFD hash for single hop. */
1591 static unsigned int bfd_key_hash_do(const void *p)
1592 {
1593 const struct bfd_session *bs = p;
1594 struct bfd_key key = bs->key;
1595
1596 /*
1597 * Local address and interface name are optional and
1598 * can be filled any time after session creation.
1599 * Hash key should not depend on these fields.
1600 */
1601 memset(&key.local, 0, sizeof(key.local));
1602 memset(key.ifname, 0, sizeof(key.ifname));
1603
1604 return jhash(&key, sizeof(key), 0);
1605 }
1606
1607 static bool bfd_key_hash_cmp(const void *n1, const void *n2)
1608 {
1609 const struct bfd_session *bs1 = n1, *bs2 = n2;
1610
1611 if (bs1->key.family != bs2->key.family)
1612 return false;
1613 if (bs1->key.mhop != bs2->key.mhop)
1614 return false;
1615 if (memcmp(&bs1->key.peer, &bs2->key.peer, sizeof(bs1->key.peer)))
1616 return false;
1617 if (memcmp(bs1->key.vrfname, bs2->key.vrfname,
1618 sizeof(bs1->key.vrfname)))
1619 return false;
1620
1621 /*
1622 * Local address is optional and can be empty.
1623 * If both addresses are not empty and different,
1624 * then the keys are different.
1625 */
1626 if (memcmp(&bs1->key.local, &zero_addr, sizeof(bs1->key.local))
1627 && memcmp(&bs2->key.local, &zero_addr, sizeof(bs2->key.local))
1628 && memcmp(&bs1->key.local, &bs2->key.local, sizeof(bs1->key.local)))
1629 return false;
1630
1631 /*
1632 * Interface name is optional and can be empty.
1633 * If both names are not empty and different,
1634 * then the keys are different.
1635 */
1636 if (bs1->key.ifname[0] && bs2->key.ifname[0]
1637 && memcmp(bs1->key.ifname, bs2->key.ifname,
1638 sizeof(bs1->key.ifname)))
1639 return false;
1640
1641 return true;
1642 }
1643
1644
1645 /*
1646 * Hash public interface / exported functions.
1647 */
1648
1649 /* Lookup functions. */
1650 struct bfd_session *bfd_id_lookup(uint32_t id)
1651 {
1652 struct bfd_session bs;
1653
1654 bs.discrs.my_discr = id;
1655
1656 return hash_lookup(bfd_id_hash, &bs);
1657 }
1658
1659 struct bfd_session *bfd_key_lookup(struct bfd_key key)
1660 {
1661 struct bfd_session bs;
1662
1663 bs.key = key;
1664
1665 return hash_lookup(bfd_key_hash, &bs);
1666 }
1667
1668 /*
1669 * Delete functions.
1670 *
1671 * Delete functions searches and remove the item from the hash and
1672 * returns a pointer to the removed item data. If the item was not found
1673 * then it returns NULL.
1674 *
1675 * The data stored inside the hash is not free()ed, so you must do it
1676 * manually after getting the pointer back.
1677 */
1678 struct bfd_session *bfd_id_delete(uint32_t id)
1679 {
1680 struct bfd_session bs;
1681
1682 bs.discrs.my_discr = id;
1683
1684 return hash_release(bfd_id_hash, &bs);
1685 }
1686
1687 struct bfd_session *bfd_key_delete(struct bfd_key key)
1688 {
1689 struct bfd_session bs;
1690
1691 bs.key = key;
1692
1693 return hash_release(bfd_key_hash, &bs);
1694 }
1695
1696 /* Iteration functions. */
1697 void bfd_id_iterate(hash_iter_func hif, void *arg)
1698 {
1699 hash_iterate(bfd_id_hash, hif, arg);
1700 }
1701
1702 void bfd_key_iterate(hash_iter_func hif, void *arg)
1703 {
1704 hash_iterate(bfd_key_hash, hif, arg);
1705 }
1706
1707 /*
1708 * Insert functions.
1709 *
1710 * Inserts session into hash and returns `true` on success, otherwise
1711 * `false`.
1712 */
1713 bool bfd_id_insert(struct bfd_session *bs)
1714 {
1715 return (hash_get(bfd_id_hash, bs, hash_alloc_intern) == bs);
1716 }
1717
1718 bool bfd_key_insert(struct bfd_session *bs)
1719 {
1720 return (hash_get(bfd_key_hash, bs, hash_alloc_intern) == bs);
1721 }
1722
1723 void bfd_initialize(void)
1724 {
1725 bfd_id_hash = hash_create(bfd_id_hash_do, bfd_id_hash_cmp,
1726 "BFD session discriminator hash");
1727 bfd_key_hash = hash_create(bfd_key_hash_do, bfd_key_hash_cmp,
1728 "BFD session hash");
1729 TAILQ_INIT(&bplist);
1730 }
1731
1732 static void _bfd_free(struct hash_bucket *hb,
1733 void *arg __attribute__((__unused__)))
1734 {
1735 struct bfd_session *bs = hb->data;
1736
1737 bfd_session_free(bs);
1738 }
1739
1740 void bfd_shutdown(void)
1741 {
1742 struct bfd_profile *bp;
1743
1744 /*
1745 * Close and free all BFD sessions.
1746 *
1747 * _bfd_free() will call bfd_session_free() which will take care
1748 * of removing the session from all hashes, so we just run an
1749 * assert() here to make sure it really happened.
1750 */
1751 bfd_id_iterate(_bfd_free, NULL);
1752 assert(bfd_key_hash->count == 0);
1753
1754 /* Now free the hashes themselves. */
1755 hash_free(bfd_id_hash);
1756 hash_free(bfd_key_hash);
1757
1758 /* Free all profile allocations. */
1759 while ((bp = TAILQ_FIRST(&bplist)) != NULL)
1760 bfd_profile_free(bp);
1761 }
1762
1763 struct bfd_session_iterator {
1764 int bsi_stop;
1765 bool bsi_mhop;
1766 const struct bfd_session *bsi_bs;
1767 };
1768
1769 static int _bfd_session_next(struct hash_bucket *hb, void *arg)
1770 {
1771 struct bfd_session_iterator *bsi = arg;
1772 struct bfd_session *bs = hb->data;
1773
1774 /* Previous entry signaled stop. */
1775 if (bsi->bsi_stop == 1) {
1776 /* Match the single/multi hop sessions. */
1777 if (bs->key.mhop != bsi->bsi_mhop)
1778 return HASHWALK_CONTINUE;
1779
1780 bsi->bsi_bs = bs;
1781 return HASHWALK_ABORT;
1782 }
1783
1784 /* We found the current item, stop in the next one. */
1785 if (bsi->bsi_bs == hb->data) {
1786 bsi->bsi_stop = 1;
1787 /* Set entry to NULL to signal end of list. */
1788 bsi->bsi_bs = NULL;
1789 } else if (bsi->bsi_bs == NULL && bsi->bsi_mhop == bs->key.mhop) {
1790 /* We want the first list item. */
1791 bsi->bsi_stop = 1;
1792 bsi->bsi_bs = hb->data;
1793 return HASHWALK_ABORT;
1794 }
1795
1796 return HASHWALK_CONTINUE;
1797 }
1798
1799 /*
1800 * bfd_session_next: uses the current session to find the next.
1801 *
1802 * `bs` might point to NULL to get the first item of the data structure.
1803 */
1804 const struct bfd_session *bfd_session_next(const struct bfd_session *bs,
1805 bool mhop)
1806 {
1807 struct bfd_session_iterator bsi;
1808
1809 bsi.bsi_stop = 0;
1810 bsi.bsi_bs = bs;
1811 bsi.bsi_mhop = mhop;
1812 hash_walk(bfd_key_hash, _bfd_session_next, &bsi);
1813 if (bsi.bsi_stop == 0)
1814 return NULL;
1815
1816 return bsi.bsi_bs;
1817 }
1818
1819 static void _bfd_session_remove_manual(struct hash_bucket *hb,
1820 void *arg __attribute__((__unused__)))
1821 {
1822 struct bfd_session *bs = hb->data;
1823
1824 /* Delete only manually configured sessions. */
1825 if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG) == 0)
1826 return;
1827
1828 bs->refcount--;
1829 UNSET_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG);
1830
1831 /* Don't delete sessions still in use. */
1832 if (bs->refcount != 0)
1833 return;
1834
1835 bfd_session_free(bs);
1836 }
1837
1838 /*
1839 * bfd_sessions_remove_manual: remove all manually configured sessions.
1840 *
1841 * NOTE: this function doesn't remove automatically created sessions.
1842 */
1843 void bfd_sessions_remove_manual(void)
1844 {
1845 hash_iterate(bfd_key_hash, _bfd_session_remove_manual, NULL);
1846 }
1847
1848 void bfd_profiles_remove(void)
1849 {
1850 struct bfd_profile *bp;
1851
1852 while ((bp = TAILQ_FIRST(&bplist)) != NULL)
1853 bfd_profile_free(bp);
1854 }
1855
1856 /*
1857 * Profile related hash functions.
1858 */
1859 static void _bfd_profile_update(struct hash_bucket *hb, void *arg)
1860 {
1861 struct bfd_profile *bp = arg;
1862 struct bfd_session *bs = hb->data;
1863
1864 /* This session is not using the profile. */
1865 if (bs->profile_name == NULL || strcmp(bs->profile_name, bp->name) != 0)
1866 return;
1867
1868 bfd_profile_apply(bp->name, bs);
1869 }
1870
1871 void bfd_profile_update(struct bfd_profile *bp)
1872 {
1873 hash_iterate(bfd_key_hash, _bfd_profile_update, bp);
1874 }
1875
1876 static void _bfd_profile_detach(struct hash_bucket *hb, void *arg)
1877 {
1878 struct bfd_profile *bp = arg;
1879 struct bfd_session *bs = hb->data;
1880
1881 /* This session is not using the profile. */
1882 if (bs->profile_name == NULL || strcmp(bs->profile_name, bp->name) != 0)
1883 return;
1884
1885 bfd_profile_remove(bs);
1886 }
1887
1888 static void bfd_profile_detach(struct bfd_profile *bp)
1889 {
1890 hash_iterate(bfd_key_hash, _bfd_profile_detach, bp);
1891 }
1892
1893 /*
1894 * VRF related functions.
1895 */
1896 static int bfd_vrf_new(struct vrf *vrf)
1897 {
1898 if (bglobal.debug_zebra)
1899 zlog_debug("VRF Created: %s(%u)", vrf->name, vrf->vrf_id);
1900
1901 return 0;
1902 }
1903
1904 static int bfd_vrf_delete(struct vrf *vrf)
1905 {
1906 if (bglobal.debug_zebra)
1907 zlog_debug("VRF Deletion: %s(%u)", vrf->name, vrf->vrf_id);
1908
1909 return 0;
1910 }
1911
1912 static int bfd_vrf_enable(struct vrf *vrf)
1913 {
1914 struct bfd_vrf_global *bvrf;
1915
1916 /* a different name */
1917 if (!vrf->info) {
1918 bvrf = XCALLOC(MTYPE_BFDD_VRF, sizeof(struct bfd_vrf_global));
1919 bvrf->vrf = vrf;
1920 vrf->info = (void *)bvrf;
1921
1922 /* Disable sockets if using data plane. */
1923 if (bglobal.bg_use_dplane) {
1924 bvrf->bg_shop = -1;
1925 bvrf->bg_mhop = -1;
1926 bvrf->bg_shop6 = -1;
1927 bvrf->bg_mhop6 = -1;
1928 bvrf->bg_echo = -1;
1929 bvrf->bg_echov6 = -1;
1930 }
1931 } else
1932 bvrf = vrf->info;
1933
1934 if (bglobal.debug_zebra)
1935 zlog_debug("VRF enable add %s id %u", vrf->name, vrf->vrf_id);
1936
1937 if (vrf->vrf_id == VRF_DEFAULT ||
1938 vrf_get_backend() == VRF_BACKEND_NETNS) {
1939 if (!bvrf->bg_shop)
1940 bvrf->bg_shop = bp_udp_shop(vrf);
1941 if (!bvrf->bg_mhop)
1942 bvrf->bg_mhop = bp_udp_mhop(vrf);
1943 if (!bvrf->bg_shop6)
1944 bvrf->bg_shop6 = bp_udp6_shop(vrf);
1945 if (!bvrf->bg_mhop6)
1946 bvrf->bg_mhop6 = bp_udp6_mhop(vrf);
1947 if (!bvrf->bg_echo)
1948 bvrf->bg_echo = bp_echo_socket(vrf);
1949 if (!bvrf->bg_echov6)
1950 bvrf->bg_echov6 = bp_echov6_socket(vrf);
1951
1952 if (!bvrf->bg_ev[0] && bvrf->bg_shop != -1)
1953 thread_add_read(master, bfd_recv_cb, bvrf,
1954 bvrf->bg_shop, &bvrf->bg_ev[0]);
1955 if (!bvrf->bg_ev[1] && bvrf->bg_mhop != -1)
1956 thread_add_read(master, bfd_recv_cb, bvrf,
1957 bvrf->bg_mhop, &bvrf->bg_ev[1]);
1958 if (!bvrf->bg_ev[2] && bvrf->bg_shop6 != -1)
1959 thread_add_read(master, bfd_recv_cb, bvrf,
1960 bvrf->bg_shop6, &bvrf->bg_ev[2]);
1961 if (!bvrf->bg_ev[3] && bvrf->bg_mhop6 != -1)
1962 thread_add_read(master, bfd_recv_cb, bvrf,
1963 bvrf->bg_mhop6, &bvrf->bg_ev[3]);
1964 if (!bvrf->bg_ev[4] && bvrf->bg_echo != -1)
1965 thread_add_read(master, bfd_recv_cb, bvrf,
1966 bvrf->bg_echo, &bvrf->bg_ev[4]);
1967 if (!bvrf->bg_ev[5] && bvrf->bg_echov6 != -1)
1968 thread_add_read(master, bfd_recv_cb, bvrf,
1969 bvrf->bg_echov6, &bvrf->bg_ev[5]);
1970 }
1971 if (vrf->vrf_id != VRF_DEFAULT) {
1972 bfdd_zclient_register(vrf->vrf_id);
1973 bfdd_sessions_enable_vrf(vrf);
1974 }
1975 return 0;
1976 }
1977
1978 static int bfd_vrf_disable(struct vrf *vrf)
1979 {
1980 struct bfd_vrf_global *bvrf;
1981
1982 if (!vrf->info)
1983 return 0;
1984 bvrf = vrf->info;
1985
1986 if (vrf->vrf_id != VRF_DEFAULT) {
1987 bfdd_sessions_disable_vrf(vrf);
1988 bfdd_zclient_unregister(vrf->vrf_id);
1989 }
1990
1991 if (bglobal.debug_zebra)
1992 zlog_debug("VRF disable %s id %d", vrf->name, vrf->vrf_id);
1993
1994 /* Disable read/write poll triggering. */
1995 THREAD_OFF(bvrf->bg_ev[0]);
1996 THREAD_OFF(bvrf->bg_ev[1]);
1997 THREAD_OFF(bvrf->bg_ev[2]);
1998 THREAD_OFF(bvrf->bg_ev[3]);
1999 THREAD_OFF(bvrf->bg_ev[4]);
2000 THREAD_OFF(bvrf->bg_ev[5]);
2001
2002 /* Close all descriptors. */
2003 socket_close(&bvrf->bg_echo);
2004 socket_close(&bvrf->bg_shop);
2005 socket_close(&bvrf->bg_mhop);
2006 if (bvrf->bg_shop6 != -1)
2007 socket_close(&bvrf->bg_shop6);
2008 if (bvrf->bg_mhop6 != -1)
2009 socket_close(&bvrf->bg_mhop6);
2010 socket_close(&bvrf->bg_echo);
2011 if (bvrf->bg_echov6 != -1)
2012 socket_close(&bvrf->bg_echov6);
2013
2014 /* free context */
2015 XFREE(MTYPE_BFDD_VRF, bvrf);
2016 vrf->info = NULL;
2017
2018 return 0;
2019 }
2020
2021 void bfd_vrf_init(void)
2022 {
2023 vrf_init(bfd_vrf_new, bfd_vrf_enable, bfd_vrf_disable, bfd_vrf_delete);
2024 }
2025
2026 void bfd_vrf_terminate(void)
2027 {
2028 vrf_terminate();
2029 }
2030
2031 struct bfd_vrf_global *bfd_vrf_look_by_session(struct bfd_session *bfd)
2032 {
2033 struct vrf *vrf;
2034
2035 if (!vrf_is_backend_netns()) {
2036 vrf = vrf_lookup_by_id(VRF_DEFAULT);
2037 if (vrf)
2038 return (struct bfd_vrf_global *)vrf->info;
2039 return NULL;
2040 }
2041 if (!bfd)
2042 return NULL;
2043 if (!bfd->vrf)
2044 return NULL;
2045 return bfd->vrf->info;
2046 }
2047
2048 unsigned long bfd_get_session_count(void)
2049 {
2050 return bfd_key_hash->count;
2051 }