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
, int polling
)
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 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
;
157 void ptm_bfd_echo_start(struct bfd_session
*bfd
)
159 bfd
->echo_detect_TO
= (bfd
->remote_detect_mult
* bfd
->echo_xmt_TO
);
160 ptm_bfd_echo_xmt_TO(bfd
);
163 bfd
->new_timers
.desired_min_tx
= bfd
->up_min_tx
;
164 bfd
->new_timers
.required_min_rx
= bfd
->timers
.required_min_rx
;
168 void ptm_bfd_ses_up(struct bfd_session
*bfd
)
170 int old_state
= bfd
->ses_state
;
173 bfd
->ses_state
= PTM_BFD_UP
;
175 monotime(&bfd
->uptime
);
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
);
181 bfd
->new_timers
.desired_min_tx
= bfd
->up_min_tx
;
182 bfd
->new_timers
.required_min_rx
= bfd
->timers
.required_min_rx
;
188 if (old_state
!= bfd
->ses_state
) {
189 bfd
->stats
.session_up
++;
190 log_info("state-change: [%s] %s -> %s", bs_to_string(bfd
),
191 state_list
[old_state
].str
,
192 state_list
[bfd
->ses_state
].str
);
196 void ptm_bfd_ses_dn(struct bfd_session
*bfd
, uint8_t diag
)
198 int old_state
= bfd
->ses_state
;
200 bfd
->local_diag
= diag
;
201 bfd
->discrs
.remote_discr
= 0;
202 bfd
->ses_state
= PTM_BFD_DOWN
;
204 bfd
->demand_mode
= 0;
205 monotime(&bfd
->downtime
);
209 /* only signal clients when going from up->down state */
210 if (old_state
== PTM_BFD_UP
)
213 /* Stop echo packet transmission if they are active */
214 if (BFD_CHECK_FLAG(bfd
->flags
, BFD_SESS_FLAG_ECHO_ACTIVE
))
215 ptm_bfd_echo_stop(bfd
, 0);
217 if (old_state
!= bfd
->ses_state
) {
218 bfd
->stats
.session_down
++;
219 log_info("state-change: [%s] %s -> %s reason:%s",
220 bs_to_string(bfd
), state_list
[old_state
].str
,
221 state_list
[bfd
->ses_state
].str
,
222 get_diag_str(bfd
->local_diag
));
226 static int ptm_bfd_get_vrf_name(char *port_name
, char *vrf_name
)
228 struct bfd_iface
*iface
;
231 if ((port_name
== NULL
) || (vrf_name
== NULL
))
234 iface
= bfd_iface_lookup(port_name
);
236 vrf
= bfd_vrf_lookup(iface
->vrf_id
);
238 strlcpy(vrf_name
, vrf
->name
, sizeof(vrf
->name
));
245 static struct bfd_session
*bfd_find_disc(struct sockaddr_any
*sa
,
248 struct bfd_session
*bs
;
250 bs
= bfd_id_lookup(ldisc
);
254 /* Remove unused fields. */
255 switch (sa
->sa_sin
.sin_family
) {
257 sa
->sa_sin
.sin_port
= 0;
258 if (memcmp(sa
, &bs
->shop
.peer
, sizeof(sa
->sa_sin
)) == 0)
262 sa
->sa_sin6
.sin6_port
= 0;
263 if (memcmp(sa
, &bs
->shop
.peer
, sizeof(sa
->sa_sin6
)) == 0)
271 struct bfd_session
*ptm_bfd_sess_find(struct bfd_pkt
*cp
, char *port_name
,
272 struct sockaddr_any
*peer
,
273 struct sockaddr_any
*local
,
274 char *vrf_name
, bool is_mhop
)
276 struct bfd_session
*l_bfd
= NULL
;
277 struct bfd_mhop_key mhop
;
278 struct bfd_shop_key shop
;
279 char vrf_buf
[MAXNAMELEN
];
281 /* Find our session using the ID signaled by the remote end. */
282 if (cp
->discrs
.remote_discr
)
283 return bfd_find_disc(peer
, ntohl(cp
->discrs
.remote_discr
));
285 /* Search for session without using discriminator. */
287 memset(&mhop
, 0, sizeof(mhop
));
290 if (vrf_name
&& vrf_name
[0]) {
291 strlcpy(mhop
.vrf_name
, vrf_name
, sizeof(mhop
.vrf_name
));
292 } else if (port_name
&& port_name
[0]) {
293 memset(vrf_buf
, 0, sizeof(vrf_buf
));
294 if (ptm_bfd_get_vrf_name(port_name
, vrf_buf
) != -1)
295 strlcpy(mhop
.vrf_name
, vrf_buf
,
296 sizeof(mhop
.vrf_name
));
299 l_bfd
= bfd_mhop_lookup(mhop
);
301 memset(&shop
, 0, sizeof(shop
));
303 if (port_name
&& port_name
[0])
304 strlcpy(shop
.port_name
, port_name
,
305 sizeof(shop
.port_name
));
307 l_bfd
= bfd_shop_lookup(shop
);
310 /* XXX maybe remoteDiscr should be checked for remoteHeard cases. */
314 int bfd_xmt_cb(struct thread
*t
)
316 struct bfd_session
*bs
= THREAD_ARG(t
);
318 ptm_bfd_xmt_TO(bs
, 0);
323 int bfd_echo_xmt_cb(struct thread
*t
)
325 struct bfd_session
*bs
= THREAD_ARG(t
);
327 ptm_bfd_echo_xmt_TO(bs
);
332 /* Was ptm_bfd_detect_TO() */
333 int bfd_recvtimer_cb(struct thread
*t
)
335 struct bfd_session
*bs
= THREAD_ARG(t
);
337 switch (bs
->ses_state
) {
340 ptm_bfd_ses_dn(bs
, BD_CONTROL_EXPIRED
);
341 bfd_recvtimer_update(bs
);
345 /* Second detect time expiration, zero remote discr (section
348 bs
->discrs
.remote_discr
= 0;
355 /* Was ptm_bfd_echo_detect_TO() */
356 int bfd_echo_recvtimer_cb(struct thread
*t
)
358 struct bfd_session
*bs
= THREAD_ARG(t
);
360 switch (bs
->ses_state
) {
363 ptm_bfd_ses_dn(bs
, BD_ECHO_FAILED
);
370 static struct bfd_session
*bfd_session_new(int sd
)
372 struct bfd_session
*bs
;
374 bs
= XCALLOC(MTYPE_BFDD_CONFIG
, sizeof(*bs
));
378 QOBJ_REG(bs
, bfd_session
);
380 bs
->up_min_tx
= BFD_DEFDESIREDMINTX
;
381 bs
->timers
.required_min_rx
= BFD_DEFREQUIREDMINRX
;
382 bs
->timers
.required_min_echo
= BFD_DEF_REQ_MIN_ECHO
;
383 bs
->detect_mult
= BFD_DEFDETECTMULT
;
384 bs
->mh_ttl
= BFD_DEF_MHOP_TTL
;
387 monotime(&bs
->uptime
);
388 bs
->downtime
= bs
->uptime
;
393 int bfd_session_update_label(struct bfd_session
*bs
, const char *nlabel
)
395 /* New label treatment:
396 * - Check if the label is taken;
397 * - Try to allocate the memory for it and register;
399 if (bs
->pl
== NULL
) {
400 if (pl_find(nlabel
) != NULL
) {
401 /* Someone is already using it. */
405 if (pl_new(nlabel
, bs
) == NULL
)
412 * Test label change consistency:
413 * - Do nothing if it's the same label;
414 * - Check if the future label is already taken;
417 if (strcmp(nlabel
, bs
->pl
->pl_label
) == 0)
419 if (pl_find(nlabel
) != NULL
)
422 strlcpy(bs
->pl
->pl_label
, nlabel
, sizeof(bs
->pl
->pl_label
));
426 static void _bfd_session_update(struct bfd_session
*bs
,
427 struct bfd_peer_cfg
*bpc
)
430 /* Check if echo mode is already active. */
431 if (BFD_CHECK_FLAG(bs
->flags
, BFD_SESS_FLAG_ECHO
))
434 BFD_SET_FLAG(bs
->flags
, BFD_SESS_FLAG_ECHO
);
435 ptm_bfd_echo_start(bs
);
437 /* Activate/update echo receive timeout timer. */
438 bfd_echo_recvtimer_update(bs
);
440 /* Check if echo mode is already disabled. */
441 if (!BFD_CHECK_FLAG(bs
->flags
, BFD_SESS_FLAG_ECHO
))
444 BFD_UNSET_FLAG(bs
->flags
, BFD_SESS_FLAG_ECHO
);
445 ptm_bfd_echo_stop(bs
, 0);
449 if (bpc
->bpc_has_txinterval
)
450 bs
->up_min_tx
= bpc
->bpc_txinterval
* 1000;
452 if (bpc
->bpc_has_recvinterval
)
453 bs
->timers
.required_min_rx
= bpc
->bpc_recvinterval
* 1000;
455 if (bpc
->bpc_has_detectmultiplier
)
456 bs
->detect_mult
= bpc
->bpc_detectmultiplier
;
458 if (bpc
->bpc_has_echointerval
)
459 bs
->timers
.required_min_echo
= bpc
->bpc_echointerval
* 1000;
461 if (bpc
->bpc_has_label
)
462 bfd_session_update_label(bs
, bpc
->bpc_label
);
464 if (bpc
->bpc_shutdown
) {
465 /* Check if already shutdown. */
466 if (BFD_CHECK_FLAG(bs
->flags
, BFD_SESS_FLAG_SHUTDOWN
))
469 BFD_SET_FLAG(bs
->flags
, BFD_SESS_FLAG_SHUTDOWN
);
471 /* Disable all events. */
472 bfd_recvtimer_delete(bs
);
473 bfd_echo_recvtimer_delete(bs
);
474 bfd_xmttimer_delete(bs
);
475 bfd_echo_xmttimer_delete(bs
);
477 /* Change and notify state change. */
478 bs
->ses_state
= PTM_BFD_ADM_DOWN
;
483 /* Check if already working. */
484 if (!BFD_CHECK_FLAG(bs
->flags
, BFD_SESS_FLAG_SHUTDOWN
))
487 BFD_UNSET_FLAG(bs
->flags
, BFD_SESS_FLAG_SHUTDOWN
);
489 /* Change and notify state change. */
490 bs
->ses_state
= PTM_BFD_DOWN
;
493 /* Enable all timers. */
494 bfd_recvtimer_update(bs
);
495 bfd_xmttimer_update(bs
, bs
->xmt_TO
);
496 if (BFD_CHECK_FLAG(bs
->flags
, BFD_SESS_FLAG_ECHO
)) {
497 bfd_echo_recvtimer_update(bs
);
498 bfd_echo_xmttimer_update(bs
, bs
->echo_xmt_TO
);
503 static int bfd_session_update(struct bfd_session
*bs
, struct bfd_peer_cfg
*bpc
)
505 /* User didn't want to update, return failure. */
506 if (bpc
->bpc_createonly
)
509 _bfd_session_update(bs
, bpc
);
511 control_notify_config(BCM_NOTIFY_CONFIG_UPDATE
, bs
);
516 static void bfd_session_free(struct bfd_session
*bs
)
521 bfd_recvtimer_delete(bs
);
522 bfd_echo_recvtimer_delete(bs
);
523 bfd_xmttimer_delete(bs
);
524 bfd_echo_xmttimer_delete(bs
);
526 bfd_id_delete(bs
->discrs
.my_discr
);
527 if (BFD_CHECK_FLAG(bs
->flags
, BFD_SESS_FLAG_MH
))
528 bfd_mhop_delete(bs
->mhop
);
530 bfd_shop_delete(bs
->shop
);
535 XFREE(MTYPE_BFDD_CONFIG
, bs
);
538 struct bfd_session
*ptm_bfd_sess_new(struct bfd_peer_cfg
*bpc
)
540 struct bfd_session
*bfd
, *l_bfd
;
543 /* check to see if this needs a new session */
544 l_bfd
= bs_peer_find(bpc
);
546 /* Requesting a duplicated peer means update configuration. */
547 if (bfd_session_update(l_bfd
, bpc
) == 0)
554 * Get socket for transmitting control packets. Note that if we
555 * could use the destination port (3784) for the source
556 * port we wouldn't need a socket per session.
559 psock
= bp_peer_socket(bpc
);
563 psock
= bp_peer_socketv6(bpc
);
569 bfd
= bfd_session_new(psock
);
571 log_error("session-new: allocation failed");
575 if (bpc
->bpc_has_localif
&& !bpc
->bpc_mhop
) {
576 bfd
->ifindex
= ptm_bfd_fetch_ifindex(bpc
->bpc_localif
);
577 ptm_bfd_fetch_local_mac(bpc
->bpc_localif
, bfd
->local_mac
);
580 if (bpc
->bpc_ipv4
== false) {
581 BFD_SET_FLAG(bfd
->flags
, BFD_SESS_FLAG_IPV6
);
583 /* Set the IPv6 scope id for link-local addresses. */
584 if (IN6_IS_ADDR_LINKLOCAL(&bpc
->bpc_local
.sa_sin6
.sin6_addr
))
585 bpc
->bpc_local
.sa_sin6
.sin6_scope_id
= bfd
->ifindex
;
586 if (IN6_IS_ADDR_LINKLOCAL(&bpc
->bpc_peer
.sa_sin6
.sin6_addr
))
587 bpc
->bpc_peer
.sa_sin6
.sin6_scope_id
= bfd
->ifindex
;
590 /* Initialize the session */
591 bfd
->ses_state
= PTM_BFD_DOWN
;
592 bfd
->discrs
.my_discr
= ptm_bfd_gen_ID();
593 bfd
->discrs
.remote_discr
= 0;
594 bfd
->local_ip
= bpc
->bpc_local
;
595 bfd
->local_address
= bpc
->bpc_local
;
596 bfd
->timers
.desired_min_tx
= bfd
->up_min_tx
;
597 bfd
->detect_TO
= (bfd
->detect_mult
* BFD_DEF_SLOWTX
);
599 /* Use detect_TO first for slow detection, then use recvtimer_update. */
600 bfd_recvtimer_update(bfd
);
605 BFD_SET_FLAG(bfd
->flags
, BFD_SESS_FLAG_MH
);
606 bfd
->mhop
.peer
= bpc
->bpc_peer
;
607 bfd
->mhop
.local
= bpc
->bpc_local
;
608 if (bpc
->bpc_has_vrfname
)
609 strlcpy(bfd
->mhop
.vrf_name
, bpc
->bpc_vrfname
,
610 sizeof(bfd
->mhop
.vrf_name
));
612 bfd_mhop_insert(bfd
);
614 bfd
->shop
.peer
= bpc
->bpc_peer
;
615 if (bpc
->bpc_has_localif
)
616 strlcpy(bfd
->shop
.port_name
, bpc
->bpc_localif
,
617 sizeof(bfd
->shop
.port_name
));
619 bfd_shop_insert(bfd
);
623 * XXX: session update triggers echo start, so we must have our
624 * discriminator ID set first.
626 _bfd_session_update(bfd
, bpc
);
628 /* Start transmitting with slow interval until peer responds */
629 bfd
->xmt_TO
= BFD_DEF_SLOWTX
;
631 ptm_bfd_xmt_TO(bfd
, 0);
633 log_info("session-new: %s", bs_to_string(bfd
));
635 control_notify_config(BCM_NOTIFY_CONFIG_ADD
, bfd
);
640 int ptm_bfd_ses_del(struct bfd_peer_cfg
*bpc
)
642 struct bfd_session
*bs
;
644 /* Find session and call free(). */
645 bs
= bs_peer_find(bpc
);
649 /* This pointer is being referenced, don't let it be deleted. */
650 if (bs
->refcount
> 0) {
651 log_error("session-delete: refcount failure: %" PRIu64
657 log_info("session-delete: %s", bs_to_string(bs
));
659 control_notify_config(BCM_NOTIFY_CONFIG_DELETE
, bs
);
661 bfd_session_free(bs
);
666 void bfd_set_polling(struct bfd_session
*bs
)
668 bs
->new_timers
.desired_min_tx
= bs
->up_min_tx
;
669 bs
->new_timers
.required_min_rx
= bs
->timers
.required_min_rx
;
670 bs
->new_timers
.required_min_echo
= bs
->timers
.required_min_echo
;
678 static const char *get_diag_str(int diag
)
680 for (int i
= 0; diag_list
[i
].str
; i
++) {
681 if (diag_list
[i
].type
== diag
)
682 return diag_list
[i
].str
;
687 const char *satostr(struct sockaddr_any
*sa
)
689 #define INETSTR_BUFCOUNT 8
690 static char buf
[INETSTR_BUFCOUNT
][INET6_ADDRSTRLEN
];
692 struct sockaddr_in
*sin
= &sa
->sa_sin
;
693 struct sockaddr_in6
*sin6
= &sa
->sa_sin6
;
695 bufidx
+= (bufidx
+ 1) % INETSTR_BUFCOUNT
;
698 switch (sin
->sin_family
) {
700 inet_ntop(AF_INET
, &sin
->sin_addr
, buf
[bufidx
],
701 sizeof(buf
[bufidx
]));
704 inet_ntop(AF_INET6
, &sin6
->sin6_addr
, buf
[bufidx
],
705 sizeof(buf
[bufidx
]));
709 strlcpy(buf
[bufidx
], "unknown", sizeof(buf
[bufidx
]));
716 const char *diag2str(uint8_t diag
)
722 return "control detection time expired";
724 return "echo function failed";
726 return "neighbor signaled session down";
728 return "forwarding plane reset";
732 return "concatenated path down";
734 return "administratively down";
736 return "reverse concatenated path down";
742 int strtosa(const char *addr
, struct sockaddr_any
*sa
)
744 memset(sa
, 0, sizeof(*sa
));
746 if (inet_pton(AF_INET
, addr
, &sa
->sa_sin
.sin_addr
) == 1) {
747 sa
->sa_sin
.sin_family
= AF_INET
;
748 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
749 sa
->sa_sin
.sin_len
= sizeof(sa
->sa_sin
);
750 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
754 if (inet_pton(AF_INET6
, addr
, &sa
->sa_sin6
.sin6_addr
) == 1) {
755 sa
->sa_sin6
.sin6_family
= AF_INET6
;
756 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
757 sa
->sa_sin6
.sin6_len
= sizeof(sa
->sa_sin6
);
758 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
765 void integer2timestr(uint64_t time
, char *buf
, size_t buflen
)
767 unsigned int year
, month
, day
, hour
, minute
, second
;
771 #define HOURS (24 * MINUTES)
772 #define DAYS (30 * HOURS)
773 #define MONTHS (12 * DAYS)
774 #define YEARS (MONTHS)
777 time
-= year
* YEARS
;
779 rv
= snprintf(buf
, buflen
, "%u year(s), ", year
);
783 if (time
>= MONTHS
) {
784 month
= time
/ MONTHS
;
785 time
-= month
* MONTHS
;
787 rv
= snprintf(buf
, buflen
, "%u month(s), ", month
);
795 rv
= snprintf(buf
, buflen
, "%u day(s), ", day
);
801 time
-= hour
* HOURS
;
803 rv
= snprintf(buf
, buflen
, "%u hour(s), ", hour
);
807 if (time
>= MINUTES
) {
808 minute
= time
/ MINUTES
;
809 time
-= minute
* MINUTES
;
811 rv
= snprintf(buf
, buflen
, "%u minute(s), ", minute
);
815 second
= time
% MINUTES
;
816 snprintf(buf
, buflen
, "%u second(s)", second
);
819 const char *bs_to_string(struct bfd_session
*bs
)
821 static char buf
[256];
823 bool is_mhop
= BFD_CHECK_FLAG(bs
->flags
, BFD_SESS_FLAG_MH
);
825 pos
= snprintf(buf
, sizeof(buf
), "mhop:%s", is_mhop
? "yes" : "no");
826 if (BFD_CHECK_FLAG(bs
->flags
, BFD_SESS_FLAG_MH
)) {
827 pos
+= snprintf(buf
+ pos
, sizeof(buf
) - pos
,
828 " peer:%s local:%s", satostr(&bs
->mhop
.peer
),
829 satostr(&bs
->mhop
.local
));
831 if (bs
->mhop
.vrf_name
[0])
832 snprintf(buf
+ pos
, sizeof(buf
) - pos
, " vrf:%s",
835 pos
+= snprintf(buf
+ pos
, sizeof(buf
) - pos
, " peer:%s",
836 satostr(&bs
->shop
.peer
));
838 if (bs
->local_address
.sa_sin
.sin_family
)
839 pos
+= snprintf(buf
+ pos
, sizeof(buf
) - pos
,
841 satostr(&bs
->local_address
));
843 if (bs
->shop
.port_name
[0])
844 snprintf(buf
+ pos
, sizeof(buf
) - pos
, " interface:%s",
853 * BFD hash data structures to find sessions.
855 static struct hash
*bfd_id_hash
;
856 static struct hash
*bfd_shop_hash
;
857 static struct hash
*bfd_mhop_hash
;
858 static struct hash
*bfd_vrf_hash
;
859 static struct hash
*bfd_iface_hash
;
861 static unsigned int bfd_id_hash_do(void *p
);
862 static int bfd_id_hash_cmp(const void *n1
, const void *n2
);
863 static unsigned int bfd_shop_hash_do(void *p
);
864 static int bfd_shop_hash_cmp(const void *n1
, const void *n2
);
865 static unsigned int bfd_mhop_hash_do(void *p
);
866 static int bfd_mhop_hash_cmp(const void *n1
, const void *n2
);
867 static unsigned int bfd_vrf_hash_do(void *p
);
868 static int bfd_vrf_hash_cmp(const void *n1
, const void *n2
);
869 static unsigned int bfd_iface_hash_do(void *p
);
870 static int bfd_iface_hash_cmp(const void *n1
, const void *n2
);
872 static void _shop_key(struct bfd_session
*bs
, const struct bfd_shop_key
*shop
);
873 static void _shop_key2(struct bfd_session
*bs
, const struct bfd_shop_key
*shop
);
874 static void _mhop_key(struct bfd_session
*bs
, const struct bfd_mhop_key
*mhop
);
875 static int _iface_key(struct bfd_iface
*iface
, const char *ifname
);
877 static void _bfd_free(struct hash_backet
*hb
,
878 void *arg
__attribute__((__unused__
)));
879 static void _vrf_free(void *arg
);
880 static void _iface_free(void *arg
);
882 /* BFD hash for our discriminator. */
883 static unsigned int bfd_id_hash_do(void *p
)
885 struct bfd_session
*bs
= p
;
887 return jhash_1word(bs
->discrs
.my_discr
, 0);
890 static int bfd_id_hash_cmp(const void *n1
, const void *n2
)
892 const struct bfd_session
*bs1
= n1
, *bs2
= n2
;
894 return bs1
->discrs
.my_discr
== bs2
->discrs
.my_discr
;
897 /* BFD hash for single hop. */
898 static unsigned int bfd_shop_hash_do(void *p
)
900 struct bfd_session
*bs
= p
;
902 return jhash(&bs
->shop
, sizeof(bs
->shop
), 0);
905 static int bfd_shop_hash_cmp(const void *n1
, const void *n2
)
907 const struct bfd_session
*bs1
= n1
, *bs2
= n2
;
909 return memcmp(&bs1
->shop
, &bs2
->shop
, sizeof(bs1
->shop
)) == 0;
912 /* BFD hash for multi hop. */
913 static unsigned int bfd_mhop_hash_do(void *p
)
915 struct bfd_session
*bs
= p
;
917 return jhash(&bs
->mhop
, sizeof(bs
->mhop
), 0);
920 static int bfd_mhop_hash_cmp(const void *n1
, const void *n2
)
922 const struct bfd_session
*bs1
= n1
, *bs2
= n2
;
924 return memcmp(&bs1
->mhop
, &bs2
->mhop
, sizeof(bs1
->mhop
)) == 0;
927 /* BFD hash for VRFs. */
928 static unsigned int bfd_vrf_hash_do(void *p
)
930 struct bfd_vrf
*vrf
= p
;
932 return jhash_1word(vrf
->vrf_id
, 0);
935 static int bfd_vrf_hash_cmp(const void *n1
, const void *n2
)
937 const struct bfd_vrf
*v1
= n1
, *v2
= n2
;
939 return v1
->vrf_id
== v2
->vrf_id
;
942 /* BFD hash for interfaces. */
943 static unsigned int bfd_iface_hash_do(void *p
)
945 struct bfd_iface
*iface
= p
;
947 return string_hash_make(iface
->ifname
);
950 static int bfd_iface_hash_cmp(const void *n1
, const void *n2
)
952 const struct bfd_iface
*i1
= n1
, *i2
= n2
;
954 return strcmp(i1
->ifname
, i2
->ifname
) == 0;
957 /* Helper functions */
958 static void _shop_key(struct bfd_session
*bs
, const struct bfd_shop_key
*shop
)
962 /* Remove unused fields. */
963 switch (bs
->shop
.peer
.sa_sin
.sin_family
) {
965 bs
->shop
.peer
.sa_sin
.sin_port
= 0;
968 bs
->shop
.peer
.sa_sin6
.sin6_port
= 0;
973 static void _shop_key2(struct bfd_session
*bs
, const struct bfd_shop_key
*shop
)
976 memset(bs
->shop
.port_name
, 0, sizeof(bs
->shop
.port_name
));
979 static void _mhop_key(struct bfd_session
*bs
, const struct bfd_mhop_key
*mhop
)
983 /* Remove unused fields. */
984 switch (bs
->mhop
.peer
.sa_sin
.sin_family
) {
986 bs
->mhop
.peer
.sa_sin
.sin_port
= 0;
987 bs
->mhop
.local
.sa_sin
.sin_port
= 0;
990 bs
->mhop
.peer
.sa_sin6
.sin6_port
= 0;
991 bs
->mhop
.local
.sa_sin6
.sin6_port
= 0;
996 static int _iface_key(struct bfd_iface
*iface
, const char *ifname
)
998 size_t slen
= sizeof(iface
->ifname
);
1000 memset(iface
->ifname
, 0, slen
);
1001 if (strlcpy(iface
->ifname
, ifname
, slen
) >= slen
)
1008 * Hash public interface / exported functions.
1011 /* Lookup functions. */
1012 struct bfd_session
*bfd_id_lookup(uint32_t id
)
1014 struct bfd_session bs
;
1016 bs
.discrs
.my_discr
= id
;
1018 return hash_lookup(bfd_id_hash
, &bs
);
1021 struct bfd_session
*bfd_shop_lookup(struct bfd_shop_key shop
)
1023 struct bfd_session bs
, *bsp
;
1025 _shop_key(&bs
, &shop
);
1027 bsp
= hash_lookup(bfd_shop_hash
, &bs
);
1028 if (bsp
== NULL
&& bs
.shop
.port_name
[0] != 0) {
1030 * Since the local interface spec is optional, try
1031 * searching the key without it as well.
1033 _shop_key2(&bs
, &shop
);
1034 bsp
= hash_lookup(bfd_shop_hash
, &bs
);
1040 struct bfd_session
*bfd_mhop_lookup(struct bfd_mhop_key mhop
)
1042 struct bfd_session bs
;
1044 _mhop_key(&bs
, &mhop
);
1046 return hash_lookup(bfd_shop_hash
, &bs
);
1049 struct bfd_vrf
*bfd_vrf_lookup(int vrf_id
)
1053 vrf
.vrf_id
= vrf_id
;
1055 return hash_lookup(bfd_vrf_hash
, &vrf
);
1058 struct bfd_iface
*bfd_iface_lookup(const char *ifname
)
1060 struct bfd_iface iface
;
1062 if (_iface_key(&iface
, ifname
) != 0)
1065 return hash_lookup(bfd_iface_hash
, &iface
);
1071 * Delete functions searches and remove the item from the hash and
1072 * returns a pointer to the removed item data. If the item was not found
1073 * then it returns NULL.
1075 * The data stored inside the hash is not free()ed, so you must do it
1076 * manually after getting the pointer back.
1078 struct bfd_session
*bfd_id_delete(uint32_t id
)
1080 struct bfd_session bs
;
1082 bs
.discrs
.my_discr
= id
;
1084 return hash_release(bfd_id_hash
, &bs
);
1087 struct bfd_session
*bfd_shop_delete(struct bfd_shop_key shop
)
1089 struct bfd_session bs
, *bsp
;
1091 _shop_key(&bs
, &shop
);
1092 bsp
= hash_release(bfd_shop_hash
, &bs
);
1093 if (bsp
== NULL
&& shop
.port_name
[0] != 0) {
1095 * Since the local interface spec is optional, try
1096 * searching the key without it as well.
1098 _shop_key2(&bs
, &shop
);
1099 bsp
= hash_release(bfd_shop_hash
, &bs
);
1105 struct bfd_session
*bfd_mhop_delete(struct bfd_mhop_key mhop
)
1107 struct bfd_session bs
;
1109 _mhop_key(&bs
, &mhop
);
1111 return hash_release(bfd_mhop_hash
, &bs
);
1114 struct bfd_vrf
*bfd_vrf_delete(int vrf_id
)
1118 vrf
.vrf_id
= vrf_id
;
1120 return hash_release(bfd_vrf_hash
, &vrf
);
1123 struct bfd_iface
*bfd_iface_delete(const char *ifname
)
1125 struct bfd_iface iface
;
1127 if (_iface_key(&iface
, ifname
) != 0)
1130 return hash_release(bfd_iface_hash
, &iface
);
1133 /* Iteration functions. */
1134 void bfd_id_iterate(hash_iter_func hif
, void *arg
)
1136 hash_iterate(bfd_id_hash
, hif
, arg
);
1139 void bfd_shop_iterate(hash_iter_func hif
, void *arg
)
1141 hash_iterate(bfd_shop_hash
, hif
, arg
);
1144 void bfd_mhop_iterate(hash_iter_func hif
, void *arg
)
1146 hash_iterate(bfd_mhop_hash
, hif
, arg
);
1149 void bfd_vrf_iterate(hash_iter_func hif
, void *arg
)
1151 hash_iterate(bfd_vrf_hash
, hif
, arg
);
1154 void bfd_iface_iterate(hash_iter_func hif
, void *arg
)
1156 hash_iterate(bfd_iface_hash
, hif
, arg
);
1162 * Inserts session into hash and returns `true` on success, otherwise
1165 bool bfd_id_insert(struct bfd_session
*bs
)
1167 return (hash_get(bfd_id_hash
, bs
, hash_alloc_intern
) == bs
);
1170 bool bfd_shop_insert(struct bfd_session
*bs
)
1172 return (hash_get(bfd_shop_hash
, bs
, hash_alloc_intern
) == bs
);
1175 bool bfd_mhop_insert(struct bfd_session
*bs
)
1177 return (hash_get(bfd_mhop_hash
, bs
, hash_alloc_intern
) == bs
);
1180 bool bfd_vrf_insert(struct bfd_vrf
*vrf
)
1182 return (hash_get(bfd_vrf_hash
, vrf
, hash_alloc_intern
) == vrf
);
1185 bool bfd_iface_insert(struct bfd_iface
*iface
)
1187 return (hash_get(bfd_iface_hash
, iface
, hash_alloc_intern
) == iface
);
1190 void bfd_initialize(void)
1192 bfd_id_hash
= hash_create(bfd_id_hash_do
, bfd_id_hash_cmp
,
1193 "BFD discriminator hash");
1194 bfd_shop_hash
= hash_create(bfd_shop_hash_do
, bfd_shop_hash_cmp
,
1195 "BFD single hop hash");
1196 bfd_mhop_hash
= hash_create(bfd_mhop_hash_do
, bfd_mhop_hash_cmp
,
1197 "BFD multihop hop hash");
1199 hash_create(bfd_vrf_hash_do
, bfd_vrf_hash_cmp
, "BFD VRF hash");
1200 bfd_iface_hash
= hash_create(bfd_iface_hash_do
, bfd_iface_hash_cmp
,
1201 "BFD interface hash");
1204 static void _bfd_free(struct hash_backet
*hb
,
1205 void *arg
__attribute__((__unused__
)))
1207 struct bfd_session
*bs
= hb
->data
;
1209 bfd_session_free(bs
);
1212 static void _vrf_free(void *arg
)
1214 struct bfd_vrf
*vrf
= arg
;
1216 XFREE(MTYPE_BFDD_CONFIG
, vrf
);
1219 static void _iface_free(void *arg
)
1221 struct bfd_iface
*iface
= arg
;
1223 XFREE(MTYPE_BFDD_CONFIG
, iface
);
1226 void bfd_shutdown(void)
1229 * Close and free all BFD sessions.
1231 * _bfd_free() will call bfd_session_free() which will take care
1232 * of removing the session from all hashes, so we just run an
1233 * assert() here to make sure it really happened.
1235 bfd_id_iterate(_bfd_free
, NULL
);
1236 assert(bfd_shop_hash
->count
== 0);
1237 assert(bfd_mhop_hash
->count
== 0);
1239 /* Clean the VRF and interface hashes. */
1240 hash_clean(bfd_vrf_hash
, _vrf_free
);
1241 hash_clean(bfd_iface_hash
, _iface_free
);
1243 /* Now free the hashes themselves. */
1244 hash_free(bfd_id_hash
);
1245 hash_free(bfd_shop_hash
);
1246 hash_free(bfd_mhop_hash
);
1247 hash_free(bfd_vrf_hash
);
1248 hash_free(bfd_iface_hash
);