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