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