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