]> git.proxmox.com Git - mirror_frr.git/blob - bfdd/bfd.c
bfdd: Fix overflow possibility with time statements
[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 int 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 return 0;
619 }
620
621 int bfd_echo_xmt_cb(struct thread *t)
622 {
623 struct bfd_session *bs = THREAD_ARG(t);
624
625 if (bs->echo_xmt_TO > 0)
626 ptm_bfd_echo_xmt_TO(bs);
627
628 return 0;
629 }
630
631 /* Was ptm_bfd_detect_TO() */
632 int bfd_recvtimer_cb(struct thread *t)
633 {
634 struct bfd_session *bs = THREAD_ARG(t);
635
636 switch (bs->ses_state) {
637 case PTM_BFD_INIT:
638 case PTM_BFD_UP:
639 ptm_bfd_sess_dn(bs, BD_CONTROL_EXPIRED);
640 break;
641 }
642
643 return 0;
644 }
645
646 /* Was ptm_bfd_echo_detect_TO() */
647 int bfd_echo_recvtimer_cb(struct thread *t)
648 {
649 struct bfd_session *bs = THREAD_ARG(t);
650
651 switch (bs->ses_state) {
652 case PTM_BFD_INIT:
653 case PTM_BFD_UP:
654 ptm_bfd_sess_dn(bs, BD_ECHO_FAILED);
655 break;
656 }
657
658 return 0;
659 }
660
661 struct bfd_session *bfd_session_new(void)
662 {
663 struct bfd_session *bs;
664
665 bs = XCALLOC(MTYPE_BFDD_CONFIG, sizeof(*bs));
666
667 /* Set peer session defaults. */
668 bfd_profile_set_default(&bs->peer_profile);
669
670 bs->timers.desired_min_tx = BFD_DEFDESIREDMINTX;
671 bs->timers.required_min_rx = BFD_DEFREQUIREDMINRX;
672 bs->timers.required_min_echo_rx = BFD_DEF_REQ_MIN_ECHO_RX;
673 bs->timers.desired_min_echo_tx = BFD_DEF_DES_MIN_ECHO_TX;
674 bs->detect_mult = BFD_DEFDETECTMULT;
675 bs->mh_ttl = BFD_DEF_MHOP_TTL;
676 bs->ses_state = PTM_BFD_DOWN;
677
678 /* Initiate connection with slow timers. */
679 bs_set_slow_timers(bs);
680
681 /* Initiate remote settings as well. */
682 bs->remote_timers = bs->cur_timers;
683 bs->remote_detect_mult = BFD_DEFDETECTMULT;
684
685 bs->sock = -1;
686 monotime(&bs->uptime);
687 bs->downtime = bs->uptime;
688
689 return bs;
690 }
691
692 int bfd_session_update_label(struct bfd_session *bs, const char *nlabel)
693 {
694 /* New label treatment:
695 * - Check if the label is taken;
696 * - Try to allocate the memory for it and register;
697 */
698 if (bs->pl == NULL) {
699 if (pl_find(nlabel) != NULL) {
700 /* Someone is already using it. */
701 return -1;
702 }
703
704 pl_new(nlabel, bs);
705
706 return 0;
707 }
708
709 /*
710 * Test label change consistency:
711 * - Do nothing if it's the same label;
712 * - Check if the future label is already taken;
713 * - Change label;
714 */
715 if (strcmp(nlabel, bs->pl->pl_label) == 0)
716 return -1;
717 if (pl_find(nlabel) != NULL)
718 return -1;
719
720 strlcpy(bs->pl->pl_label, nlabel, sizeof(bs->pl->pl_label));
721 return 0;
722 }
723
724 static void _bfd_session_update(struct bfd_session *bs,
725 struct bfd_peer_cfg *bpc)
726 {
727 if (bpc->bpc_has_txinterval) {
728 bs->timers.desired_min_tx = bpc->bpc_txinterval * 1000;
729 bs->peer_profile.min_tx = bs->timers.desired_min_tx;
730 }
731
732 if (bpc->bpc_has_recvinterval) {
733 bs->timers.required_min_rx = bpc->bpc_recvinterval * 1000;
734 bs->peer_profile.min_rx = bs->timers.required_min_rx;
735 }
736
737 if (bpc->bpc_has_detectmultiplier) {
738 bs->detect_mult = bpc->bpc_detectmultiplier;
739 bs->peer_profile.detection_multiplier = bs->detect_mult;
740 }
741
742 if (bpc->bpc_has_echorecvinterval) {
743 bs->timers.required_min_echo_rx = bpc->bpc_echorecvinterval * 1000;
744 bs->peer_profile.min_echo_rx = bs->timers.required_min_echo_rx;
745 }
746
747 if (bpc->bpc_has_echotxinterval) {
748 bs->timers.desired_min_echo_tx = bpc->bpc_echotxinterval * 1000;
749 bs->peer_profile.min_echo_tx = bs->timers.desired_min_echo_tx;
750 }
751
752 if (bpc->bpc_has_label)
753 bfd_session_update_label(bs, bpc->bpc_label);
754
755 if (bpc->bpc_cbit)
756 SET_FLAG(bs->flags, BFD_SESS_FLAG_CBIT);
757 else
758 UNSET_FLAG(bs->flags, BFD_SESS_FLAG_CBIT);
759
760 if (bpc->bpc_has_minimum_ttl) {
761 bs->mh_ttl = bpc->bpc_minimum_ttl;
762 bs->peer_profile.minimum_ttl = bpc->bpc_minimum_ttl;
763 }
764
765 bs->peer_profile.echo_mode = bpc->bpc_echo;
766 bfd_set_echo(bs, bpc->bpc_echo);
767
768 /*
769 * Shutdown needs to be the last in order to avoid timers enable when
770 * the session is disabled.
771 */
772 bs->peer_profile.admin_shutdown = bpc->bpc_shutdown;
773 bfd_set_passive_mode(bs, bpc->bpc_passive);
774 bfd_set_shutdown(bs, bpc->bpc_shutdown);
775
776 /*
777 * Apply profile last: it also calls `bfd_set_shutdown`.
778 *
779 * There is no problem calling `shutdown` twice if the value doesn't
780 * change or if it is overriden by peer specific configuration.
781 */
782 if (bpc->bpc_has_profile)
783 bfd_profile_apply(bpc->bpc_profile, bs);
784 }
785
786 static int bfd_session_update(struct bfd_session *bs, struct bfd_peer_cfg *bpc)
787 {
788 /* User didn't want to update, return failure. */
789 if (bpc->bpc_createonly)
790 return -1;
791
792 _bfd_session_update(bs, bpc);
793
794 control_notify_config(BCM_NOTIFY_CONFIG_UPDATE, bs);
795
796 return 0;
797 }
798
799 void bfd_session_free(struct bfd_session *bs)
800 {
801 struct bfd_session_observer *bso;
802
803 bfd_session_disable(bs);
804
805 /* Remove session from data plane if any. */
806 bfd_dplane_delete_session(bs);
807
808 bfd_key_delete(bs->key);
809 bfd_id_delete(bs->discrs.my_discr);
810
811 /* Remove observer if any. */
812 TAILQ_FOREACH(bso, &bglobal.bg_obslist, bso_entry) {
813 if (bso->bso_bs != bs)
814 continue;
815
816 break;
817 }
818 if (bso != NULL)
819 bs_observer_del(bso);
820
821 pl_free(bs->pl);
822
823 XFREE(MTYPE_BFDD_PROFILE, bs->profile_name);
824 XFREE(MTYPE_BFDD_CONFIG, bs);
825 }
826
827 struct bfd_session *ptm_bfd_sess_new(struct bfd_peer_cfg *bpc)
828 {
829 struct bfd_session *bfd, *l_bfd;
830
831 /* check to see if this needs a new session */
832 l_bfd = bs_peer_find(bpc);
833 if (l_bfd) {
834 /* Requesting a duplicated peer means update configuration. */
835 if (bfd_session_update(l_bfd, bpc) == 0)
836 return l_bfd;
837 else
838 return NULL;
839 }
840
841 /* Get BFD session storage with its defaults. */
842 bfd = bfd_session_new();
843
844 /*
845 * Store interface/VRF name in case we need to delay session
846 * start. See `bfd_session_enable` for more information.
847 */
848 if (bpc->bpc_has_localif)
849 strlcpy(bfd->key.ifname, bpc->bpc_localif,
850 sizeof(bfd->key.ifname));
851
852 if (bpc->bpc_has_vrfname)
853 strlcpy(bfd->key.vrfname, bpc->bpc_vrfname,
854 sizeof(bfd->key.vrfname));
855 else
856 strlcpy(bfd->key.vrfname, VRF_DEFAULT_NAME,
857 sizeof(bfd->key.vrfname));
858
859 /* Copy remaining data. */
860 if (bpc->bpc_ipv4 == false)
861 SET_FLAG(bfd->flags, BFD_SESS_FLAG_IPV6);
862
863 bfd->key.family = (bpc->bpc_ipv4) ? AF_INET : AF_INET6;
864 switch (bfd->key.family) {
865 case AF_INET:
866 memcpy(&bfd->key.peer, &bpc->bpc_peer.sa_sin.sin_addr,
867 sizeof(bpc->bpc_peer.sa_sin.sin_addr));
868 memcpy(&bfd->key.local, &bpc->bpc_local.sa_sin.sin_addr,
869 sizeof(bpc->bpc_local.sa_sin.sin_addr));
870 break;
871
872 case AF_INET6:
873 memcpy(&bfd->key.peer, &bpc->bpc_peer.sa_sin6.sin6_addr,
874 sizeof(bpc->bpc_peer.sa_sin6.sin6_addr));
875 memcpy(&bfd->key.local, &bpc->bpc_local.sa_sin6.sin6_addr,
876 sizeof(bpc->bpc_local.sa_sin6.sin6_addr));
877 break;
878
879 default:
880 assert(1);
881 break;
882 }
883
884 if (bpc->bpc_mhop)
885 SET_FLAG(bfd->flags, BFD_SESS_FLAG_MH);
886
887 bfd->key.mhop = bpc->bpc_mhop;
888
889 if (bs_registrate(bfd) == NULL)
890 return NULL;
891
892 /* Apply other configurations. */
893 _bfd_session_update(bfd, bpc);
894
895 return bfd;
896 }
897
898 struct bfd_session *bs_registrate(struct bfd_session *bfd)
899 {
900 /* Registrate session into data structures. */
901 bfd_key_insert(bfd);
902 bfd->discrs.my_discr = ptm_bfd_gen_ID();
903 bfd_id_insert(bfd);
904
905 /* Try to enable session and schedule for packet receive/send. */
906 if (bfd_session_enable(bfd) == -1) {
907 /* Unrecoverable failure, remove the session/peer. */
908 bfd_session_free(bfd);
909 return NULL;
910 }
911
912 /* Add observer if we have moving parts. */
913 if (bfd->key.ifname[0] || bfd->key.vrfname[0] || bfd->sock == -1)
914 bs_observer_add(bfd);
915
916 if (bglobal.debug_peer_event)
917 zlog_debug("session-new: %s", bs_to_string(bfd));
918
919 control_notify_config(BCM_NOTIFY_CONFIG_ADD, bfd);
920
921 return bfd;
922 }
923
924 int ptm_bfd_sess_del(struct bfd_peer_cfg *bpc)
925 {
926 struct bfd_session *bs;
927
928 /* Find session and call free(). */
929 bs = bs_peer_find(bpc);
930 if (bs == NULL)
931 return -1;
932
933 /* This pointer is being referenced, don't let it be deleted. */
934 if (bs->refcount > 0) {
935 zlog_err("session-delete: refcount failure: %" PRIu64" references",
936 bs->refcount);
937 return -1;
938 }
939
940 if (bglobal.debug_peer_event)
941 zlog_debug("session-delete: %s", bs_to_string(bs));
942
943 control_notify_config(BCM_NOTIFY_CONFIG_DELETE, bs);
944
945 bfd_session_free(bs);
946
947 return 0;
948 }
949
950 void bfd_set_polling(struct bfd_session *bs)
951 {
952 /*
953 * Start polling procedure: the only timers that require polling
954 * to change value without losing connection are:
955 *
956 * - Desired minimum transmission interval;
957 * - Required minimum receive interval;
958 *
959 * RFC 5880, Section 6.8.3.
960 */
961 bs->polling = 1;
962 }
963
964 /*
965 * bs_<state>_handler() functions implement the BFD state machine
966 * transition mechanism. `<state>` is the current session state and
967 * the parameter `nstate` is the peer new state.
968 */
969 static void bs_admin_down_handler(struct bfd_session *bs
970 __attribute__((__unused__)),
971 int nstate __attribute__((__unused__)))
972 {
973 /*
974 * We are administratively down, there is no state machine
975 * handling.
976 */
977 }
978
979 static void bs_down_handler(struct bfd_session *bs, int nstate)
980 {
981 switch (nstate) {
982 case PTM_BFD_ADM_DOWN:
983 /*
984 * Remote peer doesn't want to talk, so lets keep the
985 * connection down.
986 */
987 case PTM_BFD_UP:
988 /* Peer can't be up yet, wait it go to 'init' or 'down'. */
989 break;
990
991 case PTM_BFD_DOWN:
992 /*
993 * Remote peer agreed that the path is down, lets try to
994 * bring it up.
995 */
996 bs->ses_state = PTM_BFD_INIT;
997
998 /*
999 * RFC 5880, Section 6.1.
1000 * A system taking the Passive role MUST NOT begin
1001 * sending BFD packets for a particular session until
1002 * it has received a BFD packet for that session, and thus
1003 * has learned the remote system's discriminator value.
1004 *
1005 * Now we can start transmission timer in passive mode.
1006 */
1007 if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_PASSIVE))
1008 ptm_bfd_xmt_TO(bs, 0);
1009
1010 break;
1011
1012 case PTM_BFD_INIT:
1013 /*
1014 * Remote peer told us his path is up, lets turn
1015 * activate the session.
1016 */
1017 ptm_bfd_sess_up(bs);
1018 break;
1019
1020 default:
1021 if (bglobal.debug_peer_event)
1022 zlog_debug("state-change: unhandled neighbor state: %d",
1023 nstate);
1024 break;
1025 }
1026 }
1027
1028 static void bs_init_handler(struct bfd_session *bs, int nstate)
1029 {
1030 switch (nstate) {
1031 case PTM_BFD_ADM_DOWN:
1032 /*
1033 * Remote peer doesn't want to talk, so lets make the
1034 * connection down.
1035 */
1036 ptm_bfd_sess_dn(bs, BD_NEIGHBOR_DOWN);
1037 break;
1038
1039 case PTM_BFD_DOWN:
1040 /* Remote peer hasn't moved to first stage yet. */
1041 break;
1042
1043 case PTM_BFD_INIT:
1044 case PTM_BFD_UP:
1045 /* We agreed on the settings and the path is up. */
1046 ptm_bfd_sess_up(bs);
1047 break;
1048
1049 default:
1050 if (bglobal.debug_peer_event)
1051 zlog_debug("state-change: unhandled neighbor state: %d",
1052 nstate);
1053 break;
1054 }
1055 }
1056
1057 static void bs_up_handler(struct bfd_session *bs, int nstate)
1058 {
1059 switch (nstate) {
1060 case PTM_BFD_ADM_DOWN:
1061 case PTM_BFD_DOWN:
1062 /* Peer lost or asked to shutdown connection. */
1063 ptm_bfd_sess_dn(bs, BD_NEIGHBOR_DOWN);
1064 break;
1065
1066 case PTM_BFD_INIT:
1067 case PTM_BFD_UP:
1068 /* Path is up and working. */
1069 break;
1070
1071 default:
1072 if (bglobal.debug_peer_event)
1073 zlog_debug("state-change: unhandled neighbor state: %d",
1074 nstate);
1075 break;
1076 }
1077 }
1078
1079 void bs_state_handler(struct bfd_session *bs, int nstate)
1080 {
1081 switch (bs->ses_state) {
1082 case PTM_BFD_ADM_DOWN:
1083 bs_admin_down_handler(bs, nstate);
1084 break;
1085 case PTM_BFD_DOWN:
1086 bs_down_handler(bs, nstate);
1087 break;
1088 case PTM_BFD_INIT:
1089 bs_init_handler(bs, nstate);
1090 break;
1091 case PTM_BFD_UP:
1092 bs_up_handler(bs, nstate);
1093 break;
1094
1095 default:
1096 if (bglobal.debug_peer_event)
1097 zlog_debug("state-change: [%s] is in invalid state: %d",
1098 bs_to_string(bs), nstate);
1099 break;
1100 }
1101 }
1102
1103 /*
1104 * Handles echo timer manipulation after updating timer.
1105 */
1106 void bs_echo_timer_handler(struct bfd_session *bs)
1107 {
1108 uint32_t old_timer;
1109
1110 /*
1111 * Before doing any echo handling, check if it is possible to
1112 * use it.
1113 *
1114 * - Check for `echo-mode` configuration.
1115 * - Check that we are not using multi hop (RFC 5883,
1116 * Section 3).
1117 * - Check that we are already at the up state.
1118 */
1119 if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO) == 0
1120 || CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)
1121 || bs->ses_state != PTM_BFD_UP)
1122 return;
1123
1124 /* Remote peer asked to stop echo. */
1125 if (bs->remote_timers.required_min_echo == 0) {
1126 if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO_ACTIVE))
1127 ptm_bfd_echo_stop(bs);
1128
1129 return;
1130 }
1131
1132 /*
1133 * Calculate the echo transmission timer: we must not send
1134 * echo packets faster than the minimum required time
1135 * announced by the remote system.
1136 *
1137 * RFC 5880, Section 6.8.9.
1138 */
1139 old_timer = bs->echo_xmt_TO;
1140 if (bs->remote_timers.required_min_echo > bs->timers.desired_min_echo_tx)
1141 bs->echo_xmt_TO = bs->remote_timers.required_min_echo;
1142 else
1143 bs->echo_xmt_TO = bs->timers.desired_min_echo_tx;
1144
1145 if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO_ACTIVE) == 0
1146 || old_timer != bs->echo_xmt_TO)
1147 ptm_bfd_echo_start(bs);
1148 }
1149
1150 /*
1151 * RFC 5880 Section 6.5.
1152 *
1153 * When a BFD control packet with the final bit is received, we must
1154 * update the session parameters.
1155 */
1156 void bs_final_handler(struct bfd_session *bs)
1157 {
1158 /* Start using our new timers. */
1159 bs->cur_timers.desired_min_tx = bs->timers.desired_min_tx;
1160 bs->cur_timers.required_min_rx = bs->timers.required_min_rx;
1161
1162 /*
1163 * TODO: demand mode. See RFC 5880 Section 6.1.
1164 *
1165 * When using demand mode we must disable the detection timer
1166 * for lost control packets.
1167 */
1168 if (bs->demand_mode) {
1169 /* Notify watchers about changed timers. */
1170 control_notify_config(BCM_NOTIFY_CONFIG_UPDATE, bs);
1171 return;
1172 }
1173
1174 /*
1175 * Calculate transmission time based on new timers.
1176 *
1177 * Transmission calculation:
1178 * Unless specified by exceptions at the end of Section 6.8.7, the
1179 * transmission time will be determined by the system with the
1180 * slowest rate.
1181 *
1182 * RFC 5880, Section 6.8.7.
1183 */
1184 if (bs->timers.desired_min_tx > bs->remote_timers.required_min_rx)
1185 bs->xmt_TO = bs->timers.desired_min_tx;
1186 else
1187 bs->xmt_TO = bs->remote_timers.required_min_rx;
1188
1189 /* Apply new transmission timer immediately. */
1190 ptm_bfd_start_xmt_timer(bs, false);
1191
1192 /* Notify watchers about changed timers. */
1193 control_notify_config(BCM_NOTIFY_CONFIG_UPDATE, bs);
1194 }
1195
1196 void bs_set_slow_timers(struct bfd_session *bs)
1197 {
1198 /*
1199 * BFD connection must use slow timers before going up or after
1200 * losing connectivity to avoid wasting bandwidth.
1201 *
1202 * RFC 5880, Section 6.8.3.
1203 */
1204 bs->cur_timers.desired_min_tx = BFD_DEF_SLOWTX;
1205 bs->cur_timers.required_min_rx = BFD_DEF_SLOWTX;
1206 bs->cur_timers.required_min_echo = 0;
1207
1208 /* Set the appropriated timeouts for slow connection. */
1209 bs->detect_TO = (BFD_DEFDETECTMULT * BFD_DEF_SLOWTX);
1210 bs->xmt_TO = BFD_DEF_SLOWTX;
1211 }
1212
1213 void bfd_set_echo(struct bfd_session *bs, bool echo)
1214 {
1215 if (echo) {
1216 /* Check if echo mode is already active. */
1217 if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO))
1218 return;
1219
1220 SET_FLAG(bs->flags, BFD_SESS_FLAG_ECHO);
1221
1222 /* Activate/update echo receive timeout timer. */
1223 if (bs->bdc == NULL)
1224 bs_echo_timer_handler(bs);
1225 } else {
1226 /* Check if echo mode is already disabled. */
1227 if (!CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO))
1228 return;
1229
1230 UNSET_FLAG(bs->flags, BFD_SESS_FLAG_ECHO);
1231
1232 /* Deactivate timeout timer. */
1233 if (bs->bdc == NULL)
1234 ptm_bfd_echo_stop(bs);
1235 }
1236 }
1237
1238 void bfd_set_shutdown(struct bfd_session *bs, bool shutdown)
1239 {
1240 bool is_shutdown;
1241
1242 /*
1243 * Special case: we are batching changes and the previous state was
1244 * not shutdown. Instead of potentially disconnect a running peer,
1245 * we'll get the current status to validate we were really down.
1246 */
1247 if (bs->ses_state == PTM_BFD_UP)
1248 is_shutdown = false;
1249 else
1250 is_shutdown = CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN);
1251
1252 if (shutdown) {
1253 /* Already shutdown. */
1254 if (is_shutdown)
1255 return;
1256
1257 SET_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN);
1258
1259 /* Handle data plane shutdown case. */
1260 if (bs->bdc) {
1261 bs->ses_state = PTM_BFD_ADM_DOWN;
1262 bfd_dplane_update_session(bs);
1263 control_notify(bs, bs->ses_state);
1264 return;
1265 }
1266
1267 /* Disable all events. */
1268 bfd_recvtimer_delete(bs);
1269 bfd_echo_recvtimer_delete(bs);
1270 bfd_xmttimer_delete(bs);
1271 bfd_echo_xmttimer_delete(bs);
1272
1273 /* Change and notify state change. */
1274 bs->ses_state = PTM_BFD_ADM_DOWN;
1275 control_notify(bs, bs->ses_state);
1276
1277 /* Don't try to send packets with a disabled session. */
1278 if (bs->sock != -1)
1279 ptm_bfd_snd(bs, 0);
1280 } else {
1281 /* Already working. */
1282 if (!is_shutdown)
1283 return;
1284
1285 UNSET_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN);
1286
1287 /* Handle data plane shutdown case. */
1288 if (bs->bdc) {
1289 bs->ses_state = PTM_BFD_DOWN;
1290 bfd_dplane_update_session(bs);
1291 control_notify(bs, bs->ses_state);
1292 return;
1293 }
1294
1295 /* Change and notify state change. */
1296 bs->ses_state = PTM_BFD_DOWN;
1297 control_notify(bs, bs->ses_state);
1298
1299 /* Enable timers if non passive, otherwise stop them. */
1300 if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_PASSIVE)) {
1301 bfd_recvtimer_delete(bs);
1302 bfd_xmttimer_delete(bs);
1303 } else {
1304 bfd_recvtimer_update(bs);
1305 bfd_xmttimer_update(bs, bs->xmt_TO);
1306 }
1307 }
1308 }
1309
1310 void bfd_set_passive_mode(struct bfd_session *bs, bool passive)
1311 {
1312 if (passive) {
1313 SET_FLAG(bs->flags, BFD_SESS_FLAG_PASSIVE);
1314
1315 /* Session is already up and running, nothing to do now. */
1316 if (bs->ses_state != PTM_BFD_DOWN)
1317 return;
1318
1319 /* Lets disable the timers since we are now passive. */
1320 bfd_recvtimer_delete(bs);
1321 bfd_xmttimer_delete(bs);
1322 } else {
1323 UNSET_FLAG(bs->flags, BFD_SESS_FLAG_PASSIVE);
1324
1325 /* Session is already up and running, nothing to do now. */
1326 if (bs->ses_state != PTM_BFD_DOWN)
1327 return;
1328
1329 /* Session is down, let it attempt to start the connection. */
1330 bfd_xmttimer_update(bs, bs->xmt_TO);
1331 bfd_recvtimer_update(bs);
1332 }
1333 }
1334
1335 /*
1336 * Helper functions.
1337 */
1338 static const char *get_diag_str(int diag)
1339 {
1340 for (int i = 0; diag_list[i].str; i++) {
1341 if (diag_list[i].type == diag)
1342 return diag_list[i].str;
1343 }
1344 return "N/A";
1345 }
1346
1347 const char *satostr(const struct sockaddr_any *sa)
1348 {
1349 #define INETSTR_BUFCOUNT 8
1350 static char buf[INETSTR_BUFCOUNT][INET6_ADDRSTRLEN];
1351 static int bufidx;
1352 const struct sockaddr_in *sin = &sa->sa_sin;
1353 const struct sockaddr_in6 *sin6 = &sa->sa_sin6;
1354
1355 bufidx += (bufidx + 1) % INETSTR_BUFCOUNT;
1356 buf[bufidx][0] = 0;
1357
1358 switch (sin->sin_family) {
1359 case AF_INET:
1360 inet_ntop(AF_INET, &sin->sin_addr, buf[bufidx],
1361 sizeof(buf[bufidx]));
1362 break;
1363 case AF_INET6:
1364 inet_ntop(AF_INET6, &sin6->sin6_addr, buf[bufidx],
1365 sizeof(buf[bufidx]));
1366 break;
1367
1368 default:
1369 strlcpy(buf[bufidx], "unknown", sizeof(buf[bufidx]));
1370 break;
1371 }
1372
1373 return buf[bufidx];
1374 }
1375
1376 const char *diag2str(uint8_t diag)
1377 {
1378 switch (diag) {
1379 case 0:
1380 return "ok";
1381 case 1:
1382 return "control detection time expired";
1383 case 2:
1384 return "echo function failed";
1385 case 3:
1386 return "neighbor signaled session down";
1387 case 4:
1388 return "forwarding plane reset";
1389 case 5:
1390 return "path down";
1391 case 6:
1392 return "concatenated path down";
1393 case 7:
1394 return "administratively down";
1395 case 8:
1396 return "reverse concatenated path down";
1397 default:
1398 return "unknown";
1399 }
1400 }
1401
1402 int strtosa(const char *addr, struct sockaddr_any *sa)
1403 {
1404 memset(sa, 0, sizeof(*sa));
1405
1406 if (inet_pton(AF_INET, addr, &sa->sa_sin.sin_addr) == 1) {
1407 sa->sa_sin.sin_family = AF_INET;
1408 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
1409 sa->sa_sin.sin_len = sizeof(sa->sa_sin);
1410 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
1411 return 0;
1412 }
1413
1414 if (inet_pton(AF_INET6, addr, &sa->sa_sin6.sin6_addr) == 1) {
1415 sa->sa_sin6.sin6_family = AF_INET6;
1416 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
1417 sa->sa_sin6.sin6_len = sizeof(sa->sa_sin6);
1418 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
1419 return 0;
1420 }
1421
1422 return -1;
1423 }
1424
1425 void integer2timestr(uint64_t time, char *buf, size_t buflen)
1426 {
1427 uint64_t year, month, day, hour, minute, second;
1428 int rv;
1429
1430 #define MINUTES (60)
1431 #define HOURS (60 * MINUTES)
1432 #define DAYS (24 * HOURS)
1433 #define MONTHS (30 * DAYS)
1434 #define YEARS (12 * MONTHS)
1435 if (time >= YEARS) {
1436 year = time / YEARS;
1437 time -= year * YEARS;
1438
1439 rv = snprintfrr(buf, buflen, "%" PRIu64 " year(s), ", year);
1440 buf += rv;
1441 buflen -= rv;
1442 }
1443 if (time >= MONTHS) {
1444 month = time / MONTHS;
1445 time -= month * MONTHS;
1446
1447 rv = snprintfrr(buf, buflen, "%" PRIu64 " month(s), ", month);
1448 buf += rv;
1449 buflen -= rv;
1450 }
1451 if (time >= DAYS) {
1452 day = time / DAYS;
1453 time -= day * DAYS;
1454
1455 rv = snprintfrr(buf, buflen, "%" PRIu64 " day(s), ", day);
1456 buf += rv;
1457 buflen -= rv;
1458 }
1459 if (time >= HOURS) {
1460 hour = time / HOURS;
1461 time -= hour * HOURS;
1462
1463 rv = snprintfrr(buf, buflen, "%" PRIu64 " hour(s), ", hour);
1464 buf += rv;
1465 buflen -= rv;
1466 }
1467 if (time >= MINUTES) {
1468 minute = time / MINUTES;
1469 time -= minute * MINUTES;
1470
1471 rv = snprintfrr(buf, buflen, "%" PRIu64 " minute(s), ", minute);
1472 buf += rv;
1473 buflen -= rv;
1474 }
1475 second = time % MINUTES;
1476 snprintfrr(buf, buflen, "%" PRIu64 " second(s)", second);
1477 }
1478
1479 const char *bs_to_string(const struct bfd_session *bs)
1480 {
1481 static char buf[256];
1482 char addr_buf[INET6_ADDRSTRLEN];
1483 int pos;
1484 bool is_mhop = CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH);
1485
1486 pos = snprintf(buf, sizeof(buf), "mhop:%s", is_mhop ? "yes" : "no");
1487 pos += snprintf(buf + pos, sizeof(buf) - pos, " peer:%s",
1488 inet_ntop(bs->key.family, &bs->key.peer, addr_buf,
1489 sizeof(addr_buf)));
1490 pos += snprintf(buf + pos, sizeof(buf) - pos, " local:%s",
1491 inet_ntop(bs->key.family, &bs->key.local, addr_buf,
1492 sizeof(addr_buf)));
1493 if (bs->key.vrfname[0])
1494 pos += snprintf(buf + pos, sizeof(buf) - pos, " vrf:%s",
1495 bs->key.vrfname);
1496 if (bs->key.ifname[0])
1497 pos += snprintf(buf + pos, sizeof(buf) - pos, " ifname:%s",
1498 bs->key.ifname);
1499
1500 (void)pos;
1501
1502 return buf;
1503 }
1504
1505 int bs_observer_add(struct bfd_session *bs)
1506 {
1507 struct bfd_session_observer *bso;
1508
1509 bso = XCALLOC(MTYPE_BFDD_SESSION_OBSERVER, sizeof(*bso));
1510 bso->bso_bs = bs;
1511 bso->bso_addr.family = bs->key.family;
1512 memcpy(&bso->bso_addr.u.prefix, &bs->key.local,
1513 sizeof(bs->key.local));
1514
1515 TAILQ_INSERT_TAIL(&bglobal.bg_obslist, bso, bso_entry);
1516
1517 return 0;
1518 }
1519
1520 void bs_observer_del(struct bfd_session_observer *bso)
1521 {
1522 TAILQ_REMOVE(&bglobal.bg_obslist, bso, bso_entry);
1523 XFREE(MTYPE_BFDD_SESSION_OBSERVER, bso);
1524 }
1525
1526 void bs_to_bpc(struct bfd_session *bs, struct bfd_peer_cfg *bpc)
1527 {
1528 memset(bpc, 0, sizeof(*bpc));
1529
1530 bpc->bpc_ipv4 = (bs->key.family == AF_INET);
1531 bpc->bpc_mhop = bs->key.mhop;
1532
1533 switch (bs->key.family) {
1534 case AF_INET:
1535 bpc->bpc_peer.sa_sin.sin_family = AF_INET;
1536 memcpy(&bpc->bpc_peer.sa_sin.sin_addr, &bs->key.peer,
1537 sizeof(bpc->bpc_peer.sa_sin.sin_addr));
1538
1539 if (memcmp(&bs->key.local, &zero_addr, sizeof(bs->key.local))) {
1540 bpc->bpc_local.sa_sin.sin_family = AF_INET6;
1541 memcpy(&bpc->bpc_local.sa_sin.sin_addr, &bs->key.local,
1542 sizeof(bpc->bpc_local.sa_sin.sin_addr));
1543 }
1544 break;
1545
1546 case AF_INET6:
1547 bpc->bpc_peer.sa_sin.sin_family = AF_INET6;
1548 memcpy(&bpc->bpc_peer.sa_sin6.sin6_addr, &bs->key.peer,
1549 sizeof(bpc->bpc_peer.sa_sin6.sin6_addr));
1550
1551 bpc->bpc_local.sa_sin6.sin6_family = AF_INET6;
1552 memcpy(&bpc->bpc_local.sa_sin6.sin6_addr, &bs->key.local,
1553 sizeof(bpc->bpc_local.sa_sin6.sin6_addr));
1554 break;
1555 }
1556
1557 if (bs->key.ifname[0]) {
1558 bpc->bpc_has_localif = true;
1559 strlcpy(bpc->bpc_localif, bs->key.ifname,
1560 sizeof(bpc->bpc_localif));
1561 }
1562
1563 if (bs->key.vrfname[0]) {
1564 bpc->bpc_has_vrfname = true;
1565 strlcpy(bpc->bpc_vrfname, bs->key.vrfname,
1566 sizeof(bpc->bpc_vrfname));
1567 }
1568 }
1569
1570
1571 /*
1572 * BFD hash data structures to find sessions.
1573 */
1574 static struct hash *bfd_id_hash;
1575 static struct hash *bfd_key_hash;
1576
1577 static unsigned int bfd_id_hash_do(const void *p);
1578 static unsigned int bfd_key_hash_do(const void *p);
1579
1580 static void _bfd_free(struct hash_bucket *hb,
1581 void *arg __attribute__((__unused__)));
1582
1583 /* BFD hash for our discriminator. */
1584 static unsigned int bfd_id_hash_do(const void *p)
1585 {
1586 const struct bfd_session *bs = p;
1587
1588 return jhash_1word(bs->discrs.my_discr, 0);
1589 }
1590
1591 static bool bfd_id_hash_cmp(const void *n1, const void *n2)
1592 {
1593 const struct bfd_session *bs1 = n1, *bs2 = n2;
1594
1595 return bs1->discrs.my_discr == bs2->discrs.my_discr;
1596 }
1597
1598 /* BFD hash for single hop. */
1599 static unsigned int bfd_key_hash_do(const void *p)
1600 {
1601 const struct bfd_session *bs = p;
1602 struct bfd_key key = bs->key;
1603
1604 /*
1605 * Local address and interface name are optional and
1606 * can be filled any time after session creation.
1607 * Hash key should not depend on these fields.
1608 */
1609 memset(&key.local, 0, sizeof(key.local));
1610 memset(key.ifname, 0, sizeof(key.ifname));
1611
1612 return jhash(&key, sizeof(key), 0);
1613 }
1614
1615 static bool bfd_key_hash_cmp(const void *n1, const void *n2)
1616 {
1617 const struct bfd_session *bs1 = n1, *bs2 = n2;
1618
1619 if (bs1->key.family != bs2->key.family)
1620 return false;
1621 if (bs1->key.mhop != bs2->key.mhop)
1622 return false;
1623 if (memcmp(&bs1->key.peer, &bs2->key.peer, sizeof(bs1->key.peer)))
1624 return false;
1625 if (memcmp(bs1->key.vrfname, bs2->key.vrfname,
1626 sizeof(bs1->key.vrfname)))
1627 return false;
1628
1629 /*
1630 * Local address is optional and can be empty.
1631 * If both addresses are not empty and different,
1632 * then the keys are different.
1633 */
1634 if (memcmp(&bs1->key.local, &zero_addr, sizeof(bs1->key.local))
1635 && memcmp(&bs2->key.local, &zero_addr, sizeof(bs2->key.local))
1636 && memcmp(&bs1->key.local, &bs2->key.local, sizeof(bs1->key.local)))
1637 return false;
1638
1639 /*
1640 * Interface name is optional and can be empty.
1641 * If both names are not empty and different,
1642 * then the keys are different.
1643 */
1644 if (bs1->key.ifname[0] && bs2->key.ifname[0]
1645 && memcmp(bs1->key.ifname, bs2->key.ifname,
1646 sizeof(bs1->key.ifname)))
1647 return false;
1648
1649 return true;
1650 }
1651
1652
1653 /*
1654 * Hash public interface / exported functions.
1655 */
1656
1657 /* Lookup functions. */
1658 struct bfd_session *bfd_id_lookup(uint32_t id)
1659 {
1660 struct bfd_session bs;
1661
1662 bs.discrs.my_discr = id;
1663
1664 return hash_lookup(bfd_id_hash, &bs);
1665 }
1666
1667 struct bfd_session *bfd_key_lookup(struct bfd_key key)
1668 {
1669 struct bfd_session bs;
1670
1671 bs.key = key;
1672
1673 return hash_lookup(bfd_key_hash, &bs);
1674 }
1675
1676 /*
1677 * Delete functions.
1678 *
1679 * Delete functions searches and remove the item from the hash and
1680 * returns a pointer to the removed item data. If the item was not found
1681 * then it returns NULL.
1682 *
1683 * The data stored inside the hash is not free()ed, so you must do it
1684 * manually after getting the pointer back.
1685 */
1686 struct bfd_session *bfd_id_delete(uint32_t id)
1687 {
1688 struct bfd_session bs;
1689
1690 bs.discrs.my_discr = id;
1691
1692 return hash_release(bfd_id_hash, &bs);
1693 }
1694
1695 struct bfd_session *bfd_key_delete(struct bfd_key key)
1696 {
1697 struct bfd_session bs;
1698
1699 bs.key = key;
1700
1701 return hash_release(bfd_key_hash, &bs);
1702 }
1703
1704 /* Iteration functions. */
1705 void bfd_id_iterate(hash_iter_func hif, void *arg)
1706 {
1707 hash_iterate(bfd_id_hash, hif, arg);
1708 }
1709
1710 void bfd_key_iterate(hash_iter_func hif, void *arg)
1711 {
1712 hash_iterate(bfd_key_hash, hif, arg);
1713 }
1714
1715 /*
1716 * Insert functions.
1717 *
1718 * Inserts session into hash and returns `true` on success, otherwise
1719 * `false`.
1720 */
1721 bool bfd_id_insert(struct bfd_session *bs)
1722 {
1723 return (hash_get(bfd_id_hash, bs, hash_alloc_intern) == bs);
1724 }
1725
1726 bool bfd_key_insert(struct bfd_session *bs)
1727 {
1728 return (hash_get(bfd_key_hash, bs, hash_alloc_intern) == bs);
1729 }
1730
1731 void bfd_initialize(void)
1732 {
1733 bfd_id_hash = hash_create(bfd_id_hash_do, bfd_id_hash_cmp,
1734 "BFD session discriminator hash");
1735 bfd_key_hash = hash_create(bfd_key_hash_do, bfd_key_hash_cmp,
1736 "BFD session hash");
1737 TAILQ_INIT(&bplist);
1738 }
1739
1740 static void _bfd_free(struct hash_bucket *hb,
1741 void *arg __attribute__((__unused__)))
1742 {
1743 struct bfd_session *bs = hb->data;
1744
1745 bfd_session_free(bs);
1746 }
1747
1748 void bfd_shutdown(void)
1749 {
1750 struct bfd_profile *bp;
1751
1752 /*
1753 * Close and free all BFD sessions.
1754 *
1755 * _bfd_free() will call bfd_session_free() which will take care
1756 * of removing the session from all hashes, so we just run an
1757 * assert() here to make sure it really happened.
1758 */
1759 bfd_id_iterate(_bfd_free, NULL);
1760 assert(bfd_key_hash->count == 0);
1761
1762 /* Now free the hashes themselves. */
1763 hash_free(bfd_id_hash);
1764 hash_free(bfd_key_hash);
1765
1766 /* Free all profile allocations. */
1767 while ((bp = TAILQ_FIRST(&bplist)) != NULL)
1768 bfd_profile_free(bp);
1769 }
1770
1771 struct bfd_session_iterator {
1772 int bsi_stop;
1773 bool bsi_mhop;
1774 const struct bfd_session *bsi_bs;
1775 };
1776
1777 static int _bfd_session_next(struct hash_bucket *hb, void *arg)
1778 {
1779 struct bfd_session_iterator *bsi = arg;
1780 struct bfd_session *bs = hb->data;
1781
1782 /* Previous entry signaled stop. */
1783 if (bsi->bsi_stop == 1) {
1784 /* Match the single/multi hop sessions. */
1785 if (bs->key.mhop != bsi->bsi_mhop)
1786 return HASHWALK_CONTINUE;
1787
1788 bsi->bsi_bs = bs;
1789 return HASHWALK_ABORT;
1790 }
1791
1792 /* We found the current item, stop in the next one. */
1793 if (bsi->bsi_bs == hb->data) {
1794 bsi->bsi_stop = 1;
1795 /* Set entry to NULL to signal end of list. */
1796 bsi->bsi_bs = NULL;
1797 } else if (bsi->bsi_bs == NULL && bsi->bsi_mhop == bs->key.mhop) {
1798 /* We want the first list item. */
1799 bsi->bsi_stop = 1;
1800 bsi->bsi_bs = hb->data;
1801 return HASHWALK_ABORT;
1802 }
1803
1804 return HASHWALK_CONTINUE;
1805 }
1806
1807 /*
1808 * bfd_session_next: uses the current session to find the next.
1809 *
1810 * `bs` might point to NULL to get the first item of the data structure.
1811 */
1812 const struct bfd_session *bfd_session_next(const struct bfd_session *bs,
1813 bool mhop)
1814 {
1815 struct bfd_session_iterator bsi;
1816
1817 bsi.bsi_stop = 0;
1818 bsi.bsi_bs = bs;
1819 bsi.bsi_mhop = mhop;
1820 hash_walk(bfd_key_hash, _bfd_session_next, &bsi);
1821 if (bsi.bsi_stop == 0)
1822 return NULL;
1823
1824 return bsi.bsi_bs;
1825 }
1826
1827 static void _bfd_session_remove_manual(struct hash_bucket *hb,
1828 void *arg __attribute__((__unused__)))
1829 {
1830 struct bfd_session *bs = hb->data;
1831
1832 /* Delete only manually configured sessions. */
1833 if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG) == 0)
1834 return;
1835
1836 bs->refcount--;
1837 UNSET_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG);
1838
1839 /* Don't delete sessions still in use. */
1840 if (bs->refcount != 0)
1841 return;
1842
1843 bfd_session_free(bs);
1844 }
1845
1846 /*
1847 * bfd_sessions_remove_manual: remove all manually configured sessions.
1848 *
1849 * NOTE: this function doesn't remove automatically created sessions.
1850 */
1851 void bfd_sessions_remove_manual(void)
1852 {
1853 hash_iterate(bfd_key_hash, _bfd_session_remove_manual, NULL);
1854 }
1855
1856 void bfd_profiles_remove(void)
1857 {
1858 struct bfd_profile *bp;
1859
1860 while ((bp = TAILQ_FIRST(&bplist)) != NULL)
1861 bfd_profile_free(bp);
1862 }
1863
1864 /*
1865 * Profile related hash functions.
1866 */
1867 static void _bfd_profile_update(struct hash_bucket *hb, void *arg)
1868 {
1869 struct bfd_profile *bp = arg;
1870 struct bfd_session *bs = hb->data;
1871
1872 /* This session is not using the profile. */
1873 if (bs->profile_name == NULL || strcmp(bs->profile_name, bp->name) != 0)
1874 return;
1875
1876 bfd_profile_apply(bp->name, bs);
1877 }
1878
1879 void bfd_profile_update(struct bfd_profile *bp)
1880 {
1881 hash_iterate(bfd_key_hash, _bfd_profile_update, bp);
1882 }
1883
1884 static void _bfd_profile_detach(struct hash_bucket *hb, void *arg)
1885 {
1886 struct bfd_profile *bp = arg;
1887 struct bfd_session *bs = hb->data;
1888
1889 /* This session is not using the profile. */
1890 if (bs->profile_name == NULL || strcmp(bs->profile_name, bp->name) != 0)
1891 return;
1892
1893 bfd_profile_remove(bs);
1894 }
1895
1896 static void bfd_profile_detach(struct bfd_profile *bp)
1897 {
1898 hash_iterate(bfd_key_hash, _bfd_profile_detach, bp);
1899 }
1900
1901 /*
1902 * VRF related functions.
1903 */
1904 static int bfd_vrf_new(struct vrf *vrf)
1905 {
1906 if (bglobal.debug_zebra)
1907 zlog_debug("VRF Created: %s(%u)", vrf->name, vrf->vrf_id);
1908
1909 return 0;
1910 }
1911
1912 static int bfd_vrf_delete(struct vrf *vrf)
1913 {
1914 if (bglobal.debug_zebra)
1915 zlog_debug("VRF Deletion: %s(%u)", vrf->name, vrf->vrf_id);
1916
1917 return 0;
1918 }
1919
1920 static int bfd_vrf_enable(struct vrf *vrf)
1921 {
1922 struct bfd_vrf_global *bvrf;
1923
1924 /* a different name */
1925 if (!vrf->info) {
1926 bvrf = XCALLOC(MTYPE_BFDD_VRF, sizeof(struct bfd_vrf_global));
1927 bvrf->vrf = vrf;
1928 vrf->info = (void *)bvrf;
1929
1930 /* Disable sockets if using data plane. */
1931 if (bglobal.bg_use_dplane) {
1932 bvrf->bg_shop = -1;
1933 bvrf->bg_mhop = -1;
1934 bvrf->bg_shop6 = -1;
1935 bvrf->bg_mhop6 = -1;
1936 bvrf->bg_echo = -1;
1937 bvrf->bg_echov6 = -1;
1938 }
1939 } else
1940 bvrf = vrf->info;
1941
1942 if (bglobal.debug_zebra)
1943 zlog_debug("VRF enable add %s id %u", vrf->name, vrf->vrf_id);
1944
1945 if (vrf->vrf_id == VRF_DEFAULT ||
1946 vrf_get_backend() == VRF_BACKEND_NETNS) {
1947 if (!bvrf->bg_shop)
1948 bvrf->bg_shop = bp_udp_shop(vrf);
1949 if (!bvrf->bg_mhop)
1950 bvrf->bg_mhop = bp_udp_mhop(vrf);
1951 if (!bvrf->bg_shop6)
1952 bvrf->bg_shop6 = bp_udp6_shop(vrf);
1953 if (!bvrf->bg_mhop6)
1954 bvrf->bg_mhop6 = bp_udp6_mhop(vrf);
1955 if (!bvrf->bg_echo)
1956 bvrf->bg_echo = bp_echo_socket(vrf);
1957 if (!bvrf->bg_echov6)
1958 bvrf->bg_echov6 = bp_echov6_socket(vrf);
1959
1960 if (!bvrf->bg_ev[0] && bvrf->bg_shop != -1)
1961 thread_add_read(master, bfd_recv_cb, bvrf,
1962 bvrf->bg_shop, &bvrf->bg_ev[0]);
1963 if (!bvrf->bg_ev[1] && bvrf->bg_mhop != -1)
1964 thread_add_read(master, bfd_recv_cb, bvrf,
1965 bvrf->bg_mhop, &bvrf->bg_ev[1]);
1966 if (!bvrf->bg_ev[2] && bvrf->bg_shop6 != -1)
1967 thread_add_read(master, bfd_recv_cb, bvrf,
1968 bvrf->bg_shop6, &bvrf->bg_ev[2]);
1969 if (!bvrf->bg_ev[3] && bvrf->bg_mhop6 != -1)
1970 thread_add_read(master, bfd_recv_cb, bvrf,
1971 bvrf->bg_mhop6, &bvrf->bg_ev[3]);
1972 if (!bvrf->bg_ev[4] && bvrf->bg_echo != -1)
1973 thread_add_read(master, bfd_recv_cb, bvrf,
1974 bvrf->bg_echo, &bvrf->bg_ev[4]);
1975 if (!bvrf->bg_ev[5] && bvrf->bg_echov6 != -1)
1976 thread_add_read(master, bfd_recv_cb, bvrf,
1977 bvrf->bg_echov6, &bvrf->bg_ev[5]);
1978 }
1979 if (vrf->vrf_id != VRF_DEFAULT) {
1980 bfdd_zclient_register(vrf->vrf_id);
1981 bfdd_sessions_enable_vrf(vrf);
1982 }
1983 return 0;
1984 }
1985
1986 static int bfd_vrf_disable(struct vrf *vrf)
1987 {
1988 struct bfd_vrf_global *bvrf;
1989
1990 if (!vrf->info)
1991 return 0;
1992 bvrf = vrf->info;
1993
1994 if (vrf->vrf_id != VRF_DEFAULT) {
1995 bfdd_sessions_disable_vrf(vrf);
1996 bfdd_zclient_unregister(vrf->vrf_id);
1997 }
1998
1999 if (bglobal.debug_zebra)
2000 zlog_debug("VRF disable %s id %d", vrf->name, vrf->vrf_id);
2001
2002 /* Disable read/write poll triggering. */
2003 THREAD_OFF(bvrf->bg_ev[0]);
2004 THREAD_OFF(bvrf->bg_ev[1]);
2005 THREAD_OFF(bvrf->bg_ev[2]);
2006 THREAD_OFF(bvrf->bg_ev[3]);
2007 THREAD_OFF(bvrf->bg_ev[4]);
2008 THREAD_OFF(bvrf->bg_ev[5]);
2009
2010 /* Close all descriptors. */
2011 socket_close(&bvrf->bg_echo);
2012 socket_close(&bvrf->bg_shop);
2013 socket_close(&bvrf->bg_mhop);
2014 if (bvrf->bg_shop6 != -1)
2015 socket_close(&bvrf->bg_shop6);
2016 if (bvrf->bg_mhop6 != -1)
2017 socket_close(&bvrf->bg_mhop6);
2018 socket_close(&bvrf->bg_echo);
2019 if (bvrf->bg_echov6 != -1)
2020 socket_close(&bvrf->bg_echov6);
2021
2022 /* free context */
2023 XFREE(MTYPE_BFDD_VRF, bvrf);
2024 vrf->info = NULL;
2025
2026 return 0;
2027 }
2028
2029 void bfd_vrf_init(void)
2030 {
2031 vrf_init(bfd_vrf_new, bfd_vrf_enable, bfd_vrf_disable, bfd_vrf_delete);
2032 }
2033
2034 void bfd_vrf_terminate(void)
2035 {
2036 vrf_terminate();
2037 }
2038
2039 struct bfd_vrf_global *bfd_vrf_look_by_session(struct bfd_session *bfd)
2040 {
2041 struct vrf *vrf;
2042
2043 if (!vrf_is_backend_netns()) {
2044 vrf = vrf_lookup_by_id(VRF_DEFAULT);
2045 if (vrf)
2046 return (struct bfd_vrf_global *)vrf->info;
2047 return NULL;
2048 }
2049 if (!bfd)
2050 return NULL;
2051 if (!bfd->vrf)
2052 return NULL;
2053 return bfd->vrf->info;
2054 }
2055
2056 unsigned long bfd_get_session_count(void)
2057 {
2058 return bfd_key_hash->count;
2059 }