]> git.proxmox.com Git - mirror_frr.git/blame - bfdd/bfd.c
*: do not check XMALLOC / XCALLOC for null ret
[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));
e9e2c950
RZ
554
555 QOBJ_REG(bs, bfd_session);
556
f43b9368 557 bs->timers.desired_min_tx = BFD_DEFDESIREDMINTX;
e9e2c950
RZ
558 bs->timers.required_min_rx = BFD_DEFREQUIREDMINRX;
559 bs->timers.required_min_echo = BFD_DEF_REQ_MIN_ECHO;
560 bs->detect_mult = BFD_DEFDETECTMULT;
561 bs->mh_ttl = BFD_DEF_MHOP_TTL;
d245e522 562 bs->ses_state = PTM_BFD_DOWN;
e9e2c950 563
b912b189
RZ
564 /* Initiate connection with slow timers. */
565 bs_set_slow_timers(bs);
f43b9368
RZ
566
567 /* Initiate remote settings as well. */
568 bs->remote_timers = bs->cur_timers;
569 bs->remote_detect_mult = BFD_DEFDETECTMULT;
570
d245e522 571 bs->sock = -1;
e9e2c950
RZ
572 monotime(&bs->uptime);
573 bs->downtime = bs->uptime;
574
575 return bs;
576}
577
578int bfd_session_update_label(struct bfd_session *bs, const char *nlabel)
579{
580 /* New label treatment:
581 * - Check if the label is taken;
582 * - Try to allocate the memory for it and register;
583 */
584 if (bs->pl == NULL) {
585 if (pl_find(nlabel) != NULL) {
586 /* Someone is already using it. */
587 return -1;
588 }
589
590 if (pl_new(nlabel, bs) == NULL)
591 return -1;
592
593 return 0;
594 }
595
596 /*
597 * Test label change consistency:
598 * - Do nothing if it's the same label;
599 * - Check if the future label is already taken;
600 * - Change label;
601 */
602 if (strcmp(nlabel, bs->pl->pl_label) == 0)
603 return -1;
604 if (pl_find(nlabel) != NULL)
605 return -1;
606
607 strlcpy(bs->pl->pl_label, nlabel, sizeof(bs->pl->pl_label));
608 return 0;
609}
610
611static void _bfd_session_update(struct bfd_session *bs,
612 struct bfd_peer_cfg *bpc)
613{
614 if (bpc->bpc_echo) {
615 /* Check if echo mode is already active. */
616 if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO))
617 goto skip_echo;
618
619 BFD_SET_FLAG(bs->flags, BFD_SESS_FLAG_ECHO);
e9e2c950
RZ
620
621 /* Activate/update echo receive timeout timer. */
73c62f8e 622 bs_echo_timer_handler(bs);
e9e2c950
RZ
623 } else {
624 /* Check if echo mode is already disabled. */
625 if (!BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO))
626 goto skip_echo;
627
628 BFD_UNSET_FLAG(bs->flags, BFD_SESS_FLAG_ECHO);
8bd859f6 629 ptm_bfd_echo_stop(bs);
e9e2c950
RZ
630 }
631
632skip_echo:
633 if (bpc->bpc_has_txinterval)
f43b9368 634 bs->timers.desired_min_tx = bpc->bpc_txinterval * 1000;
e9e2c950
RZ
635
636 if (bpc->bpc_has_recvinterval)
637 bs->timers.required_min_rx = bpc->bpc_recvinterval * 1000;
638
639 if (bpc->bpc_has_detectmultiplier)
640 bs->detect_mult = bpc->bpc_detectmultiplier;
641
642 if (bpc->bpc_has_echointerval)
643 bs->timers.required_min_echo = bpc->bpc_echointerval * 1000;
644
645 if (bpc->bpc_has_label)
646 bfd_session_update_label(bs, bpc->bpc_label);
647
648 if (bpc->bpc_shutdown) {
649 /* Check if already shutdown. */
650 if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN))
651 return;
652
653 BFD_SET_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN);
654
655 /* Disable all events. */
656 bfd_recvtimer_delete(bs);
657 bfd_echo_recvtimer_delete(bs);
658 bfd_xmttimer_delete(bs);
659 bfd_echo_xmttimer_delete(bs);
660
661 /* Change and notify state change. */
662 bs->ses_state = PTM_BFD_ADM_DOWN;
663 control_notify(bs);
664
d245e522
RZ
665 /* Don't try to send packets with a disabled session. */
666 if (bs->sock != -1)
667 ptm_bfd_snd(bs, 0);
e9e2c950
RZ
668 } else {
669 /* Check if already working. */
670 if (!BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN))
671 return;
672
673 BFD_UNSET_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN);
674
675 /* Change and notify state change. */
676 bs->ses_state = PTM_BFD_DOWN;
677 control_notify(bs);
678
679 /* Enable all timers. */
680 bfd_recvtimer_update(bs);
681 bfd_xmttimer_update(bs, bs->xmt_TO);
e9e2c950
RZ
682 }
683}
684
685static int bfd_session_update(struct bfd_session *bs, struct bfd_peer_cfg *bpc)
686{
687 /* User didn't want to update, return failure. */
688 if (bpc->bpc_createonly)
689 return -1;
690
691 _bfd_session_update(bs, bpc);
692
e9e2c950
RZ
693 control_notify_config(BCM_NOTIFY_CONFIG_UPDATE, bs);
694
695 return 0;
696}
697
698static void bfd_session_free(struct bfd_session *bs)
699{
d245e522 700 struct bfd_session_observer *bso;
e9e2c950 701
d245e522 702 bfd_session_disable(bs);
e9e2c950 703
d245e522
RZ
704 /* Remove observer if any. */
705 TAILQ_FOREACH(bso, &bglobal.bg_obslist, bso_entry) {
706 if (bso->bso_bs != bs)
707 continue;
708
709 break;
710 }
711 if (bso != NULL)
712 bs_observer_del(bso);
e9e2c950
RZ
713
714 pl_free(bs->pl);
715
716 QOBJ_UNREG(bs);
717 XFREE(MTYPE_BFDD_CONFIG, bs);
718}
719
720struct bfd_session *ptm_bfd_sess_new(struct bfd_peer_cfg *bpc)
721{
722 struct bfd_session *bfd, *l_bfd;
e9e2c950
RZ
723
724 /* check to see if this needs a new session */
725 l_bfd = bs_peer_find(bpc);
726 if (l_bfd) {
727 /* Requesting a duplicated peer means update configuration. */
728 if (bfd_session_update(l_bfd, bpc) == 0)
729 return l_bfd;
730 else
731 return NULL;
732 }
733
d245e522
RZ
734 /* Get BFD session storage with its defaults. */
735 bfd = bfd_session_new();
e9e2c950 736 if (bfd == NULL) {
03e7f088 737 log_error("session-new: allocation failed");
e9e2c950
RZ
738 return NULL;
739 }
740
d245e522
RZ
741 /*
742 * Store interface/VRF name in case we need to delay session
743 * start. See `bfd_session_enable` for more information.
744 */
745 if (bpc->bpc_has_localif)
746 strlcpy(bfd->ifname, bpc->bpc_localif, sizeof(bfd->ifname));
e9e2c950 747
d245e522
RZ
748 if (bpc->bpc_has_vrfname)
749 strlcpy(bfd->vrfname, bpc->bpc_vrfname, sizeof(bfd->vrfname));
e9e2c950 750
d245e522
RZ
751 /* Add observer if we have moving parts. */
752 if (bfd->ifname[0] || bfd->vrfname[0])
753 bs_observer_add(bfd);
43adc702 754
d245e522
RZ
755 /* Copy remaining data. */
756 if (bpc->bpc_ipv4 == false)
757 BFD_SET_FLAG(bfd->flags, BFD_SESS_FLAG_IPV6);
e9e2c950
RZ
758
759 if (bpc->bpc_mhop) {
760 BFD_SET_FLAG(bfd->flags, BFD_SESS_FLAG_MH);
761 bfd->mhop.peer = bpc->bpc_peer;
762 bfd->mhop.local = bpc->bpc_local;
e9e2c950
RZ
763 } else {
764 bfd->shop.peer = bpc->bpc_peer;
d245e522 765 }
e9e2c950 766
d245e522
RZ
767 bfd->local_ip = bpc->bpc_local;
768 bfd->local_address = bpc->bpc_local;
769
770 /* Try to enable session and schedule for packet receive/send. */
771 if (bfd_session_enable(bfd) == -1) {
772 /* Unrecoverable failure, remove the session/peer. */
773 bfd_session_free(bfd);
774 return NULL;
e9e2c950
RZ
775 }
776
d245e522 777 /* Apply other configurations. */
e9e2c950
RZ
778 _bfd_session_update(bfd, bpc);
779
03e7f088 780 log_info("session-new: %s", bs_to_string(bfd));
e9e2c950
RZ
781
782 control_notify_config(BCM_NOTIFY_CONFIG_ADD, bfd);
783
784 return bfd;
785}
786
787int ptm_bfd_ses_del(struct bfd_peer_cfg *bpc)
788{
789 struct bfd_session *bs;
790
791 /* Find session and call free(). */
792 bs = bs_peer_find(bpc);
793 if (bs == NULL)
794 return -1;
795
796 /* This pointer is being referenced, don't let it be deleted. */
797 if (bs->refcount > 0) {
03e7f088
RZ
798 log_error("session-delete: refcount failure: %" PRIu64
799 " references",
800 bs->refcount);
e9e2c950
RZ
801 return -1;
802 }
803
03e7f088 804 log_info("session-delete: %s", bs_to_string(bs));
e9e2c950
RZ
805
806 control_notify_config(BCM_NOTIFY_CONFIG_DELETE, bs);
807
808 bfd_session_free(bs);
809
810 return 0;
811}
812
d3f3a2c4
RZ
813void bfd_set_polling(struct bfd_session *bs)
814{
c0ef9a8a
RZ
815 /*
816 * Start polling procedure: the only timers that require polling
817 * to change value without losing connection are:
818 *
819 * - Desired minimum transmission interval;
820 * - Required minimum receive interval;
821 *
822 * RFC 5880, Section 6.8.3.
823 */
d3f3a2c4
RZ
824 bs->polling = 1;
825}
826
aef131af
RZ
827/*
828 * bs_<state>_handler() functions implement the BFD state machine
829 * transition mechanism. `<state>` is the current session state and
830 * the parameter `nstate` is the peer new state.
831 */
9f37770f
RZ
832static void bs_admin_down_handler(struct bfd_session *bs
833 __attribute__((__unused__)),
834 int nstate __attribute__((__unused__)))
aef131af
RZ
835{
836 /*
837 * We are administratively down, there is no state machine
838 * handling.
839 */
840}
841
9f37770f 842static void bs_down_handler(struct bfd_session *bs, int nstate)
aef131af
RZ
843{
844 switch (nstate) {
845 case PTM_BFD_ADM_DOWN:
846 /*
847 * Remote peer doesn't want to talk, so lets keep the
848 * connection down.
849 */
850 case PTM_BFD_UP:
851 /* Peer can't be up yet, wait it go to 'init' or 'down'. */
852 break;
853
854 case PTM_BFD_DOWN:
855 /*
856 * Remote peer agreed that the path is down, lets try to
857 * bring it up.
858 */
859 bs->ses_state = PTM_BFD_INIT;
860 break;
861
862 case PTM_BFD_INIT:
863 /*
864 * Remote peer told us his path is up, lets turn
865 * activate the session.
866 */
867 ptm_bfd_ses_up(bs);
868 break;
869
870 default:
871 log_debug("state-change: unhandled neighbor state: %d", nstate);
872 break;
873 }
874}
875
9f37770f 876static void bs_init_handler(struct bfd_session *bs, int nstate)
aef131af
RZ
877{
878 switch (nstate) {
879 case PTM_BFD_ADM_DOWN:
880 /*
881 * Remote peer doesn't want to talk, so lets make the
882 * connection down.
883 */
884 bs->ses_state = PTM_BFD_DOWN;
885 break;
886
887 case PTM_BFD_DOWN:
888 /* Remote peer hasn't moved to first stage yet. */
889 break;
890
891 case PTM_BFD_INIT:
892 case PTM_BFD_UP:
893 /* We agreed on the settings and the path is up. */
894 ptm_bfd_ses_up(bs);
895 break;
896
897 default:
898 log_debug("state-change: unhandled neighbor state: %d", nstate);
899 break;
900 }
901}
902
9f37770f 903static void bs_up_handler(struct bfd_session *bs, int nstate)
aef131af
RZ
904{
905 switch (nstate) {
906 case PTM_BFD_ADM_DOWN:
907 case PTM_BFD_DOWN:
908 /* Peer lost or asked to shutdown connection. */
909 ptm_bfd_ses_dn(bs, BD_NEIGHBOR_DOWN);
910 break;
911
912 case PTM_BFD_INIT:
913 case PTM_BFD_UP:
914 /* Path is up and working. */
915 break;
916
917 default:
918 log_debug("state-change: unhandled neighbor state: %d", nstate);
919 break;
920 }
921}
922
923void bs_state_handler(struct bfd_session *bs, int nstate)
924{
925 switch (bs->ses_state) {
926 case PTM_BFD_ADM_DOWN:
927 bs_admin_down_handler(bs, nstate);
928 break;
929 case PTM_BFD_DOWN:
930 bs_down_handler(bs, nstate);
931 break;
932 case PTM_BFD_INIT:
933 bs_init_handler(bs, nstate);
934 break;
935 case PTM_BFD_UP:
936 bs_up_handler(bs, nstate);
937 break;
938
939 default:
940 log_debug("state-change: [%s] is in invalid state: %d",
941 bs_to_string(bs), nstate);
942 break;
943 }
944}
945
c0ef9a8a
RZ
946/*
947 * Handles echo timer manipulation after updating timer.
948 */
949void bs_echo_timer_handler(struct bfd_session *bs)
950{
951 uint32_t old_timer;
952
953 /*
954 * Before doing any echo handling, check if it is possible to
955 * use it.
956 *
957 * - Check for `echo-mode` configuration.
958 * - Check that we are not using multi hop (RFC 5883,
959 * Section 3).
960 * - Check that we are already at the up state.
961 */
962 if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO) == 0
963 || BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)
964 || bs->ses_state != PTM_BFD_UP)
965 return;
966
967 /* Remote peer asked to stop echo. */
968 if (bs->remote_timers.required_min_echo == 0) {
969 if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO_ACTIVE))
8bd859f6 970 ptm_bfd_echo_stop(bs);
c0ef9a8a
RZ
971
972 return;
973 }
974
975 /*
976 * Calculate the echo transmission timer: we must not send
977 * echo packets faster than the minimum required time
978 * announced by the remote system.
979 *
980 * RFC 5880, Section 6.8.9.
981 */
982 old_timer = bs->echo_xmt_TO;
983 if (bs->remote_timers.required_min_echo > bs->timers.required_min_echo)
984 bs->echo_xmt_TO = bs->remote_timers.required_min_echo;
985 else
986 bs->echo_xmt_TO = bs->timers.required_min_echo;
987
988 if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO_ACTIVE) == 0
989 || old_timer != bs->echo_xmt_TO)
990 ptm_bfd_echo_start(bs);
991}
992
993/*
994 * RFC 5880 Section 6.5.
995 *
996 * When a BFD control packet with the final bit is received, we must
997 * update the session parameters.
998 */
999void bs_final_handler(struct bfd_session *bs)
1000{
1001 /* Start using our new timers. */
f43b9368
RZ
1002 bs->cur_timers.desired_min_tx = bs->timers.desired_min_tx;
1003 bs->cur_timers.required_min_rx = bs->timers.required_min_rx;
c0ef9a8a
RZ
1004
1005 /*
1006 * TODO: demand mode. See RFC 5880 Section 6.1.
1007 *
1008 * When using demand mode we must disable the detection timer
1009 * for lost control packets.
1010 */
1011 if (bs->demand_mode) {
1012 /* Notify watchers about changed timers. */
1013 control_notify_config(BCM_NOTIFY_CONFIG_UPDATE, bs);
1014 return;
1015 }
1016
1017 /*
1018 * Calculate detection time based on new timers.
1019 *
1020 * Transmission calculation:
1021 * We must respect the RequiredMinRxInterval from the remote
1022 * system: if our desired transmission timer is more than the
1023 * minimum receive rate, then we must lower it to at least the
1024 * minimum receive interval.
1025 *
1026 * RFC 5880, Section 6.8.3.
1027 */
1028 if (bs->timers.desired_min_tx > bs->remote_timers.required_min_rx)
1029 bs->xmt_TO = bs->remote_timers.required_min_rx;
1030 else
1031 bs->xmt_TO = bs->timers.desired_min_tx;
1032
1033 /* Apply new transmission timer immediately. */
1034 ptm_bfd_start_xmt_timer(bs, false);
1035
1036 /*
1037 * Detection timeout calculation:
1038 * The minimum detection timeout is the remote detection
1039 * multipler (number of packets to be missed) times the agreed
1040 * transmission interval.
1041 *
1042 * RFC 5880, Section 6.8.4.
1043 *
1044 * TODO: support sending/counting more packets inside detection
1045 * timeout.
1046 */
1047 if (bs->remote_timers.required_min_rx > bs->timers.desired_min_tx)
1048 bs->detect_TO = bs->remote_detect_mult
1049 * bs->remote_timers.required_min_rx;
1050 else
1051 bs->detect_TO = bs->remote_detect_mult
1052 * bs->timers.desired_min_tx;
1053
1054 /* Apply new receive timer immediately. */
1055 bfd_recvtimer_update(bs);
1056
1057 /* Notify watchers about changed timers. */
1058 control_notify_config(BCM_NOTIFY_CONFIG_UPDATE, bs);
1059}
1060
b912b189
RZ
1061void bs_set_slow_timers(struct bfd_session *bs)
1062{
1063 /*
1064 * BFD connection must use slow timers before going up or after
1065 * losing connectivity to avoid wasting bandwidth.
1066 *
1067 * RFC 5880, Section 6.8.3.
1068 */
1069 bs->cur_timers.desired_min_tx = BFD_DEF_SLOWTX;
1070 bs->cur_timers.required_min_rx = BFD_DEF_SLOWTX;
1071 bs->cur_timers.required_min_echo = 0;
1072
1073 /* Set the appropriated timeouts for slow connection. */
1074 bs->detect_TO = (BFD_DEFDETECTMULT * BFD_DEF_SLOWTX);
1075 bs->xmt_TO = BFD_DEF_SLOWTX;
1076}
e9e2c950
RZ
1077
1078/*
1079 * Helper functions.
1080 */
1081static const char *get_diag_str(int diag)
1082{
1083 for (int i = 0; diag_list[i].str; i++) {
1084 if (diag_list[i].type == diag)
1085 return diag_list[i].str;
1086 }
1087 return "N/A";
1088}
1089
1090const char *satostr(struct sockaddr_any *sa)
1091{
1092#define INETSTR_BUFCOUNT 8
1093 static char buf[INETSTR_BUFCOUNT][INET6_ADDRSTRLEN];
1094 static int bufidx;
1095 struct sockaddr_in *sin = &sa->sa_sin;
1096 struct sockaddr_in6 *sin6 = &sa->sa_sin6;
1097
1098 bufidx += (bufidx + 1) % INETSTR_BUFCOUNT;
1099 buf[bufidx][0] = 0;
1100
1101 switch (sin->sin_family) {
1102 case AF_INET:
1103 inet_ntop(AF_INET, &sin->sin_addr, buf[bufidx],
1104 sizeof(buf[bufidx]));
1105 break;
1106 case AF_INET6:
1107 inet_ntop(AF_INET6, &sin6->sin6_addr, buf[bufidx],
1108 sizeof(buf[bufidx]));
1109 break;
1110
1111 default:
1112 strlcpy(buf[bufidx], "unknown", sizeof(buf[bufidx]));
1113 break;
1114 }
1115
1116 return buf[bufidx];
1117}
1118
1119const char *diag2str(uint8_t diag)
1120{
1121 switch (diag) {
1122 case 0:
1123 return "ok";
1124 case 1:
1125 return "control detection time expired";
1126 case 2:
1127 return "echo function failed";
1128 case 3:
1129 return "neighbor signaled session down";
1130 case 4:
1131 return "forwarding plane reset";
1132 case 5:
1133 return "path down";
1134 case 6:
1135 return "concatenated path down";
1136 case 7:
1137 return "administratively down";
1138 case 8:
1139 return "reverse concatenated path down";
1140 default:
1141 return "unknown";
1142 }
1143}
1144
1145int strtosa(const char *addr, struct sockaddr_any *sa)
1146{
1147 memset(sa, 0, sizeof(*sa));
1148
1149 if (inet_pton(AF_INET, addr, &sa->sa_sin.sin_addr) == 1) {
1150 sa->sa_sin.sin_family = AF_INET;
1151#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
1152 sa->sa_sin.sin_len = sizeof(sa->sa_sin);
1153#endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
1154 return 0;
1155 }
1156
1157 if (inet_pton(AF_INET6, addr, &sa->sa_sin6.sin6_addr) == 1) {
1158 sa->sa_sin6.sin6_family = AF_INET6;
1159#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
1160 sa->sa_sin6.sin6_len = sizeof(sa->sa_sin6);
1161#endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
1162 return 0;
1163 }
1164
1165 return -1;
1166}
1167
1168void integer2timestr(uint64_t time, char *buf, size_t buflen)
1169{
1170 unsigned int year, month, day, hour, minute, second;
1171 int rv;
1172
1173#define MINUTES (60)
23586b05
DS
1174#define HOURS (60 * MINUTES)
1175#define DAYS (24 * HOURS)
1176#define MONTHS (30 * DAYS)
1177#define YEARS (12 * MONTHS)
e9e2c950
RZ
1178 if (time >= YEARS) {
1179 year = time / YEARS;
1180 time -= year * YEARS;
1181
1182 rv = snprintf(buf, buflen, "%u year(s), ", year);
1183 buf += rv;
1184 buflen -= rv;
1185 }
1186 if (time >= MONTHS) {
1187 month = time / MONTHS;
1188 time -= month * MONTHS;
1189
1190 rv = snprintf(buf, buflen, "%u month(s), ", month);
1191 buf += rv;
1192 buflen -= rv;
1193 }
1194 if (time >= DAYS) {
1195 day = time / DAYS;
1196 time -= day * DAYS;
1197
1198 rv = snprintf(buf, buflen, "%u day(s), ", day);
1199 buf += rv;
1200 buflen -= rv;
1201 }
1202 if (time >= HOURS) {
1203 hour = time / HOURS;
1204 time -= hour * HOURS;
1205
1206 rv = snprintf(buf, buflen, "%u hour(s), ", hour);
1207 buf += rv;
1208 buflen -= rv;
1209 }
1210 if (time >= MINUTES) {
1211 minute = time / MINUTES;
1212 time -= minute * MINUTES;
1213
1214 rv = snprintf(buf, buflen, "%u minute(s), ", minute);
1215 buf += rv;
1216 buflen -= rv;
1217 }
1218 second = time % MINUTES;
1219 snprintf(buf, buflen, "%u second(s)", second);
1220}
1221
03e7f088
RZ
1222const char *bs_to_string(struct bfd_session *bs)
1223{
1224 static char buf[256];
1225 int pos;
1226 bool is_mhop = BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH);
1227
1228 pos = snprintf(buf, sizeof(buf), "mhop:%s", is_mhop ? "yes" : "no");
1229 if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)) {
1230 pos += snprintf(buf + pos, sizeof(buf) - pos,
1231 " peer:%s local:%s", satostr(&bs->mhop.peer),
1232 satostr(&bs->mhop.local));
1233
b333abc2
RZ
1234 if (bs->mhop.vrfid != VRF_DEFAULT)
1235 snprintf(buf + pos, sizeof(buf) - pos, " vrf:%u",
1236 bs->mhop.vrfid);
03e7f088
RZ
1237 } else {
1238 pos += snprintf(buf + pos, sizeof(buf) - pos, " peer:%s",
1239 satostr(&bs->shop.peer));
1240
1241 if (bs->local_address.sa_sin.sin_family)
1242 pos += snprintf(buf + pos, sizeof(buf) - pos,
1243 " local:%s",
1244 satostr(&bs->local_address));
1245
b333abc2
RZ
1246 if (bs->shop.ifindex)
1247 snprintf(buf + pos, sizeof(buf) - pos, " ifindex:%u",
1248 bs->shop.ifindex);
03e7f088
RZ
1249 }
1250
1251 return buf;
1252}
1253
d245e522
RZ
1254int bs_observer_add(struct bfd_session *bs)
1255{
1256 struct bfd_session_observer *bso;
1257
1258 bso = XMALLOC(MTYPE_BFDD_SESSION_OBSERVER, sizeof(*bso));
1259 bso->bso_bs = bs;
1260 bso->bso_isinterface = !BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH);
1261 if (bso->bso_isinterface)
1262 strlcpy(bso->bso_entryname, bs->ifname,
1263 sizeof(bso->bso_entryname));
1264 else
1265 strlcpy(bso->bso_entryname, bs->vrfname,
1266 sizeof(bso->bso_entryname));
1267
1268 TAILQ_INSERT_TAIL(&bglobal.bg_obslist, bso, bso_entry);
1269
1270 return 0;
1271}
1272
1273void bs_observer_del(struct bfd_session_observer *bso)
1274{
1275 TAILQ_REMOVE(&bglobal.bg_obslist, bso, bso_entry);
1276 XFREE(MTYPE_BFDD_SESSION_OBSERVER, bso);
1277}
1278
e9e2c950
RZ
1279
1280/*
1281 * BFD hash data structures to find sessions.
1282 */
1283static struct hash *bfd_id_hash;
1284static struct hash *bfd_shop_hash;
1285static struct hash *bfd_mhop_hash;
e9e2c950
RZ
1286
1287static unsigned int bfd_id_hash_do(void *p);
e9e2c950 1288static unsigned int bfd_shop_hash_do(void *p);
e9e2c950 1289static unsigned int bfd_mhop_hash_do(void *p);
e9e2c950
RZ
1290
1291static void _shop_key(struct bfd_session *bs, const struct bfd_shop_key *shop);
1292static void _shop_key2(struct bfd_session *bs, const struct bfd_shop_key *shop);
1293static void _mhop_key(struct bfd_session *bs, const struct bfd_mhop_key *mhop);
e9e2c950 1294
e3b78da8 1295static void _bfd_free(struct hash_bucket *hb,
e9e2c950 1296 void *arg __attribute__((__unused__)));
e9e2c950
RZ
1297
1298/* BFD hash for our discriminator. */
1299static unsigned int bfd_id_hash_do(void *p)
1300{
1301 struct bfd_session *bs = p;
1302
1303 return jhash_1word(bs->discrs.my_discr, 0);
1304}
1305
74df8d6d 1306static bool bfd_id_hash_cmp(const void *n1, const void *n2)
e9e2c950
RZ
1307{
1308 const struct bfd_session *bs1 = n1, *bs2 = n2;
1309
1310 return bs1->discrs.my_discr == bs2->discrs.my_discr;
1311}
1312
1313/* BFD hash for single hop. */
1314static unsigned int bfd_shop_hash_do(void *p)
1315{
1316 struct bfd_session *bs = p;
1317
1318 return jhash(&bs->shop, sizeof(bs->shop), 0);
1319}
1320
74df8d6d 1321static bool bfd_shop_hash_cmp(const void *n1, const void *n2)
e9e2c950
RZ
1322{
1323 const struct bfd_session *bs1 = n1, *bs2 = n2;
1324
1325 return memcmp(&bs1->shop, &bs2->shop, sizeof(bs1->shop)) == 0;
1326}
1327
1328/* BFD hash for multi hop. */
1329static unsigned int bfd_mhop_hash_do(void *p)
1330{
1331 struct bfd_session *bs = p;
1332
1333 return jhash(&bs->mhop, sizeof(bs->mhop), 0);
1334}
1335
74df8d6d 1336static bool bfd_mhop_hash_cmp(const void *n1, const void *n2)
e9e2c950
RZ
1337{
1338 const struct bfd_session *bs1 = n1, *bs2 = n2;
1339
1340 return memcmp(&bs1->mhop, &bs2->mhop, sizeof(bs1->mhop)) == 0;
1341}
1342
e9e2c950
RZ
1343/* Helper functions */
1344static void _shop_key(struct bfd_session *bs, const struct bfd_shop_key *shop)
1345{
1346 bs->shop = *shop;
1347
1348 /* Remove unused fields. */
1349 switch (bs->shop.peer.sa_sin.sin_family) {
1350 case AF_INET:
1351 bs->shop.peer.sa_sin.sin_port = 0;
1352 break;
1353 case AF_INET6:
1354 bs->shop.peer.sa_sin6.sin6_port = 0;
1355 break;
1356 }
1357}
1358
1359static void _shop_key2(struct bfd_session *bs, const struct bfd_shop_key *shop)
1360{
1361 _shop_key(bs, shop);
b333abc2 1362 bs->shop.ifindex = IFINDEX_INTERNAL;
e9e2c950
RZ
1363}
1364
1365static void _mhop_key(struct bfd_session *bs, const struct bfd_mhop_key *mhop)
1366{
1367 bs->mhop = *mhop;
1368
1369 /* Remove unused fields. */
1370 switch (bs->mhop.peer.sa_sin.sin_family) {
1371 case AF_INET:
1372 bs->mhop.peer.sa_sin.sin_port = 0;
1373 bs->mhop.local.sa_sin.sin_port = 0;
1374 break;
1375 case AF_INET6:
1376 bs->mhop.peer.sa_sin6.sin6_port = 0;
1377 bs->mhop.local.sa_sin6.sin6_port = 0;
1378 break;
1379 }
1380}
1381
e9e2c950
RZ
1382/*
1383 * Hash public interface / exported functions.
1384 */
1385
1386/* Lookup functions. */
1387struct bfd_session *bfd_id_lookup(uint32_t id)
1388{
1389 struct bfd_session bs;
1390
1391 bs.discrs.my_discr = id;
1392
1393 return hash_lookup(bfd_id_hash, &bs);
1394}
1395
1396struct bfd_session *bfd_shop_lookup(struct bfd_shop_key shop)
1397{
1398 struct bfd_session bs, *bsp;
1399
1400 _shop_key(&bs, &shop);
1401
1402 bsp = hash_lookup(bfd_shop_hash, &bs);
b333abc2 1403 if (bsp == NULL && bs.shop.ifindex != 0) {
e9e2c950
RZ
1404 /*
1405 * Since the local interface spec is optional, try
1406 * searching the key without it as well.
1407 */
1408 _shop_key2(&bs, &shop);
1409 bsp = hash_lookup(bfd_shop_hash, &bs);
1410 }
1411
1412 return bsp;
1413}
1414
1415struct bfd_session *bfd_mhop_lookup(struct bfd_mhop_key mhop)
1416{
1417 struct bfd_session bs;
1418
1419 _mhop_key(&bs, &mhop);
1420
2055ea09 1421 return hash_lookup(bfd_mhop_hash, &bs);
e9e2c950
RZ
1422}
1423
e9e2c950
RZ
1424/*
1425 * Delete functions.
1426 *
1427 * Delete functions searches and remove the item from the hash and
1428 * returns a pointer to the removed item data. If the item was not found
1429 * then it returns NULL.
1430 *
1431 * The data stored inside the hash is not free()ed, so you must do it
1432 * manually after getting the pointer back.
1433 */
1434struct bfd_session *bfd_id_delete(uint32_t id)
1435{
1436 struct bfd_session bs;
1437
1438 bs.discrs.my_discr = id;
1439
1440 return hash_release(bfd_id_hash, &bs);
1441}
1442
1443struct bfd_session *bfd_shop_delete(struct bfd_shop_key shop)
1444{
1445 struct bfd_session bs, *bsp;
1446
1447 _shop_key(&bs, &shop);
1448 bsp = hash_release(bfd_shop_hash, &bs);
b333abc2 1449 if (bsp == NULL && shop.ifindex != 0) {
e9e2c950
RZ
1450 /*
1451 * Since the local interface spec is optional, try
1452 * searching the key without it as well.
1453 */
1454 _shop_key2(&bs, &shop);
1455 bsp = hash_release(bfd_shop_hash, &bs);
1456 }
1457
1458 return bsp;
1459}
1460
1461struct bfd_session *bfd_mhop_delete(struct bfd_mhop_key mhop)
1462{
1463 struct bfd_session bs;
1464
1465 _mhop_key(&bs, &mhop);
1466
1467 return hash_release(bfd_mhop_hash, &bs);
1468}
1469
e9e2c950
RZ
1470/* Iteration functions. */
1471void bfd_id_iterate(hash_iter_func hif, void *arg)
1472{
1473 hash_iterate(bfd_id_hash, hif, arg);
1474}
1475
1476void bfd_shop_iterate(hash_iter_func hif, void *arg)
1477{
1478 hash_iterate(bfd_shop_hash, hif, arg);
1479}
1480
1481void bfd_mhop_iterate(hash_iter_func hif, void *arg)
1482{
1483 hash_iterate(bfd_mhop_hash, hif, arg);
1484}
1485
e9e2c950
RZ
1486/*
1487 * Insert functions.
1488 *
1489 * Inserts session into hash and returns `true` on success, otherwise
1490 * `false`.
1491 */
1492bool bfd_id_insert(struct bfd_session *bs)
1493{
1494 return (hash_get(bfd_id_hash, bs, hash_alloc_intern) == bs);
1495}
1496
1497bool bfd_shop_insert(struct bfd_session *bs)
1498{
1499 return (hash_get(bfd_shop_hash, bs, hash_alloc_intern) == bs);
1500}
1501
1502bool bfd_mhop_insert(struct bfd_session *bs)
1503{
1504 return (hash_get(bfd_mhop_hash, bs, hash_alloc_intern) == bs);
1505}
1506
e9e2c950
RZ
1507void bfd_initialize(void)
1508{
1509 bfd_id_hash = hash_create(bfd_id_hash_do, bfd_id_hash_cmp,
1510 "BFD discriminator hash");
1511 bfd_shop_hash = hash_create(bfd_shop_hash_do, bfd_shop_hash_cmp,
1512 "BFD single hop hash");
1513 bfd_mhop_hash = hash_create(bfd_mhop_hash_do, bfd_mhop_hash_cmp,
1514 "BFD multihop hop hash");
e9e2c950
RZ
1515}
1516
e3b78da8 1517static void _bfd_free(struct hash_bucket *hb,
e9e2c950
RZ
1518 void *arg __attribute__((__unused__)))
1519{
1520 struct bfd_session *bs = hb->data;
1521
1522 bfd_session_free(bs);
1523}
1524
e9e2c950
RZ
1525void bfd_shutdown(void)
1526{
1527 /*
1528 * Close and free all BFD sessions.
1529 *
1530 * _bfd_free() will call bfd_session_free() which will take care
1531 * of removing the session from all hashes, so we just run an
1532 * assert() here to make sure it really happened.
1533 */
1534 bfd_id_iterate(_bfd_free, NULL);
1535 assert(bfd_shop_hash->count == 0);
1536 assert(bfd_mhop_hash->count == 0);
1537
e9e2c950
RZ
1538 /* Now free the hashes themselves. */
1539 hash_free(bfd_id_hash);
1540 hash_free(bfd_shop_hash);
1541 hash_free(bfd_mhop_hash);
e9e2c950 1542}