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