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