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