]> git.proxmox.com Git - mirror_frr.git/blob - bfdd/bfd.c
Merge pull request #3888 from donaldsharp/ospf_cleanup
[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
32 #include "bfd.h"
33
34 DEFINE_MTYPE_STATIC(BFDD, BFDD_CONFIG, "long-lived configuration memory")
35 DEFINE_MTYPE_STATIC(BFDD, BFDD_SESSION_OBSERVER, "Session observer")
36 DEFINE_MTYPE_STATIC(BFDD, BFDD_VRF, "BFD VRF")
37 DEFINE_QOBJ_TYPE(bfd_session)
38
39 /*
40 * Prototypes
41 */
42 void gen_bfd_key(struct bfd_key *key, struct sockaddr_any *peer,
43 struct sockaddr_any *local, bool mhop, const char *ifname,
44 const char *vrfname);
45
46 static uint32_t ptm_bfd_gen_ID(void);
47 static void ptm_bfd_echo_xmt_TO(struct bfd_session *bfd);
48 static void bfd_session_free(struct bfd_session *bs);
49 static struct bfd_session *bfd_session_new(void);
50 static struct bfd_session *bfd_find_disc(struct sockaddr_any *sa,
51 uint32_t ldisc);
52 static int bfd_session_update(struct bfd_session *bs, struct bfd_peer_cfg *bpc);
53 static const char *get_diag_str(int diag);
54
55 static void bs_admin_down_handler(struct bfd_session *bs, int nstate);
56 static void bs_down_handler(struct bfd_session *bs, int nstate);
57 static void bs_init_handler(struct bfd_session *bs, int nstate);
58 static void bs_up_handler(struct bfd_session *bs, int nstate);
59
60 /* Zeroed array with the size of an IPv6 address. */
61 struct in6_addr zero_addr;
62
63 /*
64 * Functions
65 */
66 void gen_bfd_key(struct bfd_key *key, struct sockaddr_any *peer,
67 struct sockaddr_any *local, bool mhop, const char *ifname,
68 const char *vrfname)
69 {
70 memset(key, 0, sizeof(*key));
71
72 switch (peer->sa_sin.sin_family) {
73 case AF_INET:
74 key->family = AF_INET;
75 memcpy(&key->peer, &peer->sa_sin.sin_addr,
76 sizeof(peer->sa_sin.sin_addr));
77 memcpy(&key->local, &local->sa_sin.sin_addr,
78 sizeof(local->sa_sin.sin_addr));
79 break;
80 case AF_INET6:
81 key->family = AF_INET6;
82 memcpy(&key->peer, &peer->sa_sin6.sin6_addr,
83 sizeof(peer->sa_sin6.sin6_addr));
84 memcpy(&key->local, &local->sa_sin6.sin6_addr,
85 sizeof(local->sa_sin6.sin6_addr));
86 break;
87 }
88
89 key->mhop = mhop;
90 if (ifname && ifname[0])
91 strlcpy(key->ifname, ifname, sizeof(key->ifname));
92 if (vrfname && vrfname[0])
93 strlcpy(key->vrfname, vrfname, sizeof(key->vrfname));
94 }
95
96 struct bfd_session *bs_peer_find(struct bfd_peer_cfg *bpc)
97 {
98 struct bfd_session *bs;
99 struct peer_label *pl;
100 struct bfd_key key;
101
102 /* Try to find label first. */
103 if (bpc->bpc_has_label) {
104 pl = pl_find(bpc->bpc_label);
105 if (pl != NULL) {
106 bs = pl->pl_bs;
107 return bs;
108 }
109 }
110
111 /* Otherwise fallback to peer/local hash lookup. */
112 gen_bfd_key(&key, &bpc->bpc_peer, &bpc->bpc_local, bpc->bpc_mhop,
113 bpc->bpc_localif, bpc->bpc_vrfname);
114
115 return bfd_key_lookup(key);
116 }
117
118 /*
119 * Starts a disabled BFD session.
120 *
121 * A session is disabled when the specified interface/VRF doesn't exist
122 * yet. It might happen on FRR boot or with virtual interfaces.
123 */
124 int bfd_session_enable(struct bfd_session *bs)
125 {
126 struct interface *ifp = NULL;
127 struct vrf *vrf = NULL;
128 int psock;
129
130 /*
131 * If the interface or VRF doesn't exist, then we must register
132 * the session but delay its start.
133 */
134 if (bs->key.vrfname[0]) {
135 vrf = vrf_lookup_by_name(bs->key.vrfname);
136 if (vrf == NULL) {
137 log_error(
138 "session-enable: specified VRF doesn't exists.");
139 return 0;
140 }
141 }
142
143 if (bs->key.ifname[0]) {
144 if (vrf)
145 ifp = if_lookup_by_name(bs->key.ifname, vrf->vrf_id);
146 else
147 ifp = if_lookup_by_name_all_vrf(bs->key.ifname);
148 if (ifp == NULL) {
149 log_error(
150 "session-enable: specified interface doesn't exists.");
151 return 0;
152 }
153 if (bs->key.ifname[0] && !vrf) {
154 vrf = vrf_lookup_by_id(ifp->vrf_id);
155 if (vrf == NULL) {
156 log_error(
157 "session-enable: specified VRF doesn't exists.");
158 return 0;
159 }
160 }
161 }
162
163 /* Assign interface/VRF pointers. */
164 bs->vrf = vrf;
165 if (bs->vrf == NULL)
166 bs->vrf = vrf_lookup_by_id(VRF_DEFAULT);
167
168 if (bs->key.ifname[0]
169 && BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH) == 0)
170 bs->ifp = ifp;
171
172 /* Sanity check: don't leak open sockets. */
173 if (bs->sock != -1) {
174 log_debug("session-enable: previous socket open");
175 close(bs->sock);
176 bs->sock = -1;
177 }
178
179 /*
180 * Get socket for transmitting control packets. Note that if we
181 * could use the destination port (3784) for the source
182 * port we wouldn't need a socket per session.
183 */
184 if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_IPV6) == 0) {
185 psock = bp_peer_socket(bs);
186 if (psock == -1)
187 return 0;
188 } else {
189 psock = bp_peer_socketv6(bs);
190 if (psock == -1)
191 return 0;
192 }
193
194 /*
195 * We've got a valid socket, lets start the timers and the
196 * protocol.
197 */
198 bs->sock = psock;
199 bfd_recvtimer_update(bs);
200 ptm_bfd_start_xmt_timer(bs, false);
201
202 return 0;
203 }
204
205 /*
206 * Disabled a running BFD session.
207 *
208 * A session is disabled when the specified interface/VRF gets removed
209 * (e.g. virtual interfaces).
210 */
211 void bfd_session_disable(struct bfd_session *bs)
212 {
213 /* Free up socket resources. */
214 if (bs->sock != -1) {
215 close(bs->sock);
216 bs->sock = -1;
217 }
218
219 /* Disable all timers. */
220 bfd_recvtimer_delete(bs);
221 bfd_echo_recvtimer_delete(bs);
222 bfd_xmttimer_delete(bs);
223 bfd_echo_xmttimer_delete(bs);
224 }
225
226 static uint32_t ptm_bfd_gen_ID(void)
227 {
228 uint32_t session_id;
229
230 /*
231 * RFC 5880, Section 6.8.1. recommends that we should generate
232 * random session identification numbers.
233 */
234 do {
235 session_id = ((random() << 16) & 0xFFFF0000)
236 | (random() & 0x0000FFFF);
237 } while (session_id == 0 || bfd_id_lookup(session_id) != NULL);
238
239 return session_id;
240 }
241
242 void ptm_bfd_start_xmt_timer(struct bfd_session *bfd, bool is_echo)
243 {
244 uint64_t jitter, xmt_TO;
245 int maxpercent;
246
247 xmt_TO = is_echo ? bfd->echo_xmt_TO : bfd->xmt_TO;
248
249 /*
250 * From section 6.5.2: trasmit interval should be randomly jittered
251 * between
252 * 75% and 100% of nominal value, unless detect_mult is 1, then should
253 * be
254 * between 75% and 90%.
255 */
256 maxpercent = (bfd->detect_mult == 1) ? 16 : 26;
257 jitter = (xmt_TO * (75 + (random() % maxpercent))) / 100;
258 /* XXX remove that division above */
259
260 if (is_echo)
261 bfd_echo_xmttimer_update(bfd, jitter);
262 else
263 bfd_xmttimer_update(bfd, jitter);
264 }
265
266 static void ptm_bfd_echo_xmt_TO(struct bfd_session *bfd)
267 {
268 /* Send the scheduled echo packet */
269 ptm_bfd_echo_snd(bfd);
270
271 /* Restart the timer for next time */
272 ptm_bfd_start_xmt_timer(bfd, true);
273 }
274
275 void ptm_bfd_xmt_TO(struct bfd_session *bfd, int fbit)
276 {
277 /* Send the scheduled control packet */
278 ptm_bfd_snd(bfd, fbit);
279
280 /* Restart the timer for next time */
281 ptm_bfd_start_xmt_timer(bfd, false);
282 }
283
284 void ptm_bfd_echo_stop(struct bfd_session *bfd)
285 {
286 bfd->echo_xmt_TO = 0;
287 bfd->echo_detect_TO = 0;
288 BFD_UNSET_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE);
289
290 bfd_echo_xmttimer_delete(bfd);
291 bfd_echo_recvtimer_delete(bfd);
292 }
293
294 void ptm_bfd_echo_start(struct bfd_session *bfd)
295 {
296 bfd->echo_detect_TO = (bfd->remote_detect_mult * bfd->echo_xmt_TO);
297 if (bfd->echo_detect_TO > 0)
298 ptm_bfd_echo_xmt_TO(bfd);
299 }
300
301 void ptm_bfd_sess_up(struct bfd_session *bfd)
302 {
303 int old_state = bfd->ses_state;
304
305 bfd->local_diag = 0;
306 bfd->ses_state = PTM_BFD_UP;
307 monotime(&bfd->uptime);
308
309 /* Connection is up, lets negotiate timers. */
310 bfd_set_polling(bfd);
311
312 /* Start sending control packets with poll bit immediately. */
313 ptm_bfd_snd(bfd, 0);
314
315 control_notify(bfd);
316
317 if (old_state != bfd->ses_state) {
318 bfd->stats.session_up++;
319 log_info("state-change: [%s] %s -> %s", bs_to_string(bfd),
320 state_list[old_state].str,
321 state_list[bfd->ses_state].str);
322 }
323 }
324
325 void ptm_bfd_sess_dn(struct bfd_session *bfd, uint8_t diag)
326 {
327 int old_state = bfd->ses_state;
328
329 bfd->local_diag = diag;
330 bfd->discrs.remote_discr = 0;
331 bfd->ses_state = PTM_BFD_DOWN;
332 bfd->polling = 0;
333 bfd->demand_mode = 0;
334 monotime(&bfd->downtime);
335
336 ptm_bfd_snd(bfd, 0);
337
338 /* Slow down the control packets, the connection is down. */
339 bs_set_slow_timers(bfd);
340
341 /* only signal clients when going from up->down state */
342 if (old_state == PTM_BFD_UP)
343 control_notify(bfd);
344
345 /* Stop echo packet transmission if they are active */
346 if (BFD_CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE))
347 ptm_bfd_echo_stop(bfd);
348
349 if (old_state != bfd->ses_state) {
350 bfd->stats.session_down++;
351 log_info("state-change: [%s] %s -> %s reason:%s",
352 bs_to_string(bfd), state_list[old_state].str,
353 state_list[bfd->ses_state].str,
354 get_diag_str(bfd->local_diag));
355 }
356 }
357
358 static struct bfd_session *bfd_find_disc(struct sockaddr_any *sa,
359 uint32_t ldisc)
360 {
361 struct bfd_session *bs;
362
363 bs = bfd_id_lookup(ldisc);
364 if (bs == NULL)
365 return NULL;
366
367 switch (bs->key.family) {
368 case AF_INET:
369 if (memcmp(&sa->sa_sin.sin_addr, &bs->key.peer,
370 sizeof(sa->sa_sin.sin_addr)))
371 return NULL;
372 break;
373 case AF_INET6:
374 if (memcmp(&sa->sa_sin6.sin6_addr, &bs->key.peer,
375 sizeof(sa->sa_sin6.sin6_addr)))
376 return NULL;
377 break;
378 }
379
380 return bs;
381 }
382
383 struct bfd_session *ptm_bfd_sess_find(struct bfd_pkt *cp,
384 struct sockaddr_any *peer,
385 struct sockaddr_any *local,
386 ifindex_t ifindex, vrf_id_t vrfid,
387 bool is_mhop)
388 {
389 struct interface *ifp;
390 struct vrf *vrf;
391 struct bfd_key key;
392
393 /* Find our session using the ID signaled by the remote end. */
394 if (cp->discrs.remote_discr)
395 return bfd_find_disc(peer, ntohl(cp->discrs.remote_discr));
396
397 /* Search for session without using discriminator. */
398 ifp = if_lookup_by_index(ifindex, vrfid);
399 if (vrfid == VRF_DEFAULT) {
400 /*
401 * Don't use the default vrf, otherwise we won't find
402 * sessions that doesn't specify it.
403 */
404 vrf = NULL;
405 } else
406 vrf = vrf_lookup_by_id(vrfid);
407
408 gen_bfd_key(&key, peer, local, is_mhop, ifp ? ifp->name : NULL,
409 vrf ? vrf->name : NULL);
410
411 /* XXX maybe remoteDiscr should be checked for remoteHeard cases. */
412 return bfd_key_lookup(key);
413 }
414
415 int bfd_xmt_cb(struct thread *t)
416 {
417 struct bfd_session *bs = THREAD_ARG(t);
418
419 ptm_bfd_xmt_TO(bs, 0);
420
421 return 0;
422 }
423
424 int bfd_echo_xmt_cb(struct thread *t)
425 {
426 struct bfd_session *bs = THREAD_ARG(t);
427
428 if (bs->echo_xmt_TO > 0)
429 ptm_bfd_echo_xmt_TO(bs);
430
431 return 0;
432 }
433
434 /* Was ptm_bfd_detect_TO() */
435 int bfd_recvtimer_cb(struct thread *t)
436 {
437 struct bfd_session *bs = THREAD_ARG(t);
438
439 switch (bs->ses_state) {
440 case PTM_BFD_INIT:
441 case PTM_BFD_UP:
442 ptm_bfd_sess_dn(bs, BD_CONTROL_EXPIRED);
443 bfd_recvtimer_update(bs);
444 break;
445
446 default:
447 /* Second detect time expiration, zero remote discr (section
448 * 6.5.1)
449 */
450 bs->discrs.remote_discr = 0;
451 break;
452 }
453
454 return 0;
455 }
456
457 /* Was ptm_bfd_echo_detect_TO() */
458 int bfd_echo_recvtimer_cb(struct thread *t)
459 {
460 struct bfd_session *bs = THREAD_ARG(t);
461
462 switch (bs->ses_state) {
463 case PTM_BFD_INIT:
464 case PTM_BFD_UP:
465 ptm_bfd_sess_dn(bs, BD_ECHO_FAILED);
466 break;
467 }
468
469 return 0;
470 }
471
472 static struct bfd_session *bfd_session_new(void)
473 {
474 struct bfd_session *bs;
475
476 bs = XCALLOC(MTYPE_BFDD_CONFIG, sizeof(*bs));
477
478 QOBJ_REG(bs, bfd_session);
479
480 bs->timers.desired_min_tx = BFD_DEFDESIREDMINTX;
481 bs->timers.required_min_rx = BFD_DEFREQUIREDMINRX;
482 bs->timers.required_min_echo = BFD_DEF_REQ_MIN_ECHO;
483 bs->detect_mult = BFD_DEFDETECTMULT;
484 bs->mh_ttl = BFD_DEF_MHOP_TTL;
485 bs->ses_state = PTM_BFD_DOWN;
486
487 /* Initiate connection with slow timers. */
488 bs_set_slow_timers(bs);
489
490 /* Initiate remote settings as well. */
491 bs->remote_timers = bs->cur_timers;
492 bs->remote_detect_mult = BFD_DEFDETECTMULT;
493
494 bs->sock = -1;
495 monotime(&bs->uptime);
496 bs->downtime = bs->uptime;
497
498 return bs;
499 }
500
501 int bfd_session_update_label(struct bfd_session *bs, const char *nlabel)
502 {
503 /* New label treatment:
504 * - Check if the label is taken;
505 * - Try to allocate the memory for it and register;
506 */
507 if (bs->pl == NULL) {
508 if (pl_find(nlabel) != NULL) {
509 /* Someone is already using it. */
510 return -1;
511 }
512
513 if (pl_new(nlabel, bs) == NULL)
514 return -1;
515
516 return 0;
517 }
518
519 /*
520 * Test label change consistency:
521 * - Do nothing if it's the same label;
522 * - Check if the future label is already taken;
523 * - Change label;
524 */
525 if (strcmp(nlabel, bs->pl->pl_label) == 0)
526 return -1;
527 if (pl_find(nlabel) != NULL)
528 return -1;
529
530 strlcpy(bs->pl->pl_label, nlabel, sizeof(bs->pl->pl_label));
531 return 0;
532 }
533
534 static void _bfd_session_update(struct bfd_session *bs,
535 struct bfd_peer_cfg *bpc)
536 {
537 if (bpc->bpc_echo) {
538 /* Check if echo mode is already active. */
539 if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO))
540 goto skip_echo;
541
542 BFD_SET_FLAG(bs->flags, BFD_SESS_FLAG_ECHO);
543
544 /* Activate/update echo receive timeout timer. */
545 bs_echo_timer_handler(bs);
546 } else {
547 /* Check if echo mode is already disabled. */
548 if (!BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO))
549 goto skip_echo;
550
551 BFD_UNSET_FLAG(bs->flags, BFD_SESS_FLAG_ECHO);
552 ptm_bfd_echo_stop(bs);
553 }
554
555 skip_echo:
556 if (bpc->bpc_has_txinterval)
557 bs->timers.desired_min_tx = bpc->bpc_txinterval * 1000;
558
559 if (bpc->bpc_has_recvinterval)
560 bs->timers.required_min_rx = bpc->bpc_recvinterval * 1000;
561
562 if (bpc->bpc_has_detectmultiplier)
563 bs->detect_mult = bpc->bpc_detectmultiplier;
564
565 if (bpc->bpc_has_echointerval)
566 bs->timers.required_min_echo = bpc->bpc_echointerval * 1000;
567
568 if (bpc->bpc_has_label)
569 bfd_session_update_label(bs, bpc->bpc_label);
570
571 if (bpc->bpc_shutdown) {
572 /* Check if already shutdown. */
573 if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN))
574 return;
575
576 BFD_SET_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN);
577
578 /* Disable all events. */
579 bfd_recvtimer_delete(bs);
580 bfd_echo_recvtimer_delete(bs);
581 bfd_xmttimer_delete(bs);
582 bfd_echo_xmttimer_delete(bs);
583
584 /* Change and notify state change. */
585 bs->ses_state = PTM_BFD_ADM_DOWN;
586 control_notify(bs);
587
588 /* Don't try to send packets with a disabled session. */
589 if (bs->sock != -1)
590 ptm_bfd_snd(bs, 0);
591 } else {
592 /* Check if already working. */
593 if (!BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN))
594 return;
595
596 BFD_UNSET_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN);
597
598 /* Change and notify state change. */
599 bs->ses_state = PTM_BFD_DOWN;
600 control_notify(bs);
601
602 /* Enable all timers. */
603 bfd_recvtimer_update(bs);
604 bfd_xmttimer_update(bs, bs->xmt_TO);
605 }
606 if (bpc->bpc_cbit) {
607 if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_CBIT))
608 return;
609
610 BFD_SET_FLAG(bs->flags, BFD_SESS_FLAG_CBIT);
611 } else {
612 if (!BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_CBIT))
613 return;
614
615 BFD_UNSET_FLAG(bs->flags, BFD_SESS_FLAG_CBIT);
616 }
617 }
618
619 static int bfd_session_update(struct bfd_session *bs, struct bfd_peer_cfg *bpc)
620 {
621 /* User didn't want to update, return failure. */
622 if (bpc->bpc_createonly)
623 return -1;
624
625 _bfd_session_update(bs, bpc);
626
627 control_notify_config(BCM_NOTIFY_CONFIG_UPDATE, bs);
628
629 return 0;
630 }
631
632 static void bfd_session_free(struct bfd_session *bs)
633 {
634 struct bfd_session_observer *bso;
635
636 bfd_session_disable(bs);
637
638 bfd_key_delete(bs->key);
639 bfd_id_delete(bs->discrs.my_discr);
640
641 /* Remove observer if any. */
642 TAILQ_FOREACH(bso, &bglobal.bg_obslist, bso_entry) {
643 if (bso->bso_bs != bs)
644 continue;
645
646 break;
647 }
648 if (bso != NULL)
649 bs_observer_del(bso);
650
651 pl_free(bs->pl);
652
653 QOBJ_UNREG(bs);
654 XFREE(MTYPE_BFDD_CONFIG, bs);
655 }
656
657 struct bfd_session *ptm_bfd_sess_new(struct bfd_peer_cfg *bpc)
658 {
659 struct bfd_session *bfd, *l_bfd;
660
661 /* check to see if this needs a new session */
662 l_bfd = bs_peer_find(bpc);
663 if (l_bfd) {
664 /* Requesting a duplicated peer means update configuration. */
665 if (bfd_session_update(l_bfd, bpc) == 0)
666 return l_bfd;
667 else
668 return NULL;
669 }
670
671 /* Get BFD session storage with its defaults. */
672 bfd = bfd_session_new();
673 if (bfd == NULL) {
674 log_error("session-new: allocation failed");
675 return NULL;
676 }
677
678 /*
679 * Store interface/VRF name in case we need to delay session
680 * start. See `bfd_session_enable` for more information.
681 */
682 if (bpc->bpc_has_localif)
683 strlcpy(bfd->key.ifname, bpc->bpc_localif,
684 sizeof(bfd->key.ifname));
685
686 if (bpc->bpc_has_vrfname)
687 strlcpy(bfd->key.vrfname, bpc->bpc_vrfname,
688 sizeof(bfd->key.vrfname));
689
690 /* Copy remaining data. */
691 if (bpc->bpc_ipv4 == false)
692 BFD_SET_FLAG(bfd->flags, BFD_SESS_FLAG_IPV6);
693
694 bfd->key.family = (bpc->bpc_ipv4) ? AF_INET : AF_INET6;
695 switch (bfd->key.family) {
696 case AF_INET:
697 memcpy(&bfd->key.peer, &bpc->bpc_peer.sa_sin.sin_addr,
698 sizeof(bpc->bpc_peer.sa_sin.sin_addr));
699 memcpy(&bfd->key.local, &bpc->bpc_local.sa_sin.sin_addr,
700 sizeof(bpc->bpc_local.sa_sin.sin_addr));
701 break;
702
703 case AF_INET6:
704 memcpy(&bfd->key.peer, &bpc->bpc_peer.sa_sin6.sin6_addr,
705 sizeof(bpc->bpc_peer.sa_sin6.sin6_addr));
706 memcpy(&bfd->key.local, &bpc->bpc_local.sa_sin6.sin6_addr,
707 sizeof(bpc->bpc_local.sa_sin6.sin6_addr));
708 break;
709
710 default:
711 assert(1);
712 break;
713 }
714
715 if (bpc->bpc_mhop)
716 BFD_SET_FLAG(bfd->flags, BFD_SESS_FLAG_MH);
717
718 bfd->key.mhop = bpc->bpc_mhop;
719
720 /* Registrate session into data structures. */
721 bfd_key_insert(bfd);
722 bfd->discrs.my_discr = ptm_bfd_gen_ID();
723 bfd_id_insert(bfd);
724
725 /* Try to enable session and schedule for packet receive/send. */
726 if (bfd_session_enable(bfd) == -1) {
727 /* Unrecoverable failure, remove the session/peer. */
728 bfd_session_free(bfd);
729 return NULL;
730 }
731
732 /* Add observer if we have moving parts. */
733 if (bfd->key.ifname[0] || bfd->key.vrfname[0] || bfd->sock == -1)
734 bs_observer_add(bfd);
735
736 /* Apply other configurations. */
737 _bfd_session_update(bfd, bpc);
738
739 log_info("session-new: %s", bs_to_string(bfd));
740
741 control_notify_config(BCM_NOTIFY_CONFIG_ADD, bfd);
742
743 return bfd;
744 }
745
746 int ptm_bfd_sess_del(struct bfd_peer_cfg *bpc)
747 {
748 struct bfd_session *bs;
749
750 /* Find session and call free(). */
751 bs = bs_peer_find(bpc);
752 if (bs == NULL)
753 return -1;
754
755 /* This pointer is being referenced, don't let it be deleted. */
756 if (bs->refcount > 0) {
757 log_error("session-delete: refcount failure: %" PRIu64
758 " references",
759 bs->refcount);
760 return -1;
761 }
762
763 log_info("session-delete: %s", bs_to_string(bs));
764
765 control_notify_config(BCM_NOTIFY_CONFIG_DELETE, bs);
766
767 bfd_session_free(bs);
768
769 return 0;
770 }
771
772 void bfd_set_polling(struct bfd_session *bs)
773 {
774 /*
775 * Start polling procedure: the only timers that require polling
776 * to change value without losing connection are:
777 *
778 * - Desired minimum transmission interval;
779 * - Required minimum receive interval;
780 *
781 * RFC 5880, Section 6.8.3.
782 */
783 bs->polling = 1;
784 }
785
786 /*
787 * bs_<state>_handler() functions implement the BFD state machine
788 * transition mechanism. `<state>` is the current session state and
789 * the parameter `nstate` is the peer new state.
790 */
791 static void bs_admin_down_handler(struct bfd_session *bs
792 __attribute__((__unused__)),
793 int nstate __attribute__((__unused__)))
794 {
795 /*
796 * We are administratively down, there is no state machine
797 * handling.
798 */
799 }
800
801 static void bs_down_handler(struct bfd_session *bs, int nstate)
802 {
803 switch (nstate) {
804 case PTM_BFD_ADM_DOWN:
805 /*
806 * Remote peer doesn't want to talk, so lets keep the
807 * connection down.
808 */
809 case PTM_BFD_UP:
810 /* Peer can't be up yet, wait it go to 'init' or 'down'. */
811 break;
812
813 case PTM_BFD_DOWN:
814 /*
815 * Remote peer agreed that the path is down, lets try to
816 * bring it up.
817 */
818 bs->ses_state = PTM_BFD_INIT;
819 break;
820
821 case PTM_BFD_INIT:
822 /*
823 * Remote peer told us his path is up, lets turn
824 * activate the session.
825 */
826 ptm_bfd_sess_up(bs);
827 break;
828
829 default:
830 log_debug("state-change: unhandled neighbor state: %d", nstate);
831 break;
832 }
833 }
834
835 static void bs_init_handler(struct bfd_session *bs, int nstate)
836 {
837 switch (nstate) {
838 case PTM_BFD_ADM_DOWN:
839 /*
840 * Remote peer doesn't want to talk, so lets make the
841 * connection down.
842 */
843 bs->ses_state = PTM_BFD_DOWN;
844 break;
845
846 case PTM_BFD_DOWN:
847 /* Remote peer hasn't moved to first stage yet. */
848 break;
849
850 case PTM_BFD_INIT:
851 case PTM_BFD_UP:
852 /* We agreed on the settings and the path is up. */
853 ptm_bfd_sess_up(bs);
854 break;
855
856 default:
857 log_debug("state-change: unhandled neighbor state: %d", nstate);
858 break;
859 }
860 }
861
862 static void bs_up_handler(struct bfd_session *bs, int nstate)
863 {
864 switch (nstate) {
865 case PTM_BFD_ADM_DOWN:
866 case PTM_BFD_DOWN:
867 /* Peer lost or asked to shutdown connection. */
868 ptm_bfd_sess_dn(bs, BD_NEIGHBOR_DOWN);
869 break;
870
871 case PTM_BFD_INIT:
872 case PTM_BFD_UP:
873 /* Path is up and working. */
874 break;
875
876 default:
877 log_debug("state-change: unhandled neighbor state: %d", nstate);
878 break;
879 }
880 }
881
882 void bs_state_handler(struct bfd_session *bs, int nstate)
883 {
884 switch (bs->ses_state) {
885 case PTM_BFD_ADM_DOWN:
886 bs_admin_down_handler(bs, nstate);
887 break;
888 case PTM_BFD_DOWN:
889 bs_down_handler(bs, nstate);
890 break;
891 case PTM_BFD_INIT:
892 bs_init_handler(bs, nstate);
893 break;
894 case PTM_BFD_UP:
895 bs_up_handler(bs, nstate);
896 break;
897
898 default:
899 log_debug("state-change: [%s] is in invalid state: %d",
900 bs_to_string(bs), nstate);
901 break;
902 }
903 }
904
905 /*
906 * Handles echo timer manipulation after updating timer.
907 */
908 void bs_echo_timer_handler(struct bfd_session *bs)
909 {
910 uint32_t old_timer;
911
912 /*
913 * Before doing any echo handling, check if it is possible to
914 * use it.
915 *
916 * - Check for `echo-mode` configuration.
917 * - Check that we are not using multi hop (RFC 5883,
918 * Section 3).
919 * - Check that we are already at the up state.
920 */
921 if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO) == 0
922 || BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)
923 || bs->ses_state != PTM_BFD_UP)
924 return;
925
926 /* Remote peer asked to stop echo. */
927 if (bs->remote_timers.required_min_echo == 0) {
928 if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO_ACTIVE))
929 ptm_bfd_echo_stop(bs);
930
931 return;
932 }
933
934 /*
935 * Calculate the echo transmission timer: we must not send
936 * echo packets faster than the minimum required time
937 * announced by the remote system.
938 *
939 * RFC 5880, Section 6.8.9.
940 */
941 old_timer = bs->echo_xmt_TO;
942 if (bs->remote_timers.required_min_echo > bs->timers.required_min_echo)
943 bs->echo_xmt_TO = bs->remote_timers.required_min_echo;
944 else
945 bs->echo_xmt_TO = bs->timers.required_min_echo;
946
947 if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO_ACTIVE) == 0
948 || old_timer != bs->echo_xmt_TO)
949 ptm_bfd_echo_start(bs);
950 }
951
952 /*
953 * RFC 5880 Section 6.5.
954 *
955 * When a BFD control packet with the final bit is received, we must
956 * update the session parameters.
957 */
958 void bs_final_handler(struct bfd_session *bs)
959 {
960 /* Start using our new timers. */
961 bs->cur_timers.desired_min_tx = bs->timers.desired_min_tx;
962 bs->cur_timers.required_min_rx = bs->timers.required_min_rx;
963
964 /*
965 * TODO: demand mode. See RFC 5880 Section 6.1.
966 *
967 * When using demand mode we must disable the detection timer
968 * for lost control packets.
969 */
970 if (bs->demand_mode) {
971 /* Notify watchers about changed timers. */
972 control_notify_config(BCM_NOTIFY_CONFIG_UPDATE, bs);
973 return;
974 }
975
976 /*
977 * Calculate detection time based on new timers.
978 *
979 * Transmission calculation:
980 * We must respect the RequiredMinRxInterval from the remote
981 * system: if our desired transmission timer is more than the
982 * minimum receive rate, then we must lower it to at least the
983 * minimum receive interval.
984 *
985 * RFC 5880, Section 6.8.3.
986 */
987 if (bs->timers.desired_min_tx > bs->remote_timers.required_min_rx)
988 bs->xmt_TO = bs->remote_timers.required_min_rx;
989 else
990 bs->xmt_TO = bs->timers.desired_min_tx;
991
992 /* Apply new transmission timer immediately. */
993 ptm_bfd_start_xmt_timer(bs, false);
994
995 /*
996 * Detection timeout calculation:
997 * The minimum detection timeout is the remote detection
998 * multipler (number of packets to be missed) times the agreed
999 * transmission interval.
1000 *
1001 * RFC 5880, Section 6.8.4.
1002 *
1003 * TODO: support sending/counting more packets inside detection
1004 * timeout.
1005 */
1006 if (bs->remote_timers.required_min_rx > bs->timers.desired_min_tx)
1007 bs->detect_TO = bs->remote_detect_mult
1008 * bs->remote_timers.required_min_rx;
1009 else
1010 bs->detect_TO = bs->remote_detect_mult
1011 * bs->timers.desired_min_tx;
1012
1013 /* Apply new receive timer immediately. */
1014 bfd_recvtimer_update(bs);
1015
1016 /* Notify watchers about changed timers. */
1017 control_notify_config(BCM_NOTIFY_CONFIG_UPDATE, bs);
1018 }
1019
1020 void bs_set_slow_timers(struct bfd_session *bs)
1021 {
1022 /*
1023 * BFD connection must use slow timers before going up or after
1024 * losing connectivity to avoid wasting bandwidth.
1025 *
1026 * RFC 5880, Section 6.8.3.
1027 */
1028 bs->cur_timers.desired_min_tx = BFD_DEF_SLOWTX;
1029 bs->cur_timers.required_min_rx = BFD_DEF_SLOWTX;
1030 bs->cur_timers.required_min_echo = 0;
1031
1032 /* Set the appropriated timeouts for slow connection. */
1033 bs->detect_TO = (BFD_DEFDETECTMULT * BFD_DEF_SLOWTX);
1034 bs->xmt_TO = BFD_DEF_SLOWTX;
1035 }
1036
1037 /*
1038 * Helper functions.
1039 */
1040 static const char *get_diag_str(int diag)
1041 {
1042 for (int i = 0; diag_list[i].str; i++) {
1043 if (diag_list[i].type == diag)
1044 return diag_list[i].str;
1045 }
1046 return "N/A";
1047 }
1048
1049 const char *satostr(struct sockaddr_any *sa)
1050 {
1051 #define INETSTR_BUFCOUNT 8
1052 static char buf[INETSTR_BUFCOUNT][INET6_ADDRSTRLEN];
1053 static int bufidx;
1054 struct sockaddr_in *sin = &sa->sa_sin;
1055 struct sockaddr_in6 *sin6 = &sa->sa_sin6;
1056
1057 bufidx += (bufidx + 1) % INETSTR_BUFCOUNT;
1058 buf[bufidx][0] = 0;
1059
1060 switch (sin->sin_family) {
1061 case AF_INET:
1062 inet_ntop(AF_INET, &sin->sin_addr, buf[bufidx],
1063 sizeof(buf[bufidx]));
1064 break;
1065 case AF_INET6:
1066 inet_ntop(AF_INET6, &sin6->sin6_addr, buf[bufidx],
1067 sizeof(buf[bufidx]));
1068 break;
1069
1070 default:
1071 strlcpy(buf[bufidx], "unknown", sizeof(buf[bufidx]));
1072 break;
1073 }
1074
1075 return buf[bufidx];
1076 }
1077
1078 const char *diag2str(uint8_t diag)
1079 {
1080 switch (diag) {
1081 case 0:
1082 return "ok";
1083 case 1:
1084 return "control detection time expired";
1085 case 2:
1086 return "echo function failed";
1087 case 3:
1088 return "neighbor signaled session down";
1089 case 4:
1090 return "forwarding plane reset";
1091 case 5:
1092 return "path down";
1093 case 6:
1094 return "concatenated path down";
1095 case 7:
1096 return "administratively down";
1097 case 8:
1098 return "reverse concatenated path down";
1099 default:
1100 return "unknown";
1101 }
1102 }
1103
1104 int strtosa(const char *addr, struct sockaddr_any *sa)
1105 {
1106 memset(sa, 0, sizeof(*sa));
1107
1108 if (inet_pton(AF_INET, addr, &sa->sa_sin.sin_addr) == 1) {
1109 sa->sa_sin.sin_family = AF_INET;
1110 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
1111 sa->sa_sin.sin_len = sizeof(sa->sa_sin);
1112 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
1113 return 0;
1114 }
1115
1116 if (inet_pton(AF_INET6, addr, &sa->sa_sin6.sin6_addr) == 1) {
1117 sa->sa_sin6.sin6_family = AF_INET6;
1118 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
1119 sa->sa_sin6.sin6_len = sizeof(sa->sa_sin6);
1120 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
1121 return 0;
1122 }
1123
1124 return -1;
1125 }
1126
1127 void integer2timestr(uint64_t time, char *buf, size_t buflen)
1128 {
1129 unsigned int year, month, day, hour, minute, second;
1130 int rv;
1131
1132 #define MINUTES (60)
1133 #define HOURS (60 * MINUTES)
1134 #define DAYS (24 * HOURS)
1135 #define MONTHS (30 * DAYS)
1136 #define YEARS (12 * MONTHS)
1137 if (time >= YEARS) {
1138 year = time / YEARS;
1139 time -= year * YEARS;
1140
1141 rv = snprintf(buf, buflen, "%u year(s), ", year);
1142 buf += rv;
1143 buflen -= rv;
1144 }
1145 if (time >= MONTHS) {
1146 month = time / MONTHS;
1147 time -= month * MONTHS;
1148
1149 rv = snprintf(buf, buflen, "%u month(s), ", month);
1150 buf += rv;
1151 buflen -= rv;
1152 }
1153 if (time >= DAYS) {
1154 day = time / DAYS;
1155 time -= day * DAYS;
1156
1157 rv = snprintf(buf, buflen, "%u day(s), ", day);
1158 buf += rv;
1159 buflen -= rv;
1160 }
1161 if (time >= HOURS) {
1162 hour = time / HOURS;
1163 time -= hour * HOURS;
1164
1165 rv = snprintf(buf, buflen, "%u hour(s), ", hour);
1166 buf += rv;
1167 buflen -= rv;
1168 }
1169 if (time >= MINUTES) {
1170 minute = time / MINUTES;
1171 time -= minute * MINUTES;
1172
1173 rv = snprintf(buf, buflen, "%u minute(s), ", minute);
1174 buf += rv;
1175 buflen -= rv;
1176 }
1177 second = time % MINUTES;
1178 snprintf(buf, buflen, "%u second(s)", second);
1179 }
1180
1181 const char *bs_to_string(const struct bfd_session *bs)
1182 {
1183 static char buf[256];
1184 char addr_buf[INET6_ADDRSTRLEN];
1185 int pos;
1186 bool is_mhop = BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH);
1187
1188 pos = snprintf(buf, sizeof(buf), "mhop:%s", is_mhop ? "yes" : "no");
1189 pos += snprintf(buf + pos, sizeof(buf) - pos, " peer:%s",
1190 inet_ntop(bs->key.family, &bs->key.peer, addr_buf,
1191 sizeof(addr_buf)));
1192 pos += snprintf(buf + pos, sizeof(buf) - pos, " local:%s",
1193 inet_ntop(bs->key.family, &bs->key.local, addr_buf,
1194 sizeof(addr_buf)));
1195 if (bs->key.vrfname[0])
1196 pos += snprintf(buf + pos, sizeof(buf) - pos, " vrf:%s",
1197 bs->key.vrfname);
1198 if (bs->key.ifname[0])
1199 pos += snprintf(buf + pos, sizeof(buf) - pos, " ifname:%s",
1200 bs->key.ifname);
1201
1202 (void)pos;
1203
1204 return buf;
1205 }
1206
1207 int bs_observer_add(struct bfd_session *bs)
1208 {
1209 struct bfd_session_observer *bso;
1210
1211 bso = XCALLOC(MTYPE_BFDD_SESSION_OBSERVER, sizeof(*bso));
1212 bso->bso_isaddress = false;
1213 bso->bso_bs = bs;
1214 bso->bso_isinterface = !BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH);
1215 if (bso->bso_isinterface)
1216 strlcpy(bso->bso_entryname, bs->key.ifname,
1217 sizeof(bso->bso_entryname));
1218 /* Handle socket binding failures caused by missing local addresses. */
1219 if (bs->sock == -1) {
1220 bso->bso_isaddress = true;
1221 bso->bso_addr.family = bs->key.family;
1222 memcpy(&bso->bso_addr.u.prefix, &bs->key.local,
1223 sizeof(bs->key.local));
1224 }
1225
1226 TAILQ_INSERT_TAIL(&bglobal.bg_obslist, bso, bso_entry);
1227
1228 return 0;
1229 }
1230
1231 void bs_observer_del(struct bfd_session_observer *bso)
1232 {
1233 TAILQ_REMOVE(&bglobal.bg_obslist, bso, bso_entry);
1234 XFREE(MTYPE_BFDD_SESSION_OBSERVER, bso);
1235 }
1236
1237 void bs_to_bpc(struct bfd_session *bs, struct bfd_peer_cfg *bpc)
1238 {
1239 memset(bpc, 0, sizeof(*bpc));
1240
1241 bpc->bpc_ipv4 = (bs->key.family == AF_INET);
1242 bpc->bpc_mhop = bs->key.mhop;
1243
1244 switch (bs->key.family) {
1245 case AF_INET:
1246 bpc->bpc_peer.sa_sin.sin_family = AF_INET;
1247 memcpy(&bpc->bpc_peer.sa_sin.sin_addr, &bs->key.peer,
1248 sizeof(bpc->bpc_peer.sa_sin.sin_addr));
1249
1250 if (memcmp(&bs->key.local, &zero_addr, sizeof(bs->key.local))) {
1251 bpc->bpc_local.sa_sin.sin_family = AF_INET6;
1252 memcpy(&bpc->bpc_local.sa_sin.sin_addr, &bs->key.local,
1253 sizeof(bpc->bpc_local.sa_sin.sin_addr));
1254 }
1255 break;
1256
1257 case AF_INET6:
1258 bpc->bpc_peer.sa_sin.sin_family = AF_INET6;
1259 memcpy(&bpc->bpc_peer.sa_sin6.sin6_addr, &bs->key.peer,
1260 sizeof(bpc->bpc_peer.sa_sin6.sin6_addr));
1261
1262 bpc->bpc_local.sa_sin6.sin6_family = AF_INET6;
1263 memcpy(&bpc->bpc_local.sa_sin6.sin6_addr, &bs->key.local,
1264 sizeof(bpc->bpc_local.sa_sin6.sin6_addr));
1265 break;
1266 }
1267
1268 if (bs->key.ifname[0]) {
1269 bpc->bpc_has_localif = true;
1270 strlcpy(bpc->bpc_localif, bs->key.ifname,
1271 sizeof(bpc->bpc_localif));
1272 }
1273
1274 if (bs->key.vrfname[0]) {
1275 bpc->bpc_has_vrfname = true;
1276 strlcpy(bpc->bpc_vrfname, bs->key.vrfname,
1277 sizeof(bpc->bpc_vrfname));
1278 }
1279 }
1280
1281
1282 /*
1283 * BFD hash data structures to find sessions.
1284 */
1285 static struct hash *bfd_id_hash;
1286 static struct hash *bfd_key_hash;
1287
1288 static unsigned int bfd_id_hash_do(const void *p);
1289 static unsigned int bfd_key_hash_do(const void *p);
1290
1291 static void _bfd_free(struct hash_bucket *hb,
1292 void *arg __attribute__((__unused__)));
1293
1294 /* BFD hash for our discriminator. */
1295 static unsigned int bfd_id_hash_do(const void *p)
1296 {
1297 const struct bfd_session *bs = p;
1298
1299 return jhash_1word(bs->discrs.my_discr, 0);
1300 }
1301
1302 static bool bfd_id_hash_cmp(const void *n1, const void *n2)
1303 {
1304 const struct bfd_session *bs1 = n1, *bs2 = n2;
1305
1306 return bs1->discrs.my_discr == bs2->discrs.my_discr;
1307 }
1308
1309 /* BFD hash for single hop. */
1310 static unsigned int bfd_key_hash_do(const void *p)
1311 {
1312 const struct bfd_session *bs = p;
1313
1314 return jhash(&bs->key, sizeof(bs->key), 0);
1315 }
1316
1317 static bool bfd_key_hash_cmp(const void *n1, const void *n2)
1318 {
1319 const struct bfd_session *bs1 = n1, *bs2 = n2;
1320
1321 return memcmp(&bs1->key, &bs2->key, sizeof(bs1->key)) == 0;
1322 }
1323
1324
1325 /*
1326 * Hash public interface / exported functions.
1327 */
1328
1329 /* Lookup functions. */
1330 struct bfd_session *bfd_id_lookup(uint32_t id)
1331 {
1332 struct bfd_session bs;
1333
1334 bs.discrs.my_discr = id;
1335
1336 return hash_lookup(bfd_id_hash, &bs);
1337 }
1338
1339 struct bfd_key_walk_partial_lookup {
1340 struct bfd_session *given;
1341 struct bfd_session *result;
1342 };
1343
1344 /* ignore some parameters */
1345 static int bfd_key_lookup_ignore_partial_walker(struct hash_bucket *b, void *data)
1346 {
1347 struct bfd_key_walk_partial_lookup *ctx =
1348 (struct bfd_key_walk_partial_lookup *)data;
1349 struct bfd_session *given = ctx->given;
1350 struct bfd_session *parsed = b->data;
1351
1352 if (given->key.family != parsed->key.family)
1353 return HASHWALK_CONTINUE;
1354 if (given->key.mhop != parsed->key.mhop)
1355 return HASHWALK_CONTINUE;
1356 if (memcmp(&given->key.peer, &parsed->key.peer, sizeof(struct in6_addr)))
1357 return HASHWALK_CONTINUE;
1358 if (memcmp(given->key.vrfname, parsed->key.vrfname, MAXNAMELEN))
1359 return HASHWALK_CONTINUE;
1360 ctx->result = parsed;
1361 /* ignore localaddr or interface */
1362 return HASHWALK_ABORT;
1363 }
1364
1365 struct bfd_session *bfd_key_lookup(struct bfd_key key)
1366 {
1367 struct bfd_session bs, *bsp;
1368 struct bfd_key_walk_partial_lookup ctx;
1369 char peer_buf[INET6_ADDRSTRLEN];
1370
1371 bs.key = key;
1372 bsp = hash_lookup(bfd_key_hash, &bs);
1373 if (bsp)
1374 return bsp;
1375
1376 inet_ntop(bs.key.family, &bs.key.peer, peer_buf,
1377 sizeof(peer_buf));
1378 /* Handle cases where local-address is optional. */
1379 if (bs.key.family == AF_INET) {
1380 memset(&bs.key.local, 0, sizeof(bs.key.local));
1381 bsp = hash_lookup(bfd_key_hash, &bs);
1382 if (bsp) {
1383 char addr_buf[INET6_ADDRSTRLEN];
1384
1385 inet_ntop(bs.key.family, &key.local, addr_buf,
1386 sizeof(addr_buf));
1387 log_debug(" peer %s found, but loc-addr %s ignored",
1388 peer_buf, addr_buf);
1389 return bsp;
1390 }
1391 }
1392
1393 bs.key = key;
1394 /* Handle cases where ifname is optional. */
1395 if (bs.key.ifname[0]) {
1396 memset(bs.key.ifname, 0, sizeof(bs.key.ifname));
1397 bsp = hash_lookup(bfd_key_hash, &bs);
1398 if (bsp) {
1399 log_debug(" peer %s found, but ifp %s ignored",
1400 peer_buf, key.ifname);
1401 return bsp;
1402 }
1403 }
1404
1405 /* Handle cases where local-address and ifname are optional. */
1406 if (bs.key.family == AF_INET) {
1407 memset(&bs.key.local, 0, sizeof(bs.key.local));
1408 bsp = hash_lookup(bfd_key_hash, &bs);
1409 if (bsp) {
1410 char addr_buf[INET6_ADDRSTRLEN];
1411
1412 inet_ntop(bs.key.family, &bs.key.local, addr_buf,
1413 sizeof(addr_buf));
1414 log_debug(" peer %s found, but ifp %s"
1415 " and loc-addr %s ignored",
1416 peer_buf, key.ifname,
1417 addr_buf);
1418 return bsp;
1419 }
1420 }
1421 bs.key = key;
1422
1423 /* Handle case where a context more complex ctx is present.
1424 * input has no iface nor local-address, but a context may
1425 * exist
1426 */
1427 ctx.result = NULL;
1428 ctx.given = &bs;
1429 hash_walk(bfd_key_hash,
1430 &bfd_key_lookup_ignore_partial_walker,
1431 &ctx);
1432 /* change key */
1433 if (ctx.result) {
1434 bsp = ctx.result;
1435 log_debug(" peer %s found, but ifp"
1436 " and/or loc-addr params ignored");
1437 }
1438 return bsp;
1439 }
1440
1441 /*
1442 * Delete functions.
1443 *
1444 * Delete functions searches and remove the item from the hash and
1445 * returns a pointer to the removed item data. If the item was not found
1446 * then it returns NULL.
1447 *
1448 * The data stored inside the hash is not free()ed, so you must do it
1449 * manually after getting the pointer back.
1450 */
1451 struct bfd_session *bfd_id_delete(uint32_t id)
1452 {
1453 struct bfd_session bs;
1454
1455 bs.discrs.my_discr = id;
1456
1457 return hash_release(bfd_id_hash, &bs);
1458 }
1459
1460 struct bfd_session *bfd_key_delete(struct bfd_key key)
1461 {
1462 struct bfd_session bs, *bsp;
1463
1464 bs.key = key;
1465 bsp = hash_lookup(bfd_key_hash, &bs);
1466 if (bsp == NULL && key.ifname[0]) {
1467 memset(bs.key.ifname, 0, sizeof(bs.key.ifname));
1468 bsp = hash_lookup(bfd_key_hash, &bs);
1469 }
1470
1471 return hash_release(bfd_key_hash, bsp);
1472 }
1473
1474 /* Iteration functions. */
1475 void bfd_id_iterate(hash_iter_func hif, void *arg)
1476 {
1477 hash_iterate(bfd_id_hash, hif, arg);
1478 }
1479
1480 void bfd_key_iterate(hash_iter_func hif, void *arg)
1481 {
1482 hash_iterate(bfd_key_hash, hif, arg);
1483 }
1484
1485 /*
1486 * Insert functions.
1487 *
1488 * Inserts session into hash and returns `true` on success, otherwise
1489 * `false`.
1490 */
1491 bool bfd_id_insert(struct bfd_session *bs)
1492 {
1493 return (hash_get(bfd_id_hash, bs, hash_alloc_intern) == bs);
1494 }
1495
1496 bool bfd_key_insert(struct bfd_session *bs)
1497 {
1498 return (hash_get(bfd_key_hash, bs, hash_alloc_intern) == bs);
1499 }
1500
1501 void bfd_initialize(void)
1502 {
1503 bfd_id_hash = hash_create(bfd_id_hash_do, bfd_id_hash_cmp,
1504 "BFD session discriminator hash");
1505 bfd_key_hash = hash_create(bfd_key_hash_do, bfd_key_hash_cmp,
1506 "BFD session hash");
1507 }
1508
1509 static void _bfd_free(struct hash_bucket *hb,
1510 void *arg __attribute__((__unused__)))
1511 {
1512 struct bfd_session *bs = hb->data;
1513
1514 bfd_session_free(bs);
1515 }
1516
1517 void bfd_shutdown(void)
1518 {
1519 /*
1520 * Close and free all BFD sessions.
1521 *
1522 * _bfd_free() will call bfd_session_free() which will take care
1523 * of removing the session from all hashes, so we just run an
1524 * assert() here to make sure it really happened.
1525 */
1526 bfd_id_iterate(_bfd_free, NULL);
1527 assert(bfd_key_hash->count == 0);
1528
1529 /* Now free the hashes themselves. */
1530 hash_free(bfd_id_hash);
1531 hash_free(bfd_key_hash);
1532 }
1533
1534 static int bfd_vrf_new(struct vrf *vrf)
1535 {
1536 log_debug("VRF Created: %s(%u)", vrf->name, vrf->vrf_id);
1537 return 0;
1538 }
1539
1540 static int bfd_vrf_delete(struct vrf *vrf)
1541 {
1542 log_debug("VRF Deletion: %s(%u)", vrf->name, vrf->vrf_id);
1543 return 0;
1544 }
1545
1546 static int bfd_vrf_enable(struct vrf *vrf)
1547 {
1548 struct bfd_vrf_global *bvrf;
1549
1550 /* a different name */
1551 if (!vrf->info) {
1552 bvrf = XCALLOC(MTYPE_BFDD_VRF, sizeof(struct bfd_vrf_global));
1553 bvrf->vrf = vrf;
1554 vrf->info = (void *)bvrf;
1555 } else
1556 bvrf = vrf->info;
1557 log_debug("VRF enable add %s id %u", vrf->name, vrf->vrf_id);
1558 if (vrf->vrf_id == VRF_DEFAULT ||
1559 vrf_get_backend() == VRF_BACKEND_NETNS) {
1560 if (!bvrf->bg_shop)
1561 bvrf->bg_shop = bp_udp_shop(vrf->vrf_id);
1562 if (!bvrf->bg_mhop)
1563 bvrf->bg_mhop = bp_udp_mhop(vrf->vrf_id);
1564 if (!bvrf->bg_shop6)
1565 bvrf->bg_shop6 = bp_udp6_shop(vrf->vrf_id);
1566 if (!bvrf->bg_mhop6)
1567 bvrf->bg_mhop6 = bp_udp6_mhop(vrf->vrf_id);
1568 if (!bvrf->bg_echo)
1569 bvrf->bg_echo = bp_echo_socket(vrf->vrf_id);
1570 if (!bvrf->bg_echov6)
1571 bvrf->bg_echov6 = bp_echov6_socket(vrf->vrf_id);
1572
1573 /* Add descriptors to the event loop. */
1574 if (!bvrf->bg_ev[0])
1575 thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_shop,
1576 &bvrf->bg_ev[0]);
1577 if (!bvrf->bg_ev[1])
1578 thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_mhop,
1579 &bvrf->bg_ev[1]);
1580 if (!bvrf->bg_ev[2])
1581 thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_shop6,
1582 &bvrf->bg_ev[2]);
1583 if (!bvrf->bg_ev[3])
1584 thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_mhop6,
1585 &bvrf->bg_ev[3]);
1586 if (!bvrf->bg_ev[4])
1587 thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_echo,
1588 &bvrf->bg_ev[4]);
1589 if (!bvrf->bg_ev[5])
1590 thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_echov6,
1591 &bvrf->bg_ev[5]);
1592 }
1593 if (vrf->vrf_id != VRF_DEFAULT) {
1594 bfdd_zclient_register(vrf->vrf_id);
1595 bfdd_sessions_enable_vrf(vrf);
1596 }
1597 return 0;
1598 }
1599
1600 static int bfd_vrf_disable(struct vrf *vrf)
1601 {
1602 struct bfd_vrf_global *bvrf;
1603
1604 if (!vrf->info)
1605 return 0;
1606 bvrf = vrf->info;
1607
1608 if (vrf->vrf_id != VRF_DEFAULT) {
1609 bfdd_sessions_disable_vrf(vrf);
1610 bfdd_zclient_unregister(vrf->vrf_id);
1611 }
1612
1613 log_debug("VRF disable %s id %d", vrf->name, vrf->vrf_id);
1614 /* Close all descriptors. */
1615 socket_close(&bvrf->bg_echo);
1616 socket_close(&bvrf->bg_shop);
1617 socket_close(&bvrf->bg_mhop);
1618 socket_close(&bvrf->bg_shop6);
1619 socket_close(&bvrf->bg_mhop6);
1620
1621 /* free context */
1622 XFREE(MTYPE_BFDD_VRF, bvrf);
1623 vrf->info = NULL;
1624
1625 return 0;
1626 }
1627
1628 void bfd_vrf_init(void)
1629 {
1630 vrf_init(bfd_vrf_new, bfd_vrf_enable, bfd_vrf_disable,
1631 bfd_vrf_delete, NULL);
1632 }
1633
1634 void bfd_vrf_terminate(void)
1635 {
1636 vrf_terminate();
1637 }
1638
1639 struct bfd_vrf_global *bfd_vrf_look_by_session(struct bfd_session *bfd)
1640 {
1641 struct vrf *vrf;
1642
1643 if (!vrf_is_backend_netns()) {
1644 vrf = vrf_lookup_by_id(VRF_DEFAULT);
1645 if (vrf)
1646 return (struct bfd_vrf_global *)vrf->info;
1647 return NULL;
1648 }
1649 if (!bfd)
1650 return NULL;
1651 if (!bfd->vrf)
1652 return NULL;
1653 return bfd->vrf->info;
1654 }