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 "lib/jhash.h"
34 DEFINE_QOBJ_TYPE(bfd_session
);
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
,
45 static int bfd_session_update(struct bfd_session
*bs
, struct bfd_peer_cfg
*bpc
);
46 static const char *get_diag_str(int diag
);
52 struct bfd_session
*bs_peer_find(struct bfd_peer_cfg
*bpc
)
54 struct bfd_session
*bs
;
55 struct peer_label
*pl
;
56 struct bfd_mhop_key mhop
;
57 struct bfd_shop_key shop
;
59 /* Try to find label first. */
60 if (bpc
->bpc_has_label
) {
61 pl
= pl_find(bpc
->bpc_label
);
68 /* Otherwise fallback to peer/local hash lookup. */
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
));
77 bs
= bfd_mhop_lookup(mhop
);
79 memset(&shop
, 0, sizeof(shop
));
80 shop
.peer
= bpc
->bpc_peer
;
81 if (bpc
->bpc_has_localif
)
82 strlcpy(shop
.port_name
, bpc
->bpc_localif
,
83 sizeof(shop
.port_name
));
85 bs
= bfd_shop_lookup(shop
);
91 static uint32_t ptm_bfd_gen_ID(void)
93 static uint32_t sessionID
= 1;
98 void ptm_bfd_start_xmt_timer(struct bfd_session
*bfd
, bool is_echo
)
100 uint64_t jitter
, xmt_TO
;
103 xmt_TO
= is_echo
? bfd
->echo_xmt_TO
: bfd
->xmt_TO
;
106 * From section 6.5.2: trasmit interval should be randomly jittered
108 * 75% and 100% of nominal value, unless detect_mult is 1, then should
110 * between 75% and 90%.
112 maxpercent
= (bfd
->detect_mult
== 1) ? 16 : 26;
113 jitter
= (xmt_TO
* (75 + (random() % maxpercent
))) / 100;
114 /* XXX remove that division above */
117 bfd_echo_xmttimer_update(bfd
, jitter
);
119 bfd_xmttimer_update(bfd
, jitter
);
122 static void ptm_bfd_echo_xmt_TO(struct bfd_session
*bfd
)
124 /* Send the scheduled echo packet */
125 ptm_bfd_echo_snd(bfd
);
127 /* Restart the timer for next time */
128 ptm_bfd_start_xmt_timer(bfd
, true);
131 void ptm_bfd_xmt_TO(struct bfd_session
*bfd
, int fbit
)
133 /* Send the scheduled control packet */
134 ptm_bfd_snd(bfd
, fbit
);
136 /* Restart the timer for next time */
137 ptm_bfd_start_xmt_timer(bfd
, false);
140 void ptm_bfd_echo_stop(struct bfd_session
*bfd
)
142 bfd
->echo_xmt_TO
= 0;
143 bfd
->echo_detect_TO
= 0;
144 BFD_UNSET_FLAG(bfd
->flags
, BFD_SESS_FLAG_ECHO_ACTIVE
);
146 bfd_echo_xmttimer_delete(bfd
);
147 bfd_echo_recvtimer_delete(bfd
);
150 void ptm_bfd_echo_start(struct bfd_session
*bfd
)
152 bfd
->echo_detect_TO
= (bfd
->remote_detect_mult
* bfd
->echo_xmt_TO
);
153 if (bfd
->echo_detect_TO
> 0)
154 ptm_bfd_echo_xmt_TO(bfd
);
157 void ptm_bfd_ses_up(struct bfd_session
*bfd
)
159 int old_state
= bfd
->ses_state
;
162 bfd
->ses_state
= PTM_BFD_UP
;
163 monotime(&bfd
->uptime
);
165 /* Connection is up, lets negotiate timers. */
166 bfd_set_polling(bfd
);
168 /* Start sending control packets with poll bit immediately. */
173 if (old_state
!= bfd
->ses_state
) {
174 bfd
->stats
.session_up
++;
175 log_info("state-change: [%s] %s -> %s", bs_to_string(bfd
),
176 state_list
[old_state
].str
,
177 state_list
[bfd
->ses_state
].str
);
181 void ptm_bfd_ses_dn(struct bfd_session
*bfd
, uint8_t diag
)
183 int old_state
= bfd
->ses_state
;
185 bfd
->local_diag
= diag
;
186 bfd
->discrs
.remote_discr
= 0;
187 bfd
->ses_state
= PTM_BFD_DOWN
;
189 bfd
->demand_mode
= 0;
190 monotime(&bfd
->downtime
);
194 /* only signal clients when going from up->down state */
195 if (old_state
== PTM_BFD_UP
)
198 /* Stop echo packet transmission if they are active */
199 if (BFD_CHECK_FLAG(bfd
->flags
, BFD_SESS_FLAG_ECHO_ACTIVE
))
200 ptm_bfd_echo_stop(bfd
);
202 if (old_state
!= bfd
->ses_state
) {
203 bfd
->stats
.session_down
++;
204 log_info("state-change: [%s] %s -> %s reason:%s",
205 bs_to_string(bfd
), state_list
[old_state
].str
,
206 state_list
[bfd
->ses_state
].str
,
207 get_diag_str(bfd
->local_diag
));
211 static int ptm_bfd_get_vrf_name(char *port_name
, char *vrf_name
)
213 struct bfd_iface
*iface
;
216 if ((port_name
== NULL
) || (vrf_name
== NULL
))
219 iface
= bfd_iface_lookup(port_name
);
221 vrf
= bfd_vrf_lookup(iface
->vrf_id
);
223 strlcpy(vrf_name
, vrf
->name
, sizeof(vrf
->name
));
230 static struct bfd_session
*bfd_find_disc(struct sockaddr_any
*sa
,
233 struct bfd_session
*bs
;
235 bs
= bfd_id_lookup(ldisc
);
239 /* Remove unused fields. */
240 switch (sa
->sa_sin
.sin_family
) {
242 sa
->sa_sin
.sin_port
= 0;
243 if (memcmp(sa
, &bs
->shop
.peer
, sizeof(sa
->sa_sin
)) == 0)
247 sa
->sa_sin6
.sin6_port
= 0;
248 if (memcmp(sa
, &bs
->shop
.peer
, sizeof(sa
->sa_sin6
)) == 0)
256 struct bfd_session
*ptm_bfd_sess_find(struct bfd_pkt
*cp
, char *port_name
,
257 struct sockaddr_any
*peer
,
258 struct sockaddr_any
*local
,
259 char *vrf_name
, bool is_mhop
)
261 struct bfd_session
*l_bfd
= NULL
;
262 struct bfd_mhop_key mhop
;
263 struct bfd_shop_key shop
;
264 char vrf_buf
[MAXNAMELEN
];
266 /* Find our session using the ID signaled by the remote end. */
267 if (cp
->discrs
.remote_discr
)
268 return bfd_find_disc(peer
, ntohl(cp
->discrs
.remote_discr
));
270 /* Search for session without using discriminator. */
272 memset(&mhop
, 0, sizeof(mhop
));
275 if (vrf_name
&& vrf_name
[0]) {
276 strlcpy(mhop
.vrf_name
, vrf_name
, sizeof(mhop
.vrf_name
));
277 } else if (port_name
&& port_name
[0]) {
278 memset(vrf_buf
, 0, sizeof(vrf_buf
));
279 if (ptm_bfd_get_vrf_name(port_name
, vrf_buf
) != -1)
280 strlcpy(mhop
.vrf_name
, vrf_buf
,
281 sizeof(mhop
.vrf_name
));
284 l_bfd
= bfd_mhop_lookup(mhop
);
286 memset(&shop
, 0, sizeof(shop
));
288 if (port_name
&& port_name
[0])
289 strlcpy(shop
.port_name
, port_name
,
290 sizeof(shop
.port_name
));
292 l_bfd
= bfd_shop_lookup(shop
);
295 /* XXX maybe remoteDiscr should be checked for remoteHeard cases. */
299 int bfd_xmt_cb(struct thread
*t
)
301 struct bfd_session
*bs
= THREAD_ARG(t
);
303 ptm_bfd_xmt_TO(bs
, 0);
308 int bfd_echo_xmt_cb(struct thread
*t
)
310 struct bfd_session
*bs
= THREAD_ARG(t
);
312 if (bs
->echo_xmt_TO
> 0)
313 ptm_bfd_echo_xmt_TO(bs
);
318 /* Was ptm_bfd_detect_TO() */
319 int bfd_recvtimer_cb(struct thread
*t
)
321 struct bfd_session
*bs
= THREAD_ARG(t
);
323 switch (bs
->ses_state
) {
326 ptm_bfd_ses_dn(bs
, BD_CONTROL_EXPIRED
);
327 bfd_recvtimer_update(bs
);
331 /* Second detect time expiration, zero remote discr (section
334 bs
->discrs
.remote_discr
= 0;
341 /* Was ptm_bfd_echo_detect_TO() */
342 int bfd_echo_recvtimer_cb(struct thread
*t
)
344 struct bfd_session
*bs
= THREAD_ARG(t
);
346 switch (bs
->ses_state
) {
349 ptm_bfd_ses_dn(bs
, BD_ECHO_FAILED
);
356 static struct bfd_session
*bfd_session_new(int sd
)
358 struct bfd_session
*bs
;
360 bs
= XCALLOC(MTYPE_BFDD_CONFIG
, sizeof(*bs
));
364 QOBJ_REG(bs
, bfd_session
);
366 bs
->timers
.desired_min_tx
= BFD_DEFDESIREDMINTX
;
367 bs
->timers
.required_min_rx
= BFD_DEFREQUIREDMINRX
;
368 bs
->timers
.required_min_echo
= BFD_DEF_REQ_MIN_ECHO
;
369 bs
->detect_mult
= BFD_DEFDETECTMULT
;
370 bs
->mh_ttl
= BFD_DEF_MHOP_TTL
;
373 * BFD connection startup must use slow timer.
375 * RFC 5880, Section 6.8.3.
377 bs
->cur_timers
.desired_min_tx
= BFD_DEF_SLOWTX
;
378 bs
->cur_timers
.required_min_rx
= BFD_DEF_SLOWTX
;
379 bs
->cur_timers
.required_min_echo
= 0;
381 /* Set the appropriated timeouts for slow connection. */
382 bs
->detect_TO
= (BFD_DEFDETECTMULT
* BFD_DEF_SLOWTX
);
383 bs
->xmt_TO
= BFD_DEF_SLOWTX
;
385 /* Initiate remote settings as well. */
386 bs
->remote_timers
= bs
->cur_timers
;
387 bs
->remote_detect_mult
= BFD_DEFDETECTMULT
;
390 monotime(&bs
->uptime
);
391 bs
->downtime
= bs
->uptime
;
396 int bfd_session_update_label(struct bfd_session
*bs
, const char *nlabel
)
398 /* New label treatment:
399 * - Check if the label is taken;
400 * - Try to allocate the memory for it and register;
402 if (bs
->pl
== NULL
) {
403 if (pl_find(nlabel
) != NULL
) {
404 /* Someone is already using it. */
408 if (pl_new(nlabel
, bs
) == NULL
)
415 * Test label change consistency:
416 * - Do nothing if it's the same label;
417 * - Check if the future label is already taken;
420 if (strcmp(nlabel
, bs
->pl
->pl_label
) == 0)
422 if (pl_find(nlabel
) != NULL
)
425 strlcpy(bs
->pl
->pl_label
, nlabel
, sizeof(bs
->pl
->pl_label
));
429 static void _bfd_session_update(struct bfd_session
*bs
,
430 struct bfd_peer_cfg
*bpc
)
433 /* Check if echo mode is already active. */
434 if (BFD_CHECK_FLAG(bs
->flags
, BFD_SESS_FLAG_ECHO
))
437 BFD_SET_FLAG(bs
->flags
, BFD_SESS_FLAG_ECHO
);
439 /* Activate/update echo receive timeout timer. */
440 bs_echo_timer_handler(bs
);
442 /* Check if echo mode is already disabled. */
443 if (!BFD_CHECK_FLAG(bs
->flags
, BFD_SESS_FLAG_ECHO
))
446 BFD_UNSET_FLAG(bs
->flags
, BFD_SESS_FLAG_ECHO
);
447 ptm_bfd_echo_stop(bs
);
451 if (bpc
->bpc_has_txinterval
)
452 bs
->timers
.desired_min_tx
= bpc
->bpc_txinterval
* 1000;
454 if (bpc
->bpc_has_recvinterval
)
455 bs
->timers
.required_min_rx
= bpc
->bpc_recvinterval
* 1000;
457 if (bpc
->bpc_has_detectmultiplier
)
458 bs
->detect_mult
= bpc
->bpc_detectmultiplier
;
460 if (bpc
->bpc_has_echointerval
)
461 bs
->timers
.required_min_echo
= bpc
->bpc_echointerval
* 1000;
463 if (bpc
->bpc_has_label
)
464 bfd_session_update_label(bs
, bpc
->bpc_label
);
466 if (bpc
->bpc_shutdown
) {
467 /* Check if already shutdown. */
468 if (BFD_CHECK_FLAG(bs
->flags
, BFD_SESS_FLAG_SHUTDOWN
))
471 BFD_SET_FLAG(bs
->flags
, BFD_SESS_FLAG_SHUTDOWN
);
473 /* Disable all events. */
474 bfd_recvtimer_delete(bs
);
475 bfd_echo_recvtimer_delete(bs
);
476 bfd_xmttimer_delete(bs
);
477 bfd_echo_xmttimer_delete(bs
);
479 /* Change and notify state change. */
480 bs
->ses_state
= PTM_BFD_ADM_DOWN
;
485 /* Check if already working. */
486 if (!BFD_CHECK_FLAG(bs
->flags
, BFD_SESS_FLAG_SHUTDOWN
))
489 BFD_UNSET_FLAG(bs
->flags
, BFD_SESS_FLAG_SHUTDOWN
);
491 /* Change and notify state change. */
492 bs
->ses_state
= PTM_BFD_DOWN
;
495 /* Enable all timers. */
496 bfd_recvtimer_update(bs
);
497 bfd_xmttimer_update(bs
, bs
->xmt_TO
);
501 static int bfd_session_update(struct bfd_session
*bs
, struct bfd_peer_cfg
*bpc
)
503 /* User didn't want to update, return failure. */
504 if (bpc
->bpc_createonly
)
507 _bfd_session_update(bs
, bpc
);
509 control_notify_config(BCM_NOTIFY_CONFIG_UPDATE
, bs
);
514 static void bfd_session_free(struct bfd_session
*bs
)
519 bfd_recvtimer_delete(bs
);
520 bfd_echo_recvtimer_delete(bs
);
521 bfd_xmttimer_delete(bs
);
522 bfd_echo_xmttimer_delete(bs
);
524 bfd_id_delete(bs
->discrs
.my_discr
);
525 if (BFD_CHECK_FLAG(bs
->flags
, BFD_SESS_FLAG_MH
))
526 bfd_mhop_delete(bs
->mhop
);
528 bfd_shop_delete(bs
->shop
);
533 XFREE(MTYPE_BFDD_CONFIG
, bs
);
536 struct bfd_session
*ptm_bfd_sess_new(struct bfd_peer_cfg
*bpc
)
538 struct bfd_session
*bfd
, *l_bfd
;
539 struct interface
*ifp
= NULL
;
542 /* check to see if this needs a new session */
543 l_bfd
= bs_peer_find(bpc
);
545 /* Requesting a duplicated peer means update configuration. */
546 if (bfd_session_update(l_bfd
, bpc
) == 0)
553 * No session found, we have to allocate a new one.
555 * First a few critical checks:
557 * * Check that the specified interface exists.
558 * * Attempt to create the UDP socket (might fail if we exceed
561 if (bpc
->bpc_has_localif
) {
562 ifp
= if_lookup_by_name(bpc
->bpc_localif
, VRF_DEFAULT
);
565 "session-new: specified interface doesn't exists.");
571 * Get socket for transmitting control packets. Note that if we
572 * could use the destination port (3784) for the source
573 * port we wouldn't need a socket per session.
576 psock
= bp_peer_socket(bpc
);
580 psock
= bp_peer_socketv6(bpc
);
586 bfd
= bfd_session_new(psock
);
588 log_error("session-new: allocation failed");
592 if (bpc
->bpc_has_localif
&& !bpc
->bpc_mhop
)
595 if (bpc
->bpc_ipv4
== false) {
596 BFD_SET_FLAG(bfd
->flags
, BFD_SESS_FLAG_IPV6
);
598 /* Set the IPv6 scope id for link-local addresses. */
599 if (IN6_IS_ADDR_LINKLOCAL(&bpc
->bpc_local
.sa_sin6
.sin6_addr
))
600 bpc
->bpc_local
.sa_sin6
.sin6_scope_id
=
601 bfd
->ifp
!= NULL
? bfd
->ifp
->ifindex
603 if (IN6_IS_ADDR_LINKLOCAL(&bpc
->bpc_peer
.sa_sin6
.sin6_addr
))
604 bpc
->bpc_peer
.sa_sin6
.sin6_scope_id
=
605 bfd
->ifp
!= NULL
? bfd
->ifp
->ifindex
609 /* Initialize the session */
610 bfd
->ses_state
= PTM_BFD_DOWN
;
611 bfd
->discrs
.my_discr
= ptm_bfd_gen_ID();
612 bfd
->discrs
.remote_discr
= 0;
613 bfd
->local_ip
= bpc
->bpc_local
;
614 bfd
->local_address
= bpc
->bpc_local
;
615 bfd_recvtimer_update(bfd
);
616 ptm_bfd_start_xmt_timer(bfd
, false);
618 /* Registrate session into data structures. */
622 BFD_SET_FLAG(bfd
->flags
, BFD_SESS_FLAG_MH
);
623 bfd
->mhop
.peer
= bpc
->bpc_peer
;
624 bfd
->mhop
.local
= bpc
->bpc_local
;
625 if (bpc
->bpc_has_vrfname
)
626 strlcpy(bfd
->mhop
.vrf_name
, bpc
->bpc_vrfname
,
627 sizeof(bfd
->mhop
.vrf_name
));
629 bfd_mhop_insert(bfd
);
631 bfd
->shop
.peer
= bpc
->bpc_peer
;
632 if (bpc
->bpc_has_localif
)
633 strlcpy(bfd
->shop
.port_name
, bpc
->bpc_localif
,
634 sizeof(bfd
->shop
.port_name
));
636 bfd_shop_insert(bfd
);
639 _bfd_session_update(bfd
, bpc
);
641 log_info("session-new: %s", bs_to_string(bfd
));
643 control_notify_config(BCM_NOTIFY_CONFIG_ADD
, bfd
);
648 int ptm_bfd_ses_del(struct bfd_peer_cfg
*bpc
)
650 struct bfd_session
*bs
;
652 /* Find session and call free(). */
653 bs
= bs_peer_find(bpc
);
657 /* This pointer is being referenced, don't let it be deleted. */
658 if (bs
->refcount
> 0) {
659 log_error("session-delete: refcount failure: %" PRIu64
665 log_info("session-delete: %s", bs_to_string(bs
));
667 control_notify_config(BCM_NOTIFY_CONFIG_DELETE
, bs
);
669 bfd_session_free(bs
);
674 void bfd_set_polling(struct bfd_session
*bs
)
677 * Start polling procedure: the only timers that require polling
678 * to change value without losing connection are:
680 * - Desired minimum transmission interval;
681 * - Required minimum receive interval;
683 * RFC 5880, Section 6.8.3.
689 * bs_<state>_handler() functions implement the BFD state machine
690 * transition mechanism. `<state>` is the current session state and
691 * the parameter `nstate` is the peer new state.
693 void bs_admin_down_handler(struct bfd_session
*bs
, int nstate
);
694 void bs_down_handler(struct bfd_session
*bs
, int nstate
);
695 void bs_init_handler(struct bfd_session
*bs
, int nstate
);
696 void bs_up_handler(struct bfd_session
*bs
, int nstate
);
698 void bs_admin_down_handler(struct bfd_session
*bs
__attribute__((__unused__
)),
699 int nstate
__attribute__((__unused__
)))
702 * We are administratively down, there is no state machine
707 void bs_down_handler(struct bfd_session
*bs
, int nstate
)
710 case PTM_BFD_ADM_DOWN
:
712 * Remote peer doesn't want to talk, so lets keep the
716 /* Peer can't be up yet, wait it go to 'init' or 'down'. */
721 * Remote peer agreed that the path is down, lets try to
724 bs
->ses_state
= PTM_BFD_INIT
;
729 * Remote peer told us his path is up, lets turn
730 * activate the session.
736 log_debug("state-change: unhandled neighbor state: %d", nstate
);
741 void bs_init_handler(struct bfd_session
*bs
, int nstate
)
744 case PTM_BFD_ADM_DOWN
:
746 * Remote peer doesn't want to talk, so lets make the
749 bs
->ses_state
= PTM_BFD_DOWN
;
753 /* Remote peer hasn't moved to first stage yet. */
758 /* We agreed on the settings and the path is up. */
763 log_debug("state-change: unhandled neighbor state: %d", nstate
);
768 void bs_up_handler(struct bfd_session
*bs
, int nstate
)
771 case PTM_BFD_ADM_DOWN
:
773 /* Peer lost or asked to shutdown connection. */
774 ptm_bfd_ses_dn(bs
, BD_NEIGHBOR_DOWN
);
779 /* Path is up and working. */
783 log_debug("state-change: unhandled neighbor state: %d", nstate
);
788 void bs_state_handler(struct bfd_session
*bs
, int nstate
)
790 switch (bs
->ses_state
) {
791 case PTM_BFD_ADM_DOWN
:
792 bs_admin_down_handler(bs
, nstate
);
795 bs_down_handler(bs
, nstate
);
798 bs_init_handler(bs
, nstate
);
801 bs_up_handler(bs
, nstate
);
805 log_debug("state-change: [%s] is in invalid state: %d",
806 bs_to_string(bs
), nstate
);
812 * Handles echo timer manipulation after updating timer.
814 void bs_echo_timer_handler(struct bfd_session
*bs
)
819 * Before doing any echo handling, check if it is possible to
822 * - Check for `echo-mode` configuration.
823 * - Check that we are not using multi hop (RFC 5883,
825 * - Check that we are already at the up state.
827 if (BFD_CHECK_FLAG(bs
->flags
, BFD_SESS_FLAG_ECHO
) == 0
828 || BFD_CHECK_FLAG(bs
->flags
, BFD_SESS_FLAG_MH
)
829 || bs
->ses_state
!= PTM_BFD_UP
)
832 /* Remote peer asked to stop echo. */
833 if (bs
->remote_timers
.required_min_echo
== 0) {
834 if (BFD_CHECK_FLAG(bs
->flags
, BFD_SESS_FLAG_ECHO_ACTIVE
))
835 ptm_bfd_echo_stop(bs
);
841 * Calculate the echo transmission timer: we must not send
842 * echo packets faster than the minimum required time
843 * announced by the remote system.
845 * RFC 5880, Section 6.8.9.
847 old_timer
= bs
->echo_xmt_TO
;
848 if (bs
->remote_timers
.required_min_echo
> bs
->timers
.required_min_echo
)
849 bs
->echo_xmt_TO
= bs
->remote_timers
.required_min_echo
;
851 bs
->echo_xmt_TO
= bs
->timers
.required_min_echo
;
853 if (BFD_CHECK_FLAG(bs
->flags
, BFD_SESS_FLAG_ECHO_ACTIVE
) == 0
854 || old_timer
!= bs
->echo_xmt_TO
)
855 ptm_bfd_echo_start(bs
);
859 * RFC 5880 Section 6.5.
861 * When a BFD control packet with the final bit is received, we must
862 * update the session parameters.
864 void bs_final_handler(struct bfd_session
*bs
)
866 /* Start using our new timers. */
867 bs
->cur_timers
.desired_min_tx
= bs
->timers
.desired_min_tx
;
868 bs
->cur_timers
.required_min_rx
= bs
->timers
.required_min_rx
;
871 * TODO: demand mode. See RFC 5880 Section 6.1.
873 * When using demand mode we must disable the detection timer
874 * for lost control packets.
876 if (bs
->demand_mode
) {
877 /* Notify watchers about changed timers. */
878 control_notify_config(BCM_NOTIFY_CONFIG_UPDATE
, bs
);
883 * Calculate detection time based on new timers.
885 * Transmission calculation:
886 * We must respect the RequiredMinRxInterval from the remote
887 * system: if our desired transmission timer is more than the
888 * minimum receive rate, then we must lower it to at least the
889 * minimum receive interval.
891 * RFC 5880, Section 6.8.3.
893 if (bs
->timers
.desired_min_tx
> bs
->remote_timers
.required_min_rx
)
894 bs
->xmt_TO
= bs
->remote_timers
.required_min_rx
;
896 bs
->xmt_TO
= bs
->timers
.desired_min_tx
;
898 /* Apply new transmission timer immediately. */
899 ptm_bfd_start_xmt_timer(bs
, false);
902 * Detection timeout calculation:
903 * The minimum detection timeout is the remote detection
904 * multipler (number of packets to be missed) times the agreed
905 * transmission interval.
907 * RFC 5880, Section 6.8.4.
909 * TODO: support sending/counting more packets inside detection
912 if (bs
->remote_timers
.required_min_rx
> bs
->timers
.desired_min_tx
)
913 bs
->detect_TO
= bs
->remote_detect_mult
914 * bs
->remote_timers
.required_min_rx
;
916 bs
->detect_TO
= bs
->remote_detect_mult
917 * bs
->timers
.desired_min_tx
;
919 /* Apply new receive timer immediately. */
920 bfd_recvtimer_update(bs
);
922 /* Notify watchers about changed timers. */
923 control_notify_config(BCM_NOTIFY_CONFIG_UPDATE
, bs
);
930 static const char *get_diag_str(int diag
)
932 for (int i
= 0; diag_list
[i
].str
; i
++) {
933 if (diag_list
[i
].type
== diag
)
934 return diag_list
[i
].str
;
939 const char *satostr(struct sockaddr_any
*sa
)
941 #define INETSTR_BUFCOUNT 8
942 static char buf
[INETSTR_BUFCOUNT
][INET6_ADDRSTRLEN
];
944 struct sockaddr_in
*sin
= &sa
->sa_sin
;
945 struct sockaddr_in6
*sin6
= &sa
->sa_sin6
;
947 bufidx
+= (bufidx
+ 1) % INETSTR_BUFCOUNT
;
950 switch (sin
->sin_family
) {
952 inet_ntop(AF_INET
, &sin
->sin_addr
, buf
[bufidx
],
953 sizeof(buf
[bufidx
]));
956 inet_ntop(AF_INET6
, &sin6
->sin6_addr
, buf
[bufidx
],
957 sizeof(buf
[bufidx
]));
961 strlcpy(buf
[bufidx
], "unknown", sizeof(buf
[bufidx
]));
968 const char *diag2str(uint8_t diag
)
974 return "control detection time expired";
976 return "echo function failed";
978 return "neighbor signaled session down";
980 return "forwarding plane reset";
984 return "concatenated path down";
986 return "administratively down";
988 return "reverse concatenated path down";
994 int strtosa(const char *addr
, struct sockaddr_any
*sa
)
996 memset(sa
, 0, sizeof(*sa
));
998 if (inet_pton(AF_INET
, addr
, &sa
->sa_sin
.sin_addr
) == 1) {
999 sa
->sa_sin
.sin_family
= AF_INET
;
1000 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
1001 sa
->sa_sin
.sin_len
= sizeof(sa
->sa_sin
);
1002 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
1006 if (inet_pton(AF_INET6
, addr
, &sa
->sa_sin6
.sin6_addr
) == 1) {
1007 sa
->sa_sin6
.sin6_family
= AF_INET6
;
1008 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
1009 sa
->sa_sin6
.sin6_len
= sizeof(sa
->sa_sin6
);
1010 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
1017 void integer2timestr(uint64_t time
, char *buf
, size_t buflen
)
1019 unsigned int year
, month
, day
, hour
, minute
, second
;
1022 #define MINUTES (60)
1023 #define HOURS (60 * MINUTES)
1024 #define DAYS (24 * HOURS)
1025 #define MONTHS (30 * DAYS)
1026 #define YEARS (12 * MONTHS)
1027 if (time
>= YEARS
) {
1028 year
= time
/ YEARS
;
1029 time
-= year
* YEARS
;
1031 rv
= snprintf(buf
, buflen
, "%u year(s), ", year
);
1035 if (time
>= MONTHS
) {
1036 month
= time
/ MONTHS
;
1037 time
-= month
* MONTHS
;
1039 rv
= snprintf(buf
, buflen
, "%u month(s), ", month
);
1047 rv
= snprintf(buf
, buflen
, "%u day(s), ", day
);
1051 if (time
>= HOURS
) {
1052 hour
= time
/ HOURS
;
1053 time
-= hour
* HOURS
;
1055 rv
= snprintf(buf
, buflen
, "%u hour(s), ", hour
);
1059 if (time
>= MINUTES
) {
1060 minute
= time
/ MINUTES
;
1061 time
-= minute
* MINUTES
;
1063 rv
= snprintf(buf
, buflen
, "%u minute(s), ", minute
);
1067 second
= time
% MINUTES
;
1068 snprintf(buf
, buflen
, "%u second(s)", second
);
1071 const char *bs_to_string(struct bfd_session
*bs
)
1073 static char buf
[256];
1075 bool is_mhop
= BFD_CHECK_FLAG(bs
->flags
, BFD_SESS_FLAG_MH
);
1077 pos
= snprintf(buf
, sizeof(buf
), "mhop:%s", is_mhop
? "yes" : "no");
1078 if (BFD_CHECK_FLAG(bs
->flags
, BFD_SESS_FLAG_MH
)) {
1079 pos
+= snprintf(buf
+ pos
, sizeof(buf
) - pos
,
1080 " peer:%s local:%s", satostr(&bs
->mhop
.peer
),
1081 satostr(&bs
->mhop
.local
));
1083 if (bs
->mhop
.vrf_name
[0])
1084 snprintf(buf
+ pos
, sizeof(buf
) - pos
, " vrf:%s",
1087 pos
+= snprintf(buf
+ pos
, sizeof(buf
) - pos
, " peer:%s",
1088 satostr(&bs
->shop
.peer
));
1090 if (bs
->local_address
.sa_sin
.sin_family
)
1091 pos
+= snprintf(buf
+ pos
, sizeof(buf
) - pos
,
1093 satostr(&bs
->local_address
));
1095 if (bs
->shop
.port_name
[0])
1096 snprintf(buf
+ pos
, sizeof(buf
) - pos
, " interface:%s",
1097 bs
->shop
.port_name
);
1105 * BFD hash data structures to find sessions.
1107 static struct hash
*bfd_id_hash
;
1108 static struct hash
*bfd_shop_hash
;
1109 static struct hash
*bfd_mhop_hash
;
1110 static struct hash
*bfd_vrf_hash
;
1111 static struct hash
*bfd_iface_hash
;
1113 static unsigned int bfd_id_hash_do(void *p
);
1114 static unsigned int bfd_shop_hash_do(void *p
);
1115 static unsigned int bfd_mhop_hash_do(void *p
);
1116 static unsigned int bfd_vrf_hash_do(void *p
);
1117 static unsigned int bfd_iface_hash_do(void *p
);
1119 static void _shop_key(struct bfd_session
*bs
, const struct bfd_shop_key
*shop
);
1120 static void _shop_key2(struct bfd_session
*bs
, const struct bfd_shop_key
*shop
);
1121 static void _mhop_key(struct bfd_session
*bs
, const struct bfd_mhop_key
*mhop
);
1122 static int _iface_key(struct bfd_iface
*iface
, const char *ifname
);
1124 static void _bfd_free(struct hash_backet
*hb
,
1125 void *arg
__attribute__((__unused__
)));
1126 static void _vrf_free(void *arg
);
1127 static void _iface_free(void *arg
);
1129 /* BFD hash for our discriminator. */
1130 static unsigned int bfd_id_hash_do(void *p
)
1132 struct bfd_session
*bs
= p
;
1134 return jhash_1word(bs
->discrs
.my_discr
, 0);
1137 static bool bfd_id_hash_cmp(const void *n1
, const void *n2
)
1139 const struct bfd_session
*bs1
= n1
, *bs2
= n2
;
1141 return bs1
->discrs
.my_discr
== bs2
->discrs
.my_discr
;
1144 /* BFD hash for single hop. */
1145 static unsigned int bfd_shop_hash_do(void *p
)
1147 struct bfd_session
*bs
= p
;
1149 return jhash(&bs
->shop
, sizeof(bs
->shop
), 0);
1152 static bool bfd_shop_hash_cmp(const void *n1
, const void *n2
)
1154 const struct bfd_session
*bs1
= n1
, *bs2
= n2
;
1156 return memcmp(&bs1
->shop
, &bs2
->shop
, sizeof(bs1
->shop
)) == 0;
1159 /* BFD hash for multi hop. */
1160 static unsigned int bfd_mhop_hash_do(void *p
)
1162 struct bfd_session
*bs
= p
;
1164 return jhash(&bs
->mhop
, sizeof(bs
->mhop
), 0);
1167 static bool bfd_mhop_hash_cmp(const void *n1
, const void *n2
)
1169 const struct bfd_session
*bs1
= n1
, *bs2
= n2
;
1171 return memcmp(&bs1
->mhop
, &bs2
->mhop
, sizeof(bs1
->mhop
)) == 0;
1174 /* BFD hash for VRFs. */
1175 static unsigned int bfd_vrf_hash_do(void *p
)
1177 struct bfd_vrf
*vrf
= p
;
1179 return jhash_1word(vrf
->vrf_id
, 0);
1182 static bool bfd_vrf_hash_cmp(const void *n1
, const void *n2
)
1184 const struct bfd_vrf
*v1
= n1
, *v2
= n2
;
1186 return v1
->vrf_id
== v2
->vrf_id
;
1189 /* BFD hash for interfaces. */
1190 static unsigned int bfd_iface_hash_do(void *p
)
1192 struct bfd_iface
*iface
= p
;
1194 return string_hash_make(iface
->ifname
);
1197 static bool bfd_iface_hash_cmp(const void *n1
, const void *n2
)
1199 const struct bfd_iface
*i1
= n1
, *i2
= n2
;
1201 return strcmp(i1
->ifname
, i2
->ifname
) == 0;
1204 /* Helper functions */
1205 static void _shop_key(struct bfd_session
*bs
, const struct bfd_shop_key
*shop
)
1209 /* Remove unused fields. */
1210 switch (bs
->shop
.peer
.sa_sin
.sin_family
) {
1212 bs
->shop
.peer
.sa_sin
.sin_port
= 0;
1215 bs
->shop
.peer
.sa_sin6
.sin6_port
= 0;
1220 static void _shop_key2(struct bfd_session
*bs
, const struct bfd_shop_key
*shop
)
1222 _shop_key(bs
, shop
);
1223 memset(bs
->shop
.port_name
, 0, sizeof(bs
->shop
.port_name
));
1226 static void _mhop_key(struct bfd_session
*bs
, const struct bfd_mhop_key
*mhop
)
1230 /* Remove unused fields. */
1231 switch (bs
->mhop
.peer
.sa_sin
.sin_family
) {
1233 bs
->mhop
.peer
.sa_sin
.sin_port
= 0;
1234 bs
->mhop
.local
.sa_sin
.sin_port
= 0;
1237 bs
->mhop
.peer
.sa_sin6
.sin6_port
= 0;
1238 bs
->mhop
.local
.sa_sin6
.sin6_port
= 0;
1243 static int _iface_key(struct bfd_iface
*iface
, const char *ifname
)
1245 size_t slen
= sizeof(iface
->ifname
);
1247 memset(iface
->ifname
, 0, slen
);
1248 if (strlcpy(iface
->ifname
, ifname
, slen
) >= slen
)
1255 * Hash public interface / exported functions.
1258 /* Lookup functions. */
1259 struct bfd_session
*bfd_id_lookup(uint32_t id
)
1261 struct bfd_session bs
;
1263 bs
.discrs
.my_discr
= id
;
1265 return hash_lookup(bfd_id_hash
, &bs
);
1268 struct bfd_session
*bfd_shop_lookup(struct bfd_shop_key shop
)
1270 struct bfd_session bs
, *bsp
;
1272 _shop_key(&bs
, &shop
);
1274 bsp
= hash_lookup(bfd_shop_hash
, &bs
);
1275 if (bsp
== NULL
&& bs
.shop
.port_name
[0] != 0) {
1277 * Since the local interface spec is optional, try
1278 * searching the key without it as well.
1280 _shop_key2(&bs
, &shop
);
1281 bsp
= hash_lookup(bfd_shop_hash
, &bs
);
1287 struct bfd_session
*bfd_mhop_lookup(struct bfd_mhop_key mhop
)
1289 struct bfd_session bs
;
1291 _mhop_key(&bs
, &mhop
);
1293 return hash_lookup(bfd_mhop_hash
, &bs
);
1296 struct bfd_vrf
*bfd_vrf_lookup(int vrf_id
)
1300 vrf
.vrf_id
= vrf_id
;
1302 return hash_lookup(bfd_vrf_hash
, &vrf
);
1305 struct bfd_iface
*bfd_iface_lookup(const char *ifname
)
1307 struct bfd_iface iface
;
1309 if (_iface_key(&iface
, ifname
) != 0)
1312 return hash_lookup(bfd_iface_hash
, &iface
);
1318 * Delete functions searches and remove the item from the hash and
1319 * returns a pointer to the removed item data. If the item was not found
1320 * then it returns NULL.
1322 * The data stored inside the hash is not free()ed, so you must do it
1323 * manually after getting the pointer back.
1325 struct bfd_session
*bfd_id_delete(uint32_t id
)
1327 struct bfd_session bs
;
1329 bs
.discrs
.my_discr
= id
;
1331 return hash_release(bfd_id_hash
, &bs
);
1334 struct bfd_session
*bfd_shop_delete(struct bfd_shop_key shop
)
1336 struct bfd_session bs
, *bsp
;
1338 _shop_key(&bs
, &shop
);
1339 bsp
= hash_release(bfd_shop_hash
, &bs
);
1340 if (bsp
== NULL
&& shop
.port_name
[0] != 0) {
1342 * Since the local interface spec is optional, try
1343 * searching the key without it as well.
1345 _shop_key2(&bs
, &shop
);
1346 bsp
= hash_release(bfd_shop_hash
, &bs
);
1352 struct bfd_session
*bfd_mhop_delete(struct bfd_mhop_key mhop
)
1354 struct bfd_session bs
;
1356 _mhop_key(&bs
, &mhop
);
1358 return hash_release(bfd_mhop_hash
, &bs
);
1361 struct bfd_vrf
*bfd_vrf_delete(int vrf_id
)
1365 vrf
.vrf_id
= vrf_id
;
1367 return hash_release(bfd_vrf_hash
, &vrf
);
1370 struct bfd_iface
*bfd_iface_delete(const char *ifname
)
1372 struct bfd_iface iface
;
1374 if (_iface_key(&iface
, ifname
) != 0)
1377 return hash_release(bfd_iface_hash
, &iface
);
1380 /* Iteration functions. */
1381 void bfd_id_iterate(hash_iter_func hif
, void *arg
)
1383 hash_iterate(bfd_id_hash
, hif
, arg
);
1386 void bfd_shop_iterate(hash_iter_func hif
, void *arg
)
1388 hash_iterate(bfd_shop_hash
, hif
, arg
);
1391 void bfd_mhop_iterate(hash_iter_func hif
, void *arg
)
1393 hash_iterate(bfd_mhop_hash
, hif
, arg
);
1396 void bfd_vrf_iterate(hash_iter_func hif
, void *arg
)
1398 hash_iterate(bfd_vrf_hash
, hif
, arg
);
1401 void bfd_iface_iterate(hash_iter_func hif
, void *arg
)
1403 hash_iterate(bfd_iface_hash
, hif
, arg
);
1409 * Inserts session into hash and returns `true` on success, otherwise
1412 bool bfd_id_insert(struct bfd_session
*bs
)
1414 return (hash_get(bfd_id_hash
, bs
, hash_alloc_intern
) == bs
);
1417 bool bfd_shop_insert(struct bfd_session
*bs
)
1419 return (hash_get(bfd_shop_hash
, bs
, hash_alloc_intern
) == bs
);
1422 bool bfd_mhop_insert(struct bfd_session
*bs
)
1424 return (hash_get(bfd_mhop_hash
, bs
, hash_alloc_intern
) == bs
);
1427 bool bfd_vrf_insert(struct bfd_vrf
*vrf
)
1429 return (hash_get(bfd_vrf_hash
, vrf
, hash_alloc_intern
) == vrf
);
1432 bool bfd_iface_insert(struct bfd_iface
*iface
)
1434 return (hash_get(bfd_iface_hash
, iface
, hash_alloc_intern
) == iface
);
1437 void bfd_initialize(void)
1439 bfd_id_hash
= hash_create(bfd_id_hash_do
, bfd_id_hash_cmp
,
1440 "BFD discriminator hash");
1441 bfd_shop_hash
= hash_create(bfd_shop_hash_do
, bfd_shop_hash_cmp
,
1442 "BFD single hop hash");
1443 bfd_mhop_hash
= hash_create(bfd_mhop_hash_do
, bfd_mhop_hash_cmp
,
1444 "BFD multihop hop hash");
1446 hash_create(bfd_vrf_hash_do
, bfd_vrf_hash_cmp
, "BFD VRF hash");
1447 bfd_iface_hash
= hash_create(bfd_iface_hash_do
, bfd_iface_hash_cmp
,
1448 "BFD interface hash");
1451 static void _bfd_free(struct hash_backet
*hb
,
1452 void *arg
__attribute__((__unused__
)))
1454 struct bfd_session
*bs
= hb
->data
;
1456 bfd_session_free(bs
);
1459 static void _vrf_free(void *arg
)
1461 struct bfd_vrf
*vrf
= arg
;
1463 XFREE(MTYPE_BFDD_CONFIG
, vrf
);
1466 static void _iface_free(void *arg
)
1468 struct bfd_iface
*iface
= arg
;
1470 XFREE(MTYPE_BFDD_CONFIG
, iface
);
1473 void bfd_shutdown(void)
1476 * Close and free all BFD sessions.
1478 * _bfd_free() will call bfd_session_free() which will take care
1479 * of removing the session from all hashes, so we just run an
1480 * assert() here to make sure it really happened.
1482 bfd_id_iterate(_bfd_free
, NULL
);
1483 assert(bfd_shop_hash
->count
== 0);
1484 assert(bfd_mhop_hash
->count
== 0);
1486 /* Clean the VRF and interface hashes. */
1487 hash_clean(bfd_vrf_hash
, _vrf_free
);
1488 hash_clean(bfd_iface_hash
, _iface_free
);
1490 /* Now free the hashes themselves. */
1491 hash_free(bfd_id_hash
);
1492 hash_free(bfd_shop_hash
);
1493 hash_free(bfd_mhop_hash
);
1494 hash_free(bfd_vrf_hash
);
1495 hash_free(bfd_iface_hash
);