]> git.proxmox.com Git - mirror_frr.git/blob - bfdd/bfd.c
Merge pull request #7782 from kuldeepkash/multicast_pim_sm_topo2
[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 #include "lib/network.h"
32
33 #include "bfd.h"
34
35 DEFINE_MTYPE_STATIC(BFDD, BFDD_CONFIG, "long-lived configuration memory")
36 DEFINE_MTYPE_STATIC(BFDD, BFDD_PROFILE, "long-lived profile memory")
37 DEFINE_MTYPE_STATIC(BFDD, BFDD_SESSION_OBSERVER, "Session observer")
38 DEFINE_MTYPE_STATIC(BFDD, BFDD_VRF, "BFD VRF")
39
40 /*
41 * Prototypes
42 */
43 static uint32_t ptm_bfd_gen_ID(void);
44 static void ptm_bfd_echo_xmt_TO(struct bfd_session *bfd);
45 static struct bfd_session *bfd_find_disc(struct sockaddr_any *sa,
46 uint32_t ldisc);
47 static int bfd_session_update(struct bfd_session *bs, struct bfd_peer_cfg *bpc);
48 static const char *get_diag_str(int diag);
49
50 static void bs_admin_down_handler(struct bfd_session *bs, int nstate);
51 static void bs_down_handler(struct bfd_session *bs, int nstate);
52 static void bs_init_handler(struct bfd_session *bs, int nstate);
53 static void bs_up_handler(struct bfd_session *bs, int nstate);
54 static void bs_neighbour_admin_down_handler(struct bfd_session *bfd,
55 uint8_t diag);
56
57 /**
58 * Remove BFD profile from all BFD sessions so we don't leave dangling
59 * pointers.
60 */
61 static void bfd_profile_detach(struct bfd_profile *bp);
62
63 /* Zeroed array with the size of an IPv6 address. */
64 struct in6_addr zero_addr;
65
66 /** BFD profiles list. */
67 struct bfdproflist bplist;
68
69 /*
70 * Functions
71 */
72 struct bfd_profile *bfd_profile_lookup(const char *name)
73 {
74 struct bfd_profile *bp;
75
76 TAILQ_FOREACH (bp, &bplist, entry) {
77 if (strcmp(name, bp->name))
78 continue;
79
80 return bp;
81 }
82
83 return NULL;
84 }
85
86 static void bfd_profile_set_default(struct bfd_profile *bp)
87 {
88 bp->admin_shutdown = true;
89 bp->detection_multiplier = BFD_DEFDETECTMULT;
90 bp->echo_mode = false;
91 bp->passive = false;
92 bp->minimum_ttl = BFD_DEF_MHOP_TTL;
93 bp->min_echo_rx = BFD_DEF_REQ_MIN_ECHO;
94 bp->min_rx = BFD_DEFREQUIREDMINRX;
95 bp->min_tx = BFD_DEFDESIREDMINTX;
96 }
97
98 struct bfd_profile *bfd_profile_new(const char *name)
99 {
100 struct bfd_profile *bp;
101
102 /* Search for duplicates. */
103 if (bfd_profile_lookup(name) != NULL)
104 return NULL;
105
106 /* Allocate, name it and put into list. */
107 bp = XCALLOC(MTYPE_BFDD_PROFILE, sizeof(*bp));
108 strlcpy(bp->name, name, sizeof(bp->name));
109 TAILQ_INSERT_TAIL(&bplist, bp, entry);
110
111 /* Set default values. */
112 bfd_profile_set_default(bp);
113
114 return bp;
115 }
116
117 void bfd_profile_free(struct bfd_profile *bp)
118 {
119 /* Detach from any session. */
120 if (bglobal.bg_shutdown == false)
121 bfd_profile_detach(bp);
122
123 /* Remove from global list. */
124 TAILQ_REMOVE(&bplist, bp, entry);
125
126 XFREE(MTYPE_BFDD_PROFILE, bp);
127 }
128
129 void bfd_profile_apply(const char *profname, struct bfd_session *bs)
130 {
131 struct bfd_profile *bp;
132
133 /* Remove previous profile if any. */
134 if (bs->profile_name) {
135 /* We are changing profiles. */
136 if (strcmp(bs->profile_name, profname)) {
137 XFREE(MTYPE_BFDD_PROFILE, bs->profile_name);
138 bs->profile_name =
139 XSTRDUP(MTYPE_BFDD_PROFILE, profname);
140 }
141 } else /* Save the current profile name (in case it doesn't exist). */
142 bs->profile_name = XSTRDUP(MTYPE_BFDD_PROFILE, profname);
143
144 /* Look up new profile to apply. */
145 bp = bfd_profile_lookup(profname);
146
147 /* Point to profile if it exists. */
148 bs->profile = bp;
149
150 /* Apply configuration. */
151 bfd_session_apply(bs);
152 }
153
154 void bfd_session_apply(struct bfd_session *bs)
155 {
156 struct bfd_profile *bp;
157 uint32_t min_tx = bs->timers.desired_min_tx;
158 uint32_t min_rx = bs->timers.required_min_rx;
159
160 /* Pick the source of configuration. */
161 bp = bs->profile ? bs->profile : &bs->peer_profile;
162
163 /* Set multiplier if not the default. */
164 if (bs->peer_profile.detection_multiplier == BFD_DEFDETECTMULT)
165 bs->detect_mult = bp->detection_multiplier;
166 else
167 bs->detect_mult = bs->peer_profile.detection_multiplier;
168
169 /* Set timers if not the default. */
170 if (bs->peer_profile.min_tx == BFD_DEFDESIREDMINTX)
171 bs->timers.desired_min_tx = bp->min_tx;
172 else
173 bs->timers.desired_min_tx = bs->peer_profile.min_tx;
174
175 if (bs->peer_profile.min_rx == BFD_DEFREQUIREDMINRX)
176 bs->timers.required_min_rx = bp->min_rx;
177 else
178 bs->timers.required_min_rx = bs->peer_profile.min_rx;
179
180 /* We can only apply echo options on single hop sessions. */
181 if (!CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)) {
182 /* Configure remote echo if it was default. */
183 if (bs->peer_profile.min_echo_rx == BFD_DEF_REQ_MIN_ECHO)
184 bs->timers.required_min_echo = bp->min_echo_rx;
185 else
186 bs->timers.required_min_echo =
187 bs->peer_profile.min_echo_rx;
188
189 /* Toggle echo if default value. */
190 if (bs->peer_profile.echo_mode == false)
191 bfd_set_echo(bs, bp->echo_mode);
192 else
193 bfd_set_echo(bs, bs->peer_profile.echo_mode);
194 } else {
195 /* Configure the TTL packet filter. */
196 if (bs->peer_profile.minimum_ttl == BFD_DEF_MHOP_TTL)
197 bs->mh_ttl = bp->minimum_ttl;
198 else
199 bs->mh_ttl = bs->peer_profile.minimum_ttl;
200 }
201
202 /* Toggle 'passive-mode' if default value. */
203 if (bs->peer_profile.passive == false)
204 bfd_set_passive_mode(bs, bp->passive);
205 else
206 bfd_set_passive_mode(bs, bs->peer_profile.passive);
207
208 /* Toggle 'no shutdown' if default value. */
209 if (bs->peer_profile.admin_shutdown)
210 bfd_set_shutdown(bs, bp->admin_shutdown);
211 else
212 bfd_set_shutdown(bs, bs->peer_profile.admin_shutdown);
213
214 /* If session interval changed negotiate new timers. */
215 if (bs->ses_state == PTM_BFD_UP
216 && (bs->timers.desired_min_tx != min_tx
217 || bs->timers.required_min_rx != min_rx))
218 bfd_set_polling(bs);
219
220 /* Send updated information to data plane. */
221 bfd_dplane_update_session(bs);
222 }
223
224 void bfd_profile_remove(struct bfd_session *bs)
225 {
226 /* Remove any previous set profile name. */
227 XFREE(MTYPE_BFDD_PROFILE, bs->profile_name);
228 bs->profile = NULL;
229
230 bfd_session_apply(bs);
231 }
232
233 void gen_bfd_key(struct bfd_key *key, struct sockaddr_any *peer,
234 struct sockaddr_any *local, bool mhop, const char *ifname,
235 const char *vrfname)
236 {
237 memset(key, 0, sizeof(*key));
238
239 switch (peer->sa_sin.sin_family) {
240 case AF_INET:
241 key->family = AF_INET;
242 memcpy(&key->peer, &peer->sa_sin.sin_addr,
243 sizeof(peer->sa_sin.sin_addr));
244 memcpy(&key->local, &local->sa_sin.sin_addr,
245 sizeof(local->sa_sin.sin_addr));
246 break;
247 case AF_INET6:
248 key->family = AF_INET6;
249 memcpy(&key->peer, &peer->sa_sin6.sin6_addr,
250 sizeof(peer->sa_sin6.sin6_addr));
251 memcpy(&key->local, &local->sa_sin6.sin6_addr,
252 sizeof(local->sa_sin6.sin6_addr));
253 break;
254 }
255
256 key->mhop = mhop;
257 if (ifname && ifname[0])
258 strlcpy(key->ifname, ifname, sizeof(key->ifname));
259 if (vrfname && vrfname[0])
260 strlcpy(key->vrfname, vrfname, sizeof(key->vrfname));
261 else
262 strlcpy(key->vrfname, VRF_DEFAULT_NAME, sizeof(key->vrfname));
263 }
264
265 struct bfd_session *bs_peer_find(struct bfd_peer_cfg *bpc)
266 {
267 struct bfd_session *bs;
268 struct peer_label *pl;
269 struct bfd_key key;
270
271 /* Try to find label first. */
272 if (bpc->bpc_has_label) {
273 pl = pl_find(bpc->bpc_label);
274 if (pl != NULL) {
275 bs = pl->pl_bs;
276 return bs;
277 }
278 }
279
280 /* Otherwise fallback to peer/local hash lookup. */
281 gen_bfd_key(&key, &bpc->bpc_peer, &bpc->bpc_local, bpc->bpc_mhop,
282 bpc->bpc_localif, bpc->bpc_vrfname);
283
284 return bfd_key_lookup(key);
285 }
286
287 /*
288 * Starts a disabled BFD session.
289 *
290 * A session is disabled when the specified interface/VRF doesn't exist
291 * yet. It might happen on FRR boot or with virtual interfaces.
292 */
293 int bfd_session_enable(struct bfd_session *bs)
294 {
295 struct interface *ifp = NULL;
296 struct vrf *vrf = NULL;
297 int psock;
298
299 /* We are using data plane, we don't need software. */
300 if (bs->bdc)
301 return 0;
302
303 /*
304 * If the interface or VRF doesn't exist, then we must register
305 * the session but delay its start.
306 */
307 if (bs->key.vrfname[0]) {
308 vrf = vrf_lookup_by_name(bs->key.vrfname);
309 if (vrf == NULL) {
310 zlog_err(
311 "session-enable: specified VRF doesn't exists.");
312 return 0;
313 }
314 }
315
316 if (!vrf_is_backend_netns() && vrf && vrf->vrf_id != VRF_DEFAULT
317 && !if_lookup_by_name(vrf->name, vrf->vrf_id)) {
318 zlog_err("session-enable: vrf interface %s not available yet",
319 vrf->name);
320 return 0;
321 }
322
323 if (bs->key.ifname[0]) {
324 if (vrf)
325 ifp = if_lookup_by_name(bs->key.ifname, vrf->vrf_id);
326 else
327 ifp = if_lookup_by_name_all_vrf(bs->key.ifname);
328 if (ifp == NULL) {
329 zlog_err(
330 "session-enable: specified interface %s (VRF %s) doesn't exist.",
331 bs->key.ifname, vrf ? vrf->name : "<all>");
332 return 0;
333 }
334 if (bs->key.ifname[0] && !vrf) {
335 vrf = vrf_lookup_by_id(ifp->vrf_id);
336 if (vrf == NULL) {
337 zlog_err(
338 "session-enable: specified VRF %u doesn't exist.",
339 ifp->vrf_id);
340 return 0;
341 }
342 }
343 }
344
345 /* Assign interface/VRF pointers. */
346 bs->vrf = vrf;
347 if (bs->vrf == NULL)
348 bs->vrf = vrf_lookup_by_id(VRF_DEFAULT);
349 assert(bs->vrf);
350
351 /* Assign interface pointer (if any). */
352 bs->ifp = ifp;
353
354 /* Attempt to use data plane. */
355 if (bglobal.bg_use_dplane && bfd_dplane_add_session(bs) == 0) {
356 control_notify_config(BCM_NOTIFY_CONFIG_ADD, bs);
357 return 0;
358 }
359
360 /* Sanity check: don't leak open sockets. */
361 if (bs->sock != -1) {
362 if (bglobal.debug_peer_event)
363 zlog_debug("session-enable: previous socket open");
364
365 close(bs->sock);
366 bs->sock = -1;
367 }
368
369 /*
370 * Get socket for transmitting control packets. Note that if we
371 * could use the destination port (3784) for the source
372 * port we wouldn't need a socket per session.
373 */
374 if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_IPV6) == 0) {
375 psock = bp_peer_socket(bs);
376 if (psock == -1)
377 return 0;
378 } else {
379 psock = bp_peer_socketv6(bs);
380 if (psock == -1)
381 return 0;
382 }
383
384 /*
385 * We've got a valid socket, lets start the timers and the
386 * protocol.
387 */
388 bs->sock = psock;
389
390 /* Only start timers if we are using active mode. */
391 if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_PASSIVE) == 0) {
392 bfd_recvtimer_update(bs);
393 ptm_bfd_start_xmt_timer(bs, false);
394 }
395
396 return 0;
397 }
398
399 /*
400 * Disabled a running BFD session.
401 *
402 * A session is disabled when the specified interface/VRF gets removed
403 * (e.g. virtual interfaces).
404 */
405 void bfd_session_disable(struct bfd_session *bs)
406 {
407 /* We are using data plane, we don't need software. */
408 if (bs->bdc)
409 return;
410
411 /* Free up socket resources. */
412 if (bs->sock != -1) {
413 close(bs->sock);
414 bs->sock = -1;
415 }
416
417 /* Disable all timers. */
418 bfd_recvtimer_delete(bs);
419 bfd_xmttimer_delete(bs);
420 ptm_bfd_echo_stop(bs);
421
422 /* Set session down so it doesn't report UP and disabled. */
423 ptm_bfd_sess_dn(bs, BD_PATH_DOWN);
424 }
425
426 static uint32_t ptm_bfd_gen_ID(void)
427 {
428 uint32_t session_id;
429
430 /*
431 * RFC 5880, Section 6.8.1. recommends that we should generate
432 * random session identification numbers.
433 */
434 do {
435 session_id = ((frr_weak_random() << 16) & 0xFFFF0000)
436 | (frr_weak_random() & 0x0000FFFF);
437 } while (session_id == 0 || bfd_id_lookup(session_id) != NULL);
438
439 return session_id;
440 }
441
442 void ptm_bfd_start_xmt_timer(struct bfd_session *bfd, bool is_echo)
443 {
444 uint64_t jitter, xmt_TO;
445 int maxpercent;
446
447 xmt_TO = is_echo ? bfd->echo_xmt_TO : bfd->xmt_TO;
448
449 /*
450 * From section 6.5.2: trasmit interval should be randomly jittered
451 * between
452 * 75% and 100% of nominal value, unless detect_mult is 1, then should
453 * be
454 * between 75% and 90%.
455 */
456 maxpercent = (bfd->detect_mult == 1) ? 16 : 26;
457 jitter = (xmt_TO * (75 + (frr_weak_random() % maxpercent))) / 100;
458 /* XXX remove that division above */
459
460 if (is_echo)
461 bfd_echo_xmttimer_update(bfd, jitter);
462 else
463 bfd_xmttimer_update(bfd, jitter);
464 }
465
466 static void ptm_bfd_echo_xmt_TO(struct bfd_session *bfd)
467 {
468 /* Send the scheduled echo packet */
469 ptm_bfd_echo_snd(bfd);
470
471 /* Restart the timer for next time */
472 ptm_bfd_start_xmt_timer(bfd, true);
473 }
474
475 void ptm_bfd_xmt_TO(struct bfd_session *bfd, int fbit)
476 {
477 /* Send the scheduled control packet */
478 ptm_bfd_snd(bfd, fbit);
479
480 /* Restart the timer for next time */
481 ptm_bfd_start_xmt_timer(bfd, false);
482 }
483
484 void ptm_bfd_echo_stop(struct bfd_session *bfd)
485 {
486 bfd->echo_xmt_TO = 0;
487 bfd->echo_detect_TO = 0;
488 UNSET_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE);
489
490 bfd_echo_xmttimer_delete(bfd);
491 bfd_echo_recvtimer_delete(bfd);
492 }
493
494 void ptm_bfd_echo_start(struct bfd_session *bfd)
495 {
496 bfd->echo_detect_TO = (bfd->remote_detect_mult * bfd->echo_xmt_TO);
497 if (bfd->echo_detect_TO > 0)
498 ptm_bfd_echo_xmt_TO(bfd);
499 }
500
501 void ptm_bfd_sess_up(struct bfd_session *bfd)
502 {
503 int old_state = bfd->ses_state;
504
505 bfd->local_diag = 0;
506 bfd->ses_state = PTM_BFD_UP;
507 monotime(&bfd->uptime);
508
509 /* Connection is up, lets negotiate timers. */
510 bfd_set_polling(bfd);
511
512 /* Start sending control packets with poll bit immediately. */
513 ptm_bfd_snd(bfd, 0);
514
515 control_notify(bfd, bfd->ses_state);
516
517 if (old_state != bfd->ses_state) {
518 bfd->stats.session_up++;
519 if (bglobal.debug_peer_event)
520 zlog_debug("state-change: [%s] %s -> %s",
521 bs_to_string(bfd), state_list[old_state].str,
522 state_list[bfd->ses_state].str);
523 }
524 }
525
526 void ptm_bfd_sess_dn(struct bfd_session *bfd, uint8_t diag)
527 {
528 int old_state = bfd->ses_state;
529
530 bfd->local_diag = diag;
531 bfd->discrs.remote_discr = 0;
532 bfd->ses_state = PTM_BFD_DOWN;
533 bfd->polling = 0;
534 bfd->demand_mode = 0;
535 monotime(&bfd->downtime);
536
537 /*
538 * Only attempt to send if we have a valid socket:
539 * this function might be called by session disablers and in
540 * this case we won't have a valid socket (i.e. interface was
541 * removed or VRF doesn't exist anymore).
542 */
543 if (bfd->sock != -1)
544 ptm_bfd_snd(bfd, 0);
545
546 /* Slow down the control packets, the connection is down. */
547 bs_set_slow_timers(bfd);
548
549 /* only signal clients when going from up->down state */
550 if (old_state == PTM_BFD_UP)
551 control_notify(bfd, PTM_BFD_DOWN);
552
553 /* Stop echo packet transmission if they are active */
554 if (CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE))
555 ptm_bfd_echo_stop(bfd);
556
557 /* Stop attempting to transmit or expect control packets if passive. */
558 if (CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_PASSIVE)) {
559 bfd_recvtimer_delete(bfd);
560 bfd_xmttimer_delete(bfd);
561 }
562
563 if (old_state != bfd->ses_state) {
564 bfd->stats.session_down++;
565 if (bglobal.debug_peer_event)
566 zlog_debug("state-change: [%s] %s -> %s reason:%s",
567 bs_to_string(bfd), state_list[old_state].str,
568 state_list[bfd->ses_state].str,
569 get_diag_str(bfd->local_diag));
570 }
571 }
572
573 static struct bfd_session *bfd_find_disc(struct sockaddr_any *sa,
574 uint32_t ldisc)
575 {
576 struct bfd_session *bs;
577
578 bs = bfd_id_lookup(ldisc);
579 if (bs == NULL)
580 return NULL;
581
582 switch (bs->key.family) {
583 case AF_INET:
584 if (memcmp(&sa->sa_sin.sin_addr, &bs->key.peer,
585 sizeof(sa->sa_sin.sin_addr)))
586 return NULL;
587 break;
588 case AF_INET6:
589 if (memcmp(&sa->sa_sin6.sin6_addr, &bs->key.peer,
590 sizeof(sa->sa_sin6.sin6_addr)))
591 return NULL;
592 break;
593 }
594
595 return bs;
596 }
597
598 struct bfd_session *ptm_bfd_sess_find(struct bfd_pkt *cp,
599 struct sockaddr_any *peer,
600 struct sockaddr_any *local,
601 ifindex_t ifindex, vrf_id_t vrfid,
602 bool is_mhop)
603 {
604 struct interface *ifp;
605 struct vrf *vrf;
606 struct bfd_key key;
607
608 /* Find our session using the ID signaled by the remote end. */
609 if (cp->discrs.remote_discr)
610 return bfd_find_disc(peer, ntohl(cp->discrs.remote_discr));
611
612 /*
613 * Search for session without using discriminator.
614 *
615 * XXX: we can't trust `vrfid` because the VRF handling is not
616 * properly implemented. Meanwhile we should use the interface
617 * VRF to find out which one it belongs.
618 */
619 ifp = if_lookup_by_index_all_vrf(ifindex);
620 if (ifp == NULL) {
621 if (vrfid != VRF_DEFAULT)
622 vrf = vrf_lookup_by_id(vrfid);
623 else
624 vrf = NULL;
625 } else
626 vrf = vrf_lookup_by_id(ifp->vrf_id);
627
628 gen_bfd_key(&key, peer, local, is_mhop, ifp ? ifp->name : NULL,
629 vrf ? vrf->name : VRF_DEFAULT_NAME);
630
631 /* XXX maybe remoteDiscr should be checked for remoteHeard cases. */
632 return bfd_key_lookup(key);
633 }
634
635 int bfd_xmt_cb(struct thread *t)
636 {
637 struct bfd_session *bs = THREAD_ARG(t);
638
639 ptm_bfd_xmt_TO(bs, 0);
640
641 return 0;
642 }
643
644 int bfd_echo_xmt_cb(struct thread *t)
645 {
646 struct bfd_session *bs = THREAD_ARG(t);
647
648 if (bs->echo_xmt_TO > 0)
649 ptm_bfd_echo_xmt_TO(bs);
650
651 return 0;
652 }
653
654 /* Was ptm_bfd_detect_TO() */
655 int bfd_recvtimer_cb(struct thread *t)
656 {
657 struct bfd_session *bs = THREAD_ARG(t);
658
659 switch (bs->ses_state) {
660 case PTM_BFD_INIT:
661 case PTM_BFD_UP:
662 ptm_bfd_sess_dn(bs, BD_CONTROL_EXPIRED);
663 bfd_recvtimer_update(bs);
664 break;
665
666 default:
667 /* Second detect time expiration, zero remote discr (section
668 * 6.5.1)
669 */
670 bs->discrs.remote_discr = 0;
671 break;
672 }
673
674 return 0;
675 }
676
677 /* Was ptm_bfd_echo_detect_TO() */
678 int bfd_echo_recvtimer_cb(struct thread *t)
679 {
680 struct bfd_session *bs = THREAD_ARG(t);
681
682 switch (bs->ses_state) {
683 case PTM_BFD_INIT:
684 case PTM_BFD_UP:
685 ptm_bfd_sess_dn(bs, BD_ECHO_FAILED);
686 break;
687 }
688
689 return 0;
690 }
691
692 struct bfd_session *bfd_session_new(void)
693 {
694 struct bfd_session *bs;
695
696 bs = XCALLOC(MTYPE_BFDD_CONFIG, sizeof(*bs));
697
698 /* Set peer session defaults. */
699 bfd_profile_set_default(&bs->peer_profile);
700
701 bs->timers.desired_min_tx = BFD_DEFDESIREDMINTX;
702 bs->timers.required_min_rx = BFD_DEFREQUIREDMINRX;
703 bs->timers.required_min_echo = BFD_DEF_REQ_MIN_ECHO;
704 bs->detect_mult = BFD_DEFDETECTMULT;
705 bs->mh_ttl = BFD_DEF_MHOP_TTL;
706 bs->ses_state = PTM_BFD_DOWN;
707
708 /* Initiate connection with slow timers. */
709 bs_set_slow_timers(bs);
710
711 /* Initiate remote settings as well. */
712 bs->remote_timers = bs->cur_timers;
713 bs->remote_detect_mult = BFD_DEFDETECTMULT;
714
715 bs->sock = -1;
716 monotime(&bs->uptime);
717 bs->downtime = bs->uptime;
718
719 return bs;
720 }
721
722 int bfd_session_update_label(struct bfd_session *bs, const char *nlabel)
723 {
724 /* New label treatment:
725 * - Check if the label is taken;
726 * - Try to allocate the memory for it and register;
727 */
728 if (bs->pl == NULL) {
729 if (pl_find(nlabel) != NULL) {
730 /* Someone is already using it. */
731 return -1;
732 }
733
734 pl_new(nlabel, bs);
735
736 return 0;
737 }
738
739 /*
740 * Test label change consistency:
741 * - Do nothing if it's the same label;
742 * - Check if the future label is already taken;
743 * - Change label;
744 */
745 if (strcmp(nlabel, bs->pl->pl_label) == 0)
746 return -1;
747 if (pl_find(nlabel) != NULL)
748 return -1;
749
750 strlcpy(bs->pl->pl_label, nlabel, sizeof(bs->pl->pl_label));
751 return 0;
752 }
753
754 static void _bfd_session_update(struct bfd_session *bs,
755 struct bfd_peer_cfg *bpc)
756 {
757 if (bpc->bpc_has_txinterval) {
758 bs->timers.desired_min_tx = bpc->bpc_txinterval * 1000;
759 bs->peer_profile.min_tx = bs->timers.desired_min_tx;
760 }
761
762 if (bpc->bpc_has_recvinterval) {
763 bs->timers.required_min_rx = bpc->bpc_recvinterval * 1000;
764 bs->peer_profile.min_rx = bs->timers.required_min_rx;
765 }
766
767 if (bpc->bpc_has_detectmultiplier) {
768 bs->detect_mult = bpc->bpc_detectmultiplier;
769 bs->peer_profile.detection_multiplier = bs->detect_mult;
770 }
771
772 if (bpc->bpc_has_echointerval) {
773 bs->timers.required_min_echo = bpc->bpc_echointerval * 1000;
774 bs->peer_profile.min_echo_rx = bs->timers.required_min_echo;
775 }
776
777 if (bpc->bpc_has_label)
778 bfd_session_update_label(bs, bpc->bpc_label);
779
780 if (bpc->bpc_cbit)
781 SET_FLAG(bs->flags, BFD_SESS_FLAG_CBIT);
782 else
783 UNSET_FLAG(bs->flags, BFD_SESS_FLAG_CBIT);
784
785 if (bpc->bpc_has_minimum_ttl) {
786 bs->mh_ttl = bpc->bpc_minimum_ttl;
787 bs->peer_profile.minimum_ttl = bpc->bpc_minimum_ttl;
788 }
789
790 bs->peer_profile.echo_mode = bpc->bpc_echo;
791 bfd_set_echo(bs, bpc->bpc_echo);
792
793 /*
794 * Shutdown needs to be the last in order to avoid timers enable when
795 * the session is disabled.
796 */
797 bs->peer_profile.admin_shutdown = bpc->bpc_shutdown;
798 bfd_set_passive_mode(bs, bpc->bpc_passive);
799 bfd_set_shutdown(bs, bpc->bpc_shutdown);
800
801 /*
802 * Apply profile last: it also calls `bfd_set_shutdown`.
803 *
804 * There is no problem calling `shutdown` twice if the value doesn't
805 * change or if it is overriden by peer specific configuration.
806 */
807 if (bpc->bpc_has_profile)
808 bfd_profile_apply(bpc->bpc_profile, bs);
809 }
810
811 static int bfd_session_update(struct bfd_session *bs, struct bfd_peer_cfg *bpc)
812 {
813 /* User didn't want to update, return failure. */
814 if (bpc->bpc_createonly)
815 return -1;
816
817 _bfd_session_update(bs, bpc);
818
819 control_notify_config(BCM_NOTIFY_CONFIG_UPDATE, bs);
820
821 return 0;
822 }
823
824 void bfd_session_free(struct bfd_session *bs)
825 {
826 struct bfd_session_observer *bso;
827
828 bfd_session_disable(bs);
829
830 /* Remove session from data plane if any. */
831 bfd_dplane_delete_session(bs);
832
833 bfd_key_delete(bs->key);
834 bfd_id_delete(bs->discrs.my_discr);
835
836 /* Remove observer if any. */
837 TAILQ_FOREACH(bso, &bglobal.bg_obslist, bso_entry) {
838 if (bso->bso_bs != bs)
839 continue;
840
841 break;
842 }
843 if (bso != NULL)
844 bs_observer_del(bso);
845
846 pl_free(bs->pl);
847
848 XFREE(MTYPE_BFDD_PROFILE, bs->profile_name);
849 XFREE(MTYPE_BFDD_CONFIG, bs);
850 }
851
852 struct bfd_session *ptm_bfd_sess_new(struct bfd_peer_cfg *bpc)
853 {
854 struct bfd_session *bfd, *l_bfd;
855
856 /* check to see if this needs a new session */
857 l_bfd = bs_peer_find(bpc);
858 if (l_bfd) {
859 /* Requesting a duplicated peer means update configuration. */
860 if (bfd_session_update(l_bfd, bpc) == 0)
861 return l_bfd;
862 else
863 return NULL;
864 }
865
866 /* Get BFD session storage with its defaults. */
867 bfd = bfd_session_new();
868
869 /*
870 * Store interface/VRF name in case we need to delay session
871 * start. See `bfd_session_enable` for more information.
872 */
873 if (bpc->bpc_has_localif)
874 strlcpy(bfd->key.ifname, bpc->bpc_localif,
875 sizeof(bfd->key.ifname));
876
877 if (bpc->bpc_has_vrfname)
878 strlcpy(bfd->key.vrfname, bpc->bpc_vrfname,
879 sizeof(bfd->key.vrfname));
880 else
881 strlcpy(bfd->key.vrfname, VRF_DEFAULT_NAME,
882 sizeof(bfd->key.vrfname));
883
884 /* Copy remaining data. */
885 if (bpc->bpc_ipv4 == false)
886 SET_FLAG(bfd->flags, BFD_SESS_FLAG_IPV6);
887
888 bfd->key.family = (bpc->bpc_ipv4) ? AF_INET : AF_INET6;
889 switch (bfd->key.family) {
890 case AF_INET:
891 memcpy(&bfd->key.peer, &bpc->bpc_peer.sa_sin.sin_addr,
892 sizeof(bpc->bpc_peer.sa_sin.sin_addr));
893 memcpy(&bfd->key.local, &bpc->bpc_local.sa_sin.sin_addr,
894 sizeof(bpc->bpc_local.sa_sin.sin_addr));
895 break;
896
897 case AF_INET6:
898 memcpy(&bfd->key.peer, &bpc->bpc_peer.sa_sin6.sin6_addr,
899 sizeof(bpc->bpc_peer.sa_sin6.sin6_addr));
900 memcpy(&bfd->key.local, &bpc->bpc_local.sa_sin6.sin6_addr,
901 sizeof(bpc->bpc_local.sa_sin6.sin6_addr));
902 break;
903
904 default:
905 assert(1);
906 break;
907 }
908
909 if (bpc->bpc_mhop)
910 SET_FLAG(bfd->flags, BFD_SESS_FLAG_MH);
911
912 bfd->key.mhop = bpc->bpc_mhop;
913
914 if (bs_registrate(bfd) == NULL)
915 return NULL;
916
917 /* Apply other configurations. */
918 _bfd_session_update(bfd, bpc);
919
920 return bfd;
921 }
922
923 struct bfd_session *bs_registrate(struct bfd_session *bfd)
924 {
925 /* Registrate session into data structures. */
926 bfd_key_insert(bfd);
927 bfd->discrs.my_discr = ptm_bfd_gen_ID();
928 bfd_id_insert(bfd);
929
930 /* Try to enable session and schedule for packet receive/send. */
931 if (bfd_session_enable(bfd) == -1) {
932 /* Unrecoverable failure, remove the session/peer. */
933 bfd_session_free(bfd);
934 return NULL;
935 }
936
937 /* Add observer if we have moving parts. */
938 if (bfd->key.ifname[0] || bfd->key.vrfname[0] || bfd->sock == -1)
939 bs_observer_add(bfd);
940
941 if (bglobal.debug_peer_event)
942 zlog_debug("session-new: %s", bs_to_string(bfd));
943
944 control_notify_config(BCM_NOTIFY_CONFIG_ADD, bfd);
945
946 return bfd;
947 }
948
949 int ptm_bfd_sess_del(struct bfd_peer_cfg *bpc)
950 {
951 struct bfd_session *bs;
952
953 /* Find session and call free(). */
954 bs = bs_peer_find(bpc);
955 if (bs == NULL)
956 return -1;
957
958 /* This pointer is being referenced, don't let it be deleted. */
959 if (bs->refcount > 0) {
960 zlog_err("session-delete: refcount failure: %" PRIu64" references",
961 bs->refcount);
962 return -1;
963 }
964
965 if (bglobal.debug_peer_event)
966 zlog_debug("session-delete: %s", bs_to_string(bs));
967
968 control_notify_config(BCM_NOTIFY_CONFIG_DELETE, bs);
969
970 bfd_session_free(bs);
971
972 return 0;
973 }
974
975 void bfd_set_polling(struct bfd_session *bs)
976 {
977 /*
978 * Start polling procedure: the only timers that require polling
979 * to change value without losing connection are:
980 *
981 * - Desired minimum transmission interval;
982 * - Required minimum receive interval;
983 *
984 * RFC 5880, Section 6.8.3.
985 */
986 bs->polling = 1;
987 }
988
989 /*
990 * bs_<state>_handler() functions implement the BFD state machine
991 * transition mechanism. `<state>` is the current session state and
992 * the parameter `nstate` is the peer new state.
993 */
994 static void bs_admin_down_handler(struct bfd_session *bs
995 __attribute__((__unused__)),
996 int nstate __attribute__((__unused__)))
997 {
998 /*
999 * We are administratively down, there is no state machine
1000 * handling.
1001 */
1002 }
1003
1004 static void bs_down_handler(struct bfd_session *bs, int nstate)
1005 {
1006 switch (nstate) {
1007 case PTM_BFD_ADM_DOWN:
1008 /*
1009 * Remote peer doesn't want to talk, so lets keep the
1010 * connection down.
1011 */
1012 case PTM_BFD_UP:
1013 /* Peer can't be up yet, wait it go to 'init' or 'down'. */
1014 break;
1015
1016 case PTM_BFD_DOWN:
1017 /*
1018 * Remote peer agreed that the path is down, lets try to
1019 * bring it up.
1020 */
1021 bs->ses_state = PTM_BFD_INIT;
1022
1023 /* Answer peer with INIT immediately in passive mode. */
1024 if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_PASSIVE))
1025 ptm_bfd_snd(bs, 0);
1026 break;
1027
1028 case PTM_BFD_INIT:
1029 /*
1030 * Remote peer told us his path is up, lets turn
1031 * activate the session.
1032 */
1033 ptm_bfd_sess_up(bs);
1034 break;
1035
1036 default:
1037 if (bglobal.debug_peer_event)
1038 zlog_debug("state-change: unhandled neighbor state: %d",
1039 nstate);
1040 break;
1041 }
1042 }
1043
1044 static void bs_init_handler(struct bfd_session *bs, int nstate)
1045 {
1046 switch (nstate) {
1047 case PTM_BFD_ADM_DOWN:
1048 /*
1049 * Remote peer doesn't want to talk, so lets make the
1050 * connection down.
1051 */
1052 bs->ses_state = PTM_BFD_DOWN;
1053 break;
1054
1055 case PTM_BFD_DOWN:
1056 /* Remote peer hasn't moved to first stage yet. */
1057 break;
1058
1059 case PTM_BFD_INIT:
1060 case PTM_BFD_UP:
1061 /* We agreed on the settings and the path is up. */
1062 ptm_bfd_sess_up(bs);
1063 break;
1064
1065 default:
1066 if (bglobal.debug_peer_event)
1067 zlog_debug("state-change: unhandled neighbor state: %d",
1068 nstate);
1069 break;
1070 }
1071 }
1072
1073 static void bs_neighbour_admin_down_handler(struct bfd_session *bfd,
1074 uint8_t diag)
1075 {
1076 int old_state = bfd->ses_state;
1077
1078 bfd->local_diag = diag;
1079 bfd->discrs.remote_discr = 0;
1080 bfd->ses_state = PTM_BFD_DOWN;
1081 bfd->polling = 0;
1082 bfd->demand_mode = 0;
1083 monotime(&bfd->downtime);
1084
1085 /* Slow down the control packets, the connection is down. */
1086 bs_set_slow_timers(bfd);
1087
1088 /* only signal clients when going from up->down state */
1089 if (old_state == PTM_BFD_UP)
1090 control_notify(bfd, PTM_BFD_ADM_DOWN);
1091
1092 /* Stop echo packet transmission if they are active */
1093 if (CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE))
1094 ptm_bfd_echo_stop(bfd);
1095
1096 if (old_state != bfd->ses_state) {
1097 bfd->stats.session_down++;
1098 if (bglobal.debug_peer_event)
1099 zlog_debug("state-change: [%s] %s -> %s reason:%s",
1100 bs_to_string(bfd), state_list[old_state].str,
1101 state_list[bfd->ses_state].str,
1102 get_diag_str(bfd->local_diag));
1103 }
1104 }
1105
1106 static void bs_up_handler(struct bfd_session *bs, int nstate)
1107 {
1108 switch (nstate) {
1109 case PTM_BFD_ADM_DOWN:
1110 bs_neighbour_admin_down_handler(bs, BD_ADMIN_DOWN);
1111 break;
1112
1113 case PTM_BFD_DOWN:
1114 /* Peer lost or asked to shutdown connection. */
1115 ptm_bfd_sess_dn(bs, BD_NEIGHBOR_DOWN);
1116 break;
1117
1118 case PTM_BFD_INIT:
1119 case PTM_BFD_UP:
1120 /* Path is up and working. */
1121 break;
1122
1123 default:
1124 if (bglobal.debug_peer_event)
1125 zlog_debug("state-change: unhandled neighbor state: %d",
1126 nstate);
1127 break;
1128 }
1129 }
1130
1131 void bs_state_handler(struct bfd_session *bs, int nstate)
1132 {
1133 switch (bs->ses_state) {
1134 case PTM_BFD_ADM_DOWN:
1135 bs_admin_down_handler(bs, nstate);
1136 break;
1137 case PTM_BFD_DOWN:
1138 bs_down_handler(bs, nstate);
1139 break;
1140 case PTM_BFD_INIT:
1141 bs_init_handler(bs, nstate);
1142 break;
1143 case PTM_BFD_UP:
1144 bs_up_handler(bs, nstate);
1145 break;
1146
1147 default:
1148 if (bglobal.debug_peer_event)
1149 zlog_debug("state-change: [%s] is in invalid state: %d",
1150 bs_to_string(bs), nstate);
1151 break;
1152 }
1153 }
1154
1155 /*
1156 * Handles echo timer manipulation after updating timer.
1157 */
1158 void bs_echo_timer_handler(struct bfd_session *bs)
1159 {
1160 uint32_t old_timer;
1161
1162 /*
1163 * Before doing any echo handling, check if it is possible to
1164 * use it.
1165 *
1166 * - Check for `echo-mode` configuration.
1167 * - Check that we are not using multi hop (RFC 5883,
1168 * Section 3).
1169 * - Check that we are already at the up state.
1170 */
1171 if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO) == 0
1172 || CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)
1173 || bs->ses_state != PTM_BFD_UP)
1174 return;
1175
1176 /* Remote peer asked to stop echo. */
1177 if (bs->remote_timers.required_min_echo == 0) {
1178 if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO_ACTIVE))
1179 ptm_bfd_echo_stop(bs);
1180
1181 return;
1182 }
1183
1184 /*
1185 * Calculate the echo transmission timer: we must not send
1186 * echo packets faster than the minimum required time
1187 * announced by the remote system.
1188 *
1189 * RFC 5880, Section 6.8.9.
1190 */
1191 old_timer = bs->echo_xmt_TO;
1192 if (bs->remote_timers.required_min_echo > bs->timers.required_min_echo)
1193 bs->echo_xmt_TO = bs->remote_timers.required_min_echo;
1194 else
1195 bs->echo_xmt_TO = bs->timers.required_min_echo;
1196
1197 if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO_ACTIVE) == 0
1198 || old_timer != bs->echo_xmt_TO)
1199 ptm_bfd_echo_start(bs);
1200 }
1201
1202 /*
1203 * RFC 5880 Section 6.5.
1204 *
1205 * When a BFD control packet with the final bit is received, we must
1206 * update the session parameters.
1207 */
1208 void bs_final_handler(struct bfd_session *bs)
1209 {
1210 /* Start using our new timers. */
1211 bs->cur_timers.desired_min_tx = bs->timers.desired_min_tx;
1212 bs->cur_timers.required_min_rx = bs->timers.required_min_rx;
1213
1214 /*
1215 * TODO: demand mode. See RFC 5880 Section 6.1.
1216 *
1217 * When using demand mode we must disable the detection timer
1218 * for lost control packets.
1219 */
1220 if (bs->demand_mode) {
1221 /* Notify watchers about changed timers. */
1222 control_notify_config(BCM_NOTIFY_CONFIG_UPDATE, bs);
1223 return;
1224 }
1225
1226 /*
1227 * Calculate transmission time based on new timers.
1228 *
1229 * Transmission calculation:
1230 * Unless specified by exceptions at the end of Section 6.8.7, the
1231 * transmission time will be determined by the system with the
1232 * slowest rate.
1233 *
1234 * RFC 5880, Section 6.8.7.
1235 */
1236 if (bs->timers.desired_min_tx > bs->remote_timers.required_min_rx)
1237 bs->xmt_TO = bs->timers.desired_min_tx;
1238 else
1239 bs->xmt_TO = bs->remote_timers.required_min_rx;
1240
1241 /* Apply new transmission timer immediately. */
1242 ptm_bfd_start_xmt_timer(bs, false);
1243
1244 /*
1245 * Detection timeout calculation:
1246 * The minimum detection timeout is the remote detection
1247 * multipler (number of packets to be missed) times the agreed
1248 * transmission interval.
1249 *
1250 * RFC 5880, Section 6.8.4.
1251 *
1252 * TODO: support sending/counting more packets inside detection
1253 * timeout.
1254 */
1255 if (bs->remote_timers.required_min_rx > bs->timers.desired_min_tx)
1256 bs->detect_TO = bs->remote_detect_mult
1257 * bs->remote_timers.required_min_rx;
1258 else
1259 bs->detect_TO = bs->remote_detect_mult
1260 * bs->timers.desired_min_tx;
1261
1262 /* Apply new receive timer immediately. */
1263 bfd_recvtimer_update(bs);
1264
1265 /* Notify watchers about changed timers. */
1266 control_notify_config(BCM_NOTIFY_CONFIG_UPDATE, bs);
1267 }
1268
1269 void bs_set_slow_timers(struct bfd_session *bs)
1270 {
1271 /*
1272 * BFD connection must use slow timers before going up or after
1273 * losing connectivity to avoid wasting bandwidth.
1274 *
1275 * RFC 5880, Section 6.8.3.
1276 */
1277 bs->cur_timers.desired_min_tx = BFD_DEF_SLOWTX;
1278 bs->cur_timers.required_min_rx = BFD_DEF_SLOWTX;
1279 bs->cur_timers.required_min_echo = 0;
1280
1281 /* Set the appropriated timeouts for slow connection. */
1282 bs->detect_TO = (BFD_DEFDETECTMULT * BFD_DEF_SLOWTX);
1283 bs->xmt_TO = BFD_DEF_SLOWTX;
1284 }
1285
1286 void bfd_set_echo(struct bfd_session *bs, bool echo)
1287 {
1288 if (echo) {
1289 /* Check if echo mode is already active. */
1290 if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO))
1291 return;
1292
1293 SET_FLAG(bs->flags, BFD_SESS_FLAG_ECHO);
1294
1295 /* Activate/update echo receive timeout timer. */
1296 if (bs->bdc == NULL)
1297 bs_echo_timer_handler(bs);
1298 } else {
1299 /* Check if echo mode is already disabled. */
1300 if (!CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO))
1301 return;
1302
1303 UNSET_FLAG(bs->flags, BFD_SESS_FLAG_ECHO);
1304
1305 /* Deactivate timeout timer. */
1306 if (bs->bdc == NULL)
1307 ptm_bfd_echo_stop(bs);
1308 }
1309 }
1310
1311 void bfd_set_shutdown(struct bfd_session *bs, bool shutdown)
1312 {
1313 bool is_shutdown;
1314
1315 /*
1316 * Special case: we are batching changes and the previous state was
1317 * not shutdown. Instead of potentially disconnect a running peer,
1318 * we'll get the current status to validate we were really down.
1319 */
1320 if (bs->ses_state == PTM_BFD_UP)
1321 is_shutdown = false;
1322 else
1323 is_shutdown = CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN);
1324
1325 if (shutdown) {
1326 /* Already shutdown. */
1327 if (is_shutdown)
1328 return;
1329
1330 SET_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN);
1331
1332 /* Handle data plane shutdown case. */
1333 if (bs->bdc) {
1334 bs->ses_state = PTM_BFD_ADM_DOWN;
1335 bfd_dplane_update_session(bs);
1336 control_notify(bs, bs->ses_state);
1337 return;
1338 }
1339
1340 /* Disable all events. */
1341 bfd_recvtimer_delete(bs);
1342 bfd_echo_recvtimer_delete(bs);
1343 bfd_xmttimer_delete(bs);
1344 bfd_echo_xmttimer_delete(bs);
1345
1346 /* Change and notify state change. */
1347 bs->ses_state = PTM_BFD_ADM_DOWN;
1348 control_notify(bs, bs->ses_state);
1349
1350 /* Don't try to send packets with a disabled session. */
1351 if (bs->sock != -1)
1352 ptm_bfd_snd(bs, 0);
1353 } else {
1354 /* Already working. */
1355 if (!is_shutdown)
1356 return;
1357
1358 UNSET_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN);
1359
1360 /* Handle data plane shutdown case. */
1361 if (bs->bdc) {
1362 bs->ses_state = PTM_BFD_DOWN;
1363 bfd_dplane_update_session(bs);
1364 control_notify(bs, bs->ses_state);
1365 return;
1366 }
1367
1368 /* Change and notify state change. */
1369 bs->ses_state = PTM_BFD_DOWN;
1370 control_notify(bs, bs->ses_state);
1371
1372 /* Enable timers if non passive, otherwise stop them. */
1373 if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_PASSIVE)) {
1374 bfd_recvtimer_delete(bs);
1375 bfd_xmttimer_delete(bs);
1376 } else {
1377 bfd_recvtimer_update(bs);
1378 bfd_xmttimer_update(bs, bs->xmt_TO);
1379 }
1380 }
1381 }
1382
1383 void bfd_set_passive_mode(struct bfd_session *bs, bool passive)
1384 {
1385 if (passive) {
1386 SET_FLAG(bs->flags, BFD_SESS_FLAG_PASSIVE);
1387
1388 /* Session is already up and running, nothing to do now. */
1389 if (bs->ses_state != PTM_BFD_DOWN)
1390 return;
1391
1392 /* Lets disable the timers since we are now passive. */
1393 bfd_recvtimer_delete(bs);
1394 bfd_xmttimer_delete(bs);
1395 } else {
1396 UNSET_FLAG(bs->flags, BFD_SESS_FLAG_PASSIVE);
1397
1398 /* Session is already up and running, nothing to do now. */
1399 if (bs->ses_state != PTM_BFD_DOWN)
1400 return;
1401
1402 /* Session is down, let it attempt to start the connection. */
1403 bfd_xmttimer_update(bs, bs->xmt_TO);
1404 bfd_recvtimer_update(bs);
1405 }
1406 }
1407
1408 /*
1409 * Helper functions.
1410 */
1411 static const char *get_diag_str(int diag)
1412 {
1413 for (int i = 0; diag_list[i].str; i++) {
1414 if (diag_list[i].type == diag)
1415 return diag_list[i].str;
1416 }
1417 return "N/A";
1418 }
1419
1420 const char *satostr(const struct sockaddr_any *sa)
1421 {
1422 #define INETSTR_BUFCOUNT 8
1423 static char buf[INETSTR_BUFCOUNT][INET6_ADDRSTRLEN];
1424 static int bufidx;
1425 const struct sockaddr_in *sin = &sa->sa_sin;
1426 const struct sockaddr_in6 *sin6 = &sa->sa_sin6;
1427
1428 bufidx += (bufidx + 1) % INETSTR_BUFCOUNT;
1429 buf[bufidx][0] = 0;
1430
1431 switch (sin->sin_family) {
1432 case AF_INET:
1433 inet_ntop(AF_INET, &sin->sin_addr, buf[bufidx],
1434 sizeof(buf[bufidx]));
1435 break;
1436 case AF_INET6:
1437 inet_ntop(AF_INET6, &sin6->sin6_addr, buf[bufidx],
1438 sizeof(buf[bufidx]));
1439 break;
1440
1441 default:
1442 strlcpy(buf[bufidx], "unknown", sizeof(buf[bufidx]));
1443 break;
1444 }
1445
1446 return buf[bufidx];
1447 }
1448
1449 const char *diag2str(uint8_t diag)
1450 {
1451 switch (diag) {
1452 case 0:
1453 return "ok";
1454 case 1:
1455 return "control detection time expired";
1456 case 2:
1457 return "echo function failed";
1458 case 3:
1459 return "neighbor signaled session down";
1460 case 4:
1461 return "forwarding plane reset";
1462 case 5:
1463 return "path down";
1464 case 6:
1465 return "concatenated path down";
1466 case 7:
1467 return "administratively down";
1468 case 8:
1469 return "reverse concatenated path down";
1470 default:
1471 return "unknown";
1472 }
1473 }
1474
1475 int strtosa(const char *addr, struct sockaddr_any *sa)
1476 {
1477 memset(sa, 0, sizeof(*sa));
1478
1479 if (inet_pton(AF_INET, addr, &sa->sa_sin.sin_addr) == 1) {
1480 sa->sa_sin.sin_family = AF_INET;
1481 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
1482 sa->sa_sin.sin_len = sizeof(sa->sa_sin);
1483 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
1484 return 0;
1485 }
1486
1487 if (inet_pton(AF_INET6, addr, &sa->sa_sin6.sin6_addr) == 1) {
1488 sa->sa_sin6.sin6_family = AF_INET6;
1489 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
1490 sa->sa_sin6.sin6_len = sizeof(sa->sa_sin6);
1491 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
1492 return 0;
1493 }
1494
1495 return -1;
1496 }
1497
1498 void integer2timestr(uint64_t time, char *buf, size_t buflen)
1499 {
1500 unsigned int year, month, day, hour, minute, second;
1501 int rv;
1502
1503 #define MINUTES (60)
1504 #define HOURS (60 * MINUTES)
1505 #define DAYS (24 * HOURS)
1506 #define MONTHS (30 * DAYS)
1507 #define YEARS (12 * MONTHS)
1508 if (time >= YEARS) {
1509 year = time / YEARS;
1510 time -= year * YEARS;
1511
1512 rv = snprintf(buf, buflen, "%u year(s), ", year);
1513 buf += rv;
1514 buflen -= rv;
1515 }
1516 if (time >= MONTHS) {
1517 month = time / MONTHS;
1518 time -= month * MONTHS;
1519
1520 rv = snprintf(buf, buflen, "%u month(s), ", month);
1521 buf += rv;
1522 buflen -= rv;
1523 }
1524 if (time >= DAYS) {
1525 day = time / DAYS;
1526 time -= day * DAYS;
1527
1528 rv = snprintf(buf, buflen, "%u day(s), ", day);
1529 buf += rv;
1530 buflen -= rv;
1531 }
1532 if (time >= HOURS) {
1533 hour = time / HOURS;
1534 time -= hour * HOURS;
1535
1536 rv = snprintf(buf, buflen, "%u hour(s), ", hour);
1537 buf += rv;
1538 buflen -= rv;
1539 }
1540 if (time >= MINUTES) {
1541 minute = time / MINUTES;
1542 time -= minute * MINUTES;
1543
1544 rv = snprintf(buf, buflen, "%u minute(s), ", minute);
1545 buf += rv;
1546 buflen -= rv;
1547 }
1548 second = time % MINUTES;
1549 snprintf(buf, buflen, "%u second(s)", second);
1550 }
1551
1552 const char *bs_to_string(const struct bfd_session *bs)
1553 {
1554 static char buf[256];
1555 char addr_buf[INET6_ADDRSTRLEN];
1556 int pos;
1557 bool is_mhop = CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH);
1558
1559 pos = snprintf(buf, sizeof(buf), "mhop:%s", is_mhop ? "yes" : "no");
1560 pos += snprintf(buf + pos, sizeof(buf) - pos, " peer:%s",
1561 inet_ntop(bs->key.family, &bs->key.peer, addr_buf,
1562 sizeof(addr_buf)));
1563 pos += snprintf(buf + pos, sizeof(buf) - pos, " local:%s",
1564 inet_ntop(bs->key.family, &bs->key.local, addr_buf,
1565 sizeof(addr_buf)));
1566 if (bs->key.vrfname[0])
1567 pos += snprintf(buf + pos, sizeof(buf) - pos, " vrf:%s",
1568 bs->key.vrfname);
1569 if (bs->key.ifname[0])
1570 pos += snprintf(buf + pos, sizeof(buf) - pos, " ifname:%s",
1571 bs->key.ifname);
1572
1573 (void)pos;
1574
1575 return buf;
1576 }
1577
1578 int bs_observer_add(struct bfd_session *bs)
1579 {
1580 struct bfd_session_observer *bso;
1581
1582 bso = XCALLOC(MTYPE_BFDD_SESSION_OBSERVER, sizeof(*bso));
1583 bso->bso_bs = bs;
1584 bso->bso_addr.family = bs->key.family;
1585 memcpy(&bso->bso_addr.u.prefix, &bs->key.local,
1586 sizeof(bs->key.local));
1587
1588 TAILQ_INSERT_TAIL(&bglobal.bg_obslist, bso, bso_entry);
1589
1590 return 0;
1591 }
1592
1593 void bs_observer_del(struct bfd_session_observer *bso)
1594 {
1595 TAILQ_REMOVE(&bglobal.bg_obslist, bso, bso_entry);
1596 XFREE(MTYPE_BFDD_SESSION_OBSERVER, bso);
1597 }
1598
1599 void bs_to_bpc(struct bfd_session *bs, struct bfd_peer_cfg *bpc)
1600 {
1601 memset(bpc, 0, sizeof(*bpc));
1602
1603 bpc->bpc_ipv4 = (bs->key.family == AF_INET);
1604 bpc->bpc_mhop = bs->key.mhop;
1605
1606 switch (bs->key.family) {
1607 case AF_INET:
1608 bpc->bpc_peer.sa_sin.sin_family = AF_INET;
1609 memcpy(&bpc->bpc_peer.sa_sin.sin_addr, &bs->key.peer,
1610 sizeof(bpc->bpc_peer.sa_sin.sin_addr));
1611
1612 if (memcmp(&bs->key.local, &zero_addr, sizeof(bs->key.local))) {
1613 bpc->bpc_local.sa_sin.sin_family = AF_INET6;
1614 memcpy(&bpc->bpc_local.sa_sin.sin_addr, &bs->key.local,
1615 sizeof(bpc->bpc_local.sa_sin.sin_addr));
1616 }
1617 break;
1618
1619 case AF_INET6:
1620 bpc->bpc_peer.sa_sin.sin_family = AF_INET6;
1621 memcpy(&bpc->bpc_peer.sa_sin6.sin6_addr, &bs->key.peer,
1622 sizeof(bpc->bpc_peer.sa_sin6.sin6_addr));
1623
1624 bpc->bpc_local.sa_sin6.sin6_family = AF_INET6;
1625 memcpy(&bpc->bpc_local.sa_sin6.sin6_addr, &bs->key.local,
1626 sizeof(bpc->bpc_local.sa_sin6.sin6_addr));
1627 break;
1628 }
1629
1630 if (bs->key.ifname[0]) {
1631 bpc->bpc_has_localif = true;
1632 strlcpy(bpc->bpc_localif, bs->key.ifname,
1633 sizeof(bpc->bpc_localif));
1634 }
1635
1636 if (bs->key.vrfname[0]) {
1637 bpc->bpc_has_vrfname = true;
1638 strlcpy(bpc->bpc_vrfname, bs->key.vrfname,
1639 sizeof(bpc->bpc_vrfname));
1640 }
1641 }
1642
1643
1644 /*
1645 * BFD hash data structures to find sessions.
1646 */
1647 static struct hash *bfd_id_hash;
1648 static struct hash *bfd_key_hash;
1649
1650 static unsigned int bfd_id_hash_do(const void *p);
1651 static unsigned int bfd_key_hash_do(const void *p);
1652
1653 static void _bfd_free(struct hash_bucket *hb,
1654 void *arg __attribute__((__unused__)));
1655
1656 /* BFD hash for our discriminator. */
1657 static unsigned int bfd_id_hash_do(const void *p)
1658 {
1659 const struct bfd_session *bs = p;
1660
1661 return jhash_1word(bs->discrs.my_discr, 0);
1662 }
1663
1664 static bool bfd_id_hash_cmp(const void *n1, const void *n2)
1665 {
1666 const struct bfd_session *bs1 = n1, *bs2 = n2;
1667
1668 return bs1->discrs.my_discr == bs2->discrs.my_discr;
1669 }
1670
1671 /* BFD hash for single hop. */
1672 static unsigned int bfd_key_hash_do(const void *p)
1673 {
1674 const struct bfd_session *bs = p;
1675
1676 return jhash(&bs->key, sizeof(bs->key), 0);
1677 }
1678
1679 static bool bfd_key_hash_cmp(const void *n1, const void *n2)
1680 {
1681 const struct bfd_session *bs1 = n1, *bs2 = n2;
1682
1683 return memcmp(&bs1->key, &bs2->key, sizeof(bs1->key)) == 0;
1684 }
1685
1686
1687 /*
1688 * Hash public interface / exported functions.
1689 */
1690
1691 /* Lookup functions. */
1692 struct bfd_session *bfd_id_lookup(uint32_t id)
1693 {
1694 struct bfd_session bs;
1695
1696 bs.discrs.my_discr = id;
1697
1698 return hash_lookup(bfd_id_hash, &bs);
1699 }
1700
1701 struct bfd_key_walk_partial_lookup {
1702 struct bfd_session *given;
1703 struct bfd_session *result;
1704 };
1705
1706 /* ignore some parameters */
1707 static int bfd_key_lookup_ignore_partial_walker(struct hash_bucket *b,
1708 void *data)
1709 {
1710 struct bfd_key_walk_partial_lookup *ctx =
1711 (struct bfd_key_walk_partial_lookup *)data;
1712 struct bfd_session *given = ctx->given;
1713 struct bfd_session *parsed = b->data;
1714
1715 if (given->key.family != parsed->key.family)
1716 return HASHWALK_CONTINUE;
1717 if (given->key.mhop != parsed->key.mhop)
1718 return HASHWALK_CONTINUE;
1719 if (memcmp(&given->key.peer, &parsed->key.peer,
1720 sizeof(struct in6_addr)))
1721 return HASHWALK_CONTINUE;
1722 if (memcmp(given->key.vrfname, parsed->key.vrfname, MAXNAMELEN))
1723 return HASHWALK_CONTINUE;
1724 ctx->result = parsed;
1725 /* ignore localaddr or interface */
1726 return HASHWALK_ABORT;
1727 }
1728
1729 struct bfd_session *bfd_key_lookup(struct bfd_key key)
1730 {
1731 struct bfd_session bs, *bsp;
1732 struct bfd_key_walk_partial_lookup ctx;
1733 char peer_buf[INET6_ADDRSTRLEN];
1734
1735 bs.key = key;
1736 bsp = hash_lookup(bfd_key_hash, &bs);
1737 if (bsp)
1738 return bsp;
1739
1740 inet_ntop(bs.key.family, &bs.key.peer, peer_buf,
1741 sizeof(peer_buf));
1742 /* Handle cases where local-address is optional. */
1743 if (memcmp(&bs.key.local, &zero_addr, sizeof(bs.key.local))) {
1744 memset(&bs.key.local, 0, sizeof(bs.key.local));
1745 bsp = hash_lookup(bfd_key_hash, &bs);
1746 if (bsp) {
1747 if (bglobal.debug_peer_event) {
1748 char addr_buf[INET6_ADDRSTRLEN];
1749 inet_ntop(bs.key.family, &key.local, addr_buf,
1750 sizeof(addr_buf));
1751 zlog_debug(
1752 " peer %s found, but loc-addr %s ignored",
1753 peer_buf, addr_buf);
1754 }
1755 return bsp;
1756 }
1757 }
1758
1759 bs.key = key;
1760 /* Handle cases where ifname is optional. */
1761 if (bs.key.ifname[0]) {
1762 memset(bs.key.ifname, 0, sizeof(bs.key.ifname));
1763 bsp = hash_lookup(bfd_key_hash, &bs);
1764 if (bsp) {
1765 if (bglobal.debug_peer_event)
1766 zlog_debug(" peer %s found, but ifp %s ignored",
1767 peer_buf, key.ifname);
1768 return bsp;
1769 }
1770 }
1771
1772 /* Handle cases where local-address and ifname are optional. */
1773 if (bs.key.family == AF_INET) {
1774 memset(&bs.key.local, 0, sizeof(bs.key.local));
1775 bsp = hash_lookup(bfd_key_hash, &bs);
1776 if (bsp) {
1777 if (bglobal.debug_peer_event) {
1778 char addr_buf[INET6_ADDRSTRLEN];
1779 inet_ntop(bs.key.family, &bs.key.local,
1780 addr_buf, sizeof(addr_buf));
1781 zlog_debug(
1782 " peer %s found, but ifp %s and loc-addr %s ignored",
1783 peer_buf, key.ifname, addr_buf);
1784 }
1785 return bsp;
1786 }
1787 }
1788 bs.key = key;
1789
1790 /* Handle case where a context more complex ctx is present.
1791 * input has no iface nor local-address, but a context may
1792 * exist.
1793 *
1794 * Only applies to IPv4, because IPv6 requires either
1795 * local-address or interface.
1796 */
1797 if (!bs.key.mhop && bs.key.family == AF_INET) {
1798 ctx.result = NULL;
1799 ctx.given = &bs;
1800 hash_walk(bfd_key_hash, &bfd_key_lookup_ignore_partial_walker,
1801 &ctx);
1802 /* change key */
1803 if (ctx.result) {
1804 bsp = ctx.result;
1805 if (bglobal.debug_peer_event)
1806 zlog_debug(
1807 " peer %s found, but ifp and/or loc-addr params ignored",
1808 peer_buf);
1809 }
1810 }
1811 return bsp;
1812 }
1813
1814 /*
1815 * Delete functions.
1816 *
1817 * Delete functions searches and remove the item from the hash and
1818 * returns a pointer to the removed item data. If the item was not found
1819 * then it returns NULL.
1820 *
1821 * The data stored inside the hash is not free()ed, so you must do it
1822 * manually after getting the pointer back.
1823 */
1824 struct bfd_session *bfd_id_delete(uint32_t id)
1825 {
1826 struct bfd_session bs;
1827
1828 bs.discrs.my_discr = id;
1829
1830 return hash_release(bfd_id_hash, &bs);
1831 }
1832
1833 struct bfd_session *bfd_key_delete(struct bfd_key key)
1834 {
1835 struct bfd_session bs, *bsp;
1836
1837 bs.key = key;
1838 bsp = hash_lookup(bfd_key_hash, &bs);
1839 if (bsp == NULL && key.ifname[0]) {
1840 memset(bs.key.ifname, 0, sizeof(bs.key.ifname));
1841 bsp = hash_lookup(bfd_key_hash, &bs);
1842 }
1843
1844 return hash_release(bfd_key_hash, bsp);
1845 }
1846
1847 /* Iteration functions. */
1848 void bfd_id_iterate(hash_iter_func hif, void *arg)
1849 {
1850 hash_iterate(bfd_id_hash, hif, arg);
1851 }
1852
1853 void bfd_key_iterate(hash_iter_func hif, void *arg)
1854 {
1855 hash_iterate(bfd_key_hash, hif, arg);
1856 }
1857
1858 /*
1859 * Insert functions.
1860 *
1861 * Inserts session into hash and returns `true` on success, otherwise
1862 * `false`.
1863 */
1864 bool bfd_id_insert(struct bfd_session *bs)
1865 {
1866 return (hash_get(bfd_id_hash, bs, hash_alloc_intern) == bs);
1867 }
1868
1869 bool bfd_key_insert(struct bfd_session *bs)
1870 {
1871 return (hash_get(bfd_key_hash, bs, hash_alloc_intern) == bs);
1872 }
1873
1874 void bfd_initialize(void)
1875 {
1876 bfd_id_hash = hash_create(bfd_id_hash_do, bfd_id_hash_cmp,
1877 "BFD session discriminator hash");
1878 bfd_key_hash = hash_create(bfd_key_hash_do, bfd_key_hash_cmp,
1879 "BFD session hash");
1880 TAILQ_INIT(&bplist);
1881 }
1882
1883 static void _bfd_free(struct hash_bucket *hb,
1884 void *arg __attribute__((__unused__)))
1885 {
1886 struct bfd_session *bs = hb->data;
1887
1888 bfd_session_free(bs);
1889 }
1890
1891 void bfd_shutdown(void)
1892 {
1893 struct bfd_profile *bp;
1894
1895 /*
1896 * Close and free all BFD sessions.
1897 *
1898 * _bfd_free() will call bfd_session_free() which will take care
1899 * of removing the session from all hashes, so we just run an
1900 * assert() here to make sure it really happened.
1901 */
1902 bfd_id_iterate(_bfd_free, NULL);
1903 assert(bfd_key_hash->count == 0);
1904
1905 /* Now free the hashes themselves. */
1906 hash_free(bfd_id_hash);
1907 hash_free(bfd_key_hash);
1908
1909 /* Free all profile allocations. */
1910 while ((bp = TAILQ_FIRST(&bplist)) != NULL)
1911 bfd_profile_free(bp);
1912 }
1913
1914 struct bfd_session_iterator {
1915 int bsi_stop;
1916 bool bsi_mhop;
1917 const struct bfd_session *bsi_bs;
1918 };
1919
1920 static int _bfd_session_next(struct hash_bucket *hb, void *arg)
1921 {
1922 struct bfd_session_iterator *bsi = arg;
1923 struct bfd_session *bs = hb->data;
1924
1925 /* Previous entry signaled stop. */
1926 if (bsi->bsi_stop == 1) {
1927 /* Match the single/multi hop sessions. */
1928 if (bs->key.mhop != bsi->bsi_mhop)
1929 return HASHWALK_CONTINUE;
1930
1931 bsi->bsi_bs = bs;
1932 return HASHWALK_ABORT;
1933 }
1934
1935 /* We found the current item, stop in the next one. */
1936 if (bsi->bsi_bs == hb->data) {
1937 bsi->bsi_stop = 1;
1938 /* Set entry to NULL to signal end of list. */
1939 bsi->bsi_bs = NULL;
1940 } else if (bsi->bsi_bs == NULL && bsi->bsi_mhop == bs->key.mhop) {
1941 /* We want the first list item. */
1942 bsi->bsi_stop = 1;
1943 bsi->bsi_bs = hb->data;
1944 return HASHWALK_ABORT;
1945 }
1946
1947 return HASHWALK_CONTINUE;
1948 }
1949
1950 /*
1951 * bfd_session_next: uses the current session to find the next.
1952 *
1953 * `bs` might point to NULL to get the first item of the data structure.
1954 */
1955 const struct bfd_session *bfd_session_next(const struct bfd_session *bs,
1956 bool mhop)
1957 {
1958 struct bfd_session_iterator bsi;
1959
1960 bsi.bsi_stop = 0;
1961 bsi.bsi_bs = bs;
1962 bsi.bsi_mhop = mhop;
1963 hash_walk(bfd_key_hash, _bfd_session_next, &bsi);
1964 if (bsi.bsi_stop == 0)
1965 return NULL;
1966
1967 return bsi.bsi_bs;
1968 }
1969
1970 static void _bfd_session_remove_manual(struct hash_bucket *hb,
1971 void *arg __attribute__((__unused__)))
1972 {
1973 struct bfd_session *bs = hb->data;
1974
1975 /* Delete only manually configured sessions. */
1976 if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG) == 0)
1977 return;
1978
1979 bs->refcount--;
1980 UNSET_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG);
1981
1982 /* Don't delete sessions still in use. */
1983 if (bs->refcount != 0)
1984 return;
1985
1986 bfd_session_free(bs);
1987 }
1988
1989 /*
1990 * bfd_sessions_remove_manual: remove all manually configured sessions.
1991 *
1992 * NOTE: this function doesn't remove automatically created sessions.
1993 */
1994 void bfd_sessions_remove_manual(void)
1995 {
1996 hash_iterate(bfd_key_hash, _bfd_session_remove_manual, NULL);
1997 }
1998
1999 /*
2000 * Profile related hash functions.
2001 */
2002 static void _bfd_profile_update(struct hash_bucket *hb, void *arg)
2003 {
2004 struct bfd_profile *bp = arg;
2005 struct bfd_session *bs = hb->data;
2006
2007 /* This session is not using the profile. */
2008 if (bs->profile_name == NULL || strcmp(bs->profile_name, bp->name) != 0)
2009 return;
2010
2011 bfd_profile_apply(bp->name, bs);
2012 }
2013
2014 void bfd_profile_update(struct bfd_profile *bp)
2015 {
2016 hash_iterate(bfd_key_hash, _bfd_profile_update, bp);
2017 }
2018
2019 static void _bfd_profile_detach(struct hash_bucket *hb, void *arg)
2020 {
2021 struct bfd_profile *bp = arg;
2022 struct bfd_session *bs = hb->data;
2023
2024 /* This session is not using the profile. */
2025 if (bs->profile_name == NULL || strcmp(bs->profile_name, bp->name) != 0)
2026 return;
2027
2028 bfd_profile_remove(bs);
2029 }
2030
2031 static void bfd_profile_detach(struct bfd_profile *bp)
2032 {
2033 hash_iterate(bfd_key_hash, _bfd_profile_detach, bp);
2034 }
2035
2036 /*
2037 * VRF related functions.
2038 */
2039 static int bfd_vrf_new(struct vrf *vrf)
2040 {
2041 if (bglobal.debug_zebra)
2042 zlog_debug("VRF Created: %s(%u)", vrf->name, vrf->vrf_id);
2043
2044 return 0;
2045 }
2046
2047 static int bfd_vrf_delete(struct vrf *vrf)
2048 {
2049 if (bglobal.debug_zebra)
2050 zlog_debug("VRF Deletion: %s(%u)", vrf->name, vrf->vrf_id);
2051
2052 return 0;
2053 }
2054
2055 static int bfd_vrf_update(struct vrf *vrf)
2056 {
2057 if (!vrf_is_enabled(vrf))
2058 return 0;
2059
2060 if (bglobal.debug_zebra)
2061 zlog_debug("VRF update: %s(%u)", vrf->name, vrf->vrf_id);
2062
2063 /* a different name is given; update bfd list */
2064 bfdd_sessions_enable_vrf(vrf);
2065 return 0;
2066 }
2067
2068 static int bfd_vrf_enable(struct vrf *vrf)
2069 {
2070 struct bfd_vrf_global *bvrf;
2071
2072 /* a different name */
2073 if (!vrf->info) {
2074 bvrf = XCALLOC(MTYPE_BFDD_VRF, sizeof(struct bfd_vrf_global));
2075 bvrf->vrf = vrf;
2076 vrf->info = (void *)bvrf;
2077
2078 /* Disable sockets if using data plane. */
2079 if (bglobal.bg_use_dplane) {
2080 bvrf->bg_shop = -1;
2081 bvrf->bg_mhop = -1;
2082 bvrf->bg_shop6 = -1;
2083 bvrf->bg_mhop6 = -1;
2084 bvrf->bg_echo = -1;
2085 bvrf->bg_echov6 = -1;
2086 }
2087 } else
2088 bvrf = vrf->info;
2089
2090 if (bglobal.debug_zebra)
2091 zlog_debug("VRF enable add %s id %u", vrf->name, vrf->vrf_id);
2092
2093 if (vrf->vrf_id == VRF_DEFAULT ||
2094 vrf_get_backend() == VRF_BACKEND_NETNS) {
2095 if (!bvrf->bg_shop)
2096 bvrf->bg_shop = bp_udp_shop(vrf);
2097 if (!bvrf->bg_mhop)
2098 bvrf->bg_mhop = bp_udp_mhop(vrf);
2099 if (!bvrf->bg_shop6)
2100 bvrf->bg_shop6 = bp_udp6_shop(vrf);
2101 if (!bvrf->bg_mhop6)
2102 bvrf->bg_mhop6 = bp_udp6_mhop(vrf);
2103 if (!bvrf->bg_echo)
2104 bvrf->bg_echo = bp_echo_socket(vrf);
2105 if (!bvrf->bg_echov6)
2106 bvrf->bg_echov6 = bp_echov6_socket(vrf);
2107
2108 if (!bvrf->bg_ev[0] && bvrf->bg_shop != -1)
2109 thread_add_read(master, bfd_recv_cb, bvrf,
2110 bvrf->bg_shop, &bvrf->bg_ev[0]);
2111 if (!bvrf->bg_ev[1] && bvrf->bg_mhop != -1)
2112 thread_add_read(master, bfd_recv_cb, bvrf,
2113 bvrf->bg_mhop, &bvrf->bg_ev[1]);
2114 if (!bvrf->bg_ev[2] && bvrf->bg_shop6 != -1)
2115 thread_add_read(master, bfd_recv_cb, bvrf,
2116 bvrf->bg_shop6, &bvrf->bg_ev[2]);
2117 if (!bvrf->bg_ev[3] && bvrf->bg_mhop6 != -1)
2118 thread_add_read(master, bfd_recv_cb, bvrf,
2119 bvrf->bg_mhop6, &bvrf->bg_ev[3]);
2120 if (!bvrf->bg_ev[4] && bvrf->bg_echo != -1)
2121 thread_add_read(master, bfd_recv_cb, bvrf,
2122 bvrf->bg_echo, &bvrf->bg_ev[4]);
2123 if (!bvrf->bg_ev[5] && bvrf->bg_echov6 != -1)
2124 thread_add_read(master, bfd_recv_cb, bvrf,
2125 bvrf->bg_echov6, &bvrf->bg_ev[5]);
2126 }
2127 if (vrf->vrf_id != VRF_DEFAULT) {
2128 bfdd_zclient_register(vrf->vrf_id);
2129 bfdd_sessions_enable_vrf(vrf);
2130 }
2131 return 0;
2132 }
2133
2134 static int bfd_vrf_disable(struct vrf *vrf)
2135 {
2136 struct bfd_vrf_global *bvrf;
2137
2138 if (!vrf->info)
2139 return 0;
2140 bvrf = vrf->info;
2141
2142 if (vrf->vrf_id != VRF_DEFAULT) {
2143 bfdd_sessions_disable_vrf(vrf);
2144 bfdd_zclient_unregister(vrf->vrf_id);
2145 }
2146
2147 if (bglobal.debug_zebra)
2148 zlog_debug("VRF disable %s id %d", vrf->name, vrf->vrf_id);
2149
2150 /* Disable read/write poll triggering. */
2151 THREAD_OFF(bvrf->bg_ev[0]);
2152 THREAD_OFF(bvrf->bg_ev[1]);
2153 THREAD_OFF(bvrf->bg_ev[2]);
2154 THREAD_OFF(bvrf->bg_ev[3]);
2155 THREAD_OFF(bvrf->bg_ev[4]);
2156 THREAD_OFF(bvrf->bg_ev[5]);
2157
2158 /* Close all descriptors. */
2159 socket_close(&bvrf->bg_echo);
2160 socket_close(&bvrf->bg_shop);
2161 socket_close(&bvrf->bg_mhop);
2162 if (bvrf->bg_shop6 != -1)
2163 socket_close(&bvrf->bg_shop6);
2164 if (bvrf->bg_mhop6 != -1)
2165 socket_close(&bvrf->bg_mhop6);
2166 socket_close(&bvrf->bg_echo);
2167 if (bvrf->bg_echov6 != -1)
2168 socket_close(&bvrf->bg_echov6);
2169
2170 /* free context */
2171 XFREE(MTYPE_BFDD_VRF, bvrf);
2172 vrf->info = NULL;
2173
2174 return 0;
2175 }
2176
2177 void bfd_vrf_init(void)
2178 {
2179 vrf_init(bfd_vrf_new, bfd_vrf_enable, bfd_vrf_disable,
2180 bfd_vrf_delete, bfd_vrf_update);
2181 }
2182
2183 void bfd_vrf_terminate(void)
2184 {
2185 vrf_terminate();
2186 }
2187
2188 struct bfd_vrf_global *bfd_vrf_look_by_session(struct bfd_session *bfd)
2189 {
2190 struct vrf *vrf;
2191
2192 if (!vrf_is_backend_netns()) {
2193 vrf = vrf_lookup_by_id(VRF_DEFAULT);
2194 if (vrf)
2195 return (struct bfd_vrf_global *)vrf->info;
2196 return NULL;
2197 }
2198 if (!bfd)
2199 return NULL;
2200 if (!bfd->vrf)
2201 return NULL;
2202 return bfd->vrf->info;
2203 }
2204
2205 void bfd_session_update_vrf_name(struct bfd_session *bs, struct vrf *vrf)
2206 {
2207 if (!vrf || !bs)
2208 return;
2209 /* update key */
2210 hash_release(bfd_key_hash, bs);
2211 /*
2212 * HACK: Change the BFD VRF in the running configuration directly,
2213 * bypassing the northbound layer. This is necessary to avoid deleting
2214 * the BFD and readding it in the new VRF, which would have
2215 * several implications.
2216 */
2217 if (yang_module_find("frr-bfdd") && bs->key.vrfname[0]) {
2218 struct lyd_node *bfd_dnode;
2219 char xpath[XPATH_MAXLEN], xpath_srcaddr[XPATH_MAXLEN + 32];
2220 char oldpath[XPATH_MAXLEN], newpath[XPATH_MAXLEN];
2221 char addr_buf[INET6_ADDRSTRLEN];
2222 int slen;
2223
2224 /* build xpath */
2225 if (bs->key.mhop) {
2226 inet_ntop(bs->key.family, &bs->key.local, addr_buf, sizeof(addr_buf));
2227 snprintf(xpath_srcaddr, sizeof(xpath_srcaddr), "[source-addr='%s']",
2228 addr_buf);
2229 } else
2230 xpath_srcaddr[0] = 0;
2231 inet_ntop(bs->key.family, &bs->key.peer, addr_buf, sizeof(addr_buf));
2232 slen = snprintf(xpath, sizeof(xpath),
2233 "/frr-bfdd:bfdd/bfd/sessions/%s%s[dest-addr='%s']",
2234 bs->key.mhop ? "multi-hop" : "single-hop", xpath_srcaddr,
2235 addr_buf);
2236 if (bs->key.ifname[0])
2237 slen += snprintf(xpath + slen, sizeof(xpath) - slen,
2238 "[interface='%s']", bs->key.ifname);
2239 else
2240 slen += snprintf(xpath + slen, sizeof(xpath) - slen,
2241 "[interface='*']");
2242 snprintf(xpath + slen, sizeof(xpath) - slen, "[vrf='%s']/vrf",
2243 bs->key.vrfname);
2244
2245 bfd_dnode = yang_dnode_get(running_config->dnode, xpath,
2246 bs->key.vrfname);
2247 if (bfd_dnode) {
2248 yang_dnode_get_path(bfd_dnode->parent, oldpath,
2249 sizeof(oldpath));
2250 yang_dnode_change_leaf(bfd_dnode, vrf->name);
2251 yang_dnode_get_path(bfd_dnode->parent, newpath,
2252 sizeof(newpath));
2253 nb_running_move_tree(oldpath, newpath);
2254 running_config->version++;
2255 }
2256 }
2257 memset(bs->key.vrfname, 0, sizeof(bs->key.vrfname));
2258 strlcpy(bs->key.vrfname, vrf->name, sizeof(bs->key.vrfname));
2259 hash_get(bfd_key_hash, bs, hash_alloc_intern);
2260 }
2261
2262 unsigned long bfd_get_session_count(void)
2263 {
2264 return bfd_key_hash->count;
2265 }