1 /*********************************************************************
2 * Copyright 2013 Cumulus Networks, LLC. All rights reserved.
3 * Copyright 2014,2015,2016,2017 Cumulus Networks, Inc. All rights reserved.
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)
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
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
19 * bfd.c: implements the BFD protocol.
23 * Shrijeet Mukherjee [shm@cumulusnetworks.com]
24 * Kanna Rajagopal [kanna@cumulusnetworks.com]
25 * Radhika Mahankali [Radhika@cumulusnetworks.com]
30 #include <sys/types.h>
31 #include <sys/socket.h>
33 #include <netinet/in.h>
35 #include <arpa/inet.h>
36 #include <sys/ioctl.h>
49 #include "lib/jhash.h"
53 DEFINE_QOBJ_TYPE(bfd_session
);
58 static uint32_t ptm_bfd_gen_ID(void);
59 static void ptm_bfd_echo_xmt_TO(struct bfd_session
*bfd
);
60 static void bfd_session_free(struct bfd_session
*bs
);
61 static struct bfd_session
*bfd_session_new(int sd
);
62 static struct bfd_session
*bfd_find_disc(struct sockaddr_any
*sa
,
64 static int bfd_session_update(struct bfd_session
*bs
, struct bfd_peer_cfg
*bpc
);
65 static const char *get_diag_str(int diag
);
71 struct bfd_session
*bs_peer_find(struct bfd_peer_cfg
*bpc
)
73 struct bfd_session
*bs
;
74 struct peer_label
*pl
;
75 struct bfd_mhop_key mhop
;
76 struct bfd_shop_key shop
;
78 /* Try to find label first. */
79 if (bpc
->bpc_has_label
) {
80 pl
= pl_find(bpc
->bpc_label
);
87 /* Otherwise fallback to peer/local hash lookup. */
89 memset(&mhop
, 0, sizeof(mhop
));
90 mhop
.peer
= bpc
->bpc_peer
;
91 mhop
.local
= bpc
->bpc_local
;
92 if (bpc
->bpc_has_vrfname
)
93 strlcpy(mhop
.vrf_name
, bpc
->bpc_vrfname
,
94 sizeof(mhop
.vrf_name
));
96 bs
= bfd_mhop_lookup(mhop
);
98 memset(&shop
, 0, sizeof(shop
));
99 shop
.peer
= bpc
->bpc_peer
;
100 if (!bpc
->bpc_has_vxlan
&& bpc
->bpc_has_localif
)
101 strlcpy(shop
.port_name
, bpc
->bpc_localif
,
102 sizeof(shop
.port_name
));
104 bs
= bfd_shop_lookup(shop
);
110 static uint32_t ptm_bfd_gen_ID(void)
112 static uint32_t sessionID
= 1;
114 return (sessionID
++);
117 void ptm_bfd_start_xmt_timer(struct bfd_session
*bfd
, bool is_echo
)
119 uint64_t jitter
, xmt_TO
;
122 xmt_TO
= is_echo
? bfd
->echo_xmt_TO
: bfd
->xmt_TO
;
125 * From section 6.5.2: trasmit interval should be randomly jittered
127 * 75% and 100% of nominal value, unless detect_mult is 1, then should
129 * between 75% and 90%.
131 maxpercent
= (bfd
->detect_mult
== 1) ? 16 : 26;
132 jitter
= (xmt_TO
* (75 + (random() % maxpercent
))) / 100;
133 /* XXX remove that division above */
136 bfd_echo_xmttimer_update(bfd
, jitter
);
138 bfd_xmttimer_update(bfd
, jitter
);
141 static void ptm_bfd_echo_xmt_TO(struct bfd_session
*bfd
)
143 /* Send the scheduled echo packet */
144 ptm_bfd_echo_snd(bfd
);
146 /* Restart the timer for next time */
147 ptm_bfd_start_xmt_timer(bfd
, true);
150 void ptm_bfd_xmt_TO(struct bfd_session
*bfd
, int fbit
)
152 /* Send the scheduled control packet */
153 ptm_bfd_snd(bfd
, fbit
);
155 /* Restart the timer for next time */
156 ptm_bfd_start_xmt_timer(bfd
, false);
159 void ptm_bfd_echo_stop(struct bfd_session
*bfd
, int polling
)
161 bfd
->echo_xmt_TO
= 0;
162 bfd
->echo_detect_TO
= 0;
163 BFD_UNSET_FLAG(bfd
->flags
, BFD_SESS_FLAG_ECHO_ACTIVE
);
165 bfd_echo_xmttimer_delete(bfd
);
166 bfd_echo_recvtimer_delete(bfd
);
169 bfd
->polling
= polling
;
170 bfd
->new_timers
.desired_min_tx
= bfd
->up_min_tx
;
171 bfd
->new_timers
.required_min_rx
= bfd
->timers
.required_min_rx
;
176 void ptm_bfd_echo_start(struct bfd_session
*bfd
)
178 bfd
->echo_detect_TO
= (bfd
->remote_detect_mult
* bfd
->echo_xmt_TO
);
179 ptm_bfd_echo_xmt_TO(bfd
);
182 bfd
->new_timers
.desired_min_tx
= bfd
->up_min_tx
;
183 bfd
->new_timers
.required_min_rx
= bfd
->timers
.required_min_rx
;
187 void ptm_bfd_ses_up(struct bfd_session
*bfd
)
190 bfd
->ses_state
= PTM_BFD_UP
;
192 monotime(&bfd
->uptime
);
194 /* If the peer is capable to receiving Echo pkts */
195 if (bfd
->echo_xmt_TO
&& !BFD_CHECK_FLAG(bfd
->flags
, BFD_SESS_FLAG_MH
)) {
196 ptm_bfd_echo_start(bfd
);
198 bfd
->new_timers
.desired_min_tx
= bfd
->up_min_tx
;
199 bfd
->new_timers
.required_min_rx
= bfd
->timers
.required_min_rx
;
205 INFOLOG("Session 0x%x up peer %s", bfd
->discrs
.my_discr
,
206 satostr(&bfd
->shop
.peer
));
209 void ptm_bfd_ses_dn(struct bfd_session
*bfd
, uint8_t diag
)
211 int old_state
= bfd
->ses_state
;
213 bfd
->local_diag
= diag
;
214 bfd
->discrs
.remote_discr
= 0;
215 bfd
->ses_state
= PTM_BFD_DOWN
;
217 bfd
->demand_mode
= 0;
218 monotime(&bfd
->downtime
);
222 /* only signal clients when going from up->down state */
223 if (old_state
== PTM_BFD_UP
)
226 INFOLOG("Session 0x%x down peer %s Rsn %s prev st %s",
227 bfd
->discrs
.my_discr
, satostr(&bfd
->shop
.peer
),
228 get_diag_str(bfd
->local_diag
), state_list
[old_state
].str
);
230 /* Stop echo packet transmission if they are active */
231 if (BFD_CHECK_FLAG(bfd
->flags
, BFD_SESS_FLAG_ECHO_ACTIVE
))
232 ptm_bfd_echo_stop(bfd
, 0);
235 static int ptm_bfd_get_vrf_name(char *port_name
, char *vrf_name
)
237 struct bfd_iface
*iface
;
240 if ((port_name
== NULL
) || (vrf_name
== NULL
))
243 iface
= bfd_iface_lookup(port_name
);
245 vrf
= bfd_vrf_lookup(iface
->vrf_id
);
247 strlcpy(vrf_name
, vrf
->name
, sizeof(vrf
->name
));
254 static struct bfd_session
*bfd_find_disc(struct sockaddr_any
*sa
,
257 struct bfd_session
*bs
;
259 bs
= bfd_id_lookup(ldisc
);
263 /* Remove unused fields. */
264 switch (sa
->sa_sin
.sin_family
) {
266 sa
->sa_sin
.sin_port
= 0;
267 if (memcmp(sa
, &bs
->shop
.peer
, sizeof(sa
->sa_sin
)) == 0)
271 sa
->sa_sin6
.sin6_port
= 0;
272 if (memcmp(sa
, &bs
->shop
.peer
, sizeof(sa
->sa_sin6
)) == 0)
280 struct bfd_session
*ptm_bfd_sess_find(struct bfd_pkt
*cp
, char *port_name
,
281 struct sockaddr_any
*peer
,
282 struct sockaddr_any
*local
,
283 char *vrf_name
, bool is_mhop
)
285 struct bfd_session
*l_bfd
= NULL
;
286 struct bfd_mhop_key mhop
;
287 struct bfd_shop_key shop
;
288 char vrf_buf
[MAXNAMELEN
];
290 /* Find our session using the ID signaled by the remote end. */
291 if (cp
->discrs
.remote_discr
)
292 return bfd_find_disc(peer
, ntohl(cp
->discrs
.remote_discr
));
294 /* Search for session without using discriminator. */
296 memset(&mhop
, 0, sizeof(mhop
));
299 if (vrf_name
&& vrf_name
[0]) {
300 strlcpy(mhop
.vrf_name
, vrf_name
, sizeof(mhop
.vrf_name
));
301 } else if (port_name
&& port_name
[0]) {
302 memset(vrf_buf
, 0, sizeof(vrf_buf
));
303 if (ptm_bfd_get_vrf_name(port_name
, vrf_buf
) != -1)
304 strlcpy(mhop
.vrf_name
, vrf_buf
, sizeof(mhop
.vrf_name
));
307 l_bfd
= bfd_mhop_lookup(mhop
);
309 memset(&shop
, 0, sizeof(shop
));
311 if (port_name
&& port_name
[0])
312 strlcpy(shop
.port_name
, port_name
, sizeof(shop
.port_name
));
314 l_bfd
= bfd_shop_lookup(shop
);
317 /* XXX maybe remoteDiscr should be checked for remoteHeard cases. */
321 #if 0 /* TODO VxLAN Support */
323 _update_vxlan_sess_parms(struct bfd_session
*bfd
, bfd_sess_parms
*sess_parms
)
325 struct bfd_session_vxlan_info
*vxlan_info
= &bfd
->vxlan_info
;
326 bfd_parms_list
*parms
= &sess_parms
->parms
;
328 vxlan_info
->vnid
= parms
->vnid
;
329 vxlan_info
->check_tnl_key
= parms
->check_tnl_key
;
330 vxlan_info
->forwarding_if_rx
= parms
->forwarding_if_rx
;
331 vxlan_info
->cpath_down
= parms
->cpath_down
;
332 vxlan_info
->decay_min_rx
= parms
->decay_min_rx
;
334 inet_aton(parms
->local_dst_ip
, &vxlan_info
->local_dst_ip
);
335 inet_aton(parms
->remote_dst_ip
, &vxlan_info
->peer_dst_ip
);
337 memcpy(vxlan_info
->local_dst_mac
, parms
->local_dst_mac
, ETH_ALEN
);
338 memcpy(vxlan_info
->peer_dst_mac
, parms
->remote_dst_mac
, ETH_ALEN
);
340 /* The interface may change for Vxlan BFD sessions, so update
341 * the local mac and ifindex
343 bfd
->ifindex
= sess_parms
->ifindex
;
344 memcpy(bfd
->local_mac
, sess_parms
->local_mac
, sizeof(bfd
->local_mac
));
346 #endif /* VxLAN support */
348 int bfd_xmt_cb(struct thread
*t
)
350 struct bfd_session
*bs
= THREAD_ARG(t
);
352 ptm_bfd_xmt_TO(bs
, 0);
357 int bfd_echo_xmt_cb(struct thread
*t
)
359 struct bfd_session
*bs
= THREAD_ARG(t
);
361 ptm_bfd_echo_xmt_TO(bs
);
366 /* Was ptm_bfd_detect_TO() */
367 int bfd_recvtimer_cb(struct thread
*t
)
369 struct bfd_session
*bs
= THREAD_ARG(t
);
372 old_state
= bs
->ses_state
;
374 switch (bs
->ses_state
) {
377 ptm_bfd_ses_dn(bs
, BFD_DIAGDETECTTIME
);
378 INFOLOG("%s Detect timeout on session 0x%x with peer %s, in state %d",
379 __func__
, bs
->discrs
.my_discr
, satostr(&bs
->shop
.peer
),
381 bfd_recvtimer_update(bs
);
385 /* Second detect time expiration, zero remote discr (section
388 bs
->discrs
.remote_discr
= 0;
392 if (old_state
!= bs
->ses_state
) {
393 DLOG("BFD Sess %d [%s] Old State [%s] : New State [%s]",
394 bs
->discrs
.my_discr
, satostr(&bs
->shop
.peer
),
395 state_list
[old_state
].str
, state_list
[bs
->ses_state
].str
);
401 /* Was ptm_bfd_echo_detect_TO() */
402 int bfd_echo_recvtimer_cb(struct thread
*t
)
404 struct bfd_session
*bs
= THREAD_ARG(t
);
407 old_state
= bs
->ses_state
;
409 switch (bs
->ses_state
) {
412 ptm_bfd_ses_dn(bs
, BFD_DIAGDETECTTIME
);
413 INFOLOG("%s Detect timeout on session 0x%x with peer %s, in state %d",
414 __func__
, bs
->discrs
.my_discr
, satostr(&bs
->shop
.peer
),
419 if (old_state
!= bs
->ses_state
) {
420 DLOG("BFD Sess %d [%s] Old State [%s] : New State [%s]",
421 bs
->discrs
.my_discr
, satostr(&bs
->shop
.peer
),
422 state_list
[old_state
].str
, state_list
[bs
->ses_state
].str
);
428 static struct bfd_session
*bfd_session_new(int sd
)
430 struct bfd_session
*bs
;
432 bs
= XCALLOC(MTYPE_BFDD_CONFIG
, sizeof(*bs
));
436 QOBJ_REG(bs
, bfd_session
);
438 bs
->up_min_tx
= BFD_DEFDESIREDMINTX
;
439 bs
->timers
.required_min_rx
= BFD_DEFREQUIREDMINRX
;
440 bs
->timers
.required_min_echo
= BFD_DEF_REQ_MIN_ECHO
;
441 bs
->detect_mult
= BFD_DEFDETECTMULT
;
442 bs
->mh_ttl
= BFD_DEF_MHOP_TTL
;
445 monotime(&bs
->uptime
);
446 bs
->downtime
= bs
->uptime
;
451 int bfd_session_update_label(struct bfd_session
*bs
, const char *nlabel
)
453 /* New label treatment:
454 * - Check if the label is taken;
455 * - Try to allocate the memory for it and register;
457 if (bs
->pl
== NULL
) {
458 if (pl_find(nlabel
) != NULL
) {
459 /* Someone is already using it. */
463 if (pl_new(nlabel
, bs
) == NULL
)
470 * Test label change consistency:
471 * - Do nothing if it's the same label;
472 * - Check if the future label is already taken;
475 if (strcmp(nlabel
, bs
->pl
->pl_label
) == 0)
477 if (pl_find(nlabel
) != NULL
)
480 strlcpy(bs
->pl
->pl_label
, nlabel
, sizeof(bs
->pl
->pl_label
));
484 static void _bfd_session_update(struct bfd_session
*bs
,
485 struct bfd_peer_cfg
*bpc
)
488 /* Check if echo mode is already active. */
489 if (BFD_CHECK_FLAG(bs
->flags
, BFD_SESS_FLAG_ECHO
))
492 BFD_SET_FLAG(bs
->flags
, BFD_SESS_FLAG_ECHO
);
493 ptm_bfd_echo_start(bs
);
495 /* Activate/update echo receive timeout timer. */
496 bfd_echo_recvtimer_update(bs
);
498 /* Check if echo mode is already disabled. */
499 if (!BFD_CHECK_FLAG(bs
->flags
, BFD_SESS_FLAG_ECHO
))
502 BFD_UNSET_FLAG(bs
->flags
, BFD_SESS_FLAG_ECHO
);
503 ptm_bfd_echo_stop(bs
, 0);
507 if (bpc
->bpc_has_txinterval
)
508 bs
->up_min_tx
= bpc
->bpc_txinterval
* 1000;
510 if (bpc
->bpc_has_recvinterval
)
511 bs
->timers
.required_min_rx
= bpc
->bpc_recvinterval
* 1000;
513 if (bpc
->bpc_has_detectmultiplier
)
514 bs
->detect_mult
= bpc
->bpc_detectmultiplier
;
516 if (bpc
->bpc_has_echointerval
)
517 bs
->timers
.required_min_echo
= bpc
->bpc_echointerval
* 1000;
519 if (bpc
->bpc_has_label
)
520 bfd_session_update_label(bs
, bpc
->bpc_label
);
522 if (bpc
->bpc_shutdown
) {
523 /* Check if already shutdown. */
524 if (BFD_CHECK_FLAG(bs
->flags
, BFD_SESS_FLAG_SHUTDOWN
))
527 BFD_SET_FLAG(bs
->flags
, BFD_SESS_FLAG_SHUTDOWN
);
529 /* Disable all events. */
530 bfd_recvtimer_delete(bs
);
531 bfd_echo_recvtimer_delete(bs
);
532 bfd_xmttimer_delete(bs
);
533 bfd_echo_xmttimer_delete(bs
);
535 /* Change and notify state change. */
536 bs
->ses_state
= PTM_BFD_ADM_DOWN
;
541 /* Check if already working. */
542 if (!BFD_CHECK_FLAG(bs
->flags
, BFD_SESS_FLAG_SHUTDOWN
))
545 BFD_UNSET_FLAG(bs
->flags
, BFD_SESS_FLAG_SHUTDOWN
);
547 /* Change and notify state change. */
548 bs
->ses_state
= PTM_BFD_DOWN
;
551 /* Enable all timers. */
552 bfd_recvtimer_update(bs
);
553 bfd_xmttimer_update(bs
, bs
->xmt_TO
);
554 if (BFD_CHECK_FLAG(bs
->flags
, BFD_SESS_FLAG_ECHO
)) {
555 bfd_echo_recvtimer_update(bs
);
556 bfd_echo_xmttimer_update(bs
, bs
->echo_xmt_TO
);
561 static int bfd_session_update(struct bfd_session
*bs
, struct bfd_peer_cfg
*bpc
)
563 /* User didn't want to update, return failure. */
564 if (bpc
->bpc_createonly
)
567 _bfd_session_update(bs
, bpc
);
569 /* TODO add VxLAN support. */
571 control_notify_config(BCM_NOTIFY_CONFIG_UPDATE
, bs
);
576 static void bfd_session_free(struct bfd_session
*bs
)
581 bfd_recvtimer_delete(bs
);
582 bfd_echo_recvtimer_delete(bs
);
583 bfd_xmttimer_delete(bs
);
584 bfd_echo_xmttimer_delete(bs
);
586 bfd_id_delete(bs
->discrs
.my_discr
);
587 if (BFD_CHECK_FLAG(bs
->flags
, BFD_SESS_FLAG_MH
))
588 bfd_mhop_delete(bs
->mhop
);
590 bfd_shop_delete(bs
->shop
);
595 XFREE(MTYPE_BFDD_CONFIG
, bs
);
598 struct bfd_session
*ptm_bfd_sess_new(struct bfd_peer_cfg
*bpc
)
600 struct bfd_session
*bfd
, *l_bfd
;
603 /* check to see if this needs a new session */
604 l_bfd
= bs_peer_find(bpc
);
606 /* Requesting a duplicated peer means update configuration. */
607 if (bfd_session_update(l_bfd
, bpc
) == 0)
614 * Get socket for transmitting control packets. Note that if we
615 * could use the destination port (3784) for the source
616 * port we wouldn't need a socket per session.
619 psock
= bp_peer_socket(bpc
);
621 ERRLOG("Can't get socket for new session: %s",
626 psock
= bp_peer_socketv6(bpc
);
628 ERRLOG("Can't get IPv6 socket for new session: %s",
635 bfd
= bfd_session_new(psock
);
637 ERRLOG("Can't malloc memory for new session: %s",
642 if (bpc
->bpc_has_localif
&& !bpc
->bpc_mhop
) {
643 bfd
->ifindex
= ptm_bfd_fetch_ifindex(bpc
->bpc_localif
);
644 ptm_bfd_fetch_local_mac(bpc
->bpc_localif
, bfd
->local_mac
);
647 if (bpc
->bpc_has_vxlan
)
648 BFD_SET_FLAG(bfd
->flags
, BFD_SESS_FLAG_VXLAN
);
650 if (bpc
->bpc_ipv4
== false)
651 BFD_SET_FLAG(bfd
->flags
, BFD_SESS_FLAG_IPV6
);
653 /* Initialize the session */
654 bfd
->ses_state
= PTM_BFD_DOWN
;
655 bfd
->discrs
.my_discr
= ptm_bfd_gen_ID();
656 bfd
->discrs
.remote_discr
= 0;
657 bfd
->local_ip
= bpc
->bpc_local
;
658 bfd
->local_address
= bpc
->bpc_local
;
659 bfd
->timers
.desired_min_tx
= bfd
->up_min_tx
;
660 bfd
->detect_TO
= (bfd
->detect_mult
* BFD_DEF_SLOWTX
);
662 /* Use detect_TO first for slow detection, then use recvtimer_update. */
663 bfd_recvtimer_update(bfd
);
668 BFD_SET_FLAG(bfd
->flags
, BFD_SESS_FLAG_MH
);
669 bfd
->mhop
.peer
= bpc
->bpc_peer
;
670 bfd
->mhop
.local
= bpc
->bpc_local
;
671 if (bpc
->bpc_has_vrfname
)
672 strlcpy(bfd
->mhop
.vrf_name
, bpc
->bpc_vrfname
,
673 sizeof(bfd
->mhop
.vrf_name
));
675 bfd_mhop_insert(bfd
);
677 bfd
->shop
.peer
= bpc
->bpc_peer
;
678 if (!bpc
->bpc_has_vxlan
&& bpc
->bpc_has_localif
)
679 strlcpy(bfd
->shop
.port_name
, bpc
->bpc_localif
,
680 sizeof(bfd
->shop
.port_name
));
682 bfd_shop_insert(bfd
);
685 if (BFD_CHECK_FLAG(bfd
->flags
, BFD_SESS_FLAG_VXLAN
)) {
686 static uint8_t bfd_def_vxlan_dmac
[] = {0x00, 0x23, 0x20,
688 memcpy(bfd
->peer_mac
, bfd_def_vxlan_dmac
,
689 sizeof(bfd_def_vxlan_dmac
));
692 else if (event
->rmac
) {
693 if (sscanf(event
->rmac
, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
694 &bfd
->peer_mac
[0], &bfd
->peer_mac
[1], &bfd
->peer_mac
[2],
695 &bfd
->peer_mac
[3], &bfd
->peer_mac
[4], &bfd
->peer_mac
[5])
697 DLOG("%s: Assigning remote mac = %s", __func__
,
703 * XXX: session update triggers echo start, so we must have our
704 * discriminator ID set first.
706 _bfd_session_update(bfd
, bpc
);
708 /* Start transmitting with slow interval until peer responds */
709 bfd
->xmt_TO
= BFD_DEF_SLOWTX
;
711 ptm_bfd_xmt_TO(bfd
, 0);
714 INFOLOG("Created new session 0x%x with vrf %s peer %s local %s",
715 bfd
->discrs
.my_discr
,
716 (bpc
->bpc_has_vrfname
) ? bfd
->mhop
.vrf_name
: "N/A",
717 satostr(&bfd
->mhop
.peer
), satostr(&bfd
->mhop
.local
));
719 INFOLOG("Created new session 0x%x with peer %s port %s",
720 bfd
->discrs
.my_discr
, satostr(&bfd
->shop
.peer
),
721 bfd
->shop
.port_name
[0] ? bfd
->shop
.port_name
: "N/A");
724 control_notify_config(BCM_NOTIFY_CONFIG_ADD
, bfd
);
729 int ptm_bfd_ses_del(struct bfd_peer_cfg
*bpc
)
731 struct bfd_session
*bs
;
733 /* Find session and call free(). */
734 bs
= bs_peer_find(bpc
);
738 /* This pointer is being referenced, don't let it be deleted. */
739 if (bs
->refcount
> 0) {
740 zlog_debug("%s: trying to free in-use session: %" PRIu64
742 __func__
, bs
->refcount
);
746 if (BFD_CHECK_FLAG(bs
->flags
, BFD_SESS_FLAG_MH
)) {
747 INFOLOG("Deleting session 0x%x with vrf %s peer %s local %s",
749 bpc
->bpc_has_vrfname
? bpc
->bpc_vrfname
: "N/A",
750 satostr(&bs
->mhop
.peer
), satostr(&bs
->mhop
.local
));
752 INFOLOG("Deleting session 0x%x with peer %s port %s",
753 bs
->discrs
.my_discr
, satostr(&bs
->shop
.peer
),
757 control_notify_config(BCM_NOTIFY_CONFIG_DELETE
, bs
);
759 bfd_session_free(bs
);
764 void bfd_set_polling(struct bfd_session
*bs
)
766 bs
->new_timers
.desired_min_tx
= bs
->up_min_tx
;
767 bs
->new_timers
.required_min_rx
= bs
->timers
.required_min_rx
;
768 bs
->new_timers
.required_min_echo
= bs
->timers
.required_min_echo
;
776 static const char *get_diag_str(int diag
)
778 for (int i
= 0; diag_list
[i
].str
; i
++) {
779 if (diag_list
[i
].type
== diag
)
780 return diag_list
[i
].str
;
785 const char *satostr(struct sockaddr_any
*sa
)
787 #define INETSTR_BUFCOUNT 8
788 static char buf
[INETSTR_BUFCOUNT
][INET6_ADDRSTRLEN
];
790 struct sockaddr_in
*sin
= &sa
->sa_sin
;
791 struct sockaddr_in6
*sin6
= &sa
->sa_sin6
;
793 bufidx
+= (bufidx
+ 1) % INETSTR_BUFCOUNT
;
796 switch (sin
->sin_family
) {
798 inet_ntop(AF_INET
, &sin
->sin_addr
, buf
[bufidx
],
799 sizeof(buf
[bufidx
]));
802 inet_ntop(AF_INET6
, &sin6
->sin6_addr
, buf
[bufidx
],
803 sizeof(buf
[bufidx
]));
807 strlcpy(buf
[bufidx
], "unknown", sizeof(buf
[bufidx
]));
814 const char *diag2str(uint8_t diag
)
820 return "control detection time expired";
822 return "echo function failed";
824 return "neighbor signaled session down";
826 return "forwarding plane reset";
830 return "concatenated path down";
832 return "administratively down";
834 return "reverse concatenated path down";
840 int strtosa(const char *addr
, struct sockaddr_any
*sa
)
842 memset(sa
, 0, sizeof(*sa
));
844 if (inet_pton(AF_INET
, addr
, &sa
->sa_sin
.sin_addr
) == 1) {
845 sa
->sa_sin
.sin_family
= AF_INET
;
846 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
847 sa
->sa_sin
.sin_len
= sizeof(sa
->sa_sin
);
848 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
852 if (inet_pton(AF_INET6
, addr
, &sa
->sa_sin6
.sin6_addr
) == 1) {
853 sa
->sa_sin6
.sin6_family
= AF_INET6
;
854 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
855 sa
->sa_sin6
.sin6_len
= sizeof(sa
->sa_sin6
);
856 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
863 void integer2timestr(uint64_t time
, char *buf
, size_t buflen
)
865 unsigned int year
, month
, day
, hour
, minute
, second
;
869 #define HOURS (24 * MINUTES)
870 #define DAYS (30 * HOURS)
871 #define MONTHS (12 * DAYS)
872 #define YEARS (MONTHS)
875 time
-= year
* YEARS
;
877 rv
= snprintf(buf
, buflen
, "%u year(s), ", year
);
881 if (time
>= MONTHS
) {
882 month
= time
/ MONTHS
;
883 time
-= month
* MONTHS
;
885 rv
= snprintf(buf
, buflen
, "%u month(s), ", month
);
893 rv
= snprintf(buf
, buflen
, "%u day(s), ", day
);
899 time
-= hour
* HOURS
;
901 rv
= snprintf(buf
, buflen
, "%u hour(s), ", hour
);
905 if (time
>= MINUTES
) {
906 minute
= time
/ MINUTES
;
907 time
-= minute
* MINUTES
;
909 rv
= snprintf(buf
, buflen
, "%u minute(s), ", minute
);
913 second
= time
% MINUTES
;
914 snprintf(buf
, buflen
, "%u second(s)", second
);
919 * BFD hash data structures to find sessions.
921 static struct hash
*bfd_id_hash
;
922 static struct hash
*bfd_shop_hash
;
923 static struct hash
*bfd_mhop_hash
;
924 static struct hash
*bfd_vrf_hash
;
925 static struct hash
*bfd_iface_hash
;
927 static unsigned int bfd_id_hash_do(void *p
);
928 static int bfd_id_hash_cmp(const void *n1
, const void *n2
);
929 static unsigned int bfd_shop_hash_do(void *p
);
930 static int bfd_shop_hash_cmp(const void *n1
, const void *n2
);
931 static unsigned int bfd_mhop_hash_do(void *p
);
932 static int bfd_mhop_hash_cmp(const void *n1
, const void *n2
);
933 static unsigned int bfd_vrf_hash_do(void *p
);
934 static int bfd_vrf_hash_cmp(const void *n1
, const void *n2
);
935 static unsigned int bfd_iface_hash_do(void *p
);
936 static int bfd_iface_hash_cmp(const void *n1
, const void *n2
);
938 static void _shop_key(struct bfd_session
*bs
, const struct bfd_shop_key
*shop
);
939 static void _shop_key2(struct bfd_session
*bs
, const struct bfd_shop_key
*shop
);
940 static void _mhop_key(struct bfd_session
*bs
, const struct bfd_mhop_key
*mhop
);
941 static int _iface_key(struct bfd_iface
*iface
, const char *ifname
);
943 static void _bfd_free(struct hash_backet
*hb
,
944 void *arg
__attribute__((__unused__
)));
945 static void _vrf_free(void *arg
);
946 static void _iface_free(void *arg
);
948 /* BFD hash for our discriminator. */
949 static unsigned int bfd_id_hash_do(void *p
)
951 struct bfd_session
*bs
= p
;
953 return jhash_1word(bs
->discrs
.my_discr
, 0);
956 static int bfd_id_hash_cmp(const void *n1
, const void *n2
)
958 const struct bfd_session
*bs1
= n1
, *bs2
= n2
;
960 return bs1
->discrs
.my_discr
== bs2
->discrs
.my_discr
;
963 /* BFD hash for single hop. */
964 static unsigned int bfd_shop_hash_do(void *p
)
966 struct bfd_session
*bs
= p
;
968 return jhash(&bs
->shop
, sizeof(bs
->shop
), 0);
971 static int bfd_shop_hash_cmp(const void *n1
, const void *n2
)
973 const struct bfd_session
*bs1
= n1
, *bs2
= n2
;
975 return memcmp(&bs1
->shop
, &bs2
->shop
, sizeof(bs1
->shop
)) == 0;
978 /* BFD hash for multi hop. */
979 static unsigned int bfd_mhop_hash_do(void *p
)
981 struct bfd_session
*bs
= p
;
983 return jhash(&bs
->mhop
, sizeof(bs
->mhop
), 0);
986 static int bfd_mhop_hash_cmp(const void *n1
, const void *n2
)
988 const struct bfd_session
*bs1
= n1
, *bs2
= n2
;
990 return memcmp(&bs1
->mhop
, &bs2
->mhop
, sizeof(bs1
->mhop
)) == 0;
993 /* BFD hash for VRFs. */
994 static unsigned int bfd_vrf_hash_do(void *p
)
996 struct bfd_vrf
*vrf
= p
;
998 return jhash_1word(vrf
->vrf_id
, 0);
1001 static int bfd_vrf_hash_cmp(const void *n1
, const void *n2
)
1003 const struct bfd_vrf
*v1
= n1
, *v2
= n2
;
1005 return v1
->vrf_id
== v2
->vrf_id
;
1008 /* BFD hash for interfaces. */
1009 static unsigned int bfd_iface_hash_do(void *p
)
1011 struct bfd_iface
*iface
= p
;
1013 return string_hash_make(iface
->ifname
);
1016 static int bfd_iface_hash_cmp(const void *n1
, const void *n2
)
1018 const struct bfd_iface
*i1
= n1
, *i2
= n2
;
1020 return strcmp(i1
->ifname
, i2
->ifname
) == 0;
1023 /* Helper functions */
1024 static void _shop_key(struct bfd_session
*bs
, const struct bfd_shop_key
*shop
)
1028 /* Remove unused fields. */
1029 switch (bs
->shop
.peer
.sa_sin
.sin_family
) {
1031 bs
->shop
.peer
.sa_sin
.sin_port
= 0;
1034 bs
->shop
.peer
.sa_sin6
.sin6_port
= 0;
1039 static void _shop_key2(struct bfd_session
*bs
, const struct bfd_shop_key
*shop
)
1041 _shop_key(bs
, shop
);
1042 memset(bs
->shop
.port_name
, 0, sizeof(bs
->shop
.port_name
));
1045 static void _mhop_key(struct bfd_session
*bs
, const struct bfd_mhop_key
*mhop
)
1049 /* Remove unused fields. */
1050 switch (bs
->mhop
.peer
.sa_sin
.sin_family
) {
1052 bs
->mhop
.peer
.sa_sin
.sin_port
= 0;
1053 bs
->mhop
.local
.sa_sin
.sin_port
= 0;
1056 bs
->mhop
.peer
.sa_sin6
.sin6_port
= 0;
1057 bs
->mhop
.local
.sa_sin6
.sin6_port
= 0;
1062 static int _iface_key(struct bfd_iface
*iface
, const char *ifname
)
1064 size_t slen
= sizeof(iface
->ifname
);
1066 memset(iface
->ifname
, 0, slen
);
1067 if (strlcpy(iface
->ifname
, ifname
, slen
) >= slen
)
1075 * Hash public interface / exported functions.
1078 /* Lookup functions. */
1079 struct bfd_session
*bfd_id_lookup(uint32_t id
)
1081 struct bfd_session bs
;
1083 bs
.discrs
.my_discr
= id
;
1085 return hash_lookup(bfd_id_hash
, &bs
);
1088 struct bfd_session
*bfd_shop_lookup(struct bfd_shop_key shop
)
1090 struct bfd_session bs
, *bsp
;
1092 _shop_key(&bs
, &shop
);
1094 bsp
= hash_lookup(bfd_shop_hash
, &bs
);
1095 if (bsp
== NULL
&& bs
.shop
.port_name
[0] != 0) {
1097 * Since the local interface spec is optional, try
1098 * searching the key without it as well.
1100 _shop_key2(&bs
, &shop
);
1101 bsp
= hash_lookup(bfd_shop_hash
, &bs
);
1107 struct bfd_session
*bfd_mhop_lookup(struct bfd_mhop_key mhop
)
1109 struct bfd_session bs
;
1111 _mhop_key(&bs
, &mhop
);
1113 return hash_lookup(bfd_shop_hash
, &bs
);
1116 struct bfd_vrf
*bfd_vrf_lookup(int vrf_id
)
1120 vrf
.vrf_id
= vrf_id
;
1122 return hash_lookup(bfd_vrf_hash
, &vrf
);
1125 struct bfd_iface
*bfd_iface_lookup(const char *ifname
)
1127 struct bfd_iface iface
;
1129 if (_iface_key(&iface
, ifname
) != 0)
1132 return hash_lookup(bfd_iface_hash
, &iface
);
1138 * Delete functions searches and remove the item from the hash and
1139 * returns a pointer to the removed item data. If the item was not found
1140 * then it returns NULL.
1142 * The data stored inside the hash is not free()ed, so you must do it
1143 * manually after getting the pointer back.
1145 struct bfd_session
*bfd_id_delete(uint32_t id
)
1147 struct bfd_session bs
;
1149 bs
.discrs
.my_discr
= id
;
1151 return hash_release(bfd_id_hash
, &bs
);
1154 struct bfd_session
*bfd_shop_delete(struct bfd_shop_key shop
)
1156 struct bfd_session bs
, *bsp
;
1158 _shop_key(&bs
, &shop
);
1159 bsp
= hash_release(bfd_shop_hash
, &bs
);
1160 if (bsp
== NULL
&& shop
.port_name
[0] != 0) {
1162 * Since the local interface spec is optional, try
1163 * searching the key without it as well.
1165 _shop_key2(&bs
, &shop
);
1166 bsp
= hash_release(bfd_shop_hash
, &bs
);
1172 struct bfd_session
*bfd_mhop_delete(struct bfd_mhop_key mhop
)
1174 struct bfd_session bs
;
1176 _mhop_key(&bs
, &mhop
);
1178 return hash_release(bfd_mhop_hash
, &bs
);
1181 struct bfd_vrf
*bfd_vrf_delete(int vrf_id
)
1185 vrf
.vrf_id
= vrf_id
;
1187 return hash_release(bfd_vrf_hash
, &vrf
);
1190 struct bfd_iface
*bfd_iface_delete(const char *ifname
)
1192 struct bfd_iface iface
;
1194 if (_iface_key(&iface
, ifname
) != 0)
1197 return hash_release(bfd_iface_hash
, &iface
);
1200 /* Iteration functions. */
1201 void bfd_id_iterate(hash_iter_func hif
, void *arg
)
1203 hash_iterate(bfd_id_hash
, hif
, arg
);
1206 void bfd_shop_iterate(hash_iter_func hif
, void *arg
)
1208 hash_iterate(bfd_shop_hash
, hif
, arg
);
1211 void bfd_mhop_iterate(hash_iter_func hif
, void *arg
)
1213 hash_iterate(bfd_mhop_hash
, hif
, arg
);
1216 void bfd_vrf_iterate(hash_iter_func hif
, void *arg
)
1218 hash_iterate(bfd_vrf_hash
, hif
, arg
);
1221 void bfd_iface_iterate(hash_iter_func hif
, void *arg
)
1223 hash_iterate(bfd_iface_hash
, hif
, arg
);
1229 * Inserts session into hash and returns `true` on success, otherwise
1232 bool bfd_id_insert(struct bfd_session
*bs
)
1234 return (hash_get(bfd_id_hash
, bs
, hash_alloc_intern
) == bs
);
1237 bool bfd_shop_insert(struct bfd_session
*bs
)
1239 return (hash_get(bfd_shop_hash
, bs
, hash_alloc_intern
) == bs
);
1242 bool bfd_mhop_insert(struct bfd_session
*bs
)
1244 return (hash_get(bfd_mhop_hash
, bs
, hash_alloc_intern
) == bs
);
1247 bool bfd_vrf_insert(struct bfd_vrf
*vrf
)
1249 return (hash_get(bfd_vrf_hash
, vrf
, hash_alloc_intern
) == vrf
);
1252 bool bfd_iface_insert(struct bfd_iface
*iface
)
1254 return (hash_get(bfd_iface_hash
, iface
, hash_alloc_intern
) == iface
);
1257 void bfd_initialize(void)
1259 bfd_id_hash
= hash_create(bfd_id_hash_do
, bfd_id_hash_cmp
,
1260 "BFD discriminator hash");
1261 bfd_shop_hash
= hash_create(bfd_shop_hash_do
, bfd_shop_hash_cmp
,
1262 "BFD single hop hash");
1263 bfd_mhop_hash
= hash_create(bfd_mhop_hash_do
, bfd_mhop_hash_cmp
,
1264 "BFD multihop hop hash");
1266 hash_create(bfd_vrf_hash_do
, bfd_vrf_hash_cmp
, "BFD VRF hash");
1267 bfd_iface_hash
= hash_create(bfd_iface_hash_do
, bfd_iface_hash_cmp
,
1268 "BFD interface hash");
1271 static void _bfd_free(struct hash_backet
*hb
,
1272 void *arg
__attribute__((__unused__
)))
1274 struct bfd_session
*bs
= hb
->data
;
1276 bfd_session_free(bs
);
1279 static void _vrf_free(void *arg
)
1281 struct bfd_vrf
*vrf
= arg
;
1283 XFREE(MTYPE_BFDD_CONFIG
, vrf
);
1286 static void _iface_free(void *arg
)
1288 struct bfd_iface
*iface
= arg
;
1290 XFREE(MTYPE_BFDD_CONFIG
, iface
);
1293 void bfd_shutdown(void)
1296 * Close and free all BFD sessions.
1298 * _bfd_free() will call bfd_session_free() which will take care
1299 * of removing the session from all hashes, so we just run an
1300 * assert() here to make sure it really happened.
1302 bfd_id_iterate(_bfd_free
, NULL
);
1303 assert(bfd_shop_hash
->count
== 0);
1304 assert(bfd_mhop_hash
->count
== 0);
1306 /* Clean the VRF and interface hashes. */
1307 hash_clean(bfd_vrf_hash
, _vrf_free
);
1308 hash_clean(bfd_iface_hash
, _iface_free
);
1310 /* Now free the hashes themselves. */
1311 hash_free(bfd_id_hash
);
1312 hash_free(bfd_shop_hash
);
1313 hash_free(bfd_mhop_hash
);
1314 hash_free(bfd_vrf_hash
);
1315 hash_free(bfd_iface_hash
);