]> git.proxmox.com Git - mirror_frr.git/blob - bfdd/bfd.c
bfdd: clean up header inclusion
[mirror_frr.git] / bfdd / bfd.c
1 /*********************************************************************
2 * Copyright 2013 Cumulus Networks, LLC. All rights reserved.
3 * Copyright 2014,2015,2016,2017 Cumulus Networks, Inc. All rights reserved.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the Free
7 * Software Foundation; either version 2 of the License, or (at your option)
8 * any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 * more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; see the file COPYING; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 *
19 * bfd.c: implements the BFD protocol.
20 *
21 * Authors
22 * -------
23 * Shrijeet Mukherjee [shm@cumulusnetworks.com]
24 * Kanna Rajagopal [kanna@cumulusnetworks.com]
25 * Radhika Mahankali [Radhika@cumulusnetworks.com]
26 */
27
28 #include <zebra.h>
29
30 #include "lib/jhash.h"
31
32 #include "bfd.h"
33
34 DEFINE_QOBJ_TYPE(bfd_session);
35
36 /*
37 * Prototypes
38 */
39 static uint32_t ptm_bfd_gen_ID(void);
40 static void ptm_bfd_echo_xmt_TO(struct bfd_session *bfd);
41 static void bfd_session_free(struct bfd_session *bs);
42 static struct bfd_session *bfd_session_new(int sd);
43 static struct bfd_session *bfd_find_disc(struct sockaddr_any *sa,
44 uint32_t ldisc);
45 static int bfd_session_update(struct bfd_session *bs, struct bfd_peer_cfg *bpc);
46 static const char *get_diag_str(int diag);
47
48
49 /*
50 * Functions
51 */
52 struct bfd_session *bs_peer_find(struct bfd_peer_cfg *bpc)
53 {
54 struct bfd_session *bs;
55 struct peer_label *pl;
56 struct bfd_mhop_key mhop;
57 struct bfd_shop_key shop;
58
59 /* Try to find label first. */
60 if (bpc->bpc_has_label) {
61 pl = pl_find(bpc->bpc_label);
62 if (pl != NULL) {
63 bs = pl->pl_bs;
64 return bs;
65 }
66 }
67
68 /* Otherwise fallback to peer/local hash lookup. */
69 if (bpc->bpc_mhop) {
70 memset(&mhop, 0, sizeof(mhop));
71 mhop.peer = bpc->bpc_peer;
72 mhop.local = bpc->bpc_local;
73 if (bpc->bpc_has_vrfname)
74 strlcpy(mhop.vrf_name, bpc->bpc_vrfname,
75 sizeof(mhop.vrf_name));
76
77 bs = bfd_mhop_lookup(mhop);
78 } else {
79 memset(&shop, 0, sizeof(shop));
80 shop.peer = bpc->bpc_peer;
81 if (!bpc->bpc_has_vxlan && bpc->bpc_has_localif)
82 strlcpy(shop.port_name, bpc->bpc_localif,
83 sizeof(shop.port_name));
84
85 bs = bfd_shop_lookup(shop);
86 }
87
88 return bs;
89 }
90
91 static uint32_t ptm_bfd_gen_ID(void)
92 {
93 static uint32_t sessionID = 1;
94
95 return (sessionID++);
96 }
97
98 void ptm_bfd_start_xmt_timer(struct bfd_session *bfd, bool is_echo)
99 {
100 uint64_t jitter, xmt_TO;
101 int maxpercent;
102
103 xmt_TO = is_echo ? bfd->echo_xmt_TO : bfd->xmt_TO;
104
105 /*
106 * From section 6.5.2: trasmit interval should be randomly jittered
107 * between
108 * 75% and 100% of nominal value, unless detect_mult is 1, then should
109 * be
110 * between 75% and 90%.
111 */
112 maxpercent = (bfd->detect_mult == 1) ? 16 : 26;
113 jitter = (xmt_TO * (75 + (random() % maxpercent))) / 100;
114 /* XXX remove that division above */
115
116 if (is_echo)
117 bfd_echo_xmttimer_update(bfd, jitter);
118 else
119 bfd_xmttimer_update(bfd, jitter);
120 }
121
122 static void ptm_bfd_echo_xmt_TO(struct bfd_session *bfd)
123 {
124 /* Send the scheduled echo packet */
125 ptm_bfd_echo_snd(bfd);
126
127 /* Restart the timer for next time */
128 ptm_bfd_start_xmt_timer(bfd, true);
129 }
130
131 void ptm_bfd_xmt_TO(struct bfd_session *bfd, int fbit)
132 {
133 /* Send the scheduled control packet */
134 ptm_bfd_snd(bfd, fbit);
135
136 /* Restart the timer for next time */
137 ptm_bfd_start_xmt_timer(bfd, false);
138 }
139
140 void ptm_bfd_echo_stop(struct bfd_session *bfd, int polling)
141 {
142 bfd->echo_xmt_TO = 0;
143 bfd->echo_detect_TO = 0;
144 BFD_UNSET_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE);
145
146 bfd_echo_xmttimer_delete(bfd);
147 bfd_echo_recvtimer_delete(bfd);
148
149 if (polling) {
150 bfd->polling = polling;
151 bfd->new_timers.desired_min_tx = bfd->up_min_tx;
152 bfd->new_timers.required_min_rx = bfd->timers.required_min_rx;
153 ptm_bfd_snd(bfd, 0);
154 }
155 }
156
157 void ptm_bfd_echo_start(struct bfd_session *bfd)
158 {
159 bfd->echo_detect_TO = (bfd->remote_detect_mult * bfd->echo_xmt_TO);
160 ptm_bfd_echo_xmt_TO(bfd);
161
162 bfd->polling = 1;
163 bfd->new_timers.desired_min_tx = bfd->up_min_tx;
164 bfd->new_timers.required_min_rx = bfd->timers.required_min_rx;
165 ptm_bfd_snd(bfd, 0);
166 }
167
168 void ptm_bfd_ses_up(struct bfd_session *bfd)
169 {
170 bfd->local_diag = 0;
171 bfd->ses_state = PTM_BFD_UP;
172 bfd->polling = 1;
173 monotime(&bfd->uptime);
174
175 /* If the peer is capable to receiving Echo pkts */
176 if (bfd->echo_xmt_TO && !BFD_CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_MH)) {
177 ptm_bfd_echo_start(bfd);
178 } else {
179 bfd->new_timers.desired_min_tx = bfd->up_min_tx;
180 bfd->new_timers.required_min_rx = bfd->timers.required_min_rx;
181 ptm_bfd_snd(bfd, 0);
182 }
183
184 control_notify(bfd);
185
186 INFOLOG("Session 0x%x up peer %s", bfd->discrs.my_discr,
187 satostr(&bfd->shop.peer));
188 }
189
190 void ptm_bfd_ses_dn(struct bfd_session *bfd, uint8_t diag)
191 {
192 int old_state = bfd->ses_state;
193
194 bfd->local_diag = diag;
195 bfd->discrs.remote_discr = 0;
196 bfd->ses_state = PTM_BFD_DOWN;
197 bfd->polling = 0;
198 bfd->demand_mode = 0;
199 monotime(&bfd->downtime);
200
201 ptm_bfd_snd(bfd, 0);
202
203 /* only signal clients when going from up->down state */
204 if (old_state == PTM_BFD_UP)
205 control_notify(bfd);
206
207 INFOLOG("Session 0x%x down peer %s Rsn %s prev st %s",
208 bfd->discrs.my_discr, satostr(&bfd->shop.peer),
209 get_diag_str(bfd->local_diag), state_list[old_state].str);
210
211 /* Stop echo packet transmission if they are active */
212 if (BFD_CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE))
213 ptm_bfd_echo_stop(bfd, 0);
214 }
215
216 static int ptm_bfd_get_vrf_name(char *port_name, char *vrf_name)
217 {
218 struct bfd_iface *iface;
219 struct bfd_vrf *vrf;
220
221 if ((port_name == NULL) || (vrf_name == NULL))
222 return -1;
223
224 iface = bfd_iface_lookup(port_name);
225 if (iface) {
226 vrf = bfd_vrf_lookup(iface->vrf_id);
227 if (vrf) {
228 strlcpy(vrf_name, vrf->name, sizeof(vrf->name));
229 return 0;
230 }
231 }
232 return -1;
233 }
234
235 static struct bfd_session *bfd_find_disc(struct sockaddr_any *sa,
236 uint32_t ldisc)
237 {
238 struct bfd_session *bs;
239
240 bs = bfd_id_lookup(ldisc);
241 if (bs == NULL)
242 return NULL;
243
244 /* Remove unused fields. */
245 switch (sa->sa_sin.sin_family) {
246 case AF_INET:
247 sa->sa_sin.sin_port = 0;
248 if (memcmp(sa, &bs->shop.peer, sizeof(sa->sa_sin)) == 0)
249 return bs;
250 break;
251 case AF_INET6:
252 sa->sa_sin6.sin6_port = 0;
253 if (memcmp(sa, &bs->shop.peer, sizeof(sa->sa_sin6)) == 0)
254 return bs;
255 break;
256 }
257
258 return NULL;
259 }
260
261 struct bfd_session *ptm_bfd_sess_find(struct bfd_pkt *cp, char *port_name,
262 struct sockaddr_any *peer,
263 struct sockaddr_any *local,
264 char *vrf_name, bool is_mhop)
265 {
266 struct bfd_session *l_bfd = NULL;
267 struct bfd_mhop_key mhop;
268 struct bfd_shop_key shop;
269 char vrf_buf[MAXNAMELEN];
270
271 /* Find our session using the ID signaled by the remote end. */
272 if (cp->discrs.remote_discr)
273 return bfd_find_disc(peer, ntohl(cp->discrs.remote_discr));
274
275 /* Search for session without using discriminator. */
276 if (is_mhop) {
277 memset(&mhop, 0, sizeof(mhop));
278 mhop.peer = *peer;
279 mhop.local = *local;
280 if (vrf_name && vrf_name[0]) {
281 strlcpy(mhop.vrf_name, vrf_name, sizeof(mhop.vrf_name));
282 } else if (port_name && port_name[0]) {
283 memset(vrf_buf, 0, sizeof(vrf_buf));
284 if (ptm_bfd_get_vrf_name(port_name, vrf_buf) != -1)
285 strlcpy(mhop.vrf_name, vrf_buf, sizeof(mhop.vrf_name));
286 }
287
288 l_bfd = bfd_mhop_lookup(mhop);
289 } else {
290 memset(&shop, 0, sizeof(shop));
291 shop.peer = *peer;
292 if (port_name && port_name[0])
293 strlcpy(shop.port_name, port_name, sizeof(shop.port_name));
294
295 l_bfd = bfd_shop_lookup(shop);
296 }
297
298 /* XXX maybe remoteDiscr should be checked for remoteHeard cases. */
299 return l_bfd;
300 }
301
302 #if 0 /* TODO VxLAN Support */
303 static void
304 _update_vxlan_sess_parms(struct bfd_session *bfd, bfd_sess_parms *sess_parms)
305 {
306 struct bfd_session_vxlan_info *vxlan_info = &bfd->vxlan_info;
307 bfd_parms_list *parms = &sess_parms->parms;
308
309 vxlan_info->vnid = parms->vnid;
310 vxlan_info->check_tnl_key = parms->check_tnl_key;
311 vxlan_info->forwarding_if_rx = parms->forwarding_if_rx;
312 vxlan_info->cpath_down = parms->cpath_down;
313 vxlan_info->decay_min_rx = parms->decay_min_rx;
314
315 inet_aton(parms->local_dst_ip, &vxlan_info->local_dst_ip);
316 inet_aton(parms->remote_dst_ip, &vxlan_info->peer_dst_ip);
317
318 memcpy(vxlan_info->local_dst_mac, parms->local_dst_mac, ETH_ALEN);
319 memcpy(vxlan_info->peer_dst_mac, parms->remote_dst_mac, ETH_ALEN);
320
321 /* The interface may change for Vxlan BFD sessions, so update
322 * the local mac and ifindex
323 */
324 bfd->ifindex = sess_parms->ifindex;
325 memcpy(bfd->local_mac, sess_parms->local_mac, sizeof(bfd->local_mac));
326 }
327 #endif /* VxLAN support */
328
329 int bfd_xmt_cb(struct thread *t)
330 {
331 struct bfd_session *bs = THREAD_ARG(t);
332
333 ptm_bfd_xmt_TO(bs, 0);
334
335 return 0;
336 }
337
338 int bfd_echo_xmt_cb(struct thread *t)
339 {
340 struct bfd_session *bs = THREAD_ARG(t);
341
342 ptm_bfd_echo_xmt_TO(bs);
343
344 return 0;
345 }
346
347 /* Was ptm_bfd_detect_TO() */
348 int bfd_recvtimer_cb(struct thread *t)
349 {
350 struct bfd_session *bs = THREAD_ARG(t);
351 uint8_t old_state;
352
353 old_state = bs->ses_state;
354
355 switch (bs->ses_state) {
356 case PTM_BFD_INIT:
357 case PTM_BFD_UP:
358 ptm_bfd_ses_dn(bs, BFD_DIAGDETECTTIME);
359 INFOLOG("%s Detect timeout on session 0x%x with peer %s, in state %d",
360 __func__, bs->discrs.my_discr, satostr(&bs->shop.peer),
361 bs->ses_state);
362 bfd_recvtimer_update(bs);
363 break;
364
365 default:
366 /* Second detect time expiration, zero remote discr (section
367 * 6.5.1)
368 */
369 bs->discrs.remote_discr = 0;
370 break;
371 }
372
373 if (old_state != bs->ses_state) {
374 DLOG("BFD Sess %d [%s] Old State [%s] : New State [%s]",
375 bs->discrs.my_discr, satostr(&bs->shop.peer),
376 state_list[old_state].str, state_list[bs->ses_state].str);
377 }
378
379 return 0;
380 }
381
382 /* Was ptm_bfd_echo_detect_TO() */
383 int bfd_echo_recvtimer_cb(struct thread *t)
384 {
385 struct bfd_session *bs = THREAD_ARG(t);
386 uint8_t old_state;
387
388 old_state = bs->ses_state;
389
390 switch (bs->ses_state) {
391 case PTM_BFD_INIT:
392 case PTM_BFD_UP:
393 ptm_bfd_ses_dn(bs, BFD_DIAGDETECTTIME);
394 INFOLOG("%s Detect timeout on session 0x%x with peer %s, in state %d",
395 __func__, bs->discrs.my_discr, satostr(&bs->shop.peer),
396 bs->ses_state);
397 break;
398 }
399
400 if (old_state != bs->ses_state) {
401 DLOG("BFD Sess %d [%s] Old State [%s] : New State [%s]",
402 bs->discrs.my_discr, satostr(&bs->shop.peer),
403 state_list[old_state].str, state_list[bs->ses_state].str);
404 }
405
406 return 0;
407 }
408
409 static struct bfd_session *bfd_session_new(int sd)
410 {
411 struct bfd_session *bs;
412
413 bs = XCALLOC(MTYPE_BFDD_CONFIG, sizeof(*bs));
414 if (bs == NULL)
415 return NULL;
416
417 QOBJ_REG(bs, bfd_session);
418
419 bs->up_min_tx = BFD_DEFDESIREDMINTX;
420 bs->timers.required_min_rx = BFD_DEFREQUIREDMINRX;
421 bs->timers.required_min_echo = BFD_DEF_REQ_MIN_ECHO;
422 bs->detect_mult = BFD_DEFDETECTMULT;
423 bs->mh_ttl = BFD_DEF_MHOP_TTL;
424
425 bs->sock = sd;
426 monotime(&bs->uptime);
427 bs->downtime = bs->uptime;
428
429 return bs;
430 }
431
432 int bfd_session_update_label(struct bfd_session *bs, const char *nlabel)
433 {
434 /* New label treatment:
435 * - Check if the label is taken;
436 * - Try to allocate the memory for it and register;
437 */
438 if (bs->pl == NULL) {
439 if (pl_find(nlabel) != NULL) {
440 /* Someone is already using it. */
441 return -1;
442 }
443
444 if (pl_new(nlabel, bs) == NULL)
445 return -1;
446
447 return 0;
448 }
449
450 /*
451 * Test label change consistency:
452 * - Do nothing if it's the same label;
453 * - Check if the future label is already taken;
454 * - Change label;
455 */
456 if (strcmp(nlabel, bs->pl->pl_label) == 0)
457 return -1;
458 if (pl_find(nlabel) != NULL)
459 return -1;
460
461 strlcpy(bs->pl->pl_label, nlabel, sizeof(bs->pl->pl_label));
462 return 0;
463 }
464
465 static void _bfd_session_update(struct bfd_session *bs,
466 struct bfd_peer_cfg *bpc)
467 {
468 if (bpc->bpc_echo) {
469 /* Check if echo mode is already active. */
470 if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO))
471 goto skip_echo;
472
473 BFD_SET_FLAG(bs->flags, BFD_SESS_FLAG_ECHO);
474 ptm_bfd_echo_start(bs);
475
476 /* Activate/update echo receive timeout timer. */
477 bfd_echo_recvtimer_update(bs);
478 } else {
479 /* Check if echo mode is already disabled. */
480 if (!BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO))
481 goto skip_echo;
482
483 BFD_UNSET_FLAG(bs->flags, BFD_SESS_FLAG_ECHO);
484 ptm_bfd_echo_stop(bs, 0);
485 }
486
487 skip_echo:
488 if (bpc->bpc_has_txinterval)
489 bs->up_min_tx = bpc->bpc_txinterval * 1000;
490
491 if (bpc->bpc_has_recvinterval)
492 bs->timers.required_min_rx = bpc->bpc_recvinterval * 1000;
493
494 if (bpc->bpc_has_detectmultiplier)
495 bs->detect_mult = bpc->bpc_detectmultiplier;
496
497 if (bpc->bpc_has_echointerval)
498 bs->timers.required_min_echo = bpc->bpc_echointerval * 1000;
499
500 if (bpc->bpc_has_label)
501 bfd_session_update_label(bs, bpc->bpc_label);
502
503 if (bpc->bpc_shutdown) {
504 /* Check if already shutdown. */
505 if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN))
506 return;
507
508 BFD_SET_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN);
509
510 /* Disable all events. */
511 bfd_recvtimer_delete(bs);
512 bfd_echo_recvtimer_delete(bs);
513 bfd_xmttimer_delete(bs);
514 bfd_echo_xmttimer_delete(bs);
515
516 /* Change and notify state change. */
517 bs->ses_state = PTM_BFD_ADM_DOWN;
518 control_notify(bs);
519
520 ptm_bfd_snd(bs, 0);
521 } else {
522 /* Check if already working. */
523 if (!BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN))
524 return;
525
526 BFD_UNSET_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN);
527
528 /* Change and notify state change. */
529 bs->ses_state = PTM_BFD_DOWN;
530 control_notify(bs);
531
532 /* Enable all timers. */
533 bfd_recvtimer_update(bs);
534 bfd_xmttimer_update(bs, bs->xmt_TO);
535 if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO)) {
536 bfd_echo_recvtimer_update(bs);
537 bfd_echo_xmttimer_update(bs, bs->echo_xmt_TO);
538 }
539 }
540 }
541
542 static int bfd_session_update(struct bfd_session *bs, struct bfd_peer_cfg *bpc)
543 {
544 /* User didn't want to update, return failure. */
545 if (bpc->bpc_createonly)
546 return -1;
547
548 _bfd_session_update(bs, bpc);
549
550 /* TODO add VxLAN support. */
551
552 control_notify_config(BCM_NOTIFY_CONFIG_UPDATE, bs);
553
554 return 0;
555 }
556
557 static void bfd_session_free(struct bfd_session *bs)
558 {
559 if (bs->sock != -1)
560 close(bs->sock);
561
562 bfd_recvtimer_delete(bs);
563 bfd_echo_recvtimer_delete(bs);
564 bfd_xmttimer_delete(bs);
565 bfd_echo_xmttimer_delete(bs);
566
567 bfd_id_delete(bs->discrs.my_discr);
568 if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH))
569 bfd_mhop_delete(bs->mhop);
570 else
571 bfd_shop_delete(bs->shop);
572
573 pl_free(bs->pl);
574
575 QOBJ_UNREG(bs);
576 XFREE(MTYPE_BFDD_CONFIG, bs);
577 }
578
579 struct bfd_session *ptm_bfd_sess_new(struct bfd_peer_cfg *bpc)
580 {
581 struct bfd_session *bfd, *l_bfd;
582 int psock;
583
584 /* check to see if this needs a new session */
585 l_bfd = bs_peer_find(bpc);
586 if (l_bfd) {
587 /* Requesting a duplicated peer means update configuration. */
588 if (bfd_session_update(l_bfd, bpc) == 0)
589 return l_bfd;
590 else
591 return NULL;
592 }
593
594 /*
595 * Get socket for transmitting control packets. Note that if we
596 * could use the destination port (3784) for the source
597 * port we wouldn't need a socket per session.
598 */
599 if (bpc->bpc_ipv4) {
600 psock = bp_peer_socket(bpc);
601 if (psock == -1) {
602 ERRLOG("Can't get socket for new session: %s",
603 strerror(errno));
604 return NULL;
605 }
606 } else {
607 psock = bp_peer_socketv6(bpc);
608 if (psock == -1) {
609 ERRLOG("Can't get IPv6 socket for new session: %s",
610 strerror(errno));
611 return NULL;
612 }
613 }
614
615 /* Get memory */
616 bfd = bfd_session_new(psock);
617 if (bfd == NULL) {
618 ERRLOG("Can't malloc memory for new session: %s",
619 strerror(errno));
620 return NULL;
621 }
622
623 if (bpc->bpc_has_localif && !bpc->bpc_mhop) {
624 bfd->ifindex = ptm_bfd_fetch_ifindex(bpc->bpc_localif);
625 ptm_bfd_fetch_local_mac(bpc->bpc_localif, bfd->local_mac);
626 }
627
628 if (bpc->bpc_has_vxlan)
629 BFD_SET_FLAG(bfd->flags, BFD_SESS_FLAG_VXLAN);
630
631 if (bpc->bpc_ipv4 == false)
632 BFD_SET_FLAG(bfd->flags, BFD_SESS_FLAG_IPV6);
633
634 /* Initialize the session */
635 bfd->ses_state = PTM_BFD_DOWN;
636 bfd->discrs.my_discr = ptm_bfd_gen_ID();
637 bfd->discrs.remote_discr = 0;
638 bfd->local_ip = bpc->bpc_local;
639 bfd->local_address = bpc->bpc_local;
640 bfd->timers.desired_min_tx = bfd->up_min_tx;
641 bfd->detect_TO = (bfd->detect_mult * BFD_DEF_SLOWTX);
642
643 /* Use detect_TO first for slow detection, then use recvtimer_update. */
644 bfd_recvtimer_update(bfd);
645
646 bfd_id_insert(bfd);
647
648 if (bpc->bpc_mhop) {
649 BFD_SET_FLAG(bfd->flags, BFD_SESS_FLAG_MH);
650 bfd->mhop.peer = bpc->bpc_peer;
651 bfd->mhop.local = bpc->bpc_local;
652 if (bpc->bpc_has_vrfname)
653 strlcpy(bfd->mhop.vrf_name, bpc->bpc_vrfname,
654 sizeof(bfd->mhop.vrf_name));
655
656 bfd_mhop_insert(bfd);
657 } else {
658 bfd->shop.peer = bpc->bpc_peer;
659 if (!bpc->bpc_has_vxlan && bpc->bpc_has_localif)
660 strlcpy(bfd->shop.port_name, bpc->bpc_localif,
661 sizeof(bfd->shop.port_name));
662
663 bfd_shop_insert(bfd);
664 }
665
666 if (BFD_CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_VXLAN)) {
667 static uint8_t bfd_def_vxlan_dmac[] = {0x00, 0x23, 0x20,
668 0x00, 0x00, 0x01};
669 memcpy(bfd->peer_mac, bfd_def_vxlan_dmac,
670 sizeof(bfd_def_vxlan_dmac));
671 }
672 #if 0 /* TODO */
673 else if (event->rmac) {
674 if (sscanf(event->rmac, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
675 &bfd->peer_mac[0], &bfd->peer_mac[1], &bfd->peer_mac[2],
676 &bfd->peer_mac[3], &bfd->peer_mac[4], &bfd->peer_mac[5])
677 != 6)
678 DLOG("%s: Assigning remote mac = %s", __func__,
679 event->rmac);
680 }
681 #endif
682
683 /*
684 * XXX: session update triggers echo start, so we must have our
685 * discriminator ID set first.
686 */
687 _bfd_session_update(bfd, bpc);
688
689 /* Start transmitting with slow interval until peer responds */
690 bfd->xmt_TO = BFD_DEF_SLOWTX;
691
692 ptm_bfd_xmt_TO(bfd, 0);
693
694 if (bpc->bpc_mhop) {
695 INFOLOG("Created new session 0x%x with vrf %s peer %s local %s",
696 bfd->discrs.my_discr,
697 (bpc->bpc_has_vrfname) ? bfd->mhop.vrf_name : "N/A",
698 satostr(&bfd->mhop.peer), satostr(&bfd->mhop.local));
699 } else {
700 INFOLOG("Created new session 0x%x with peer %s port %s",
701 bfd->discrs.my_discr, satostr(&bfd->shop.peer),
702 bfd->shop.port_name[0] ? bfd->shop.port_name : "N/A");
703 }
704
705 control_notify_config(BCM_NOTIFY_CONFIG_ADD, bfd);
706
707 return bfd;
708 }
709
710 int ptm_bfd_ses_del(struct bfd_peer_cfg *bpc)
711 {
712 struct bfd_session *bs;
713
714 /* Find session and call free(). */
715 bs = bs_peer_find(bpc);
716 if (bs == NULL)
717 return -1;
718
719 /* This pointer is being referenced, don't let it be deleted. */
720 if (bs->refcount > 0) {
721 zlog_debug("%s: trying to free in-use session: %" PRIu64
722 " references",
723 __func__, bs->refcount);
724 return -1;
725 }
726
727 if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)) {
728 INFOLOG("Deleting session 0x%x with vrf %s peer %s local %s",
729 bs->discrs.my_discr,
730 bpc->bpc_has_vrfname ? bpc->bpc_vrfname : "N/A",
731 satostr(&bs->mhop.peer), satostr(&bs->mhop.local));
732 } else {
733 INFOLOG("Deleting session 0x%x with peer %s port %s",
734 bs->discrs.my_discr, satostr(&bs->shop.peer),
735 bs->shop.port_name);
736 }
737
738 control_notify_config(BCM_NOTIFY_CONFIG_DELETE, bs);
739
740 bfd_session_free(bs);
741
742 return 0;
743 }
744
745 void bfd_set_polling(struct bfd_session *bs)
746 {
747 bs->new_timers.desired_min_tx = bs->up_min_tx;
748 bs->new_timers.required_min_rx = bs->timers.required_min_rx;
749 bs->new_timers.required_min_echo = bs->timers.required_min_echo;
750 bs->polling = 1;
751 }
752
753
754 /*
755 * Helper functions.
756 */
757 static const char *get_diag_str(int diag)
758 {
759 for (int i = 0; diag_list[i].str; i++) {
760 if (diag_list[i].type == diag)
761 return diag_list[i].str;
762 }
763 return "N/A";
764 }
765
766 const char *satostr(struct sockaddr_any *sa)
767 {
768 #define INETSTR_BUFCOUNT 8
769 static char buf[INETSTR_BUFCOUNT][INET6_ADDRSTRLEN];
770 static int bufidx;
771 struct sockaddr_in *sin = &sa->sa_sin;
772 struct sockaddr_in6 *sin6 = &sa->sa_sin6;
773
774 bufidx += (bufidx + 1) % INETSTR_BUFCOUNT;
775 buf[bufidx][0] = 0;
776
777 switch (sin->sin_family) {
778 case AF_INET:
779 inet_ntop(AF_INET, &sin->sin_addr, buf[bufidx],
780 sizeof(buf[bufidx]));
781 break;
782 case AF_INET6:
783 inet_ntop(AF_INET6, &sin6->sin6_addr, buf[bufidx],
784 sizeof(buf[bufidx]));
785 break;
786
787 default:
788 strlcpy(buf[bufidx], "unknown", sizeof(buf[bufidx]));
789 break;
790 }
791
792 return buf[bufidx];
793 }
794
795 const char *diag2str(uint8_t diag)
796 {
797 switch (diag) {
798 case 0:
799 return "ok";
800 case 1:
801 return "control detection time expired";
802 case 2:
803 return "echo function failed";
804 case 3:
805 return "neighbor signaled session down";
806 case 4:
807 return "forwarding plane reset";
808 case 5:
809 return "path down";
810 case 6:
811 return "concatenated path down";
812 case 7:
813 return "administratively down";
814 case 8:
815 return "reverse concatenated path down";
816 default:
817 return "unknown";
818 }
819 }
820
821 int strtosa(const char *addr, struct sockaddr_any *sa)
822 {
823 memset(sa, 0, sizeof(*sa));
824
825 if (inet_pton(AF_INET, addr, &sa->sa_sin.sin_addr) == 1) {
826 sa->sa_sin.sin_family = AF_INET;
827 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
828 sa->sa_sin.sin_len = sizeof(sa->sa_sin);
829 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
830 return 0;
831 }
832
833 if (inet_pton(AF_INET6, addr, &sa->sa_sin6.sin6_addr) == 1) {
834 sa->sa_sin6.sin6_family = AF_INET6;
835 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
836 sa->sa_sin6.sin6_len = sizeof(sa->sa_sin6);
837 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
838 return 0;
839 }
840
841 return -1;
842 }
843
844 void integer2timestr(uint64_t time, char *buf, size_t buflen)
845 {
846 unsigned int year, month, day, hour, minute, second;
847 int rv;
848
849 #define MINUTES (60)
850 #define HOURS (24 * MINUTES)
851 #define DAYS (30 * HOURS)
852 #define MONTHS (12 * DAYS)
853 #define YEARS (MONTHS)
854 if (time >= YEARS) {
855 year = time / YEARS;
856 time -= year * YEARS;
857
858 rv = snprintf(buf, buflen, "%u year(s), ", year);
859 buf += rv;
860 buflen -= rv;
861 }
862 if (time >= MONTHS) {
863 month = time / MONTHS;
864 time -= month * MONTHS;
865
866 rv = snprintf(buf, buflen, "%u month(s), ", month);
867 buf += rv;
868 buflen -= rv;
869 }
870 if (time >= DAYS) {
871 day = time / DAYS;
872 time -= day * DAYS;
873
874 rv = snprintf(buf, buflen, "%u day(s), ", day);
875 buf += rv;
876 buflen -= rv;
877 }
878 if (time >= HOURS) {
879 hour = time / HOURS;
880 time -= hour * HOURS;
881
882 rv = snprintf(buf, buflen, "%u hour(s), ", hour);
883 buf += rv;
884 buflen -= rv;
885 }
886 if (time >= MINUTES) {
887 minute = time / MINUTES;
888 time -= minute * MINUTES;
889
890 rv = snprintf(buf, buflen, "%u minute(s), ", minute);
891 buf += rv;
892 buflen -= rv;
893 }
894 second = time % MINUTES;
895 snprintf(buf, buflen, "%u second(s)", second);
896 }
897
898
899 /*
900 * BFD hash data structures to find sessions.
901 */
902 static struct hash *bfd_id_hash;
903 static struct hash *bfd_shop_hash;
904 static struct hash *bfd_mhop_hash;
905 static struct hash *bfd_vrf_hash;
906 static struct hash *bfd_iface_hash;
907
908 static unsigned int bfd_id_hash_do(void *p);
909 static int bfd_id_hash_cmp(const void *n1, const void *n2);
910 static unsigned int bfd_shop_hash_do(void *p);
911 static int bfd_shop_hash_cmp(const void *n1, const void *n2);
912 static unsigned int bfd_mhop_hash_do(void *p);
913 static int bfd_mhop_hash_cmp(const void *n1, const void *n2);
914 static unsigned int bfd_vrf_hash_do(void *p);
915 static int bfd_vrf_hash_cmp(const void *n1, const void *n2);
916 static unsigned int bfd_iface_hash_do(void *p);
917 static int bfd_iface_hash_cmp(const void *n1, const void *n2);
918
919 static void _shop_key(struct bfd_session *bs, const struct bfd_shop_key *shop);
920 static void _shop_key2(struct bfd_session *bs, const struct bfd_shop_key *shop);
921 static void _mhop_key(struct bfd_session *bs, const struct bfd_mhop_key *mhop);
922 static int _iface_key(struct bfd_iface *iface, const char *ifname);
923
924 static void _bfd_free(struct hash_backet *hb,
925 void *arg __attribute__((__unused__)));
926 static void _vrf_free(void *arg);
927 static void _iface_free(void *arg);
928
929 /* BFD hash for our discriminator. */
930 static unsigned int bfd_id_hash_do(void *p)
931 {
932 struct bfd_session *bs = p;
933
934 return jhash_1word(bs->discrs.my_discr, 0);
935 }
936
937 static int bfd_id_hash_cmp(const void *n1, const void *n2)
938 {
939 const struct bfd_session *bs1 = n1, *bs2 = n2;
940
941 return bs1->discrs.my_discr == bs2->discrs.my_discr;
942 }
943
944 /* BFD hash for single hop. */
945 static unsigned int bfd_shop_hash_do(void *p)
946 {
947 struct bfd_session *bs = p;
948
949 return jhash(&bs->shop, sizeof(bs->shop), 0);
950 }
951
952 static int bfd_shop_hash_cmp(const void *n1, const void *n2)
953 {
954 const struct bfd_session *bs1 = n1, *bs2 = n2;
955
956 return memcmp(&bs1->shop, &bs2->shop, sizeof(bs1->shop)) == 0;
957 }
958
959 /* BFD hash for multi hop. */
960 static unsigned int bfd_mhop_hash_do(void *p)
961 {
962 struct bfd_session *bs = p;
963
964 return jhash(&bs->mhop, sizeof(bs->mhop), 0);
965 }
966
967 static int bfd_mhop_hash_cmp(const void *n1, const void *n2)
968 {
969 const struct bfd_session *bs1 = n1, *bs2 = n2;
970
971 return memcmp(&bs1->mhop, &bs2->mhop, sizeof(bs1->mhop)) == 0;
972 }
973
974 /* BFD hash for VRFs. */
975 static unsigned int bfd_vrf_hash_do(void *p)
976 {
977 struct bfd_vrf *vrf = p;
978
979 return jhash_1word(vrf->vrf_id, 0);
980 }
981
982 static int bfd_vrf_hash_cmp(const void *n1, const void *n2)
983 {
984 const struct bfd_vrf *v1 = n1, *v2 = n2;
985
986 return v1->vrf_id == v2->vrf_id;
987 }
988
989 /* BFD hash for interfaces. */
990 static unsigned int bfd_iface_hash_do(void *p)
991 {
992 struct bfd_iface *iface = p;
993
994 return string_hash_make(iface->ifname);
995 }
996
997 static int bfd_iface_hash_cmp(const void *n1, const void *n2)
998 {
999 const struct bfd_iface *i1 = n1, *i2 = n2;
1000
1001 return strcmp(i1->ifname, i2->ifname) == 0;
1002 }
1003
1004 /* Helper functions */
1005 static void _shop_key(struct bfd_session *bs, const struct bfd_shop_key *shop)
1006 {
1007 bs->shop = *shop;
1008
1009 /* Remove unused fields. */
1010 switch (bs->shop.peer.sa_sin.sin_family) {
1011 case AF_INET:
1012 bs->shop.peer.sa_sin.sin_port = 0;
1013 break;
1014 case AF_INET6:
1015 bs->shop.peer.sa_sin6.sin6_port = 0;
1016 break;
1017 }
1018 }
1019
1020 static void _shop_key2(struct bfd_session *bs, const struct bfd_shop_key *shop)
1021 {
1022 _shop_key(bs, shop);
1023 memset(bs->shop.port_name, 0, sizeof(bs->shop.port_name));
1024 }
1025
1026 static void _mhop_key(struct bfd_session *bs, const struct bfd_mhop_key *mhop)
1027 {
1028 bs->mhop = *mhop;
1029
1030 /* Remove unused fields. */
1031 switch (bs->mhop.peer.sa_sin.sin_family) {
1032 case AF_INET:
1033 bs->mhop.peer.sa_sin.sin_port = 0;
1034 bs->mhop.local.sa_sin.sin_port = 0;
1035 break;
1036 case AF_INET6:
1037 bs->mhop.peer.sa_sin6.sin6_port = 0;
1038 bs->mhop.local.sa_sin6.sin6_port = 0;
1039 break;
1040 }
1041 }
1042
1043 static int _iface_key(struct bfd_iface *iface, const char *ifname)
1044 {
1045 size_t slen = sizeof(iface->ifname);
1046
1047 memset(iface->ifname, 0, slen);
1048 if (strlcpy(iface->ifname, ifname, slen) >= slen)
1049 return -1;
1050
1051 return 0;
1052 }
1053
1054
1055 /*
1056 * Hash public interface / exported functions.
1057 */
1058
1059 /* Lookup functions. */
1060 struct bfd_session *bfd_id_lookup(uint32_t id)
1061 {
1062 struct bfd_session bs;
1063
1064 bs.discrs.my_discr = id;
1065
1066 return hash_lookup(bfd_id_hash, &bs);
1067 }
1068
1069 struct bfd_session *bfd_shop_lookup(struct bfd_shop_key shop)
1070 {
1071 struct bfd_session bs, *bsp;
1072
1073 _shop_key(&bs, &shop);
1074
1075 bsp = hash_lookup(bfd_shop_hash, &bs);
1076 if (bsp == NULL && bs.shop.port_name[0] != 0) {
1077 /*
1078 * Since the local interface spec is optional, try
1079 * searching the key without it as well.
1080 */
1081 _shop_key2(&bs, &shop);
1082 bsp = hash_lookup(bfd_shop_hash, &bs);
1083 }
1084
1085 return bsp;
1086 }
1087
1088 struct bfd_session *bfd_mhop_lookup(struct bfd_mhop_key mhop)
1089 {
1090 struct bfd_session bs;
1091
1092 _mhop_key(&bs, &mhop);
1093
1094 return hash_lookup(bfd_shop_hash, &bs);
1095 }
1096
1097 struct bfd_vrf *bfd_vrf_lookup(int vrf_id)
1098 {
1099 struct bfd_vrf vrf;
1100
1101 vrf.vrf_id = vrf_id;
1102
1103 return hash_lookup(bfd_vrf_hash, &vrf);
1104 }
1105
1106 struct bfd_iface *bfd_iface_lookup(const char *ifname)
1107 {
1108 struct bfd_iface iface;
1109
1110 if (_iface_key(&iface, ifname) != 0)
1111 return NULL;
1112
1113 return hash_lookup(bfd_iface_hash, &iface);
1114 }
1115
1116 /*
1117 * Delete functions.
1118 *
1119 * Delete functions searches and remove the item from the hash and
1120 * returns a pointer to the removed item data. If the item was not found
1121 * then it returns NULL.
1122 *
1123 * The data stored inside the hash is not free()ed, so you must do it
1124 * manually after getting the pointer back.
1125 */
1126 struct bfd_session *bfd_id_delete(uint32_t id)
1127 {
1128 struct bfd_session bs;
1129
1130 bs.discrs.my_discr = id;
1131
1132 return hash_release(bfd_id_hash, &bs);
1133 }
1134
1135 struct bfd_session *bfd_shop_delete(struct bfd_shop_key shop)
1136 {
1137 struct bfd_session bs, *bsp;
1138
1139 _shop_key(&bs, &shop);
1140 bsp = hash_release(bfd_shop_hash, &bs);
1141 if (bsp == NULL && shop.port_name[0] != 0) {
1142 /*
1143 * Since the local interface spec is optional, try
1144 * searching the key without it as well.
1145 */
1146 _shop_key2(&bs, &shop);
1147 bsp = hash_release(bfd_shop_hash, &bs);
1148 }
1149
1150 return bsp;
1151 }
1152
1153 struct bfd_session *bfd_mhop_delete(struct bfd_mhop_key mhop)
1154 {
1155 struct bfd_session bs;
1156
1157 _mhop_key(&bs, &mhop);
1158
1159 return hash_release(bfd_mhop_hash, &bs);
1160 }
1161
1162 struct bfd_vrf *bfd_vrf_delete(int vrf_id)
1163 {
1164 struct bfd_vrf vrf;
1165
1166 vrf.vrf_id = vrf_id;
1167
1168 return hash_release(bfd_vrf_hash, &vrf);
1169 }
1170
1171 struct bfd_iface *bfd_iface_delete(const char *ifname)
1172 {
1173 struct bfd_iface iface;
1174
1175 if (_iface_key(&iface, ifname) != 0)
1176 return NULL;
1177
1178 return hash_release(bfd_iface_hash, &iface);
1179 }
1180
1181 /* Iteration functions. */
1182 void bfd_id_iterate(hash_iter_func hif, void *arg)
1183 {
1184 hash_iterate(bfd_id_hash, hif, arg);
1185 }
1186
1187 void bfd_shop_iterate(hash_iter_func hif, void *arg)
1188 {
1189 hash_iterate(bfd_shop_hash, hif, arg);
1190 }
1191
1192 void bfd_mhop_iterate(hash_iter_func hif, void *arg)
1193 {
1194 hash_iterate(bfd_mhop_hash, hif, arg);
1195 }
1196
1197 void bfd_vrf_iterate(hash_iter_func hif, void *arg)
1198 {
1199 hash_iterate(bfd_vrf_hash, hif, arg);
1200 }
1201
1202 void bfd_iface_iterate(hash_iter_func hif, void *arg)
1203 {
1204 hash_iterate(bfd_iface_hash, hif, arg);
1205 }
1206
1207 /*
1208 * Insert functions.
1209 *
1210 * Inserts session into hash and returns `true` on success, otherwise
1211 * `false`.
1212 */
1213 bool bfd_id_insert(struct bfd_session *bs)
1214 {
1215 return (hash_get(bfd_id_hash, bs, hash_alloc_intern) == bs);
1216 }
1217
1218 bool bfd_shop_insert(struct bfd_session *bs)
1219 {
1220 return (hash_get(bfd_shop_hash, bs, hash_alloc_intern) == bs);
1221 }
1222
1223 bool bfd_mhop_insert(struct bfd_session *bs)
1224 {
1225 return (hash_get(bfd_mhop_hash, bs, hash_alloc_intern) == bs);
1226 }
1227
1228 bool bfd_vrf_insert(struct bfd_vrf *vrf)
1229 {
1230 return (hash_get(bfd_vrf_hash, vrf, hash_alloc_intern) == vrf);
1231 }
1232
1233 bool bfd_iface_insert(struct bfd_iface *iface)
1234 {
1235 return (hash_get(bfd_iface_hash, iface, hash_alloc_intern) == iface);
1236 }
1237
1238 void bfd_initialize(void)
1239 {
1240 bfd_id_hash = hash_create(bfd_id_hash_do, bfd_id_hash_cmp,
1241 "BFD discriminator hash");
1242 bfd_shop_hash = hash_create(bfd_shop_hash_do, bfd_shop_hash_cmp,
1243 "BFD single hop hash");
1244 bfd_mhop_hash = hash_create(bfd_mhop_hash_do, bfd_mhop_hash_cmp,
1245 "BFD multihop hop hash");
1246 bfd_vrf_hash =
1247 hash_create(bfd_vrf_hash_do, bfd_vrf_hash_cmp, "BFD VRF hash");
1248 bfd_iface_hash = hash_create(bfd_iface_hash_do, bfd_iface_hash_cmp,
1249 "BFD interface hash");
1250 }
1251
1252 static void _bfd_free(struct hash_backet *hb,
1253 void *arg __attribute__((__unused__)))
1254 {
1255 struct bfd_session *bs = hb->data;
1256
1257 bfd_session_free(bs);
1258 }
1259
1260 static void _vrf_free(void *arg)
1261 {
1262 struct bfd_vrf *vrf = arg;
1263
1264 XFREE(MTYPE_BFDD_CONFIG, vrf);
1265 }
1266
1267 static void _iface_free(void *arg)
1268 {
1269 struct bfd_iface *iface = arg;
1270
1271 XFREE(MTYPE_BFDD_CONFIG, iface);
1272 }
1273
1274 void bfd_shutdown(void)
1275 {
1276 /*
1277 * Close and free all BFD sessions.
1278 *
1279 * _bfd_free() will call bfd_session_free() which will take care
1280 * of removing the session from all hashes, so we just run an
1281 * assert() here to make sure it really happened.
1282 */
1283 bfd_id_iterate(_bfd_free, NULL);
1284 assert(bfd_shop_hash->count == 0);
1285 assert(bfd_mhop_hash->count == 0);
1286
1287 /* Clean the VRF and interface hashes. */
1288 hash_clean(bfd_vrf_hash, _vrf_free);
1289 hash_clean(bfd_iface_hash, _iface_free);
1290
1291 /* Now free the hashes themselves. */
1292 hash_free(bfd_id_hash);
1293 hash_free(bfd_shop_hash);
1294 hash_free(bfd_mhop_hash);
1295 hash_free(bfd_vrf_hash);
1296 hash_free(bfd_iface_hash);
1297 }