#include <zebra.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/uio.h>
-#include <netinet/in.h>
-#include <net/if.h>
-#include <arpa/inet.h>
-#include <sys/ioctl.h>
-
-#include <err.h>
-#include <errno.h>
-#include <stdio.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <netdb.h>
-#include <time.h>
-#include <signal.h>
-
-#include "lib/hash.h"
#include "lib/jhash.h"
#include "bfd.h"
} else {
memset(&shop, 0, sizeof(shop));
shop.peer = bpc->bpc_peer;
- if (!bpc->bpc_has_vxlan && bpc->bpc_has_localif)
+ if (bpc->bpc_has_localif)
strlcpy(shop.port_name, bpc->bpc_localif,
sizeof(shop.port_name));
void ptm_bfd_echo_start(struct bfd_session *bfd)
{
bfd->echo_detect_TO = (bfd->remote_detect_mult * bfd->echo_xmt_TO);
- ptm_bfd_echo_xmt_TO(bfd);
+ if (bfd->echo_detect_TO > 0)
+ ptm_bfd_echo_xmt_TO(bfd);
bfd->polling = 1;
bfd->new_timers.desired_min_tx = bfd->up_min_tx;
void ptm_bfd_ses_up(struct bfd_session *bfd)
{
+ int old_state = bfd->ses_state;
+
bfd->local_diag = 0;
bfd->ses_state = PTM_BFD_UP;
bfd->polling = 1;
control_notify(bfd);
- INFOLOG("Session 0x%x up peer %s", bfd->discrs.my_discr,
- satostr(&bfd->shop.peer));
+ if (old_state != bfd->ses_state) {
+ bfd->stats.session_up++;
+ log_info("state-change: [%s] %s -> %s", bs_to_string(bfd),
+ state_list[old_state].str,
+ state_list[bfd->ses_state].str);
+ }
}
void ptm_bfd_ses_dn(struct bfd_session *bfd, uint8_t diag)
if (old_state == PTM_BFD_UP)
control_notify(bfd);
- INFOLOG("Session 0x%x down peer %s Rsn %s prev st %s",
- bfd->discrs.my_discr, satostr(&bfd->shop.peer),
- get_diag_str(bfd->local_diag), state_list[old_state].str);
-
/* Stop echo packet transmission if they are active */
if (BFD_CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE))
ptm_bfd_echo_stop(bfd, 0);
+
+ if (old_state != bfd->ses_state) {
+ bfd->stats.session_down++;
+ log_info("state-change: [%s] %s -> %s reason:%s",
+ bs_to_string(bfd), state_list[old_state].str,
+ state_list[bfd->ses_state].str,
+ get_diag_str(bfd->local_diag));
+ }
}
static int ptm_bfd_get_vrf_name(char *port_name, char *vrf_name)
} else if (port_name && port_name[0]) {
memset(vrf_buf, 0, sizeof(vrf_buf));
if (ptm_bfd_get_vrf_name(port_name, vrf_buf) != -1)
- strlcpy(mhop.vrf_name, vrf_buf, sizeof(mhop.vrf_name));
+ strlcpy(mhop.vrf_name, vrf_buf,
+ sizeof(mhop.vrf_name));
}
l_bfd = bfd_mhop_lookup(mhop);
memset(&shop, 0, sizeof(shop));
shop.peer = *peer;
if (port_name && port_name[0])
- strlcpy(shop.port_name, port_name, sizeof(shop.port_name));
+ strlcpy(shop.port_name, port_name,
+ sizeof(shop.port_name));
l_bfd = bfd_shop_lookup(shop);
}
return l_bfd;
}
-#if 0 /* TODO VxLAN Support */
-static void
-_update_vxlan_sess_parms(struct bfd_session *bfd, bfd_sess_parms *sess_parms)
-{
- struct bfd_session_vxlan_info *vxlan_info = &bfd->vxlan_info;
- bfd_parms_list *parms = &sess_parms->parms;
-
- vxlan_info->vnid = parms->vnid;
- vxlan_info->check_tnl_key = parms->check_tnl_key;
- vxlan_info->forwarding_if_rx = parms->forwarding_if_rx;
- vxlan_info->cpath_down = parms->cpath_down;
- vxlan_info->decay_min_rx = parms->decay_min_rx;
-
- inet_aton(parms->local_dst_ip, &vxlan_info->local_dst_ip);
- inet_aton(parms->remote_dst_ip, &vxlan_info->peer_dst_ip);
-
- memcpy(vxlan_info->local_dst_mac, parms->local_dst_mac, ETH_ALEN);
- memcpy(vxlan_info->peer_dst_mac, parms->remote_dst_mac, ETH_ALEN);
-
- /* The interface may change for Vxlan BFD sessions, so update
- * the local mac and ifindex
- */
- bfd->ifindex = sess_parms->ifindex;
- memcpy(bfd->local_mac, sess_parms->local_mac, sizeof(bfd->local_mac));
-}
-#endif /* VxLAN support */
-
int bfd_xmt_cb(struct thread *t)
{
struct bfd_session *bs = THREAD_ARG(t);
{
struct bfd_session *bs = THREAD_ARG(t);
- ptm_bfd_echo_xmt_TO(bs);
+ if (bs->echo_xmt_TO > 0)
+ ptm_bfd_echo_xmt_TO(bs);
return 0;
}
int bfd_recvtimer_cb(struct thread *t)
{
struct bfd_session *bs = THREAD_ARG(t);
- uint8_t old_state;
-
- old_state = bs->ses_state;
switch (bs->ses_state) {
case PTM_BFD_INIT:
case PTM_BFD_UP:
- ptm_bfd_ses_dn(bs, BFD_DIAGDETECTTIME);
- INFOLOG("%s Detect timeout on session 0x%x with peer %s, in state %d",
- __func__, bs->discrs.my_discr, satostr(&bs->shop.peer),
- bs->ses_state);
+ ptm_bfd_ses_dn(bs, BD_CONTROL_EXPIRED);
bfd_recvtimer_update(bs);
break;
break;
}
- if (old_state != bs->ses_state) {
- DLOG("BFD Sess %d [%s] Old State [%s] : New State [%s]",
- bs->discrs.my_discr, satostr(&bs->shop.peer),
- state_list[old_state].str, state_list[bs->ses_state].str);
- }
-
return 0;
}
int bfd_echo_recvtimer_cb(struct thread *t)
{
struct bfd_session *bs = THREAD_ARG(t);
- uint8_t old_state;
-
- old_state = bs->ses_state;
switch (bs->ses_state) {
case PTM_BFD_INIT:
case PTM_BFD_UP:
- ptm_bfd_ses_dn(bs, BFD_DIAGDETECTTIME);
- INFOLOG("%s Detect timeout on session 0x%x with peer %s, in state %d",
- __func__, bs->discrs.my_discr, satostr(&bs->shop.peer),
- bs->ses_state);
+ ptm_bfd_ses_dn(bs, BD_ECHO_FAILED);
break;
}
- if (old_state != bs->ses_state) {
- DLOG("BFD Sess %d [%s] Old State [%s] : New State [%s]",
- bs->discrs.my_discr, satostr(&bs->shop.peer),
- state_list[old_state].str, state_list[bs->ses_state].str);
- }
-
return 0;
}
_bfd_session_update(bs, bpc);
- /* TODO add VxLAN support. */
-
control_notify_config(BCM_NOTIFY_CONFIG_UPDATE, bs);
return 0;
*/
if (bpc->bpc_ipv4) {
psock = bp_peer_socket(bpc);
- if (psock == -1) {
- ERRLOG("Can't get socket for new session: %s",
- strerror(errno));
+ if (psock == -1)
return NULL;
- }
} else {
psock = bp_peer_socketv6(bpc);
- if (psock == -1) {
- ERRLOG("Can't get IPv6 socket for new session: %s",
- strerror(errno));
+ if (psock == -1)
return NULL;
- }
}
/* Get memory */
bfd = bfd_session_new(psock);
if (bfd == NULL) {
- ERRLOG("Can't malloc memory for new session: %s",
- strerror(errno));
+ log_error("session-new: allocation failed");
return NULL;
}
ptm_bfd_fetch_local_mac(bpc->bpc_localif, bfd->local_mac);
}
- if (bpc->bpc_has_vxlan)
- BFD_SET_FLAG(bfd->flags, BFD_SESS_FLAG_VXLAN);
-
- if (bpc->bpc_ipv4 == false)
+ if (bpc->bpc_ipv4 == false) {
BFD_SET_FLAG(bfd->flags, BFD_SESS_FLAG_IPV6);
+ /* Set the IPv6 scope id for link-local addresses. */
+ if (IN6_IS_ADDR_LINKLOCAL(&bpc->bpc_local.sa_sin6.sin6_addr))
+ bpc->bpc_local.sa_sin6.sin6_scope_id = bfd->ifindex;
+ if (IN6_IS_ADDR_LINKLOCAL(&bpc->bpc_peer.sa_sin6.sin6_addr))
+ bpc->bpc_peer.sa_sin6.sin6_scope_id = bfd->ifindex;
+ }
+
/* Initialize the session */
bfd->ses_state = PTM_BFD_DOWN;
bfd->discrs.my_discr = ptm_bfd_gen_ID();
bfd_mhop_insert(bfd);
} else {
bfd->shop.peer = bpc->bpc_peer;
- if (!bpc->bpc_has_vxlan && bpc->bpc_has_localif)
+ if (bpc->bpc_has_localif)
strlcpy(bfd->shop.port_name, bpc->bpc_localif,
sizeof(bfd->shop.port_name));
bfd_shop_insert(bfd);
}
- if (BFD_CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_VXLAN)) {
- static uint8_t bfd_def_vxlan_dmac[] = {0x00, 0x23, 0x20,
- 0x00, 0x00, 0x01};
- memcpy(bfd->peer_mac, bfd_def_vxlan_dmac,
- sizeof(bfd_def_vxlan_dmac));
- }
-#if 0 /* TODO */
- else if (event->rmac) {
- if (sscanf(event->rmac, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
- &bfd->peer_mac[0], &bfd->peer_mac[1], &bfd->peer_mac[2],
- &bfd->peer_mac[3], &bfd->peer_mac[4], &bfd->peer_mac[5])
- != 6)
- DLOG("%s: Assigning remote mac = %s", __func__,
- event->rmac);
- }
-#endif
-
/*
* XXX: session update triggers echo start, so we must have our
* discriminator ID set first.
ptm_bfd_xmt_TO(bfd, 0);
- if (bpc->bpc_mhop) {
- INFOLOG("Created new session 0x%x with vrf %s peer %s local %s",
- bfd->discrs.my_discr,
- (bpc->bpc_has_vrfname) ? bfd->mhop.vrf_name : "N/A",
- satostr(&bfd->mhop.peer), satostr(&bfd->mhop.local));
- } else {
- INFOLOG("Created new session 0x%x with peer %s port %s",
- bfd->discrs.my_discr, satostr(&bfd->shop.peer),
- bfd->shop.port_name[0] ? bfd->shop.port_name : "N/A");
- }
+ log_info("session-new: %s", bs_to_string(bfd));
control_notify_config(BCM_NOTIFY_CONFIG_ADD, bfd);
/* This pointer is being referenced, don't let it be deleted. */
if (bs->refcount > 0) {
- zlog_debug("%s: trying to free in-use session: %" PRIu64
- " references",
- __func__, bs->refcount);
+ log_error("session-delete: refcount failure: %" PRIu64
+ " references",
+ bs->refcount);
return -1;
}
- if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)) {
- INFOLOG("Deleting session 0x%x with vrf %s peer %s local %s",
- bs->discrs.my_discr,
- bpc->bpc_has_vrfname ? bpc->bpc_vrfname : "N/A",
- satostr(&bs->mhop.peer), satostr(&bs->mhop.local));
- } else {
- INFOLOG("Deleting session 0x%x with peer %s port %s",
- bs->discrs.my_discr, satostr(&bs->shop.peer),
- bs->shop.port_name);
- }
+ log_info("session-delete: %s", bs_to_string(bs));
control_notify_config(BCM_NOTIFY_CONFIG_DELETE, bs);
snprintf(buf, buflen, "%u second(s)", second);
}
+const char *bs_to_string(struct bfd_session *bs)
+{
+ static char buf[256];
+ int pos;
+ bool is_mhop = BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH);
+
+ pos = snprintf(buf, sizeof(buf), "mhop:%s", is_mhop ? "yes" : "no");
+ if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)) {
+ pos += snprintf(buf + pos, sizeof(buf) - pos,
+ " peer:%s local:%s", satostr(&bs->mhop.peer),
+ satostr(&bs->mhop.local));
+
+ if (bs->mhop.vrf_name[0])
+ snprintf(buf + pos, sizeof(buf) - pos, " vrf:%s",
+ bs->mhop.vrf_name);
+ } else {
+ pos += snprintf(buf + pos, sizeof(buf) - pos, " peer:%s",
+ satostr(&bs->shop.peer));
+
+ if (bs->local_address.sa_sin.sin_family)
+ pos += snprintf(buf + pos, sizeof(buf) - pos,
+ " local:%s",
+ satostr(&bs->local_address));
+
+ if (bs->shop.port_name[0])
+ snprintf(buf + pos, sizeof(buf) - pos, " interface:%s",
+ bs->shop.port_name);
+ }
+
+ return buf;
+}
+
/*
* BFD hash data structures to find sessions.
static struct hash *bfd_iface_hash;
static unsigned int bfd_id_hash_do(void *p);
-static int bfd_id_hash_cmp(const void *n1, const void *n2);
static unsigned int bfd_shop_hash_do(void *p);
-static int bfd_shop_hash_cmp(const void *n1, const void *n2);
static unsigned int bfd_mhop_hash_do(void *p);
-static int bfd_mhop_hash_cmp(const void *n1, const void *n2);
static unsigned int bfd_vrf_hash_do(void *p);
-static int bfd_vrf_hash_cmp(const void *n1, const void *n2);
static unsigned int bfd_iface_hash_do(void *p);
-static int bfd_iface_hash_cmp(const void *n1, const void *n2);
static void _shop_key(struct bfd_session *bs, const struct bfd_shop_key *shop);
static void _shop_key2(struct bfd_session *bs, const struct bfd_shop_key *shop);
return jhash_1word(bs->discrs.my_discr, 0);
}
-static int bfd_id_hash_cmp(const void *n1, const void *n2)
+static bool bfd_id_hash_cmp(const void *n1, const void *n2)
{
const struct bfd_session *bs1 = n1, *bs2 = n2;
return jhash(&bs->shop, sizeof(bs->shop), 0);
}
-static int bfd_shop_hash_cmp(const void *n1, const void *n2)
+static bool bfd_shop_hash_cmp(const void *n1, const void *n2)
{
const struct bfd_session *bs1 = n1, *bs2 = n2;
return jhash(&bs->mhop, sizeof(bs->mhop), 0);
}
-static int bfd_mhop_hash_cmp(const void *n1, const void *n2)
+static bool bfd_mhop_hash_cmp(const void *n1, const void *n2)
{
const struct bfd_session *bs1 = n1, *bs2 = n2;
return jhash_1word(vrf->vrf_id, 0);
}
-static int bfd_vrf_hash_cmp(const void *n1, const void *n2)
+static bool bfd_vrf_hash_cmp(const void *n1, const void *n2)
{
const struct bfd_vrf *v1 = n1, *v2 = n2;
return string_hash_make(iface->ifname);
}
-static int bfd_iface_hash_cmp(const void *n1, const void *n2)
+static bool bfd_iface_hash_cmp(const void *n1, const void *n2)
{
const struct bfd_iface *i1 = n1, *i2 = n2;
return 0;
}
-
/*
* Hash public interface / exported functions.
*/
_mhop_key(&bs, &mhop);
- return hash_lookup(bfd_shop_hash, &bs);
+ return hash_lookup(bfd_mhop_hash, &bs);
}
struct bfd_vrf *bfd_vrf_lookup(int vrf_id)