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