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