]> git.proxmox.com Git - mirror_frr.git/blob - bfdd/bfd.c
Merge pull request #5134 from sudhanshukumar22/bgp-snmp-issue
[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_xmttimer_delete(bs);
217 ptm_bfd_echo_stop(bs);
218 bs->vrf = NULL;
219 bs->ifp = NULL;
220
221 /* Set session down so it doesn't report UP and disabled. */
222 ptm_bfd_sess_dn(bs, BD_PATH_DOWN);
223 }
224
225 static uint32_t ptm_bfd_gen_ID(void)
226 {
227 uint32_t session_id;
228
229 /*
230 * RFC 5880, Section 6.8.1. recommends that we should generate
231 * random session identification numbers.
232 */
233 do {
234 session_id = ((random() << 16) & 0xFFFF0000)
235 | (random() & 0x0000FFFF);
236 } while (session_id == 0 || bfd_id_lookup(session_id) != NULL);
237
238 return session_id;
239 }
240
241 void ptm_bfd_start_xmt_timer(struct bfd_session *bfd, bool is_echo)
242 {
243 uint64_t jitter, xmt_TO;
244 int maxpercent;
245
246 xmt_TO = is_echo ? bfd->echo_xmt_TO : bfd->xmt_TO;
247
248 /*
249 * From section 6.5.2: trasmit interval should be randomly jittered
250 * between
251 * 75% and 100% of nominal value, unless detect_mult is 1, then should
252 * be
253 * between 75% and 90%.
254 */
255 maxpercent = (bfd->detect_mult == 1) ? 16 : 26;
256 jitter = (xmt_TO * (75 + (random() % maxpercent))) / 100;
257 /* XXX remove that division above */
258
259 if (is_echo)
260 bfd_echo_xmttimer_update(bfd, jitter);
261 else
262 bfd_xmttimer_update(bfd, jitter);
263 }
264
265 static void ptm_bfd_echo_xmt_TO(struct bfd_session *bfd)
266 {
267 /* Send the scheduled echo packet */
268 ptm_bfd_echo_snd(bfd);
269
270 /* Restart the timer for next time */
271 ptm_bfd_start_xmt_timer(bfd, true);
272 }
273
274 void ptm_bfd_xmt_TO(struct bfd_session *bfd, int fbit)
275 {
276 /* Send the scheduled control packet */
277 ptm_bfd_snd(bfd, fbit);
278
279 /* Restart the timer for next time */
280 ptm_bfd_start_xmt_timer(bfd, false);
281 }
282
283 void ptm_bfd_echo_stop(struct bfd_session *bfd)
284 {
285 bfd->echo_xmt_TO = 0;
286 bfd->echo_detect_TO = 0;
287 BFD_UNSET_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE);
288
289 bfd_echo_xmttimer_delete(bfd);
290 bfd_echo_recvtimer_delete(bfd);
291 }
292
293 void ptm_bfd_echo_start(struct bfd_session *bfd)
294 {
295 bfd->echo_detect_TO = (bfd->remote_detect_mult * bfd->echo_xmt_TO);
296 if (bfd->echo_detect_TO > 0)
297 ptm_bfd_echo_xmt_TO(bfd);
298 }
299
300 void ptm_bfd_sess_up(struct bfd_session *bfd)
301 {
302 int old_state = bfd->ses_state;
303
304 bfd->local_diag = 0;
305 bfd->ses_state = PTM_BFD_UP;
306 monotime(&bfd->uptime);
307
308 /* Connection is up, lets negotiate timers. */
309 bfd_set_polling(bfd);
310
311 /* Start sending control packets with poll bit immediately. */
312 ptm_bfd_snd(bfd, 0);
313
314 control_notify(bfd);
315
316 if (old_state != bfd->ses_state) {
317 bfd->stats.session_up++;
318 log_info("state-change: [%s] %s -> %s", bs_to_string(bfd),
319 state_list[old_state].str,
320 state_list[bfd->ses_state].str);
321 }
322 }
323
324 void ptm_bfd_sess_dn(struct bfd_session *bfd, uint8_t diag)
325 {
326 int old_state = bfd->ses_state;
327
328 bfd->local_diag = diag;
329 bfd->discrs.remote_discr = 0;
330 bfd->ses_state = PTM_BFD_DOWN;
331 bfd->polling = 0;
332 bfd->demand_mode = 0;
333 monotime(&bfd->downtime);
334
335 /*
336 * Only attempt to send if we have a valid socket:
337 * this function might be called by session disablers and in
338 * this case we won't have a valid socket (i.e. interface was
339 * removed or VRF doesn't exist anymore).
340 */
341 if (bfd->sock != -1)
342 ptm_bfd_snd(bfd, 0);
343
344 /* Slow down the control packets, the connection is down. */
345 bs_set_slow_timers(bfd);
346
347 /* only signal clients when going from up->down state */
348 if (old_state == PTM_BFD_UP)
349 control_notify(bfd);
350
351 /* Stop echo packet transmission if they are active */
352 if (BFD_CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE))
353 ptm_bfd_echo_stop(bfd);
354
355 if (old_state != bfd->ses_state) {
356 bfd->stats.session_down++;
357 log_info("state-change: [%s] %s -> %s reason:%s",
358 bs_to_string(bfd), state_list[old_state].str,
359 state_list[bfd->ses_state].str,
360 get_diag_str(bfd->local_diag));
361 }
362 }
363
364 static struct bfd_session *bfd_find_disc(struct sockaddr_any *sa,
365 uint32_t ldisc)
366 {
367 struct bfd_session *bs;
368
369 bs = bfd_id_lookup(ldisc);
370 if (bs == NULL)
371 return NULL;
372
373 switch (bs->key.family) {
374 case AF_INET:
375 if (memcmp(&sa->sa_sin.sin_addr, &bs->key.peer,
376 sizeof(sa->sa_sin.sin_addr)))
377 return NULL;
378 break;
379 case AF_INET6:
380 if (memcmp(&sa->sa_sin6.sin6_addr, &bs->key.peer,
381 sizeof(sa->sa_sin6.sin6_addr)))
382 return NULL;
383 break;
384 }
385
386 return bs;
387 }
388
389 struct bfd_session *ptm_bfd_sess_find(struct bfd_pkt *cp,
390 struct sockaddr_any *peer,
391 struct sockaddr_any *local,
392 ifindex_t ifindex, vrf_id_t vrfid,
393 bool is_mhop)
394 {
395 struct interface *ifp;
396 struct vrf *vrf;
397 struct bfd_key key;
398
399 /* Find our session using the ID signaled by the remote end. */
400 if (cp->discrs.remote_discr)
401 return bfd_find_disc(peer, ntohl(cp->discrs.remote_discr));
402
403 /* Search for session without using discriminator. */
404 ifp = if_lookup_by_index(ifindex, vrfid);
405
406 vrf = vrf_lookup_by_id(vrfid);
407
408 gen_bfd_key(&key, peer, local, is_mhop, ifp ? ifp->name : NULL,
409 vrf ? vrf->name : VRF_DEFAULT_NAME);
410
411 /* XXX maybe remoteDiscr should be checked for remoteHeard cases. */
412 return bfd_key_lookup(key);
413 }
414
415 int bfd_xmt_cb(struct thread *t)
416 {
417 struct bfd_session *bs = THREAD_ARG(t);
418
419 ptm_bfd_xmt_TO(bs, 0);
420
421 return 0;
422 }
423
424 int bfd_echo_xmt_cb(struct thread *t)
425 {
426 struct bfd_session *bs = THREAD_ARG(t);
427
428 if (bs->echo_xmt_TO > 0)
429 ptm_bfd_echo_xmt_TO(bs);
430
431 return 0;
432 }
433
434 /* Was ptm_bfd_detect_TO() */
435 int bfd_recvtimer_cb(struct thread *t)
436 {
437 struct bfd_session *bs = THREAD_ARG(t);
438
439 switch (bs->ses_state) {
440 case PTM_BFD_INIT:
441 case PTM_BFD_UP:
442 ptm_bfd_sess_dn(bs, BD_CONTROL_EXPIRED);
443 bfd_recvtimer_update(bs);
444 break;
445
446 default:
447 /* Second detect time expiration, zero remote discr (section
448 * 6.5.1)
449 */
450 bs->discrs.remote_discr = 0;
451 break;
452 }
453
454 return 0;
455 }
456
457 /* Was ptm_bfd_echo_detect_TO() */
458 int bfd_echo_recvtimer_cb(struct thread *t)
459 {
460 struct bfd_session *bs = THREAD_ARG(t);
461
462 switch (bs->ses_state) {
463 case PTM_BFD_INIT:
464 case PTM_BFD_UP:
465 ptm_bfd_sess_dn(bs, BD_ECHO_FAILED);
466 break;
467 }
468
469 return 0;
470 }
471
472 struct bfd_session *bfd_session_new(void)
473 {
474 struct bfd_session *bs;
475
476 bs = XCALLOC(MTYPE_BFDD_CONFIG, sizeof(*bs));
477
478 bs->timers.desired_min_tx = BFD_DEFDESIREDMINTX;
479 bs->timers.required_min_rx = BFD_DEFREQUIREDMINRX;
480 bs->timers.required_min_echo = BFD_DEF_REQ_MIN_ECHO;
481 bs->detect_mult = BFD_DEFDETECTMULT;
482 bs->mh_ttl = BFD_DEF_MHOP_TTL;
483 bs->ses_state = PTM_BFD_DOWN;
484
485 /* Initiate connection with slow timers. */
486 bs_set_slow_timers(bs);
487
488 /* Initiate remote settings as well. */
489 bs->remote_timers = bs->cur_timers;
490 bs->remote_detect_mult = BFD_DEFDETECTMULT;
491
492 bs->sock = -1;
493 monotime(&bs->uptime);
494 bs->downtime = bs->uptime;
495
496 return bs;
497 }
498
499 int bfd_session_update_label(struct bfd_session *bs, const char *nlabel)
500 {
501 /* New label treatment:
502 * - Check if the label is taken;
503 * - Try to allocate the memory for it and register;
504 */
505 if (bs->pl == NULL) {
506 if (pl_find(nlabel) != NULL) {
507 /* Someone is already using it. */
508 return -1;
509 }
510
511 if (pl_new(nlabel, bs) == NULL)
512 return -1;
513
514 return 0;
515 }
516
517 /*
518 * Test label change consistency:
519 * - Do nothing if it's the same label;
520 * - Check if the future label is already taken;
521 * - Change label;
522 */
523 if (strcmp(nlabel, bs->pl->pl_label) == 0)
524 return -1;
525 if (pl_find(nlabel) != NULL)
526 return -1;
527
528 strlcpy(bs->pl->pl_label, nlabel, sizeof(bs->pl->pl_label));
529 return 0;
530 }
531
532 static void _bfd_session_update(struct bfd_session *bs,
533 struct bfd_peer_cfg *bpc)
534 {
535 if (bpc->bpc_echo) {
536 /* Check if echo mode is already active. */
537 if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO))
538 goto skip_echo;
539
540 BFD_SET_FLAG(bs->flags, BFD_SESS_FLAG_ECHO);
541
542 /* Activate/update echo receive timeout timer. */
543 bs_echo_timer_handler(bs);
544 } else {
545 /* Check if echo mode is already disabled. */
546 if (!BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO))
547 goto skip_echo;
548
549 BFD_UNSET_FLAG(bs->flags, BFD_SESS_FLAG_ECHO);
550 ptm_bfd_echo_stop(bs);
551 }
552
553 skip_echo:
554 if (bpc->bpc_has_txinterval)
555 bs->timers.desired_min_tx = bpc->bpc_txinterval * 1000;
556
557 if (bpc->bpc_has_recvinterval)
558 bs->timers.required_min_rx = bpc->bpc_recvinterval * 1000;
559
560 if (bpc->bpc_has_detectmultiplier)
561 bs->detect_mult = bpc->bpc_detectmultiplier;
562
563 if (bpc->bpc_has_echointerval)
564 bs->timers.required_min_echo = bpc->bpc_echointerval * 1000;
565
566 if (bpc->bpc_has_label)
567 bfd_session_update_label(bs, bpc->bpc_label);
568
569 if (bpc->bpc_shutdown) {
570 /* Check if already shutdown. */
571 if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN))
572 return;
573
574 BFD_SET_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN);
575
576 /* Disable all events. */
577 bfd_recvtimer_delete(bs);
578 bfd_echo_recvtimer_delete(bs);
579 bfd_xmttimer_delete(bs);
580 bfd_echo_xmttimer_delete(bs);
581
582 /* Change and notify state change. */
583 bs->ses_state = PTM_BFD_ADM_DOWN;
584 control_notify(bs);
585
586 /* Don't try to send packets with a disabled session. */
587 if (bs->sock != -1)
588 ptm_bfd_snd(bs, 0);
589 } else {
590 /* Check if already working. */
591 if (!BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN))
592 return;
593
594 BFD_UNSET_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN);
595
596 /* Change and notify state change. */
597 bs->ses_state = PTM_BFD_DOWN;
598 control_notify(bs);
599
600 /* Enable all timers. */
601 bfd_recvtimer_update(bs);
602 bfd_xmttimer_update(bs, bs->xmt_TO);
603 }
604 if (bpc->bpc_cbit) {
605 if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_CBIT))
606 return;
607
608 BFD_SET_FLAG(bs->flags, BFD_SESS_FLAG_CBIT);
609 } else {
610 if (!BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_CBIT))
611 return;
612
613 BFD_UNSET_FLAG(bs->flags, BFD_SESS_FLAG_CBIT);
614 }
615 }
616
617 static int bfd_session_update(struct bfd_session *bs, struct bfd_peer_cfg *bpc)
618 {
619 /* User didn't want to update, return failure. */
620 if (bpc->bpc_createonly)
621 return -1;
622
623 _bfd_session_update(bs, bpc);
624
625 control_notify_config(BCM_NOTIFY_CONFIG_UPDATE, bs);
626
627 return 0;
628 }
629
630 void bfd_session_free(struct bfd_session *bs)
631 {
632 struct bfd_session_observer *bso;
633
634 bfd_session_disable(bs);
635
636 bfd_key_delete(bs->key);
637 bfd_id_delete(bs->discrs.my_discr);
638
639 /* Remove observer if any. */
640 TAILQ_FOREACH(bso, &bglobal.bg_obslist, bso_entry) {
641 if (bso->bso_bs != bs)
642 continue;
643
644 break;
645 }
646 if (bso != NULL)
647 bs_observer_del(bso);
648
649 pl_free(bs->pl);
650
651 XFREE(MTYPE_BFDD_CONFIG, bs);
652 }
653
654 struct bfd_session *ptm_bfd_sess_new(struct bfd_peer_cfg *bpc)
655 {
656 struct bfd_session *bfd, *l_bfd;
657
658 /* check to see if this needs a new session */
659 l_bfd = bs_peer_find(bpc);
660 if (l_bfd) {
661 /* Requesting a duplicated peer means update configuration. */
662 if (bfd_session_update(l_bfd, bpc) == 0)
663 return l_bfd;
664 else
665 return NULL;
666 }
667
668 /* Get BFD session storage with its defaults. */
669 bfd = bfd_session_new();
670 if (bfd == NULL) {
671 log_error("session-new: allocation failed");
672 return NULL;
673 }
674
675 /*
676 * Store interface/VRF name in case we need to delay session
677 * start. See `bfd_session_enable` for more information.
678 */
679 if (bpc->bpc_has_localif)
680 strlcpy(bfd->key.ifname, bpc->bpc_localif,
681 sizeof(bfd->key.ifname));
682
683 if (bpc->bpc_has_vrfname)
684 strlcpy(bfd->key.vrfname, bpc->bpc_vrfname,
685 sizeof(bfd->key.vrfname));
686 else
687 strlcpy(bfd->key.vrfname, VRF_DEFAULT_NAME,
688 sizeof(bfd->key.vrfname));
689
690 /* Copy remaining data. */
691 if (bpc->bpc_ipv4 == false)
692 BFD_SET_FLAG(bfd->flags, BFD_SESS_FLAG_IPV6);
693
694 bfd->key.family = (bpc->bpc_ipv4) ? AF_INET : AF_INET6;
695 switch (bfd->key.family) {
696 case AF_INET:
697 memcpy(&bfd->key.peer, &bpc->bpc_peer.sa_sin.sin_addr,
698 sizeof(bpc->bpc_peer.sa_sin.sin_addr));
699 memcpy(&bfd->key.local, &bpc->bpc_local.sa_sin.sin_addr,
700 sizeof(bpc->bpc_local.sa_sin.sin_addr));
701 break;
702
703 case AF_INET6:
704 memcpy(&bfd->key.peer, &bpc->bpc_peer.sa_sin6.sin6_addr,
705 sizeof(bpc->bpc_peer.sa_sin6.sin6_addr));
706 memcpy(&bfd->key.local, &bpc->bpc_local.sa_sin6.sin6_addr,
707 sizeof(bpc->bpc_local.sa_sin6.sin6_addr));
708 break;
709
710 default:
711 assert(1);
712 break;
713 }
714
715 if (bpc->bpc_mhop)
716 BFD_SET_FLAG(bfd->flags, BFD_SESS_FLAG_MH);
717
718 bfd->key.mhop = bpc->bpc_mhop;
719
720 if (bs_registrate(bfd) == NULL)
721 return NULL;
722
723 /* Apply other configurations. */
724 _bfd_session_update(bfd, bpc);
725
726 return bfd;
727 }
728
729 struct bfd_session *bs_registrate(struct bfd_session *bfd)
730 {
731 /* Registrate session into data structures. */
732 bfd_key_insert(bfd);
733 bfd->discrs.my_discr = ptm_bfd_gen_ID();
734 bfd_id_insert(bfd);
735
736 /* Try to enable session and schedule for packet receive/send. */
737 if (bfd_session_enable(bfd) == -1) {
738 /* Unrecoverable failure, remove the session/peer. */
739 bfd_session_free(bfd);
740 return NULL;
741 }
742
743 /* Add observer if we have moving parts. */
744 if (bfd->key.ifname[0] || bfd->key.vrfname[0] || bfd->sock == -1)
745 bs_observer_add(bfd);
746
747 log_info("session-new: %s", bs_to_string(bfd));
748
749 control_notify_config(BCM_NOTIFY_CONFIG_ADD, bfd);
750
751 return bfd;
752 }
753
754 int ptm_bfd_sess_del(struct bfd_peer_cfg *bpc)
755 {
756 struct bfd_session *bs;
757
758 /* Find session and call free(). */
759 bs = bs_peer_find(bpc);
760 if (bs == NULL)
761 return -1;
762
763 /* This pointer is being referenced, don't let it be deleted. */
764 if (bs->refcount > 0) {
765 log_error("session-delete: refcount failure: %" PRIu64
766 " references",
767 bs->refcount);
768 return -1;
769 }
770
771 log_info("session-delete: %s", bs_to_string(bs));
772
773 control_notify_config(BCM_NOTIFY_CONFIG_DELETE, bs);
774
775 bfd_session_free(bs);
776
777 return 0;
778 }
779
780 void bfd_set_polling(struct bfd_session *bs)
781 {
782 /*
783 * Start polling procedure: the only timers that require polling
784 * to change value without losing connection are:
785 *
786 * - Desired minimum transmission interval;
787 * - Required minimum receive interval;
788 *
789 * RFC 5880, Section 6.8.3.
790 */
791 bs->polling = 1;
792 }
793
794 /*
795 * bs_<state>_handler() functions implement the BFD state machine
796 * transition mechanism. `<state>` is the current session state and
797 * the parameter `nstate` is the peer new state.
798 */
799 static void bs_admin_down_handler(struct bfd_session *bs
800 __attribute__((__unused__)),
801 int nstate __attribute__((__unused__)))
802 {
803 /*
804 * We are administratively down, there is no state machine
805 * handling.
806 */
807 }
808
809 static void bs_down_handler(struct bfd_session *bs, int nstate)
810 {
811 switch (nstate) {
812 case PTM_BFD_ADM_DOWN:
813 /*
814 * Remote peer doesn't want to talk, so lets keep the
815 * connection down.
816 */
817 case PTM_BFD_UP:
818 /* Peer can't be up yet, wait it go to 'init' or 'down'. */
819 break;
820
821 case PTM_BFD_DOWN:
822 /*
823 * Remote peer agreed that the path is down, lets try to
824 * bring it up.
825 */
826 bs->ses_state = PTM_BFD_INIT;
827 break;
828
829 case PTM_BFD_INIT:
830 /*
831 * Remote peer told us his path is up, lets turn
832 * activate the session.
833 */
834 ptm_bfd_sess_up(bs);
835 break;
836
837 default:
838 log_debug("state-change: unhandled neighbor state: %d", nstate);
839 break;
840 }
841 }
842
843 static void bs_init_handler(struct bfd_session *bs, int nstate)
844 {
845 switch (nstate) {
846 case PTM_BFD_ADM_DOWN:
847 /*
848 * Remote peer doesn't want to talk, so lets make the
849 * connection down.
850 */
851 bs->ses_state = PTM_BFD_DOWN;
852 break;
853
854 case PTM_BFD_DOWN:
855 /* Remote peer hasn't moved to first stage yet. */
856 break;
857
858 case PTM_BFD_INIT:
859 case PTM_BFD_UP:
860 /* We agreed on the settings and the path is up. */
861 ptm_bfd_sess_up(bs);
862 break;
863
864 default:
865 log_debug("state-change: unhandled neighbor state: %d", nstate);
866 break;
867 }
868 }
869
870 static void bs_up_handler(struct bfd_session *bs, int nstate)
871 {
872 switch (nstate) {
873 case PTM_BFD_ADM_DOWN:
874 case PTM_BFD_DOWN:
875 /* Peer lost or asked to shutdown connection. */
876 ptm_bfd_sess_dn(bs, BD_NEIGHBOR_DOWN);
877 break;
878
879 case PTM_BFD_INIT:
880 case PTM_BFD_UP:
881 /* Path is up and working. */
882 break;
883
884 default:
885 log_debug("state-change: unhandled neighbor state: %d", nstate);
886 break;
887 }
888 }
889
890 void bs_state_handler(struct bfd_session *bs, int nstate)
891 {
892 switch (bs->ses_state) {
893 case PTM_BFD_ADM_DOWN:
894 bs_admin_down_handler(bs, nstate);
895 break;
896 case PTM_BFD_DOWN:
897 bs_down_handler(bs, nstate);
898 break;
899 case PTM_BFD_INIT:
900 bs_init_handler(bs, nstate);
901 break;
902 case PTM_BFD_UP:
903 bs_up_handler(bs, nstate);
904 break;
905
906 default:
907 log_debug("state-change: [%s] is in invalid state: %d",
908 bs_to_string(bs), nstate);
909 break;
910 }
911 }
912
913 /*
914 * Handles echo timer manipulation after updating timer.
915 */
916 void bs_echo_timer_handler(struct bfd_session *bs)
917 {
918 uint32_t old_timer;
919
920 /*
921 * Before doing any echo handling, check if it is possible to
922 * use it.
923 *
924 * - Check for `echo-mode` configuration.
925 * - Check that we are not using multi hop (RFC 5883,
926 * Section 3).
927 * - Check that we are already at the up state.
928 */
929 if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO) == 0
930 || BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)
931 || bs->ses_state != PTM_BFD_UP)
932 return;
933
934 /* Remote peer asked to stop echo. */
935 if (bs->remote_timers.required_min_echo == 0) {
936 if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO_ACTIVE))
937 ptm_bfd_echo_stop(bs);
938
939 return;
940 }
941
942 /*
943 * Calculate the echo transmission timer: we must not send
944 * echo packets faster than the minimum required time
945 * announced by the remote system.
946 *
947 * RFC 5880, Section 6.8.9.
948 */
949 old_timer = bs->echo_xmt_TO;
950 if (bs->remote_timers.required_min_echo > bs->timers.required_min_echo)
951 bs->echo_xmt_TO = bs->remote_timers.required_min_echo;
952 else
953 bs->echo_xmt_TO = bs->timers.required_min_echo;
954
955 if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO_ACTIVE) == 0
956 || old_timer != bs->echo_xmt_TO)
957 ptm_bfd_echo_start(bs);
958 }
959
960 /*
961 * RFC 5880 Section 6.5.
962 *
963 * When a BFD control packet with the final bit is received, we must
964 * update the session parameters.
965 */
966 void bs_final_handler(struct bfd_session *bs)
967 {
968 /* Start using our new timers. */
969 bs->cur_timers.desired_min_tx = bs->timers.desired_min_tx;
970 bs->cur_timers.required_min_rx = bs->timers.required_min_rx;
971
972 /*
973 * TODO: demand mode. See RFC 5880 Section 6.1.
974 *
975 * When using demand mode we must disable the detection timer
976 * for lost control packets.
977 */
978 if (bs->demand_mode) {
979 /* Notify watchers about changed timers. */
980 control_notify_config(BCM_NOTIFY_CONFIG_UPDATE, bs);
981 return;
982 }
983
984 /*
985 * Calculate detection time based on new timers.
986 *
987 * Transmission calculation:
988 * We must respect the RequiredMinRxInterval from the remote
989 * system: if our desired transmission timer is more than the
990 * minimum receive rate, then we must lower it to at least the
991 * minimum receive interval.
992 *
993 * RFC 5880, Section 6.8.3.
994 */
995 if (bs->timers.desired_min_tx > bs->remote_timers.required_min_rx)
996 bs->xmt_TO = bs->remote_timers.required_min_rx;
997 else
998 bs->xmt_TO = bs->timers.desired_min_tx;
999
1000 /* Apply new transmission timer immediately. */
1001 ptm_bfd_start_xmt_timer(bs, false);
1002
1003 /*
1004 * Detection timeout calculation:
1005 * The minimum detection timeout is the remote detection
1006 * multipler (number of packets to be missed) times the agreed
1007 * transmission interval.
1008 *
1009 * RFC 5880, Section 6.8.4.
1010 *
1011 * TODO: support sending/counting more packets inside detection
1012 * timeout.
1013 */
1014 if (bs->remote_timers.required_min_rx > bs->timers.desired_min_tx)
1015 bs->detect_TO = bs->remote_detect_mult
1016 * bs->remote_timers.required_min_rx;
1017 else
1018 bs->detect_TO = bs->remote_detect_mult
1019 * bs->timers.desired_min_tx;
1020
1021 /* Apply new receive timer immediately. */
1022 bfd_recvtimer_update(bs);
1023
1024 /* Notify watchers about changed timers. */
1025 control_notify_config(BCM_NOTIFY_CONFIG_UPDATE, bs);
1026 }
1027
1028 void bs_set_slow_timers(struct bfd_session *bs)
1029 {
1030 /*
1031 * BFD connection must use slow timers before going up or after
1032 * losing connectivity to avoid wasting bandwidth.
1033 *
1034 * RFC 5880, Section 6.8.3.
1035 */
1036 bs->cur_timers.desired_min_tx = BFD_DEF_SLOWTX;
1037 bs->cur_timers.required_min_rx = BFD_DEF_SLOWTX;
1038 bs->cur_timers.required_min_echo = 0;
1039
1040 /* Set the appropriated timeouts for slow connection. */
1041 bs->detect_TO = (BFD_DEFDETECTMULT * BFD_DEF_SLOWTX);
1042 bs->xmt_TO = BFD_DEF_SLOWTX;
1043 }
1044
1045 /*
1046 * Helper functions.
1047 */
1048 static const char *get_diag_str(int diag)
1049 {
1050 for (int i = 0; diag_list[i].str; i++) {
1051 if (diag_list[i].type == diag)
1052 return diag_list[i].str;
1053 }
1054 return "N/A";
1055 }
1056
1057 const char *satostr(struct sockaddr_any *sa)
1058 {
1059 #define INETSTR_BUFCOUNT 8
1060 static char buf[INETSTR_BUFCOUNT][INET6_ADDRSTRLEN];
1061 static int bufidx;
1062 struct sockaddr_in *sin = &sa->sa_sin;
1063 struct sockaddr_in6 *sin6 = &sa->sa_sin6;
1064
1065 bufidx += (bufidx + 1) % INETSTR_BUFCOUNT;
1066 buf[bufidx][0] = 0;
1067
1068 switch (sin->sin_family) {
1069 case AF_INET:
1070 inet_ntop(AF_INET, &sin->sin_addr, buf[bufidx],
1071 sizeof(buf[bufidx]));
1072 break;
1073 case AF_INET6:
1074 inet_ntop(AF_INET6, &sin6->sin6_addr, buf[bufidx],
1075 sizeof(buf[bufidx]));
1076 break;
1077
1078 default:
1079 strlcpy(buf[bufidx], "unknown", sizeof(buf[bufidx]));
1080 break;
1081 }
1082
1083 return buf[bufidx];
1084 }
1085
1086 const char *diag2str(uint8_t diag)
1087 {
1088 switch (diag) {
1089 case 0:
1090 return "ok";
1091 case 1:
1092 return "control detection time expired";
1093 case 2:
1094 return "echo function failed";
1095 case 3:
1096 return "neighbor signaled session down";
1097 case 4:
1098 return "forwarding plane reset";
1099 case 5:
1100 return "path down";
1101 case 6:
1102 return "concatenated path down";
1103 case 7:
1104 return "administratively down";
1105 case 8:
1106 return "reverse concatenated path down";
1107 default:
1108 return "unknown";
1109 }
1110 }
1111
1112 int strtosa(const char *addr, struct sockaddr_any *sa)
1113 {
1114 memset(sa, 0, sizeof(*sa));
1115
1116 if (inet_pton(AF_INET, addr, &sa->sa_sin.sin_addr) == 1) {
1117 sa->sa_sin.sin_family = AF_INET;
1118 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
1119 sa->sa_sin.sin_len = sizeof(sa->sa_sin);
1120 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
1121 return 0;
1122 }
1123
1124 if (inet_pton(AF_INET6, addr, &sa->sa_sin6.sin6_addr) == 1) {
1125 sa->sa_sin6.sin6_family = AF_INET6;
1126 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
1127 sa->sa_sin6.sin6_len = sizeof(sa->sa_sin6);
1128 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
1129 return 0;
1130 }
1131
1132 return -1;
1133 }
1134
1135 void integer2timestr(uint64_t time, char *buf, size_t buflen)
1136 {
1137 unsigned int year, month, day, hour, minute, second;
1138 int rv;
1139
1140 #define MINUTES (60)
1141 #define HOURS (60 * MINUTES)
1142 #define DAYS (24 * HOURS)
1143 #define MONTHS (30 * DAYS)
1144 #define YEARS (12 * MONTHS)
1145 if (time >= YEARS) {
1146 year = time / YEARS;
1147 time -= year * YEARS;
1148
1149 rv = snprintf(buf, buflen, "%u year(s), ", year);
1150 buf += rv;
1151 buflen -= rv;
1152 }
1153 if (time >= MONTHS) {
1154 month = time / MONTHS;
1155 time -= month * MONTHS;
1156
1157 rv = snprintf(buf, buflen, "%u month(s), ", month);
1158 buf += rv;
1159 buflen -= rv;
1160 }
1161 if (time >= DAYS) {
1162 day = time / DAYS;
1163 time -= day * DAYS;
1164
1165 rv = snprintf(buf, buflen, "%u day(s), ", day);
1166 buf += rv;
1167 buflen -= rv;
1168 }
1169 if (time >= HOURS) {
1170 hour = time / HOURS;
1171 time -= hour * HOURS;
1172
1173 rv = snprintf(buf, buflen, "%u hour(s), ", hour);
1174 buf += rv;
1175 buflen -= rv;
1176 }
1177 if (time >= MINUTES) {
1178 minute = time / MINUTES;
1179 time -= minute * MINUTES;
1180
1181 rv = snprintf(buf, buflen, "%u minute(s), ", minute);
1182 buf += rv;
1183 buflen -= rv;
1184 }
1185 second = time % MINUTES;
1186 snprintf(buf, buflen, "%u second(s)", second);
1187 }
1188
1189 const char *bs_to_string(const struct bfd_session *bs)
1190 {
1191 static char buf[256];
1192 char addr_buf[INET6_ADDRSTRLEN];
1193 int pos;
1194 bool is_mhop = BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH);
1195
1196 pos = snprintf(buf, sizeof(buf), "mhop:%s", is_mhop ? "yes" : "no");
1197 pos += snprintf(buf + pos, sizeof(buf) - pos, " peer:%s",
1198 inet_ntop(bs->key.family, &bs->key.peer, addr_buf,
1199 sizeof(addr_buf)));
1200 pos += snprintf(buf + pos, sizeof(buf) - pos, " local:%s",
1201 inet_ntop(bs->key.family, &bs->key.local, addr_buf,
1202 sizeof(addr_buf)));
1203 if (bs->key.vrfname[0])
1204 pos += snprintf(buf + pos, sizeof(buf) - pos, " vrf:%s",
1205 bs->key.vrfname);
1206 if (bs->key.ifname[0])
1207 pos += snprintf(buf + pos, sizeof(buf) - pos, " ifname:%s",
1208 bs->key.ifname);
1209
1210 (void)pos;
1211
1212 return buf;
1213 }
1214
1215 int bs_observer_add(struct bfd_session *bs)
1216 {
1217 struct bfd_session_observer *bso;
1218
1219 bso = XCALLOC(MTYPE_BFDD_SESSION_OBSERVER, sizeof(*bso));
1220 bso->bso_bs = bs;
1221 bso->bso_addr.family = bs->key.family;
1222 memcpy(&bso->bso_addr.u.prefix, &bs->key.local,
1223 sizeof(bs->key.local));
1224
1225 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", peer_buf);
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_update(struct vrf *vrf)
1636 {
1637 if (!vrf_is_enabled(vrf))
1638 return 0;
1639 log_debug("VRF update: %s(%u)", vrf->name, vrf->vrf_id);
1640 /* a different name is given; update bfd list */
1641 bfdd_sessions_enable_vrf(vrf);
1642 return 0;
1643 }
1644
1645 static int bfd_vrf_enable(struct vrf *vrf)
1646 {
1647 struct bfd_vrf_global *bvrf;
1648
1649 /* a different name */
1650 if (!vrf->info) {
1651 bvrf = XCALLOC(MTYPE_BFDD_VRF, sizeof(struct bfd_vrf_global));
1652 bvrf->vrf = vrf;
1653 vrf->info = (void *)bvrf;
1654 } else
1655 bvrf = vrf->info;
1656 log_debug("VRF enable add %s id %u", vrf->name, vrf->vrf_id);
1657 if (vrf->vrf_id == VRF_DEFAULT ||
1658 vrf_get_backend() == VRF_BACKEND_NETNS) {
1659 if (!bvrf->bg_shop)
1660 bvrf->bg_shop = bp_udp_shop(vrf->vrf_id);
1661 if (!bvrf->bg_mhop)
1662 bvrf->bg_mhop = bp_udp_mhop(vrf->vrf_id);
1663 if (!bvrf->bg_shop6)
1664 bvrf->bg_shop6 = bp_udp6_shop(vrf->vrf_id);
1665 if (!bvrf->bg_mhop6)
1666 bvrf->bg_mhop6 = bp_udp6_mhop(vrf->vrf_id);
1667 if (!bvrf->bg_echo)
1668 bvrf->bg_echo = bp_echo_socket(vrf->vrf_id);
1669 if (!bvrf->bg_echov6)
1670 bvrf->bg_echov6 = bp_echov6_socket(vrf->vrf_id);
1671
1672 /* Add descriptors to the event loop. */
1673 if (!bvrf->bg_ev[0])
1674 thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_shop,
1675 &bvrf->bg_ev[0]);
1676 if (!bvrf->bg_ev[1])
1677 thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_mhop,
1678 &bvrf->bg_ev[1]);
1679 if (!bvrf->bg_ev[2])
1680 thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_shop6,
1681 &bvrf->bg_ev[2]);
1682 if (!bvrf->bg_ev[3])
1683 thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_mhop6,
1684 &bvrf->bg_ev[3]);
1685 if (!bvrf->bg_ev[4])
1686 thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_echo,
1687 &bvrf->bg_ev[4]);
1688 if (!bvrf->bg_ev[5])
1689 thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_echov6,
1690 &bvrf->bg_ev[5]);
1691 }
1692 if (vrf->vrf_id != VRF_DEFAULT) {
1693 bfdd_zclient_register(vrf->vrf_id);
1694 bfdd_sessions_enable_vrf(vrf);
1695 }
1696 return 0;
1697 }
1698
1699 static int bfd_vrf_disable(struct vrf *vrf)
1700 {
1701 struct bfd_vrf_global *bvrf;
1702
1703 if (!vrf->info)
1704 return 0;
1705 bvrf = vrf->info;
1706
1707 if (vrf->vrf_id != VRF_DEFAULT) {
1708 bfdd_sessions_disable_vrf(vrf);
1709 bfdd_zclient_unregister(vrf->vrf_id);
1710 }
1711
1712 log_debug("VRF disable %s id %d", vrf->name, vrf->vrf_id);
1713
1714 /* Disable read/write poll triggering. */
1715 THREAD_OFF(bvrf->bg_ev[0]);
1716 THREAD_OFF(bvrf->bg_ev[1]);
1717 THREAD_OFF(bvrf->bg_ev[2]);
1718 THREAD_OFF(bvrf->bg_ev[3]);
1719 THREAD_OFF(bvrf->bg_ev[4]);
1720 THREAD_OFF(bvrf->bg_ev[5]);
1721
1722 /* Close all descriptors. */
1723 socket_close(&bvrf->bg_echo);
1724 socket_close(&bvrf->bg_shop);
1725 socket_close(&bvrf->bg_mhop);
1726 socket_close(&bvrf->bg_shop6);
1727 socket_close(&bvrf->bg_mhop6);
1728 socket_close(&bvrf->bg_echo);
1729 socket_close(&bvrf->bg_echov6);
1730
1731 /* free context */
1732 XFREE(MTYPE_BFDD_VRF, bvrf);
1733 vrf->info = NULL;
1734
1735 return 0;
1736 }
1737
1738 void bfd_vrf_init(void)
1739 {
1740 vrf_init(bfd_vrf_new, bfd_vrf_enable, bfd_vrf_disable,
1741 bfd_vrf_delete, bfd_vrf_update);
1742 }
1743
1744 void bfd_vrf_terminate(void)
1745 {
1746 vrf_terminate();
1747 }
1748
1749 struct bfd_vrf_global *bfd_vrf_look_by_session(struct bfd_session *bfd)
1750 {
1751 struct vrf *vrf;
1752
1753 if (!vrf_is_backend_netns()) {
1754 vrf = vrf_lookup_by_id(VRF_DEFAULT);
1755 if (vrf)
1756 return (struct bfd_vrf_global *)vrf->info;
1757 return NULL;
1758 }
1759 if (!bfd)
1760 return NULL;
1761 if (!bfd->vrf)
1762 return NULL;
1763 return bfd->vrf->info;
1764 }
1765
1766 void bfd_session_update_vrf_name(struct bfd_session *bs, struct vrf *vrf)
1767 {
1768 if (!vrf || !bs)
1769 return;
1770 /* update key */
1771 hash_release(bfd_key_hash, bs);
1772 /*
1773 * HACK: Change the BFD VRF in the running configuration directly,
1774 * bypassing the northbound layer. This is necessary to avoid deleting
1775 * the BFD and readding it in the new VRF, which would have
1776 * several implications.
1777 */
1778 if (yang_module_find("frr-bfdd") && bs->key.vrfname[0]) {
1779 struct lyd_node *bfd_dnode;
1780 char xpath[XPATH_MAXLEN], xpath_srcaddr[XPATH_MAXLEN + 32];
1781 char addr_buf[INET6_ADDRSTRLEN];
1782 int slen;
1783
1784 /* build xpath */
1785 if (bs->key.mhop) {
1786 inet_ntop(bs->key.family, &bs->key.local, addr_buf, sizeof(addr_buf));
1787 snprintf(xpath_srcaddr, sizeof(xpath_srcaddr), "[source-addr='%s']",
1788 addr_buf);
1789 } else
1790 xpath_srcaddr[0] = 0;
1791 inet_ntop(bs->key.family, &bs->key.peer, addr_buf, sizeof(addr_buf));
1792 slen = snprintf(xpath, sizeof(xpath),
1793 "/frr-bfdd:bfdd/bfd/sessions/%s%s[dest-addr='%s']",
1794 bs->key.mhop ? "multi-hop" : "single-hop", xpath_srcaddr,
1795 addr_buf);
1796 if (bs->key.ifname[0])
1797 slen += snprintf(xpath + slen, sizeof(xpath) - slen,
1798 "[interface='%s']", bs->key.ifname);
1799 else
1800 slen += snprintf(xpath + slen, sizeof(xpath) - slen,
1801 "[interface='']");
1802 snprintf(xpath + slen, sizeof(xpath) - slen, "[vrf='%s']/vrf",
1803 bs->key.vrfname);
1804
1805 bfd_dnode = yang_dnode_get(running_config->dnode, xpath,
1806 bs->key.vrfname);
1807 if (bfd_dnode) {
1808 yang_dnode_change_leaf(bfd_dnode, vrf->name);
1809 running_config->version++;
1810 }
1811 }
1812 memset(bs->key.vrfname, 0, sizeof(bs->key.vrfname));
1813 strlcpy(bs->key.vrfname, vrf->name, sizeof(bs->key.vrfname));
1814 hash_get(bfd_key_hash, bs, hash_alloc_intern);
1815 }