mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-08-16 08:32:45 +00:00
Merge pull request #3918 from opensourcerouting/bfd-bug-fixes
bfdd: bug fixes
This commit is contained in:
commit
29d894affc
541
bfdd/bfd.c
541
bfdd/bfd.c
@ -36,7 +36,9 @@ DEFINE_QOBJ_TYPE(bfd_session);
|
|||||||
/*
|
/*
|
||||||
* Prototypes
|
* Prototypes
|
||||||
*/
|
*/
|
||||||
static struct bfd_session *bs_peer_waiting_find(struct bfd_peer_cfg *bpc);
|
void gen_bfd_key(struct bfd_key *key, struct sockaddr_any *peer,
|
||||||
|
struct sockaddr_any *local, bool mhop, const char *ifname,
|
||||||
|
const char *vrfname);
|
||||||
|
|
||||||
static uint32_t ptm_bfd_gen_ID(void);
|
static uint32_t ptm_bfd_gen_ID(void);
|
||||||
static void ptm_bfd_echo_xmt_TO(struct bfd_session *bfd);
|
static void ptm_bfd_echo_xmt_TO(struct bfd_session *bfd);
|
||||||
@ -52,66 +54,47 @@ static void bs_down_handler(struct bfd_session *bs, int nstate);
|
|||||||
static void bs_init_handler(struct bfd_session *bs, int nstate);
|
static void bs_init_handler(struct bfd_session *bs, int nstate);
|
||||||
static void bs_up_handler(struct bfd_session *bs, int nstate);
|
static void bs_up_handler(struct bfd_session *bs, int nstate);
|
||||||
|
|
||||||
|
/* Zeroed array with the size of an IPv6 address. */
|
||||||
|
struct in6_addr zero_addr;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Functions
|
* Functions
|
||||||
*/
|
*/
|
||||||
static struct bfd_session *bs_peer_waiting_find(struct bfd_peer_cfg *bpc)
|
void gen_bfd_key(struct bfd_key *key, struct sockaddr_any *peer,
|
||||||
|
struct sockaddr_any *local, bool mhop, const char *ifname,
|
||||||
|
const char *vrfname)
|
||||||
{
|
{
|
||||||
struct bfd_session_observer *bso;
|
memset(key, 0, sizeof(*key));
|
||||||
struct bfd_session *bs = NULL;
|
|
||||||
bool is_shop, is_ipv4;
|
|
||||||
|
|
||||||
TAILQ_FOREACH(bso, &bglobal.bg_obslist, bso_entry) {
|
|
||||||
bs = bso->bso_bs;
|
|
||||||
|
|
||||||
is_shop = !BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH);
|
|
||||||
is_ipv4 = !BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_IPV6);
|
|
||||||
/* Quick checks first. */
|
|
||||||
if (is_shop != (!bpc->bpc_mhop))
|
|
||||||
continue;
|
|
||||||
if (is_ipv4 != bpc->bpc_ipv4)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Slow lookup without hash because we don't have all
|
|
||||||
* information yet.
|
|
||||||
*/
|
|
||||||
if (is_shop) {
|
|
||||||
if (strcmp(bs->ifname, bpc->bpc_localif))
|
|
||||||
continue;
|
|
||||||
if (memcmp(&bs->shop.peer, &bpc->bpc_peer,
|
|
||||||
sizeof(bs->shop.peer)))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
|
switch (peer->sa_sin.sin_family) {
|
||||||
|
case AF_INET:
|
||||||
|
key->family = AF_INET;
|
||||||
|
memcpy(&key->peer, &peer->sa_sin.sin_addr,
|
||||||
|
sizeof(peer->sa_sin.sin_addr));
|
||||||
|
memcpy(&key->local, &local->sa_sin.sin_addr,
|
||||||
|
sizeof(local->sa_sin.sin_addr));
|
||||||
|
break;
|
||||||
|
case AF_INET6:
|
||||||
|
key->family = AF_INET6;
|
||||||
|
memcpy(&key->peer, &peer->sa_sin6.sin6_addr,
|
||||||
|
sizeof(peer->sa_sin6.sin6_addr));
|
||||||
|
memcpy(&key->local, &local->sa_sin6.sin6_addr,
|
||||||
|
sizeof(local->sa_sin6.sin6_addr));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strcmp(bs->vrfname, bpc->bpc_vrfname))
|
key->mhop = mhop;
|
||||||
continue;
|
if (ifname && ifname[0])
|
||||||
if (memcmp(&bs->mhop.peer, &bpc->bpc_peer,
|
strlcpy(key->ifname, ifname, sizeof(key->ifname));
|
||||||
sizeof(bs->mhop.peer)))
|
if (vrfname && vrfname[0])
|
||||||
continue;
|
strlcpy(key->vrfname, vrfname, sizeof(key->vrfname));
|
||||||
if (memcmp(&bs->mhop.local, &bpc->bpc_local,
|
|
||||||
sizeof(bs->mhop.local)))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (bso == NULL)
|
|
||||||
bs = NULL;
|
|
||||||
|
|
||||||
return bs;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct bfd_session *bs_peer_find(struct bfd_peer_cfg *bpc)
|
struct bfd_session *bs_peer_find(struct bfd_peer_cfg *bpc)
|
||||||
{
|
{
|
||||||
struct bfd_session *bs;
|
struct bfd_session *bs;
|
||||||
struct peer_label *pl;
|
struct peer_label *pl;
|
||||||
struct interface *ifp;
|
struct bfd_key key;
|
||||||
struct vrf *vrf;
|
|
||||||
struct bfd_mhop_key mhop;
|
|
||||||
struct bfd_shop_key shop;
|
|
||||||
|
|
||||||
/* Try to find label first. */
|
/* Try to find label first. */
|
||||||
if (bpc->bpc_has_label) {
|
if (bpc->bpc_has_label) {
|
||||||
@ -123,38 +106,10 @@ struct bfd_session *bs_peer_find(struct bfd_peer_cfg *bpc)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Otherwise fallback to peer/local hash lookup. */
|
/* Otherwise fallback to peer/local hash lookup. */
|
||||||
if (bpc->bpc_mhop) {
|
gen_bfd_key(&key, &bpc->bpc_peer, &bpc->bpc_local, bpc->bpc_mhop,
|
||||||
memset(&mhop, 0, sizeof(mhop));
|
bpc->bpc_localif, bpc->bpc_vrfname);
|
||||||
mhop.peer = bpc->bpc_peer;
|
|
||||||
mhop.local = bpc->bpc_local;
|
|
||||||
if (bpc->bpc_has_vrfname) {
|
|
||||||
vrf = vrf_lookup_by_name(bpc->bpc_vrfname);
|
|
||||||
if (vrf == NULL)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
mhop.vrfid = vrf->vrf_id;
|
return bfd_key_lookup(key);
|
||||||
}
|
|
||||||
|
|
||||||
bs = bfd_mhop_lookup(mhop);
|
|
||||||
} else {
|
|
||||||
memset(&shop, 0, sizeof(shop));
|
|
||||||
shop.peer = bpc->bpc_peer;
|
|
||||||
if (bpc->bpc_has_localif) {
|
|
||||||
ifp = if_lookup_by_name_all_vrf(bpc->bpc_localif);
|
|
||||||
if (ifp == NULL)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
shop.ifindex = ifp->ifindex;
|
|
||||||
}
|
|
||||||
|
|
||||||
bs = bfd_shop_lookup(shop);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bs != NULL)
|
|
||||||
return bs;
|
|
||||||
|
|
||||||
/* Search for entries that are incomplete. */
|
|
||||||
return bs_peer_waiting_find(bpc);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -165,7 +120,6 @@ struct bfd_session *bs_peer_find(struct bfd_peer_cfg *bpc)
|
|||||||
*/
|
*/
|
||||||
int bfd_session_enable(struct bfd_session *bs)
|
int bfd_session_enable(struct bfd_session *bs)
|
||||||
{
|
{
|
||||||
struct sockaddr_in6 *sin6;
|
|
||||||
struct interface *ifp = NULL;
|
struct interface *ifp = NULL;
|
||||||
struct vrf *vrf = NULL;
|
struct vrf *vrf = NULL;
|
||||||
int psock;
|
int psock;
|
||||||
@ -174,8 +128,8 @@ int bfd_session_enable(struct bfd_session *bs)
|
|||||||
* If the interface or VRF doesn't exist, then we must register
|
* If the interface or VRF doesn't exist, then we must register
|
||||||
* the session but delay its start.
|
* the session but delay its start.
|
||||||
*/
|
*/
|
||||||
if (bs->ifname[0] != 0) {
|
if (bs->key.ifname[0]) {
|
||||||
ifp = if_lookup_by_name_all_vrf(bs->ifname);
|
ifp = if_lookup_by_name_all_vrf(bs->key.ifname);
|
||||||
if (ifp == NULL) {
|
if (ifp == NULL) {
|
||||||
log_error(
|
log_error(
|
||||||
"session-enable: specified interface doesn't exists.");
|
"session-enable: specified interface doesn't exists.");
|
||||||
@ -184,15 +138,17 @@ int bfd_session_enable(struct bfd_session *bs)
|
|||||||
|
|
||||||
vrf = vrf_lookup_by_id(ifp->vrf_id);
|
vrf = vrf_lookup_by_id(ifp->vrf_id);
|
||||||
if (vrf == NULL) {
|
if (vrf == NULL) {
|
||||||
log_error("session-enable: specified VRF doesn't exists.");
|
log_error(
|
||||||
|
"session-enable: specified VRF doesn't exists.");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bs->vrfname[0] != 0) {
|
if (bs->key.vrfname[0]) {
|
||||||
vrf = vrf_lookup_by_name(bs->vrfname);
|
vrf = vrf_lookup_by_name(bs->key.vrfname);
|
||||||
if (vrf == NULL) {
|
if (vrf == NULL) {
|
||||||
log_error("session-enable: specified VRF doesn't exists.");
|
log_error(
|
||||||
|
"session-enable: specified VRF doesn't exists.");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -202,26 +158,15 @@ int bfd_session_enable(struct bfd_session *bs)
|
|||||||
if (bs->vrf == NULL)
|
if (bs->vrf == NULL)
|
||||||
bs->vrf = vrf_lookup_by_id(VRF_DEFAULT);
|
bs->vrf = vrf_lookup_by_id(VRF_DEFAULT);
|
||||||
|
|
||||||
if (bs->ifname[0] != 0 &&
|
if (bs->key.ifname[0]
|
||||||
BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH) == 0)
|
&& BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH) == 0)
|
||||||
bs->ifp = ifp;
|
bs->ifp = ifp;
|
||||||
|
|
||||||
/* Set the IPv6 scope id for link-local addresses. */
|
/* Sanity check: don't leak open sockets. */
|
||||||
if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_IPV6)) {
|
if (bs->sock != -1) {
|
||||||
sin6 = &bs->mhop.peer.sa_sin6;
|
zlog_debug("session-enable: previous socket open");
|
||||||
if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr))
|
close(bs->sock);
|
||||||
sin6->sin6_scope_id = bs->ifp != NULL
|
bs->sock = -1;
|
||||||
? bs->ifp->ifindex
|
|
||||||
: IFINDEX_INTERNAL;
|
|
||||||
|
|
||||||
sin6 = &bs->mhop.local.sa_sin6;
|
|
||||||
if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr))
|
|
||||||
sin6->sin6_scope_id = bs->ifp != NULL
|
|
||||||
? bs->ifp->ifindex
|
|
||||||
: IFINDEX_INTERNAL;
|
|
||||||
|
|
||||||
bs->local_ip.sa_sin6 = *sin6;
|
|
||||||
bs->local_address.sa_sin6 = *sin6;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -232,11 +177,11 @@ int bfd_session_enable(struct bfd_session *bs)
|
|||||||
if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_IPV6) == 0) {
|
if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_IPV6) == 0) {
|
||||||
psock = bp_peer_socket(bs);
|
psock = bp_peer_socket(bs);
|
||||||
if (psock == -1)
|
if (psock == -1)
|
||||||
return -1;
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
psock = bp_peer_socketv6(bs);
|
psock = bp_peer_socketv6(bs);
|
||||||
if (psock == -1)
|
if (psock == -1)
|
||||||
return -1;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -247,25 +192,6 @@ int bfd_session_enable(struct bfd_session *bs)
|
|||||||
bfd_recvtimer_update(bs);
|
bfd_recvtimer_update(bs);
|
||||||
ptm_bfd_start_xmt_timer(bs, false);
|
ptm_bfd_start_xmt_timer(bs, false);
|
||||||
|
|
||||||
/* Registrate session into data structures. */
|
|
||||||
bs->discrs.my_discr = ptm_bfd_gen_ID();
|
|
||||||
bfd_id_insert(bs);
|
|
||||||
if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)) {
|
|
||||||
if (vrf != NULL)
|
|
||||||
bs->mhop.vrfid = vrf->vrf_id;
|
|
||||||
else
|
|
||||||
bs->mhop.vrfid = VRF_DEFAULT;
|
|
||||||
|
|
||||||
bfd_mhop_insert(bs);
|
|
||||||
} else {
|
|
||||||
if (ifp != NULL)
|
|
||||||
bs->shop.ifindex = ifp->ifindex;
|
|
||||||
else
|
|
||||||
bs->shop.ifindex = IFINDEX_INTERNAL;
|
|
||||||
|
|
||||||
bfd_shop_insert(bs);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -288,13 +214,6 @@ void bfd_session_disable(struct bfd_session *bs)
|
|||||||
bfd_echo_recvtimer_delete(bs);
|
bfd_echo_recvtimer_delete(bs);
|
||||||
bfd_xmttimer_delete(bs);
|
bfd_xmttimer_delete(bs);
|
||||||
bfd_echo_xmttimer_delete(bs);
|
bfd_echo_xmttimer_delete(bs);
|
||||||
|
|
||||||
/* Unregister session from hashes to avoid unwanted activation. */
|
|
||||||
bfd_id_delete(bs->discrs.my_discr);
|
|
||||||
if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH))
|
|
||||||
bfd_mhop_delete(bs->mhop);
|
|
||||||
else
|
|
||||||
bfd_shop_delete(bs->shop);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint32_t ptm_bfd_gen_ID(void)
|
static uint32_t ptm_bfd_gen_ID(void)
|
||||||
@ -438,21 +357,20 @@ static struct bfd_session *bfd_find_disc(struct sockaddr_any *sa,
|
|||||||
if (bs == NULL)
|
if (bs == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
/* Remove unused fields. */
|
switch (bs->key.family) {
|
||||||
switch (sa->sa_sin.sin_family) {
|
|
||||||
case AF_INET:
|
case AF_INET:
|
||||||
sa->sa_sin.sin_port = 0;
|
if (memcmp(&sa->sa_sin.sin_addr, &bs->key.peer,
|
||||||
if (memcmp(sa, &bs->shop.peer, sizeof(sa->sa_sin)) == 0)
|
sizeof(sa->sa_sin.sin_addr)))
|
||||||
return bs;
|
return NULL;
|
||||||
break;
|
break;
|
||||||
case AF_INET6:
|
case AF_INET6:
|
||||||
sa->sa_sin6.sin6_port = 0;
|
if (memcmp(&sa->sa_sin6.sin6_addr, &bs->key.peer,
|
||||||
if (memcmp(sa, &bs->shop.peer, sizeof(sa->sa_sin6)) == 0)
|
sizeof(sa->sa_sin6.sin6_addr)))
|
||||||
return bs;
|
return NULL;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return NULL;
|
return bs;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct bfd_session *ptm_bfd_sess_find(struct bfd_pkt *cp,
|
struct bfd_session *ptm_bfd_sess_find(struct bfd_pkt *cp,
|
||||||
@ -461,32 +379,30 @@ struct bfd_session *ptm_bfd_sess_find(struct bfd_pkt *cp,
|
|||||||
ifindex_t ifindex, vrf_id_t vrfid,
|
ifindex_t ifindex, vrf_id_t vrfid,
|
||||||
bool is_mhop)
|
bool is_mhop)
|
||||||
{
|
{
|
||||||
struct bfd_session *l_bfd = NULL;
|
struct interface *ifp;
|
||||||
struct bfd_mhop_key mhop;
|
struct vrf *vrf;
|
||||||
struct bfd_shop_key shop;
|
struct bfd_key key;
|
||||||
|
|
||||||
/* Find our session using the ID signaled by the remote end. */
|
/* Find our session using the ID signaled by the remote end. */
|
||||||
if (cp->discrs.remote_discr)
|
if (cp->discrs.remote_discr)
|
||||||
return bfd_find_disc(peer, ntohl(cp->discrs.remote_discr));
|
return bfd_find_disc(peer, ntohl(cp->discrs.remote_discr));
|
||||||
|
|
||||||
/* Search for session without using discriminator. */
|
/* Search for session without using discriminator. */
|
||||||
if (is_mhop) {
|
ifp = if_lookup_by_index(ifindex, vrfid);
|
||||||
memset(&mhop, 0, sizeof(mhop));
|
if (vrfid == VRF_DEFAULT) {
|
||||||
mhop.peer = *peer;
|
/*
|
||||||
mhop.local = *local;
|
* Don't use the default vrf, otherwise we won't find
|
||||||
mhop.vrfid = vrfid;
|
* sessions that doesn't specify it.
|
||||||
|
*/
|
||||||
|
vrf = NULL;
|
||||||
|
} else
|
||||||
|
vrf = vrf_lookup_by_id(vrfid);
|
||||||
|
|
||||||
l_bfd = bfd_mhop_lookup(mhop);
|
gen_bfd_key(&key, peer, local, is_mhop, ifp ? ifp->name : NULL,
|
||||||
} else {
|
vrf ? vrf->name : NULL);
|
||||||
memset(&shop, 0, sizeof(shop));
|
|
||||||
shop.peer = *peer;
|
|
||||||
shop.ifindex = ifindex;
|
|
||||||
|
|
||||||
l_bfd = bfd_shop_lookup(shop);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* XXX maybe remoteDiscr should be checked for remoteHeard cases. */
|
/* XXX maybe remoteDiscr should be checked for remoteHeard cases. */
|
||||||
return l_bfd;
|
return bfd_key_lookup(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
int bfd_xmt_cb(struct thread *t)
|
int bfd_xmt_cb(struct thread *t)
|
||||||
@ -701,6 +617,9 @@ static void bfd_session_free(struct bfd_session *bs)
|
|||||||
|
|
||||||
bfd_session_disable(bs);
|
bfd_session_disable(bs);
|
||||||
|
|
||||||
|
bfd_key_delete(bs->key);
|
||||||
|
bfd_id_delete(bs->discrs.my_discr);
|
||||||
|
|
||||||
/* Remove observer if any. */
|
/* Remove observer if any. */
|
||||||
TAILQ_FOREACH(bso, &bglobal.bg_obslist, bso_entry) {
|
TAILQ_FOREACH(bso, &bglobal.bg_obslist, bso_entry) {
|
||||||
if (bso->bso_bs != bs)
|
if (bso->bso_bs != bs)
|
||||||
@ -743,29 +662,47 @@ struct bfd_session *ptm_bfd_sess_new(struct bfd_peer_cfg *bpc)
|
|||||||
* start. See `bfd_session_enable` for more information.
|
* start. See `bfd_session_enable` for more information.
|
||||||
*/
|
*/
|
||||||
if (bpc->bpc_has_localif)
|
if (bpc->bpc_has_localif)
|
||||||
strlcpy(bfd->ifname, bpc->bpc_localif, sizeof(bfd->ifname));
|
strlcpy(bfd->key.ifname, bpc->bpc_localif,
|
||||||
|
sizeof(bfd->key.ifname));
|
||||||
|
|
||||||
if (bpc->bpc_has_vrfname)
|
if (bpc->bpc_has_vrfname)
|
||||||
strlcpy(bfd->vrfname, bpc->bpc_vrfname, sizeof(bfd->vrfname));
|
strlcpy(bfd->key.vrfname, bpc->bpc_vrfname,
|
||||||
|
sizeof(bfd->key.vrfname));
|
||||||
/* Add observer if we have moving parts. */
|
|
||||||
if (bfd->ifname[0] || bfd->vrfname[0])
|
|
||||||
bs_observer_add(bfd);
|
|
||||||
|
|
||||||
/* Copy remaining data. */
|
/* Copy remaining data. */
|
||||||
if (bpc->bpc_ipv4 == false)
|
if (bpc->bpc_ipv4 == false)
|
||||||
BFD_SET_FLAG(bfd->flags, BFD_SESS_FLAG_IPV6);
|
BFD_SET_FLAG(bfd->flags, BFD_SESS_FLAG_IPV6);
|
||||||
|
|
||||||
if (bpc->bpc_mhop) {
|
bfd->key.family = (bpc->bpc_ipv4) ? AF_INET : AF_INET6;
|
||||||
BFD_SET_FLAG(bfd->flags, BFD_SESS_FLAG_MH);
|
switch (bfd->key.family) {
|
||||||
bfd->mhop.peer = bpc->bpc_peer;
|
case AF_INET:
|
||||||
bfd->mhop.local = bpc->bpc_local;
|
memcpy(&bfd->key.peer, &bpc->bpc_peer.sa_sin.sin_addr,
|
||||||
} else {
|
sizeof(bpc->bpc_peer.sa_sin.sin_addr));
|
||||||
bfd->shop.peer = bpc->bpc_peer;
|
memcpy(&bfd->key.local, &bpc->bpc_local.sa_sin.sin_addr,
|
||||||
|
sizeof(bpc->bpc_local.sa_sin.sin_addr));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AF_INET6:
|
||||||
|
memcpy(&bfd->key.peer, &bpc->bpc_peer.sa_sin6.sin6_addr,
|
||||||
|
sizeof(bpc->bpc_peer.sa_sin6.sin6_addr));
|
||||||
|
memcpy(&bfd->key.local, &bpc->bpc_local.sa_sin6.sin6_addr,
|
||||||
|
sizeof(bpc->bpc_local.sa_sin6.sin6_addr));
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
assert(1);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
bfd->local_ip = bpc->bpc_local;
|
if (bpc->bpc_mhop)
|
||||||
bfd->local_address = bpc->bpc_local;
|
BFD_SET_FLAG(bfd->flags, BFD_SESS_FLAG_MH);
|
||||||
|
|
||||||
|
bfd->key.mhop = bpc->bpc_mhop;
|
||||||
|
|
||||||
|
/* Registrate session into data structures. */
|
||||||
|
bfd_key_insert(bfd);
|
||||||
|
bfd->discrs.my_discr = ptm_bfd_gen_ID();
|
||||||
|
bfd_id_insert(bfd);
|
||||||
|
|
||||||
/* Try to enable session and schedule for packet receive/send. */
|
/* Try to enable session and schedule for packet receive/send. */
|
||||||
if (bfd_session_enable(bfd) == -1) {
|
if (bfd_session_enable(bfd) == -1) {
|
||||||
@ -774,6 +711,10 @@ struct bfd_session *ptm_bfd_sess_new(struct bfd_peer_cfg *bpc)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Add observer if we have moving parts. */
|
||||||
|
if (bfd->key.ifname[0] || bfd->key.vrfname[0] || bfd->sock == -1)
|
||||||
|
bs_observer_add(bfd);
|
||||||
|
|
||||||
/* Apply other configurations. */
|
/* Apply other configurations. */
|
||||||
_bfd_session_update(bfd, bpc);
|
_bfd_session_update(bfd, bpc);
|
||||||
|
|
||||||
@ -1219,35 +1160,26 @@ void integer2timestr(uint64_t time, char *buf, size_t buflen)
|
|||||||
snprintf(buf, buflen, "%u second(s)", second);
|
snprintf(buf, buflen, "%u second(s)", second);
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *bs_to_string(struct bfd_session *bs)
|
const char *bs_to_string(const struct bfd_session *bs)
|
||||||
{
|
{
|
||||||
static char buf[256];
|
static char buf[256];
|
||||||
|
char addr_buf[INET6_ADDRSTRLEN];
|
||||||
int pos;
|
int pos;
|
||||||
bool is_mhop = BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH);
|
bool is_mhop = BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH);
|
||||||
|
|
||||||
pos = snprintf(buf, sizeof(buf), "mhop:%s", is_mhop ? "yes" : "no");
|
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.vrfid != VRF_DEFAULT)
|
|
||||||
snprintf(buf + pos, sizeof(buf) - pos, " vrf:%u",
|
|
||||||
bs->mhop.vrfid);
|
|
||||||
} else {
|
|
||||||
pos += snprintf(buf + pos, sizeof(buf) - pos, " peer:%s",
|
pos += snprintf(buf + pos, sizeof(buf) - pos, " peer:%s",
|
||||||
satostr(&bs->shop.peer));
|
inet_ntop(bs->key.family, &bs->key.peer, addr_buf,
|
||||||
|
sizeof(addr_buf)));
|
||||||
if (bs->local_address.sa_sin.sin_family)
|
pos += snprintf(buf + pos, sizeof(buf) - pos, " local:%s",
|
||||||
pos += snprintf(buf + pos, sizeof(buf) - pos,
|
inet_ntop(bs->key.family, &bs->key.local, addr_buf,
|
||||||
" local:%s",
|
sizeof(addr_buf)));
|
||||||
satostr(&bs->local_address));
|
if (bs->key.vrfname[0])
|
||||||
|
pos += snprintf(buf + pos, sizeof(buf) - pos, " vrf:%s",
|
||||||
if (bs->shop.ifindex)
|
bs->key.vrfname);
|
||||||
snprintf(buf + pos, sizeof(buf) - pos, " ifindex:%u",
|
if (bs->key.ifname[0])
|
||||||
bs->shop.ifindex);
|
pos += snprintf(buf + pos, sizeof(buf) - pos, " ifname:%s",
|
||||||
}
|
bs->key.ifname);
|
||||||
|
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1259,12 +1191,20 @@ int bs_observer_add(struct bfd_session *bs)
|
|||||||
bso->bso_bs = bs;
|
bso->bso_bs = bs;
|
||||||
bso->bso_isinterface = !BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH);
|
bso->bso_isinterface = !BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH);
|
||||||
if (bso->bso_isinterface)
|
if (bso->bso_isinterface)
|
||||||
strlcpy(bso->bso_entryname, bs->ifname,
|
strlcpy(bso->bso_entryname, bs->key.ifname,
|
||||||
sizeof(bso->bso_entryname));
|
sizeof(bso->bso_entryname));
|
||||||
else
|
else
|
||||||
strlcpy(bso->bso_entryname, bs->vrfname,
|
strlcpy(bso->bso_entryname, bs->key.vrfname,
|
||||||
sizeof(bso->bso_entryname));
|
sizeof(bso->bso_entryname));
|
||||||
|
|
||||||
|
/* Handle socket binding failures caused by missing local addresses. */
|
||||||
|
if (bs->sock == -1) {
|
||||||
|
bso->bso_isaddress = true;
|
||||||
|
bso->bso_addr.family = bs->key.family;
|
||||||
|
memcpy(&bso->bso_addr.u.prefix, &bs->key.local,
|
||||||
|
sizeof(bs->key.local));
|
||||||
|
}
|
||||||
|
|
||||||
TAILQ_INSERT_TAIL(&bglobal.bg_obslist, bso, bso_entry);
|
TAILQ_INSERT_TAIL(&bglobal.bg_obslist, bso, bso_entry);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -1276,21 +1216,59 @@ void bs_observer_del(struct bfd_session_observer *bso)
|
|||||||
XFREE(MTYPE_BFDD_SESSION_OBSERVER, bso);
|
XFREE(MTYPE_BFDD_SESSION_OBSERVER, bso);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void bs_to_bpc(struct bfd_session *bs, struct bfd_peer_cfg *bpc)
|
||||||
|
{
|
||||||
|
memset(bpc, 0, sizeof(*bpc));
|
||||||
|
|
||||||
|
bpc->bpc_ipv4 = (bs->key.family == AF_INET);
|
||||||
|
bpc->bpc_mhop = bs->key.mhop;
|
||||||
|
|
||||||
|
switch (bs->key.family) {
|
||||||
|
case AF_INET:
|
||||||
|
bpc->bpc_peer.sa_sin.sin_family = AF_INET;
|
||||||
|
memcpy(&bpc->bpc_peer.sa_sin.sin_addr, &bs->key.peer,
|
||||||
|
sizeof(bpc->bpc_peer.sa_sin.sin_addr));
|
||||||
|
|
||||||
|
if (memcmp(&bs->key.local, &zero_addr, sizeof(bs->key.local))) {
|
||||||
|
bpc->bpc_local.sa_sin.sin_family = AF_INET6;
|
||||||
|
memcpy(&bpc->bpc_local.sa_sin.sin_addr, &bs->key.peer,
|
||||||
|
sizeof(bpc->bpc_local.sa_sin.sin_addr));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AF_INET6:
|
||||||
|
bpc->bpc_peer.sa_sin.sin_family = AF_INET6;
|
||||||
|
memcpy(&bpc->bpc_peer.sa_sin6.sin6_addr, &bs->key.peer,
|
||||||
|
sizeof(bpc->bpc_peer.sa_sin6.sin6_addr));
|
||||||
|
|
||||||
|
bpc->bpc_local.sa_sin6.sin6_family = AF_INET6;
|
||||||
|
memcpy(&bpc->bpc_local.sa_sin6.sin6_addr, &bs->key.peer,
|
||||||
|
sizeof(bpc->bpc_local.sa_sin6.sin6_addr));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bs->key.ifname[0]) {
|
||||||
|
bpc->bpc_has_localif = true;
|
||||||
|
strlcpy(bpc->bpc_localif, bs->key.ifname,
|
||||||
|
sizeof(bpc->bpc_localif));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bs->key.vrfname[0]) {
|
||||||
|
bpc->bpc_has_vrfname = true;
|
||||||
|
strlcpy(bpc->bpc_vrfname, bs->key.vrfname,
|
||||||
|
sizeof(bpc->bpc_vrfname));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* BFD hash data structures to find sessions.
|
* BFD hash data structures to find sessions.
|
||||||
*/
|
*/
|
||||||
static struct hash *bfd_id_hash;
|
static struct hash *bfd_id_hash;
|
||||||
static struct hash *bfd_shop_hash;
|
static struct hash *bfd_key_hash;
|
||||||
static struct hash *bfd_mhop_hash;
|
|
||||||
|
|
||||||
static unsigned int bfd_id_hash_do(void *p);
|
static unsigned int bfd_id_hash_do(void *p);
|
||||||
static unsigned int bfd_shop_hash_do(void *p);
|
static unsigned int bfd_key_hash_do(void *p);
|
||||||
static unsigned int bfd_mhop_hash_do(void *p);
|
|
||||||
|
|
||||||
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);
|
|
||||||
static void _mhop_key(struct bfd_session *bs, const struct bfd_mhop_key *mhop);
|
|
||||||
|
|
||||||
static void _bfd_free(struct hash_bucket *hb,
|
static void _bfd_free(struct hash_bucket *hb,
|
||||||
void *arg __attribute__((__unused__)));
|
void *arg __attribute__((__unused__)));
|
||||||
@ -1311,73 +1289,20 @@ static bool bfd_id_hash_cmp(const void *n1, const void *n2)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* BFD hash for single hop. */
|
/* BFD hash for single hop. */
|
||||||
static unsigned int bfd_shop_hash_do(void *p)
|
static unsigned int bfd_key_hash_do(void *p)
|
||||||
{
|
{
|
||||||
struct bfd_session *bs = p;
|
struct bfd_session *bs = p;
|
||||||
|
|
||||||
return jhash(&bs->shop, sizeof(bs->shop), 0);
|
return jhash(&bs->key, sizeof(bs->key), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool bfd_shop_hash_cmp(const void *n1, const void *n2)
|
static bool bfd_key_hash_cmp(const void *n1, const void *n2)
|
||||||
{
|
{
|
||||||
const struct bfd_session *bs1 = n1, *bs2 = n2;
|
const struct bfd_session *bs1 = n1, *bs2 = n2;
|
||||||
|
|
||||||
return memcmp(&bs1->shop, &bs2->shop, sizeof(bs1->shop)) == 0;
|
return memcmp(&bs1->key, &bs2->key, sizeof(bs1->key)) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* BFD hash for multi hop. */
|
|
||||||
static unsigned int bfd_mhop_hash_do(void *p)
|
|
||||||
{
|
|
||||||
struct bfd_session *bs = p;
|
|
||||||
|
|
||||||
return jhash(&bs->mhop, sizeof(bs->mhop), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool bfd_mhop_hash_cmp(const void *n1, const void *n2)
|
|
||||||
{
|
|
||||||
const struct bfd_session *bs1 = n1, *bs2 = n2;
|
|
||||||
|
|
||||||
return memcmp(&bs1->mhop, &bs2->mhop, sizeof(bs1->mhop)) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Helper functions */
|
|
||||||
static void _shop_key(struct bfd_session *bs, const struct bfd_shop_key *shop)
|
|
||||||
{
|
|
||||||
bs->shop = *shop;
|
|
||||||
|
|
||||||
/* Remove unused fields. */
|
|
||||||
switch (bs->shop.peer.sa_sin.sin_family) {
|
|
||||||
case AF_INET:
|
|
||||||
bs->shop.peer.sa_sin.sin_port = 0;
|
|
||||||
break;
|
|
||||||
case AF_INET6:
|
|
||||||
bs->shop.peer.sa_sin6.sin6_port = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _shop_key2(struct bfd_session *bs, const struct bfd_shop_key *shop)
|
|
||||||
{
|
|
||||||
_shop_key(bs, shop);
|
|
||||||
bs->shop.ifindex = IFINDEX_INTERNAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _mhop_key(struct bfd_session *bs, const struct bfd_mhop_key *mhop)
|
|
||||||
{
|
|
||||||
bs->mhop = *mhop;
|
|
||||||
|
|
||||||
/* Remove unused fields. */
|
|
||||||
switch (bs->mhop.peer.sa_sin.sin_family) {
|
|
||||||
case AF_INET:
|
|
||||||
bs->mhop.peer.sa_sin.sin_port = 0;
|
|
||||||
bs->mhop.local.sa_sin.sin_port = 0;
|
|
||||||
break;
|
|
||||||
case AF_INET6:
|
|
||||||
bs->mhop.peer.sa_sin6.sin6_port = 0;
|
|
||||||
bs->mhop.local.sa_sin6.sin6_port = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Hash public interface / exported functions.
|
* Hash public interface / exported functions.
|
||||||
@ -1393,34 +1318,35 @@ struct bfd_session *bfd_id_lookup(uint32_t id)
|
|||||||
return hash_lookup(bfd_id_hash, &bs);
|
return hash_lookup(bfd_id_hash, &bs);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct bfd_session *bfd_shop_lookup(struct bfd_shop_key shop)
|
struct bfd_session *bfd_key_lookup(struct bfd_key key)
|
||||||
{
|
{
|
||||||
struct bfd_session bs, *bsp;
|
struct bfd_session bs, *bsp;
|
||||||
|
|
||||||
_shop_key(&bs, &shop);
|
bs.key = key;
|
||||||
|
bsp = hash_lookup(bfd_key_hash, &bs);
|
||||||
|
|
||||||
bsp = hash_lookup(bfd_shop_hash, &bs);
|
/* Handle cases where local-address is optional. */
|
||||||
if (bsp == NULL && bs.shop.ifindex != 0) {
|
if (bsp == NULL && bs.key.family == AF_INET) {
|
||||||
/*
|
memset(&bs.key.local, 0, sizeof(bs.key.local));
|
||||||
* Since the local interface spec is optional, try
|
bsp = hash_lookup(bfd_key_hash, &bs);
|
||||||
* searching the key without it as well.
|
}
|
||||||
*/
|
|
||||||
_shop_key2(&bs, &shop);
|
/* Handle cases where ifname is optional. */
|
||||||
bsp = hash_lookup(bfd_shop_hash, &bs);
|
bs.key = key;
|
||||||
|
if (bsp == NULL && bs.key.ifname[0]) {
|
||||||
|
memset(bs.key.ifname, 0, sizeof(bs.key.ifname));
|
||||||
|
bsp = hash_lookup(bfd_key_hash, &bs);
|
||||||
|
|
||||||
|
/* Handle cases where local-address and ifname are optional. */
|
||||||
|
if (bsp == NULL && bs.key.family == AF_INET) {
|
||||||
|
memset(&bs.key.local, 0, sizeof(bs.key.local));
|
||||||
|
bsp = hash_lookup(bfd_key_hash, &bs);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return bsp;
|
return bsp;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct bfd_session *bfd_mhop_lookup(struct bfd_mhop_key mhop)
|
|
||||||
{
|
|
||||||
struct bfd_session bs;
|
|
||||||
|
|
||||||
_mhop_key(&bs, &mhop);
|
|
||||||
|
|
||||||
return hash_lookup(bfd_mhop_hash, &bs);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Delete functions.
|
* Delete functions.
|
||||||
*
|
*
|
||||||
@ -1440,31 +1366,18 @@ struct bfd_session *bfd_id_delete(uint32_t id)
|
|||||||
return hash_release(bfd_id_hash, &bs);
|
return hash_release(bfd_id_hash, &bs);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct bfd_session *bfd_shop_delete(struct bfd_shop_key shop)
|
struct bfd_session *bfd_key_delete(struct bfd_key key)
|
||||||
{
|
{
|
||||||
struct bfd_session bs, *bsp;
|
struct bfd_session bs, *bsp;
|
||||||
|
|
||||||
_shop_key(&bs, &shop);
|
bs.key = key;
|
||||||
bsp = hash_release(bfd_shop_hash, &bs);
|
bsp = hash_lookup(bfd_key_hash, &bs);
|
||||||
if (bsp == NULL && shop.ifindex != 0) {
|
if (bsp == NULL && key.ifname[0]) {
|
||||||
/*
|
memset(bs.key.ifname, 0, sizeof(bs.key.ifname));
|
||||||
* Since the local interface spec is optional, try
|
bsp = hash_lookup(bfd_key_hash, &bs);
|
||||||
* searching the key without it as well.
|
|
||||||
*/
|
|
||||||
_shop_key2(&bs, &shop);
|
|
||||||
bsp = hash_release(bfd_shop_hash, &bs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return bsp;
|
return hash_release(bfd_key_hash, bsp);
|
||||||
}
|
|
||||||
|
|
||||||
struct bfd_session *bfd_mhop_delete(struct bfd_mhop_key mhop)
|
|
||||||
{
|
|
||||||
struct bfd_session bs;
|
|
||||||
|
|
||||||
_mhop_key(&bs, &mhop);
|
|
||||||
|
|
||||||
return hash_release(bfd_mhop_hash, &bs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Iteration functions. */
|
/* Iteration functions. */
|
||||||
@ -1473,14 +1386,9 @@ void bfd_id_iterate(hash_iter_func hif, void *arg)
|
|||||||
hash_iterate(bfd_id_hash, hif, arg);
|
hash_iterate(bfd_id_hash, hif, arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
void bfd_shop_iterate(hash_iter_func hif, void *arg)
|
void bfd_key_iterate(hash_iter_func hif, void *arg)
|
||||||
{
|
{
|
||||||
hash_iterate(bfd_shop_hash, hif, arg);
|
hash_iterate(bfd_key_hash, hif, arg);
|
||||||
}
|
|
||||||
|
|
||||||
void bfd_mhop_iterate(hash_iter_func hif, void *arg)
|
|
||||||
{
|
|
||||||
hash_iterate(bfd_mhop_hash, hif, arg);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1494,24 +1402,17 @@ bool bfd_id_insert(struct bfd_session *bs)
|
|||||||
return (hash_get(bfd_id_hash, bs, hash_alloc_intern) == bs);
|
return (hash_get(bfd_id_hash, bs, hash_alloc_intern) == bs);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool bfd_shop_insert(struct bfd_session *bs)
|
bool bfd_key_insert(struct bfd_session *bs)
|
||||||
{
|
{
|
||||||
return (hash_get(bfd_shop_hash, bs, hash_alloc_intern) == bs);
|
return (hash_get(bfd_key_hash, bs, hash_alloc_intern) == bs);
|
||||||
}
|
|
||||||
|
|
||||||
bool bfd_mhop_insert(struct bfd_session *bs)
|
|
||||||
{
|
|
||||||
return (hash_get(bfd_mhop_hash, bs, hash_alloc_intern) == bs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void bfd_initialize(void)
|
void bfd_initialize(void)
|
||||||
{
|
{
|
||||||
bfd_id_hash = hash_create(bfd_id_hash_do, bfd_id_hash_cmp,
|
bfd_id_hash = hash_create(bfd_id_hash_do, bfd_id_hash_cmp,
|
||||||
"BFD discriminator hash");
|
"BFD session discriminator hash");
|
||||||
bfd_shop_hash = hash_create(bfd_shop_hash_do, bfd_shop_hash_cmp,
|
bfd_key_hash = hash_create(bfd_key_hash_do, bfd_key_hash_cmp,
|
||||||
"BFD single hop hash");
|
"BFD session hash");
|
||||||
bfd_mhop_hash = hash_create(bfd_mhop_hash_do, bfd_mhop_hash_cmp,
|
|
||||||
"BFD multihop hop hash");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _bfd_free(struct hash_bucket *hb,
|
static void _bfd_free(struct hash_bucket *hb,
|
||||||
@ -1532,11 +1433,9 @@ void bfd_shutdown(void)
|
|||||||
* assert() here to make sure it really happened.
|
* assert() here to make sure it really happened.
|
||||||
*/
|
*/
|
||||||
bfd_id_iterate(_bfd_free, NULL);
|
bfd_id_iterate(_bfd_free, NULL);
|
||||||
assert(bfd_shop_hash->count == 0);
|
assert(bfd_key_hash->count == 0);
|
||||||
assert(bfd_mhop_hash->count == 0);
|
|
||||||
|
|
||||||
/* Now free the hashes themselves. */
|
/* Now free the hashes themselves. */
|
||||||
hash_free(bfd_id_hash);
|
hash_free(bfd_id_hash);
|
||||||
hash_free(bfd_shop_hash);
|
hash_free(bfd_key_hash);
|
||||||
hash_free(bfd_mhop_hash);
|
|
||||||
}
|
}
|
||||||
|
57
bfdd/bfd.h
57
bfdd/bfd.h
@ -173,15 +173,13 @@ enum bfd_session_flags {
|
|||||||
#define BFD_CHECK_FLAG(field, flag) (field & flag)
|
#define BFD_CHECK_FLAG(field, flag) (field & flag)
|
||||||
|
|
||||||
/* BFD session hash keys */
|
/* BFD session hash keys */
|
||||||
struct bfd_shop_key {
|
struct bfd_key {
|
||||||
struct sockaddr_any peer;
|
uint16_t family;
|
||||||
ifindex_t ifindex;
|
uint8_t mhop;
|
||||||
};
|
struct in6_addr peer;
|
||||||
|
struct in6_addr local;
|
||||||
struct bfd_mhop_key {
|
char ifname[MAXNAMELEN];
|
||||||
struct sockaddr_any peer;
|
char vrfname[MAXNAMELEN];
|
||||||
struct sockaddr_any local;
|
|
||||||
vrf_id_t vrfid;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct bfd_session_stats {
|
struct bfd_session_stats {
|
||||||
@ -227,19 +225,14 @@ struct bfd_session {
|
|||||||
uint8_t polling;
|
uint8_t polling;
|
||||||
|
|
||||||
/* This and the localDiscr are the keys to state info */
|
/* This and the localDiscr are the keys to state info */
|
||||||
|
struct bfd_key key;
|
||||||
struct peer_label *pl;
|
struct peer_label *pl;
|
||||||
union {
|
|
||||||
struct bfd_shop_key shop;
|
|
||||||
struct bfd_mhop_key mhop;
|
|
||||||
};
|
|
||||||
int sock;
|
|
||||||
|
|
||||||
struct sockaddr_any local_address;
|
struct sockaddr_any local_address;
|
||||||
struct sockaddr_any local_ip;
|
|
||||||
struct interface *ifp;
|
struct interface *ifp;
|
||||||
struct vrf *vrf;
|
struct vrf *vrf;
|
||||||
char ifname[MAXNAMELEN];
|
|
||||||
char vrfname[MAXNAMELEN];
|
int sock;
|
||||||
|
|
||||||
/* BFD session flags */
|
/* BFD session flags */
|
||||||
enum bfd_session_flags flags;
|
enum bfd_session_flags flags;
|
||||||
@ -281,7 +274,11 @@ struct bfd_state_str_list {
|
|||||||
struct bfd_session_observer {
|
struct bfd_session_observer {
|
||||||
struct bfd_session *bso_bs;
|
struct bfd_session *bso_bs;
|
||||||
bool bso_isinterface;
|
bool bso_isinterface;
|
||||||
|
bool bso_isaddress;
|
||||||
|
union {
|
||||||
char bso_entryname[MAXNAMELEN];
|
char bso_entryname[MAXNAMELEN];
|
||||||
|
struct prefix bso_addr;
|
||||||
|
};
|
||||||
|
|
||||||
TAILQ_ENTRY(bfd_session_observer) bso_entry;
|
TAILQ_ENTRY(bfd_session_observer) bso_entry;
|
||||||
};
|
};
|
||||||
@ -531,38 +528,28 @@ const char *satostr(struct sockaddr_any *sa);
|
|||||||
const char *diag2str(uint8_t diag);
|
const char *diag2str(uint8_t diag);
|
||||||
int strtosa(const char *addr, struct sockaddr_any *sa);
|
int strtosa(const char *addr, struct sockaddr_any *sa);
|
||||||
void integer2timestr(uint64_t time, char *buf, size_t buflen);
|
void integer2timestr(uint64_t time, char *buf, size_t buflen);
|
||||||
const char *bs_to_string(struct bfd_session *bs);
|
const char *bs_to_string(const struct bfd_session *bs);
|
||||||
|
|
||||||
int bs_observer_add(struct bfd_session *bs);
|
int bs_observer_add(struct bfd_session *bs);
|
||||||
void bs_observer_del(struct bfd_session_observer *bso);
|
void bs_observer_del(struct bfd_session_observer *bso);
|
||||||
|
|
||||||
|
void bs_to_bpc(struct bfd_session *bs, struct bfd_peer_cfg *bpc);
|
||||||
|
|
||||||
/* BFD hash data structures interface */
|
/* BFD hash data structures interface */
|
||||||
void bfd_initialize(void);
|
void bfd_initialize(void);
|
||||||
void bfd_shutdown(void);
|
void bfd_shutdown(void);
|
||||||
struct bfd_session *bfd_id_lookup(uint32_t id);
|
struct bfd_session *bfd_id_lookup(uint32_t id);
|
||||||
struct bfd_session *bfd_shop_lookup(struct bfd_shop_key shop);
|
struct bfd_session *bfd_key_lookup(struct bfd_key key);
|
||||||
struct bfd_session *bfd_mhop_lookup(struct bfd_mhop_key mhop);
|
|
||||||
struct bfd_vrf *bfd_vrf_lookup(int vrf_id);
|
|
||||||
struct bfd_iface *bfd_iface_lookup(const char *ifname);
|
|
||||||
|
|
||||||
struct bfd_session *bfd_id_delete(uint32_t id);
|
struct bfd_session *bfd_id_delete(uint32_t id);
|
||||||
struct bfd_session *bfd_shop_delete(struct bfd_shop_key shop);
|
struct bfd_session *bfd_key_delete(struct bfd_key key);
|
||||||
struct bfd_session *bfd_mhop_delete(struct bfd_mhop_key mhop);
|
|
||||||
struct bfd_vrf *bfd_vrf_delete(int vrf_id);
|
|
||||||
struct bfd_iface *bfd_iface_delete(const char *ifname);
|
|
||||||
|
|
||||||
bool bfd_id_insert(struct bfd_session *bs);
|
bool bfd_id_insert(struct bfd_session *bs);
|
||||||
bool bfd_shop_insert(struct bfd_session *bs);
|
bool bfd_key_insert(struct bfd_session *bs);
|
||||||
bool bfd_mhop_insert(struct bfd_session *bs);
|
|
||||||
bool bfd_vrf_insert(struct bfd_vrf *vrf);
|
|
||||||
bool bfd_iface_insert(struct bfd_iface *iface);
|
|
||||||
|
|
||||||
typedef void (*hash_iter_func)(struct hash_bucket *hb, void *arg);
|
typedef void (*hash_iter_func)(struct hash_bucket *hb, void *arg);
|
||||||
void bfd_id_iterate(hash_iter_func hif, void *arg);
|
void bfd_id_iterate(hash_iter_func hif, void *arg);
|
||||||
void bfd_shop_iterate(hash_iter_func hif, void *arg);
|
void bfd_key_iterate(hash_iter_func hif, void *arg);
|
||||||
void bfd_mhop_iterate(hash_iter_func hif, void *arg);
|
|
||||||
void bfd_vrf_iterate(hash_iter_func hif, void *arg);
|
|
||||||
void bfd_iface_iterate(hash_iter_func hif, void *arg);
|
|
||||||
|
|
||||||
/* Export callback functions for `event.c`. */
|
/* Export callback functions for `event.c`. */
|
||||||
extern struct thread_master *master;
|
extern struct thread_master *master;
|
||||||
@ -572,6 +559,8 @@ int bfd_echo_recvtimer_cb(struct thread *t);
|
|||||||
int bfd_xmt_cb(struct thread *t);
|
int bfd_xmt_cb(struct thread *t);
|
||||||
int bfd_echo_xmt_cb(struct thread *t);
|
int bfd_echo_xmt_cb(struct thread *t);
|
||||||
|
|
||||||
|
extern struct in6_addr zero_addr;
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* bfdd_vty.c
|
* bfdd_vty.c
|
||||||
|
@ -79,7 +79,10 @@ int _ptm_bfd_send(struct bfd_session *bs, uint16_t *port, const void *data,
|
|||||||
if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_IPV6)) {
|
if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_IPV6)) {
|
||||||
memset(&sin6, 0, sizeof(sin6));
|
memset(&sin6, 0, sizeof(sin6));
|
||||||
sin6.sin6_family = AF_INET6;
|
sin6.sin6_family = AF_INET6;
|
||||||
sin6.sin6_addr = bs->shop.peer.sa_sin6.sin6_addr;
|
memcpy(&sin6.sin6_addr, &bs->key.peer, sizeof(sin6.sin6_addr));
|
||||||
|
if (IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr))
|
||||||
|
sin6.sin6_scope_id = bs->ifp->ifindex;
|
||||||
|
|
||||||
sin6.sin6_port =
|
sin6.sin6_port =
|
||||||
(port) ? *port
|
(port) ? *port
|
||||||
: (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH))
|
: (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH))
|
||||||
@ -92,7 +95,7 @@ int _ptm_bfd_send(struct bfd_session *bs, uint16_t *port, const void *data,
|
|||||||
} else {
|
} else {
|
||||||
memset(&sin, 0, sizeof(sin));
|
memset(&sin, 0, sizeof(sin));
|
||||||
sin.sin_family = AF_INET;
|
sin.sin_family = AF_INET;
|
||||||
sin.sin_addr = bs->shop.peer.sa_sin.sin_addr;
|
memcpy(&sin.sin_addr, &bs->key.peer, sizeof(sin.sin_addr));
|
||||||
sin.sin_port =
|
sin.sin_port =
|
||||||
(port) ? *port
|
(port) ? *port
|
||||||
: (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH))
|
: (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH))
|
||||||
@ -120,7 +123,7 @@ int _ptm_bfd_send(struct bfd_session *bs, uint16_t *port, const void *data,
|
|||||||
|
|
||||||
void ptm_bfd_echo_snd(struct bfd_session *bfd)
|
void ptm_bfd_echo_snd(struct bfd_session *bfd)
|
||||||
{
|
{
|
||||||
struct sockaddr_any *sa;
|
struct sockaddr *sa;
|
||||||
socklen_t salen;
|
socklen_t salen;
|
||||||
int sd;
|
int sd;
|
||||||
struct bfd_echo_pkt bep;
|
struct bfd_echo_pkt bep;
|
||||||
@ -135,31 +138,34 @@ void ptm_bfd_echo_snd(struct bfd_session *bfd)
|
|||||||
bep.len = BFD_ECHO_PKT_LEN;
|
bep.len = BFD_ECHO_PKT_LEN;
|
||||||
bep.my_discr = htonl(bfd->discrs.my_discr);
|
bep.my_discr = htonl(bfd->discrs.my_discr);
|
||||||
|
|
||||||
sa = BFD_CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_MH) ? &bfd->mhop.peer
|
|
||||||
: &bfd->shop.peer;
|
|
||||||
if (BFD_CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_IPV6)) {
|
if (BFD_CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_IPV6)) {
|
||||||
sd = bglobal.bg_echov6;
|
sd = bglobal.bg_echov6;
|
||||||
sin6 = sa->sa_sin6;
|
memset(&sin6, 0, sizeof(sin6));
|
||||||
|
memcpy(&sin6.sin6_addr, &bfd->key.peer, sizeof(sin6.sin6_addr));
|
||||||
|
if (bfd->ifp && IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr))
|
||||||
|
sin6.sin6_scope_id = bfd->ifp->ifindex;
|
||||||
|
|
||||||
sin6.sin6_port = htons(BFD_DEF_ECHO_PORT);
|
sin6.sin6_port = htons(BFD_DEF_ECHO_PORT);
|
||||||
#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
|
#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
|
||||||
sin6.sin6_len = sizeof(sin6);
|
sin6.sin6_len = sizeof(sin6);
|
||||||
#endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
|
#endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
|
||||||
|
|
||||||
sa = (struct sockaddr_any *)&sin6;
|
sa = (struct sockaddr *)&sin6;
|
||||||
salen = sizeof(sin6);
|
salen = sizeof(sin6);
|
||||||
} else {
|
} else {
|
||||||
sd = bglobal.bg_echo;
|
sd = bglobal.bg_echo;
|
||||||
sin = sa->sa_sin;
|
memset(&sin6, 0, sizeof(sin6));
|
||||||
|
memcpy(&sin.sin_addr, &bfd->key.peer, sizeof(sin.sin_addr));
|
||||||
sin.sin_port = htons(BFD_DEF_ECHO_PORT);
|
sin.sin_port = htons(BFD_DEF_ECHO_PORT);
|
||||||
#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
|
#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
|
||||||
sin.sin_len = sizeof(sin);
|
sin.sin_len = sizeof(sin);
|
||||||
#endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
|
#endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
|
||||||
|
|
||||||
sa = (struct sockaddr_any *)&sin;
|
sa = (struct sockaddr *)&sin;
|
||||||
salen = sizeof(sin);
|
salen = sizeof(sin);
|
||||||
}
|
}
|
||||||
if (bp_udp_send(sd, BFD_TTL_VAL, (uint8_t *)&bep, sizeof(bep),
|
if (bp_udp_send(sd, BFD_TTL_VAL, (uint8_t *)&bep, sizeof(bep), sa,
|
||||||
(struct sockaddr *)sa, salen)
|
salen)
|
||||||
== -1)
|
== -1)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -602,8 +608,8 @@ int bfd_recv_cb(struct thread *t)
|
|||||||
bfd->mh_ttl, BFD_TTL_VAL);
|
bfd->mh_ttl, BFD_TTL_VAL);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
} else if (bfd->local_ip.sa_sin.sin_family == AF_UNSPEC) {
|
} else if (bfd->local_address.sa_sin.sin_family == AF_UNSPEC) {
|
||||||
bfd->local_ip = local;
|
bfd->local_address = local;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -917,25 +923,26 @@ int bp_peer_socket(const struct bfd_session *bs)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bs->shop.ifindex != IFINDEX_INTERNAL) {
|
if (bs->key.ifname[0]) {
|
||||||
if (bp_bind_dev(sd, bs->ifp->name) != 0) {
|
if (bp_bind_dev(sd, bs->key.ifname) != 0) {
|
||||||
close(sd);
|
close(sd);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
} else if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH) &&
|
} else if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)
|
||||||
bs->mhop.vrfid != VRF_DEFAULT) {
|
&& bs->key.vrfname[0]) {
|
||||||
if (bp_bind_dev(sd, bs->vrf->name) != 0) {
|
if (bp_bind_dev(sd, bs->key.vrfname) != 0) {
|
||||||
close(sd);
|
close(sd);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Find an available source port in the proper range */
|
/* Find an available source port in the proper range */
|
||||||
sin = bs->local_ip.sa_sin;
|
memset(&sin, 0, sizeof(sin));
|
||||||
sin.sin_family = AF_INET;
|
sin.sin_family = AF_INET;
|
||||||
#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
|
#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
|
||||||
sin.sin_len = sizeof(sin);
|
sin.sin_len = sizeof(sin);
|
||||||
#endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
|
#endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
|
||||||
|
memcpy(&sin.sin_addr, &bs->key.local, sizeof(sin.sin_addr));
|
||||||
if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH) == 0)
|
if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH) == 0)
|
||||||
sin.sin_addr.s_addr = INADDR_ANY;
|
sin.sin_addr.s_addr = INADDR_ANY;
|
||||||
|
|
||||||
@ -987,20 +994,23 @@ int bp_peer_socketv6(const struct bfd_session *bs)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Find an available source port in the proper range */
|
/* Find an available source port in the proper range */
|
||||||
sin6 = bs->local_ip.sa_sin6;
|
memset(&sin6, 0, sizeof(sin6));
|
||||||
sin6.sin6_family = AF_INET6;
|
sin6.sin6_family = AF_INET6;
|
||||||
#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
|
#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
|
||||||
sin6.sin6_len = sizeof(sin6);
|
sin6.sin6_len = sizeof(sin6);
|
||||||
#endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
|
#endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
|
||||||
|
memcpy(&sin6.sin6_addr, &bs->key.local, sizeof(sin6.sin6_addr));
|
||||||
|
if (IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr))
|
||||||
|
sin6.sin6_scope_id = bs->ifp->ifindex;
|
||||||
|
|
||||||
if (bs->shop.ifindex != IFINDEX_INTERNAL) {
|
if (bs->key.ifname[0]) {
|
||||||
if (bp_bind_dev(sd, bs->ifp->name) != 0) {
|
if (bp_bind_dev(sd, bs->key.ifname) != 0) {
|
||||||
close(sd);
|
close(sd);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
} else if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH) &&
|
} else if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)
|
||||||
bs->mhop.vrfid != VRF_DEFAULT) {
|
&& bs->key.vrfname[0]) {
|
||||||
if (bp_bind_dev(sd, bs->vrf->name) != 0) {
|
if (bp_bind_dev(sd, bs->key.vrfname) != 0) {
|
||||||
close(sd);
|
close(sd);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -79,7 +79,6 @@ _find_peer_or_error(struct vty *vty, int argc, struct cmd_token **argv,
|
|||||||
const char *local_str, const char *ifname,
|
const char *local_str, const char *ifname,
|
||||||
const char *vrfname);
|
const char *vrfname);
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Commands definition.
|
* Commands definition.
|
||||||
*/
|
*/
|
||||||
@ -369,22 +368,25 @@ DEFPY(bfd_no_peer, bfd_no_peer_cmd,
|
|||||||
*/
|
*/
|
||||||
static void _display_peer_header(struct vty *vty, struct bfd_session *bs)
|
static void _display_peer_header(struct vty *vty, struct bfd_session *bs)
|
||||||
{
|
{
|
||||||
if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)) {
|
char addr_buf[INET6_ADDRSTRLEN];
|
||||||
vty_out(vty, "\tpeer %s", satostr(&bs->mhop.peer));
|
|
||||||
|
vty_out(vty, "\tpeer %s",
|
||||||
|
inet_ntop(bs->key.family, &bs->key.peer, addr_buf,
|
||||||
|
sizeof(addr_buf)));
|
||||||
|
|
||||||
|
if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH))
|
||||||
vty_out(vty, " multihop");
|
vty_out(vty, " multihop");
|
||||||
vty_out(vty, " local-address %s", satostr(&bs->mhop.local));
|
|
||||||
if (bs->vrfname[0])
|
if (memcmp(&bs->key.local, &zero_addr, sizeof(bs->key.local)))
|
||||||
vty_out(vty, " vrf %s", bs->vrfname);
|
|
||||||
vty_out(vty, "\n");
|
|
||||||
} else {
|
|
||||||
vty_out(vty, "\tpeer %s", satostr(&bs->shop.peer));
|
|
||||||
if (bs->local_address.sa_sin.sin_family != AF_UNSPEC)
|
|
||||||
vty_out(vty, " local-address %s",
|
vty_out(vty, " local-address %s",
|
||||||
satostr(&bs->local_address));
|
inet_ntop(bs->key.family, &bs->key.local, addr_buf,
|
||||||
if (bs->ifname[0])
|
sizeof(addr_buf)));
|
||||||
vty_out(vty, " interface %s", bs->ifname);
|
|
||||||
|
if (bs->key.vrfname[0])
|
||||||
|
vty_out(vty, " vrf %s", bs->key.vrfname);
|
||||||
|
if (bs->key.ifname[0])
|
||||||
|
vty_out(vty, " interface %s", bs->key.ifname);
|
||||||
vty_out(vty, "\n");
|
vty_out(vty, "\n");
|
||||||
}
|
|
||||||
|
|
||||||
if (bs->pl)
|
if (bs->pl)
|
||||||
vty_out(vty, "\t\tlabel: %s\n", bs->pl->pl_label);
|
vty_out(vty, "\t\tlabel: %s\n", bs->pl->pl_label);
|
||||||
@ -453,22 +455,25 @@ static void _display_peer(struct vty *vty, struct bfd_session *bs)
|
|||||||
static struct json_object *_peer_json_header(struct bfd_session *bs)
|
static struct json_object *_peer_json_header(struct bfd_session *bs)
|
||||||
{
|
{
|
||||||
struct json_object *jo = json_object_new_object();
|
struct json_object *jo = json_object_new_object();
|
||||||
|
char addr_buf[INET6_ADDRSTRLEN];
|
||||||
|
|
||||||
if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)) {
|
if (bs->key.mhop)
|
||||||
json_object_boolean_true_add(jo, "multihop");
|
json_object_boolean_true_add(jo, "multihop");
|
||||||
json_object_string_add(jo, "peer", satostr(&bs->mhop.peer));
|
else
|
||||||
json_object_string_add(jo, "local", satostr(&bs->mhop.local));
|
|
||||||
if (bs->vrfname[0])
|
|
||||||
json_object_string_add(jo, "vrf", bs->vrfname);
|
|
||||||
} else {
|
|
||||||
json_object_boolean_false_add(jo, "multihop");
|
json_object_boolean_false_add(jo, "multihop");
|
||||||
json_object_string_add(jo, "peer", satostr(&bs->shop.peer));
|
|
||||||
if (bs->local_address.sa_sin.sin_family != AF_UNSPEC)
|
json_object_string_add(jo, "peer",
|
||||||
|
inet_ntop(bs->key.family, &bs->key.peer,
|
||||||
|
addr_buf, sizeof(addr_buf)));
|
||||||
|
if (memcmp(&bs->key.local, &zero_addr, sizeof(bs->key.local)))
|
||||||
json_object_string_add(jo, "local",
|
json_object_string_add(jo, "local",
|
||||||
satostr(&bs->local_address));
|
inet_ntop(bs->key.family, &bs->key.local,
|
||||||
if (bs->ifname[0])
|
addr_buf, sizeof(addr_buf)));
|
||||||
json_object_string_add(jo, "interface", bs->ifname);
|
|
||||||
}
|
if (bs->key.vrfname[0])
|
||||||
|
json_object_string_add(jo, "vrf", bs->key.vrfname);
|
||||||
|
if (bs->key.ifname[0])
|
||||||
|
json_object_string_add(jo, "interface", bs->key.ifname);
|
||||||
|
|
||||||
if (bs->pl)
|
if (bs->pl)
|
||||||
json_object_string_add(jo, "label", bs->pl->pl_label);
|
json_object_string_add(jo, "label", bs->pl->pl_label);
|
||||||
@ -916,25 +921,29 @@ static int bfdd_write_config(struct vty *vty)
|
|||||||
|
|
||||||
static void _bfdd_peer_write_config(struct vty *vty, struct bfd_session *bs)
|
static void _bfdd_peer_write_config(struct vty *vty, struct bfd_session *bs)
|
||||||
{
|
{
|
||||||
if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)) {
|
char addr_buf[INET6_ADDRSTRLEN];
|
||||||
vty_out(vty, " peer %s", satostr(&bs->mhop.peer));
|
|
||||||
|
vty_out(vty, " peer %s",
|
||||||
|
inet_ntop(bs->key.family, &bs->key.peer, addr_buf,
|
||||||
|
sizeof(addr_buf)));
|
||||||
|
|
||||||
|
if (bs->key.mhop)
|
||||||
vty_out(vty, " multihop");
|
vty_out(vty, " multihop");
|
||||||
vty_out(vty, " local-address %s", satostr(&bs->mhop.local));
|
|
||||||
if (bs->vrfname[0])
|
if (memcmp(&bs->key.local, &zero_addr, sizeof(bs->key.local)))
|
||||||
vty_out(vty, " vrf %s", bs->vrfname);
|
|
||||||
vty_out(vty, "\n");
|
|
||||||
} else {
|
|
||||||
vty_out(vty, " peer %s", satostr(&bs->shop.peer));
|
|
||||||
if (bs->local_address.sa_sin.sin_family != AF_UNSPEC)
|
|
||||||
vty_out(vty, " local-address %s",
|
vty_out(vty, " local-address %s",
|
||||||
satostr(&bs->local_address));
|
inet_ntop(bs->key.family, &bs->key.local, addr_buf,
|
||||||
if (bs->ifname[0])
|
sizeof(addr_buf)));
|
||||||
vty_out(vty, " interface %s", bs->ifname);
|
|
||||||
|
if (bs->key.vrfname[0])
|
||||||
|
vty_out(vty, " vrf %s", bs->key.vrfname);
|
||||||
|
if (bs->key.ifname[0])
|
||||||
|
vty_out(vty, " interface %s", bs->key.ifname);
|
||||||
vty_out(vty, "\n");
|
vty_out(vty, "\n");
|
||||||
}
|
|
||||||
|
|
||||||
if (bs->sock == -1)
|
if (bs->sock == -1)
|
||||||
vty_out(vty, " ! vrf or interface doesn't exist\n");
|
vty_out(vty,
|
||||||
|
" ! vrf, interface or local-address doesn't exist\n");
|
||||||
|
|
||||||
if (bs->detect_mult != BPC_DEF_DETECTMULTIPLIER)
|
if (bs->detect_mult != BPC_DEF_DETECTMULTIPLIER)
|
||||||
vty_out(vty, " detect-multiplier %d\n", bs->detect_mult);
|
vty_out(vty, " detect-multiplier %d\n", bs->detect_mult);
|
||||||
@ -980,16 +989,7 @@ static void _bfdd_peer_write_config_iter(struct hash_bucket *hb, void *arg)
|
|||||||
|
|
||||||
static int bfdd_peer_write_config(struct vty *vty)
|
static int bfdd_peer_write_config(struct vty *vty)
|
||||||
{
|
{
|
||||||
struct bfd_session_observer *bso;
|
|
||||||
|
|
||||||
bfd_id_iterate(_bfdd_peer_write_config_iter, vty);
|
bfd_id_iterate(_bfdd_peer_write_config_iter, vty);
|
||||||
TAILQ_FOREACH(bso, &bglobal.bg_obslist, bso_entry) {
|
|
||||||
/* Only print disabled sessions here. */
|
|
||||||
if (bso->bso_bs->sock != -1)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
_bfdd_peer_write_config(vty, bso->bso_bs);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -309,24 +309,7 @@ static int parse_peer_label_config(struct json_object *jo,
|
|||||||
log_debug("\tpeer-label: %s", sval);
|
log_debug("\tpeer-label: %s", sval);
|
||||||
|
|
||||||
/* Translate the label into BFD address keys. */
|
/* Translate the label into BFD address keys. */
|
||||||
bpc->bpc_ipv4 = !BFD_CHECK_FLAG(pl->pl_bs->flags, BFD_SESS_FLAG_IPV6);
|
bs_to_bpc(pl->pl_bs, bpc);
|
||||||
bpc->bpc_mhop = BFD_CHECK_FLAG(pl->pl_bs->flags, BFD_SESS_FLAG_MH);
|
|
||||||
if (bpc->bpc_mhop) {
|
|
||||||
bpc->bpc_peer = pl->pl_bs->mhop.peer;
|
|
||||||
bpc->bpc_local = pl->pl_bs->mhop.local;
|
|
||||||
if (pl->pl_bs->mhop.vrfid != VRF_DEFAULT) {
|
|
||||||
bpc->bpc_has_vrfname = true;
|
|
||||||
strlcpy(bpc->bpc_vrfname, pl->pl_bs->vrf->name,
|
|
||||||
sizeof(bpc->bpc_vrfname));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
bpc->bpc_peer = pl->pl_bs->shop.peer;
|
|
||||||
if (pl->pl_bs->ifname[0]) {
|
|
||||||
bpc->bpc_has_localif = true;
|
|
||||||
strlcpy(bpc->bpc_localif, pl->pl_bs->ifname,
|
|
||||||
sizeof(bpc->bpc_localif));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -519,6 +502,8 @@ int config_notify_request(struct bfd_control_socket *bcs, const char *jsonstr,
|
|||||||
|
|
||||||
static int json_object_add_peer(struct json_object *jo, struct bfd_session *bs)
|
static int json_object_add_peer(struct json_object *jo, struct bfd_session *bs)
|
||||||
{
|
{
|
||||||
|
char addr_buf[INET6_ADDRSTRLEN];
|
||||||
|
|
||||||
/* Add peer 'key' information. */
|
/* Add peer 'key' information. */
|
||||||
if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_IPV6))
|
if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_IPV6))
|
||||||
json_object_boolean_true_add(jo, "ipv6");
|
json_object_boolean_true_add(jo, "ipv6");
|
||||||
@ -528,21 +513,26 @@ static int json_object_add_peer(struct json_object *jo, struct bfd_session *bs)
|
|||||||
if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)) {
|
if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)) {
|
||||||
json_object_boolean_true_add(jo, "multihop");
|
json_object_boolean_true_add(jo, "multihop");
|
||||||
json_object_string_add(jo, "peer-address",
|
json_object_string_add(jo, "peer-address",
|
||||||
satostr(&bs->mhop.peer));
|
inet_ntop(bs->key.family, &bs->key.peer,
|
||||||
|
addr_buf, sizeof(addr_buf)));
|
||||||
json_object_string_add(jo, "local-address",
|
json_object_string_add(jo, "local-address",
|
||||||
satostr(&bs->mhop.local));
|
inet_ntop(bs->key.family, &bs->key.local,
|
||||||
if (bs->vrfname[0])
|
addr_buf, sizeof(addr_buf)));
|
||||||
json_object_string_add(jo, "vrf-name", bs->vrfname);
|
if (bs->key.vrfname[0])
|
||||||
|
json_object_string_add(jo, "vrf-name", bs->key.vrfname);
|
||||||
} else {
|
} else {
|
||||||
json_object_boolean_false_add(jo, "multihop");
|
json_object_boolean_false_add(jo, "multihop");
|
||||||
json_object_string_add(jo, "peer-address",
|
json_object_string_add(jo, "peer-address",
|
||||||
satostr(&bs->shop.peer));
|
inet_ntop(bs->key.family, &bs->key.peer,
|
||||||
if (bs->local_address.sa_sin.sin_family != AF_UNSPEC)
|
addr_buf, sizeof(addr_buf)));
|
||||||
json_object_string_add(jo, "local-address",
|
if (memcmp(&bs->key.local, &zero_addr, sizeof(bs->key.local)))
|
||||||
satostr(&bs->local_address));
|
json_object_string_add(
|
||||||
if (bs->ifname[0])
|
jo, "local-address",
|
||||||
|
inet_ntop(bs->key.family, &bs->key.local,
|
||||||
|
addr_buf, sizeof(addr_buf)));
|
||||||
|
if (bs->key.ifname[0])
|
||||||
json_object_string_add(jo, "local-interface",
|
json_object_string_add(jo, "local-interface",
|
||||||
bs->ifname);
|
bs->key.ifname);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bs->pl)
|
if (bs->pl)
|
||||||
|
@ -55,7 +55,7 @@ static struct zclient *zclient;
|
|||||||
/*
|
/*
|
||||||
* Prototypes
|
* Prototypes
|
||||||
*/
|
*/
|
||||||
static int _ptm_msg_address(struct stream *msg, struct sockaddr_any *sa);
|
static int _ptm_msg_address(struct stream *msg, int family, const void *addr);
|
||||||
|
|
||||||
static void _ptm_msg_read_address(struct stream *msg, struct sockaddr_any *sa);
|
static void _ptm_msg_read_address(struct stream *msg, struct sockaddr_any *sa);
|
||||||
static int _ptm_msg_read(struct stream *msg, int command,
|
static int _ptm_msg_read(struct stream *msg, int command,
|
||||||
@ -127,24 +127,24 @@ static void debug_printbpc(const char *func, unsigned int line,
|
|||||||
#define DEBUG_PRINTBPC(bpc)
|
#define DEBUG_PRINTBPC(bpc)
|
||||||
#endif /* BFD_DEBUG */
|
#endif /* BFD_DEBUG */
|
||||||
|
|
||||||
static int _ptm_msg_address(struct stream *msg, struct sockaddr_any *sa)
|
static int _ptm_msg_address(struct stream *msg, int family, const void *addr)
|
||||||
{
|
{
|
||||||
switch (sa->sa_sin.sin_family) {
|
stream_putc(msg, family);
|
||||||
|
|
||||||
|
switch (family) {
|
||||||
case AF_INET:
|
case AF_INET:
|
||||||
stream_putc(msg, sa->sa_sin.sin_family);
|
stream_put(msg, addr, sizeof(struct in_addr));
|
||||||
stream_put_in_addr(msg, &sa->sa_sin.sin_addr);
|
|
||||||
stream_putc(msg, 32);
|
stream_putc(msg, 32);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AF_INET6:
|
case AF_INET6:
|
||||||
stream_putc(msg, sa->sa_sin6.sin6_family);
|
stream_put(msg, addr, sizeof(struct in6_addr));
|
||||||
stream_put(msg, &sa->sa_sin6.sin6_addr,
|
|
||||||
sizeof(sa->sa_sin6.sin6_addr));
|
|
||||||
stream_putc(msg, 128);
|
stream_putc(msg, 128);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return -1;
|
assert(0);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -153,7 +153,6 @@ static int _ptm_msg_address(struct stream *msg, struct sockaddr_any *sa)
|
|||||||
int ptm_bfd_notify(struct bfd_session *bs)
|
int ptm_bfd_notify(struct bfd_session *bs)
|
||||||
{
|
{
|
||||||
struct stream *msg;
|
struct stream *msg;
|
||||||
struct sockaddr_any sac;
|
|
||||||
|
|
||||||
bs->stats.znotification++;
|
bs->stats.znotification++;
|
||||||
|
|
||||||
@ -195,10 +194,7 @@ int ptm_bfd_notify(struct bfd_session *bs)
|
|||||||
stream_putl(msg, IFINDEX_INTERNAL);
|
stream_putl(msg, IFINDEX_INTERNAL);
|
||||||
|
|
||||||
/* BFD destination prefix information. */
|
/* BFD destination prefix information. */
|
||||||
if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH))
|
_ptm_msg_address(msg, bs->key.family, &bs->key.peer);
|
||||||
_ptm_msg_address(msg, &bs->mhop.peer);
|
|
||||||
else
|
|
||||||
_ptm_msg_address(msg, &bs->shop.peer);
|
|
||||||
|
|
||||||
/* BFD status */
|
/* BFD status */
|
||||||
switch (bs->ses_state) {
|
switch (bs->ses_state) {
|
||||||
@ -218,34 +214,7 @@ int ptm_bfd_notify(struct bfd_session *bs)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* BFD source prefix information. */
|
/* BFD source prefix information. */
|
||||||
if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)) {
|
_ptm_msg_address(msg, bs->key.family, &bs->key.local);
|
||||||
_ptm_msg_address(msg, &bs->mhop.local);
|
|
||||||
} else {
|
|
||||||
if (bs->local_address.sa_sin.sin_family)
|
|
||||||
_ptm_msg_address(msg, &bs->local_address);
|
|
||||||
else if (bs->local_address.sa_sin.sin_family)
|
|
||||||
_ptm_msg_address(msg, &bs->local_ip);
|
|
||||||
else {
|
|
||||||
sac = bs->shop.peer;
|
|
||||||
switch (sac.sa_sin.sin_family) {
|
|
||||||
case AF_INET:
|
|
||||||
memset(&sac.sa_sin.sin_addr, 0,
|
|
||||||
sizeof(sac.sa_sin.sin_family));
|
|
||||||
break;
|
|
||||||
case AF_INET6:
|
|
||||||
memset(&sac.sa_sin6.sin6_addr, 0,
|
|
||||||
sizeof(sac.sa_sin6.sin6_family));
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
assert(false);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* No local address found yet, so send zeroes. */
|
|
||||||
_ptm_msg_address(msg, &sac);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Write packet size. */
|
/* Write packet size. */
|
||||||
stream_putw_at(msg, 0, stream_get_endp(msg));
|
stream_putw_at(msg, 0, stream_get_endp(msg));
|
||||||
@ -290,7 +259,6 @@ stream_failure:
|
|||||||
static int _ptm_msg_read(struct stream *msg, int command,
|
static int _ptm_msg_read(struct stream *msg, int command,
|
||||||
struct bfd_peer_cfg *bpc, struct ptm_client **pc)
|
struct bfd_peer_cfg *bpc, struct ptm_client **pc)
|
||||||
{
|
{
|
||||||
struct interface *ifp;
|
|
||||||
uint32_t pid;
|
uint32_t pid;
|
||||||
uint8_t ttl __attribute__((unused));
|
uint8_t ttl __attribute__((unused));
|
||||||
size_t ifnamelen;
|
size_t ifnamelen;
|
||||||
@ -385,31 +353,6 @@ static int _ptm_msg_read(struct stream *msg, int command,
|
|||||||
if (bpc->bpc_has_localif) {
|
if (bpc->bpc_has_localif) {
|
||||||
STREAM_GET(bpc->bpc_localif, msg, ifnamelen);
|
STREAM_GET(bpc->bpc_localif, msg, ifnamelen);
|
||||||
bpc->bpc_localif[ifnamelen] = 0;
|
bpc->bpc_localif[ifnamelen] = 0;
|
||||||
|
|
||||||
/*
|
|
||||||
* IPv6 link-local addresses must use scope id,
|
|
||||||
* otherwise the session lookup will always fail
|
|
||||||
* and we'll have multiple sessions showing up.
|
|
||||||
*
|
|
||||||
* This problem only happens with single hop
|
|
||||||
* since it is not possible to have link-local
|
|
||||||
* address for multi hop sessions.
|
|
||||||
*/
|
|
||||||
if (bpc->bpc_ipv4 == false
|
|
||||||
&& IN6_IS_ADDR_LINKLOCAL(
|
|
||||||
&bpc->bpc_peer.sa_sin6.sin6_addr)) {
|
|
||||||
ifp = if_lookup_by_name_all_vrf(
|
|
||||||
bpc->bpc_localif);
|
|
||||||
if (ifp == NULL) {
|
|
||||||
log_error(
|
|
||||||
"ptm-read: interface %s doesn't exists",
|
|
||||||
bpc->bpc_localif);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
bpc->bpc_peer.sa_sin6.sin6_scope_id =
|
|
||||||
ifp->ifindex;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -608,7 +551,7 @@ static void bfdd_sessions_enable_interface(struct interface *ifp)
|
|||||||
|
|
||||||
/* Interface name mismatch. */
|
/* Interface name mismatch. */
|
||||||
bs = bso->bso_bs;
|
bs = bso->bso_bs;
|
||||||
if (strcmp(ifp->name, bs->ifname))
|
if (strcmp(ifp->name, bs->key.ifname))
|
||||||
continue;
|
continue;
|
||||||
/* Skip enabled sessions. */
|
/* Skip enabled sessions. */
|
||||||
if (bs->sock != -1)
|
if (bs->sock != -1)
|
||||||
@ -630,7 +573,7 @@ static void bfdd_sessions_disable_interface(struct interface *ifp)
|
|||||||
|
|
||||||
/* Interface name mismatch. */
|
/* Interface name mismatch. */
|
||||||
bs = bso->bso_bs;
|
bs = bso->bso_bs;
|
||||||
if (strcmp(ifp->name, bs->ifname))
|
if (strcmp(ifp->name, bs->key.ifname))
|
||||||
continue;
|
continue;
|
||||||
/* Skip disabled sessions. */
|
/* Skip disabled sessions. */
|
||||||
if (bs->sock == -1)
|
if (bs->sock == -1)
|
||||||
@ -691,6 +634,48 @@ static int bfdd_interface_vrf_update(int command __attribute__((__unused__)),
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void bfdd_sessions_enable_address(struct connected *ifc)
|
||||||
|
{
|
||||||
|
struct bfd_session_observer *bso;
|
||||||
|
struct bfd_session *bs;
|
||||||
|
struct prefix prefix;
|
||||||
|
|
||||||
|
TAILQ_FOREACH(bso, &bglobal.bg_obslist, bso_entry) {
|
||||||
|
if (bso->bso_isaddress == false)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* Skip enabled sessions. */
|
||||||
|
bs = bso->bso_bs;
|
||||||
|
if (bs->sock != -1)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* Check address. */
|
||||||
|
prefix = bso->bso_addr;
|
||||||
|
prefix.prefixlen = ifc->address->prefixlen;
|
||||||
|
if (prefix_cmp(&prefix, ifc->address))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* Try to enable it. */
|
||||||
|
bfd_session_enable(bs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bfdd_interface_address_update(int cmd, struct zclient *zc,
|
||||||
|
zebra_size_t len
|
||||||
|
__attribute__((__unused__)),
|
||||||
|
vrf_id_t vrfid)
|
||||||
|
{
|
||||||
|
struct connected *ifc;
|
||||||
|
|
||||||
|
ifc = zebra_interface_address_read(cmd, zc->ibuf, vrfid);
|
||||||
|
if (ifc == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
bfdd_sessions_enable_address(ifc);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
void bfdd_zclient_init(struct zebra_privs_t *bfdd_priv)
|
void bfdd_zclient_init(struct zebra_privs_t *bfdd_priv)
|
||||||
{
|
{
|
||||||
zclient = zclient_new(master, &zclient_options_default);
|
zclient = zclient_new(master, &zclient_options_default);
|
||||||
@ -713,6 +698,10 @@ void bfdd_zclient_init(struct zebra_privs_t *bfdd_priv)
|
|||||||
|
|
||||||
/* Learn about interface VRF. */
|
/* Learn about interface VRF. */
|
||||||
zclient->interface_vrf_update = bfdd_interface_vrf_update;
|
zclient->interface_vrf_update = bfdd_interface_vrf_update;
|
||||||
|
|
||||||
|
/* Learn about new addresses being registered. */
|
||||||
|
zclient->interface_address_add = bfdd_interface_address_update;
|
||||||
|
zclient->interface_address_delete = bfdd_interface_address_update;
|
||||||
}
|
}
|
||||||
|
|
||||||
void bfdd_zclient_stop(void)
|
void bfdd_zclient_stop(void)
|
||||||
|
0
tests/topotests/bfd-topo2/__init__.py
Normal file
0
tests/topotests/bfd-topo2/__init__.py
Normal file
5
tests/topotests/bfd-topo2/r1/bfdd.conf
Normal file
5
tests/topotests/bfd-topo2/r1/bfdd.conf
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
bfd
|
||||||
|
peer 2001:db8:4::1 multihop local-address 2001:db8:1::1
|
||||||
|
no shutdown
|
||||||
|
!
|
||||||
|
!
|
13
tests/topotests/bfd-topo2/r1/bgpd.conf
Normal file
13
tests/topotests/bfd-topo2/r1/bgpd.conf
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
router bgp 101
|
||||||
|
bgp router-id 10.254.254.1
|
||||||
|
neighbor r2g peer-group
|
||||||
|
neighbor r2g remote-as external
|
||||||
|
neighbor r2g bfd
|
||||||
|
neighbor r1-eth0 interface peer-group r2g
|
||||||
|
address-family ipv4 unicast
|
||||||
|
redistribute connected
|
||||||
|
exit-address-family
|
||||||
|
address-family ipv6 unicast
|
||||||
|
neighbor r2g activate
|
||||||
|
exit-address-family
|
||||||
|
!
|
68
tests/topotests/bfd-topo2/r1/ipv4_routes.json
Normal file
68
tests/topotests/bfd-topo2/r1/ipv4_routes.json
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
{
|
||||||
|
"10.0.3.0/24": [
|
||||||
|
{
|
||||||
|
"distance": 20,
|
||||||
|
"protocol": "bgp",
|
||||||
|
"internalFlags": 8,
|
||||||
|
"metric": 0,
|
||||||
|
"selected": true,
|
||||||
|
"installed": true,
|
||||||
|
"prefix": "10.0.3.0/24",
|
||||||
|
"internalStatus": 34,
|
||||||
|
"nexthops": [
|
||||||
|
{
|
||||||
|
"interfaceName": "r1-eth0",
|
||||||
|
"interfaceIndex": 2,
|
||||||
|
"fib": true,
|
||||||
|
"flags": 3,
|
||||||
|
"active": true,
|
||||||
|
"afi": "ipv6"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"10.254.254.2/32": [
|
||||||
|
{
|
||||||
|
"distance": 20,
|
||||||
|
"protocol": "bgp",
|
||||||
|
"internalFlags": 8,
|
||||||
|
"metric": 0,
|
||||||
|
"selected": true,
|
||||||
|
"installed": true,
|
||||||
|
"prefix": "10.254.254.2/32",
|
||||||
|
"internalStatus": 34,
|
||||||
|
"nexthops": [
|
||||||
|
{
|
||||||
|
"interfaceName": "r1-eth0",
|
||||||
|
"interfaceIndex": 2,
|
||||||
|
"fib": true,
|
||||||
|
"flags": 3,
|
||||||
|
"active": true,
|
||||||
|
"afi": "ipv6"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"10.254.254.1/32": [
|
||||||
|
{
|
||||||
|
"distance": 0,
|
||||||
|
"protocol": "connected",
|
||||||
|
"internalFlags": 8,
|
||||||
|
"metric": 0,
|
||||||
|
"selected": true,
|
||||||
|
"installed": true,
|
||||||
|
"prefix": "10.254.254.1/32",
|
||||||
|
"internalStatus": 32,
|
||||||
|
"nexthops": [
|
||||||
|
{
|
||||||
|
"directlyConnected": true,
|
||||||
|
"interfaceName": "lo",
|
||||||
|
"interfaceIndex": 1,
|
||||||
|
"fib": true,
|
||||||
|
"flags": 3,
|
||||||
|
"active": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
63
tests/topotests/bfd-topo2/r1/ipv6_routes.json
Normal file
63
tests/topotests/bfd-topo2/r1/ipv6_routes.json
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
{
|
||||||
|
"2001:db8:4::/64": [
|
||||||
|
{
|
||||||
|
"distance": 20,
|
||||||
|
"protocol": "bgp",
|
||||||
|
"internalFlags": 8,
|
||||||
|
"metric": 0,
|
||||||
|
"selected": true,
|
||||||
|
"installed": true,
|
||||||
|
"prefix": "2001:db8:4::/64",
|
||||||
|
"internalStatus": 34,
|
||||||
|
"nexthops": [
|
||||||
|
{
|
||||||
|
"interfaceName": "r1-eth0",
|
||||||
|
"interfaceIndex": 2,
|
||||||
|
"fib": true,
|
||||||
|
"flags": 3,
|
||||||
|
"active": true,
|
||||||
|
"afi": "ipv6"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"2001:db8:1::/64": [
|
||||||
|
{
|
||||||
|
"distance": 20,
|
||||||
|
"protocol": "bgp",
|
||||||
|
"internalFlags": 0,
|
||||||
|
"metric": 0,
|
||||||
|
"internalStatus": 2,
|
||||||
|
"prefix": "2001:db8:1::/64",
|
||||||
|
"nexthops": [
|
||||||
|
{
|
||||||
|
"interfaceName": "r1-eth0",
|
||||||
|
"interfaceIndex": 2,
|
||||||
|
"flags": 1,
|
||||||
|
"active": true,
|
||||||
|
"afi": "ipv6"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"distance": 0,
|
||||||
|
"protocol": "connected",
|
||||||
|
"internalFlags": 8,
|
||||||
|
"metric": 0,
|
||||||
|
"selected": true,
|
||||||
|
"installed": true,
|
||||||
|
"prefix": "2001:db8:1::/64",
|
||||||
|
"internalStatus": 32,
|
||||||
|
"nexthops": [
|
||||||
|
{
|
||||||
|
"directlyConnected": true,
|
||||||
|
"interfaceName": "r1-eth0",
|
||||||
|
"interfaceIndex": 2,
|
||||||
|
"fib": true,
|
||||||
|
"flags": 3,
|
||||||
|
"active": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
29
tests/topotests/bfd-topo2/r1/peers.json
Normal file
29
tests/topotests/bfd-topo2/r1/peers.json
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"multihop":true,
|
||||||
|
"peer":"2001:db8:4::1",
|
||||||
|
"local":"2001:db8:1::1",
|
||||||
|
"status":"up",
|
||||||
|
"diagnostic":"ok",
|
||||||
|
"remote-diagnostic":"ok",
|
||||||
|
"receive-interval":300,
|
||||||
|
"transmit-interval":300,
|
||||||
|
"echo-interval":0,
|
||||||
|
"remote-receive-interval":300,
|
||||||
|
"remote-transmit-interval":300,
|
||||||
|
"remote-echo-interval":50
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"multihop":false,
|
||||||
|
"interface":"r1-eth0",
|
||||||
|
"status":"up",
|
||||||
|
"diagnostic":"ok",
|
||||||
|
"remote-diagnostic":"ok",
|
||||||
|
"receive-interval":300,
|
||||||
|
"transmit-interval":300,
|
||||||
|
"echo-interval":0,
|
||||||
|
"remote-receive-interval":300,
|
||||||
|
"remote-transmit-interval":300,
|
||||||
|
"remote-echo-interval":50
|
||||||
|
}
|
||||||
|
]
|
6
tests/topotests/bfd-topo2/r1/zebra.conf
Normal file
6
tests/topotests/bfd-topo2/r1/zebra.conf
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
interface lo
|
||||||
|
ip address 10.254.254.1/32
|
||||||
|
!
|
||||||
|
interface r1-eth0
|
||||||
|
ipv6 address 2001:db8:1::1/64
|
||||||
|
!
|
16
tests/topotests/bfd-topo2/r2/bgpd.conf
Normal file
16
tests/topotests/bfd-topo2/r2/bgpd.conf
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
router bgp 102
|
||||||
|
bgp router-id 10.254.254.2
|
||||||
|
neighbor r2g peer-group
|
||||||
|
neighbor r2g remote-as external
|
||||||
|
neighbor r2g bfd
|
||||||
|
neighbor r2-eth0 interface peer-group r2g
|
||||||
|
!
|
||||||
|
address-family ipv4 unicast
|
||||||
|
redistribute connected
|
||||||
|
exit-address-family
|
||||||
|
!
|
||||||
|
address-family ipv6 unicast
|
||||||
|
redistribute connected
|
||||||
|
neighbor r2g activate
|
||||||
|
exit-address-family
|
||||||
|
!
|
108
tests/topotests/bfd-topo2/r2/ipv4_routes.json
Normal file
108
tests/topotests/bfd-topo2/r2/ipv4_routes.json
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
{
|
||||||
|
"10.0.3.0/24": [
|
||||||
|
{
|
||||||
|
"distance": 110,
|
||||||
|
"protocol": "ospf",
|
||||||
|
"internalFlags": 0,
|
||||||
|
"metric": 10,
|
||||||
|
"internalStatus": 2,
|
||||||
|
"prefix": "10.0.3.0/24",
|
||||||
|
"nexthops": [
|
||||||
|
{
|
||||||
|
"active": true,
|
||||||
|
"directlyConnected": true,
|
||||||
|
"flags": 1,
|
||||||
|
"interfaceIndex": 3,
|
||||||
|
"interfaceName": "r2-eth1"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"distance": 0,
|
||||||
|
"protocol": "connected",
|
||||||
|
"internalFlags": 8,
|
||||||
|
"metric": 0,
|
||||||
|
"selected": true,
|
||||||
|
"installed": true,
|
||||||
|
"prefix": "10.0.3.0/24",
|
||||||
|
"internalStatus": 32,
|
||||||
|
"nexthops": [
|
||||||
|
{
|
||||||
|
"directlyConnected": true,
|
||||||
|
"interfaceName": "r2-eth1",
|
||||||
|
"interfaceIndex": 3,
|
||||||
|
"fib": true,
|
||||||
|
"flags": 3,
|
||||||
|
"active": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"10.254.254.3/32": [
|
||||||
|
{
|
||||||
|
"distance": 110,
|
||||||
|
"protocol": "ospf",
|
||||||
|
"internalFlags": 8,
|
||||||
|
"metric": 20,
|
||||||
|
"selected": true,
|
||||||
|
"installed": true,
|
||||||
|
"prefix": "10.254.254.3/32",
|
||||||
|
"internalStatus": 34,
|
||||||
|
"nexthops": [
|
||||||
|
{
|
||||||
|
"interfaceName": "r2-eth1",
|
||||||
|
"ip": "10.0.3.1",
|
||||||
|
"interfaceIndex": 3,
|
||||||
|
"fib": true,
|
||||||
|
"flags": 3,
|
||||||
|
"active": true,
|
||||||
|
"afi": "ipv4"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"10.254.254.2/32": [
|
||||||
|
{
|
||||||
|
"distance": 0,
|
||||||
|
"protocol": "connected",
|
||||||
|
"internalFlags": 8,
|
||||||
|
"metric": 0,
|
||||||
|
"selected": true,
|
||||||
|
"installed": true,
|
||||||
|
"prefix": "10.254.254.2/32",
|
||||||
|
"internalStatus": 32,
|
||||||
|
"nexthops": [
|
||||||
|
{
|
||||||
|
"directlyConnected": true,
|
||||||
|
"interfaceName": "lo",
|
||||||
|
"interfaceIndex": 1,
|
||||||
|
"fib": true,
|
||||||
|
"flags": 3,
|
||||||
|
"active": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"10.254.254.1/32": [
|
||||||
|
{
|
||||||
|
"distance": 20,
|
||||||
|
"protocol": "bgp",
|
||||||
|
"internalFlags": 8,
|
||||||
|
"metric": 0,
|
||||||
|
"selected": true,
|
||||||
|
"installed": true,
|
||||||
|
"prefix": "10.254.254.1/32",
|
||||||
|
"internalStatus": 34,
|
||||||
|
"nexthops": [
|
||||||
|
{
|
||||||
|
"interfaceName": "r2-eth0",
|
||||||
|
"interfaceIndex": 2,
|
||||||
|
"fib": true,
|
||||||
|
"flags": 3,
|
||||||
|
"active": true,
|
||||||
|
"afi": "ipv6"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
63
tests/topotests/bfd-topo2/r2/ipv6_routes.json
Normal file
63
tests/topotests/bfd-topo2/r2/ipv6_routes.json
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
{
|
||||||
|
"2001:db8:4::/64": [
|
||||||
|
{
|
||||||
|
"distance": 110,
|
||||||
|
"protocol": "ospf6",
|
||||||
|
"internalFlags": 0,
|
||||||
|
"metric": 10,
|
||||||
|
"internalStatus": 2,
|
||||||
|
"prefix": "2001:db8:4::/64",
|
||||||
|
"nexthops": [
|
||||||
|
{
|
||||||
|
"active": true,
|
||||||
|
"directlyConnected": true,
|
||||||
|
"flags": 1,
|
||||||
|
"interfaceIndex": 4,
|
||||||
|
"interfaceName": "r2-eth2"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"distance": 0,
|
||||||
|
"protocol": "connected",
|
||||||
|
"internalFlags": 8,
|
||||||
|
"metric": 0,
|
||||||
|
"selected": true,
|
||||||
|
"installed": true,
|
||||||
|
"prefix": "2001:db8:4::/64",
|
||||||
|
"internalStatus": 32,
|
||||||
|
"nexthops": [
|
||||||
|
{
|
||||||
|
"directlyConnected": true,
|
||||||
|
"interfaceName": "r2-eth2",
|
||||||
|
"interfaceIndex": 4,
|
||||||
|
"fib": true,
|
||||||
|
"flags": 3,
|
||||||
|
"active": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"2001:db8:1::/64": [
|
||||||
|
{
|
||||||
|
"distance": 0,
|
||||||
|
"protocol": "connected",
|
||||||
|
"internalFlags": 8,
|
||||||
|
"metric": 0,
|
||||||
|
"selected": true,
|
||||||
|
"installed": true,
|
||||||
|
"prefix": "2001:db8:1::/64",
|
||||||
|
"internalStatus": 32,
|
||||||
|
"nexthops": [
|
||||||
|
{
|
||||||
|
"directlyConnected": true,
|
||||||
|
"interfaceName": "r2-eth0",
|
||||||
|
"interfaceIndex": 2,
|
||||||
|
"fib": true,
|
||||||
|
"flags": 3,
|
||||||
|
"active": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
9
tests/topotests/bfd-topo2/r2/ospf6d.conf
Normal file
9
tests/topotests/bfd-topo2/r2/ospf6d.conf
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
interface r2-eth2
|
||||||
|
ipv6 ospf6 bfd
|
||||||
|
!
|
||||||
|
router ospf6
|
||||||
|
ospf6 router-id 10.254.254.2
|
||||||
|
redistribute connected
|
||||||
|
redistribute bgp
|
||||||
|
interface r2-eth2 area 0.0.0.1
|
||||||
|
!
|
9
tests/topotests/bfd-topo2/r2/ospfd.conf
Normal file
9
tests/topotests/bfd-topo2/r2/ospfd.conf
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
interface r2-eth1
|
||||||
|
ip ospf area 0.0.0.1
|
||||||
|
ip ospf bfd
|
||||||
|
!
|
||||||
|
router ospf
|
||||||
|
ospf router-id 10.254.254.2
|
||||||
|
redistribute connected
|
||||||
|
redistribute bgp
|
||||||
|
!
|
42
tests/topotests/bfd-topo2/r2/peers.json
Normal file
42
tests/topotests/bfd-topo2/r2/peers.json
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"status": "up",
|
||||||
|
"transmit-interval": 300,
|
||||||
|
"remote-receive-interval": 300,
|
||||||
|
"echo-interval": 0,
|
||||||
|
"diagnostic": "ok",
|
||||||
|
"multihop": false,
|
||||||
|
"interface": "r2-eth0",
|
||||||
|
"remote-transmit-interval": 300,
|
||||||
|
"receive-interval": 300,
|
||||||
|
"remote-echo-interval": 50,
|
||||||
|
"remote-diagnostic": "ok"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"status": "up",
|
||||||
|
"transmit-interval": 300,
|
||||||
|
"remote-receive-interval": 300,
|
||||||
|
"echo-interval": 0,
|
||||||
|
"diagnostic": "ok",
|
||||||
|
"multihop": false,
|
||||||
|
"interface": "r2-eth2",
|
||||||
|
"remote-transmit-interval": 300,
|
||||||
|
"receive-interval": 300,
|
||||||
|
"remote-echo-interval": 50,
|
||||||
|
"remote-diagnostic": "ok"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"status": "up",
|
||||||
|
"transmit-interval": 300,
|
||||||
|
"remote-receive-interval": 300,
|
||||||
|
"echo-interval": 0,
|
||||||
|
"diagnostic": "ok",
|
||||||
|
"multihop": false,
|
||||||
|
"interface": "r2-eth1",
|
||||||
|
"remote-transmit-interval": 300,
|
||||||
|
"receive-interval": 300,
|
||||||
|
"remote-echo-interval": 50,
|
||||||
|
"remote-diagnostic": "ok",
|
||||||
|
"peer": "10.0.3.1"
|
||||||
|
}
|
||||||
|
]
|
15
tests/topotests/bfd-topo2/r2/zebra.conf
Normal file
15
tests/topotests/bfd-topo2/r2/zebra.conf
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
ip forwarding
|
||||||
|
ipv6 forwarding
|
||||||
|
!
|
||||||
|
interface lo
|
||||||
|
ip address 10.254.254.2/32
|
||||||
|
!
|
||||||
|
interface r2-eth0
|
||||||
|
ipv6 address 2001:db8:1::2/64
|
||||||
|
!
|
||||||
|
interface r2-eth1
|
||||||
|
ip address 10.0.3.2/24
|
||||||
|
!
|
||||||
|
interface r2-eth2
|
||||||
|
ipv6 address 2001:db8:4::2/64
|
||||||
|
!
|
109
tests/topotests/bfd-topo2/r3/ipv4_routes.json
Normal file
109
tests/topotests/bfd-topo2/r3/ipv4_routes.json
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
{
|
||||||
|
"10.0.3.0/24": [
|
||||||
|
{
|
||||||
|
"distance": 110,
|
||||||
|
"protocol": "ospf",
|
||||||
|
"internalFlags": 0,
|
||||||
|
"metric": 10,
|
||||||
|
"internalStatus": 0,
|
||||||
|
"prefix": "10.0.3.0/24",
|
||||||
|
"nexthops": [
|
||||||
|
{
|
||||||
|
"active": true,
|
||||||
|
"directlyConnected": true,
|
||||||
|
"flags": 1,
|
||||||
|
"interfaceIndex": 2,
|
||||||
|
"interfaceName": "r3-eth0"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"distance": 0,
|
||||||
|
"protocol": "connected",
|
||||||
|
"internalFlags": 8,
|
||||||
|
"metric": 0,
|
||||||
|
"selected": true,
|
||||||
|
"installed": true,
|
||||||
|
"prefix": "10.0.3.0/24",
|
||||||
|
"internalStatus": 32,
|
||||||
|
"nexthops": [
|
||||||
|
{
|
||||||
|
"directlyConnected": true,
|
||||||
|
"interfaceName": "r3-eth0",
|
||||||
|
"interfaceIndex": 2,
|
||||||
|
"fib": true,
|
||||||
|
"flags": 3,
|
||||||
|
"active": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"10.254.254.3/32": [
|
||||||
|
{
|
||||||
|
"distance": 0,
|
||||||
|
"protocol": "connected",
|
||||||
|
"internalFlags": 8,
|
||||||
|
"metric": 0,
|
||||||
|
"selected": true,
|
||||||
|
"installed": true,
|
||||||
|
"prefix": "10.254.254.3/32",
|
||||||
|
"internalStatus": 32,
|
||||||
|
"nexthops": [
|
||||||
|
{
|
||||||
|
"directlyConnected": true,
|
||||||
|
"interfaceName": "lo",
|
||||||
|
"interfaceIndex": 1,
|
||||||
|
"fib": true,
|
||||||
|
"flags": 3,
|
||||||
|
"active": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"10.254.254.2/32": [
|
||||||
|
{
|
||||||
|
"distance": 110,
|
||||||
|
"protocol": "ospf",
|
||||||
|
"internalFlags": 8,
|
||||||
|
"metric": 20,
|
||||||
|
"selected": true,
|
||||||
|
"installed": true,
|
||||||
|
"prefix": "10.254.254.2/32",
|
||||||
|
"internalStatus": 34,
|
||||||
|
"nexthops": [
|
||||||
|
{
|
||||||
|
"interfaceName": "r3-eth0",
|
||||||
|
"ip": "10.0.3.2",
|
||||||
|
"interfaceIndex": 2,
|
||||||
|
"fib": true,
|
||||||
|
"flags": 3,
|
||||||
|
"active": true,
|
||||||
|
"afi": "ipv4"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"10.254.254.1/32": [
|
||||||
|
{
|
||||||
|
"distance": 110,
|
||||||
|
"protocol": "ospf",
|
||||||
|
"internalFlags": 8,
|
||||||
|
"metric": 20,
|
||||||
|
"selected": true,
|
||||||
|
"installed": true,
|
||||||
|
"prefix": "10.254.254.1/32",
|
||||||
|
"internalStatus": 34,
|
||||||
|
"nexthops": [
|
||||||
|
{
|
||||||
|
"interfaceName": "r3-eth0",
|
||||||
|
"ip": "10.0.3.2",
|
||||||
|
"interfaceIndex": 2,
|
||||||
|
"fib": true,
|
||||||
|
"flags": 3,
|
||||||
|
"active": true,
|
||||||
|
"afi": "ipv4"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
2
tests/topotests/bfd-topo2/r3/ipv6_routes.json
Normal file
2
tests/topotests/bfd-topo2/r3/ipv6_routes.json
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
{
|
||||||
|
}
|
8
tests/topotests/bfd-topo2/r3/ospfd.conf
Normal file
8
tests/topotests/bfd-topo2/r3/ospfd.conf
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
interface r3-eth0
|
||||||
|
ip ospf area 0.0.0.1
|
||||||
|
ip ospf bfd
|
||||||
|
!
|
||||||
|
router ospf
|
||||||
|
ospf router-id 10.254.254.3
|
||||||
|
redistribute connected
|
||||||
|
!
|
16
tests/topotests/bfd-topo2/r3/peers.json
Normal file
16
tests/topotests/bfd-topo2/r3/peers.json
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"status": "up",
|
||||||
|
"transmit-interval": 300,
|
||||||
|
"remote-receive-interval": 300,
|
||||||
|
"echo-interval": 0,
|
||||||
|
"diagnostic": "ok",
|
||||||
|
"multihop": false,
|
||||||
|
"interface": "r3-eth0",
|
||||||
|
"remote-transmit-interval": 300,
|
||||||
|
"receive-interval": 300,
|
||||||
|
"remote-echo-interval": 50,
|
||||||
|
"remote-diagnostic": "ok",
|
||||||
|
"peer": "10.0.3.2"
|
||||||
|
}
|
||||||
|
]
|
6
tests/topotests/bfd-topo2/r3/zebra.conf
Normal file
6
tests/topotests/bfd-topo2/r3/zebra.conf
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
interface lo
|
||||||
|
ip address 10.254.254.3/32
|
||||||
|
!
|
||||||
|
interface r3-eth0
|
||||||
|
ip address 10.0.3.1/24
|
||||||
|
!
|
5
tests/topotests/bfd-topo2/r4/bfdd.conf
Normal file
5
tests/topotests/bfd-topo2/r4/bfdd.conf
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
bfd
|
||||||
|
peer 2001:db8:1::1 multihop local-address 2001:db8:4::1
|
||||||
|
no shutdown
|
||||||
|
!
|
||||||
|
!
|
24
tests/topotests/bfd-topo2/r4/ipv4_routes.json
Normal file
24
tests/topotests/bfd-topo2/r4/ipv4_routes.json
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"10.254.254.4/32": [
|
||||||
|
{
|
||||||
|
"distance": 0,
|
||||||
|
"protocol": "connected",
|
||||||
|
"internalFlags": 8,
|
||||||
|
"metric": 0,
|
||||||
|
"selected": true,
|
||||||
|
"installed": true,
|
||||||
|
"prefix": "10.254.254.4/32",
|
||||||
|
"internalStatus": 32,
|
||||||
|
"nexthops": [
|
||||||
|
{
|
||||||
|
"directlyConnected": true,
|
||||||
|
"interfaceName": "lo",
|
||||||
|
"interfaceIndex": 1,
|
||||||
|
"fib": true,
|
||||||
|
"flags": 3,
|
||||||
|
"active": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
63
tests/topotests/bfd-topo2/r4/ipv6_routes.json
Normal file
63
tests/topotests/bfd-topo2/r4/ipv6_routes.json
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
{
|
||||||
|
"2001:db8:4::/64": [
|
||||||
|
{
|
||||||
|
"distance": 110,
|
||||||
|
"protocol": "ospf6",
|
||||||
|
"internalFlags": 0,
|
||||||
|
"metric": 10,
|
||||||
|
"internalStatus": 2,
|
||||||
|
"prefix": "2001:db8:4::/64",
|
||||||
|
"nexthops": [
|
||||||
|
{
|
||||||
|
"active": true,
|
||||||
|
"directlyConnected": true,
|
||||||
|
"flags": 1,
|
||||||
|
"interfaceIndex": 2,
|
||||||
|
"interfaceName": "r4-eth0"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"distance": 0,
|
||||||
|
"protocol": "connected",
|
||||||
|
"internalFlags": 8,
|
||||||
|
"metric": 0,
|
||||||
|
"selected": true,
|
||||||
|
"installed": true,
|
||||||
|
"prefix": "2001:db8:4::/64",
|
||||||
|
"internalStatus": 32,
|
||||||
|
"nexthops": [
|
||||||
|
{
|
||||||
|
"directlyConnected": true,
|
||||||
|
"interfaceName": "r4-eth0",
|
||||||
|
"interfaceIndex": 2,
|
||||||
|
"fib": true,
|
||||||
|
"flags": 3,
|
||||||
|
"active": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"2001:db8:1::/64": [
|
||||||
|
{
|
||||||
|
"distance": 110,
|
||||||
|
"protocol": "ospf6",
|
||||||
|
"internalFlags": 8,
|
||||||
|
"metric": 10,
|
||||||
|
"selected": true,
|
||||||
|
"installed": true,
|
||||||
|
"prefix": "2001:db8:1::/64",
|
||||||
|
"internalStatus": 34,
|
||||||
|
"nexthops": [
|
||||||
|
{
|
||||||
|
"interfaceName": "r4-eth0",
|
||||||
|
"interfaceIndex": 2,
|
||||||
|
"fib": true,
|
||||||
|
"flags": 3,
|
||||||
|
"active": true,
|
||||||
|
"afi": "ipv6"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
8
tests/topotests/bfd-topo2/r4/ospf6d.conf
Normal file
8
tests/topotests/bfd-topo2/r4/ospf6d.conf
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
interface r4-eth0
|
||||||
|
ipv6 ospf6 bfd
|
||||||
|
!
|
||||||
|
router ospf6
|
||||||
|
ospf6 router-id 10.254.254.4
|
||||||
|
redistribute connected
|
||||||
|
interface r4-eth0 area 0.0.0.1
|
||||||
|
!
|
29
tests/topotests/bfd-topo2/r4/peers.json
Normal file
29
tests/topotests/bfd-topo2/r4/peers.json
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"multihop":true,
|
||||||
|
"peer":"2001:db8:1::1",
|
||||||
|
"local":"2001:db8:4::1",
|
||||||
|
"status":"up",
|
||||||
|
"diagnostic":"ok",
|
||||||
|
"remote-diagnostic":"ok",
|
||||||
|
"receive-interval":300,
|
||||||
|
"transmit-interval":300,
|
||||||
|
"echo-interval":0,
|
||||||
|
"remote-receive-interval":300,
|
||||||
|
"remote-transmit-interval":300,
|
||||||
|
"remote-echo-interval":50
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"multihop":false,
|
||||||
|
"interface":"r4-eth0",
|
||||||
|
"status":"up",
|
||||||
|
"diagnostic":"ok",
|
||||||
|
"remote-diagnostic":"ok",
|
||||||
|
"receive-interval":300,
|
||||||
|
"transmit-interval":300,
|
||||||
|
"echo-interval":0,
|
||||||
|
"remote-receive-interval":300,
|
||||||
|
"remote-transmit-interval":300,
|
||||||
|
"remote-echo-interval":50
|
||||||
|
}
|
||||||
|
]
|
6
tests/topotests/bfd-topo2/r4/zebra.conf
Normal file
6
tests/topotests/bfd-topo2/r4/zebra.conf
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
interface lo
|
||||||
|
ip address 10.254.254.4/32
|
||||||
|
!
|
||||||
|
interface r4-eth0
|
||||||
|
ipv6 address 2001:db8:4::1/64
|
||||||
|
!
|
73
tests/topotests/bfd-topo2/test_bfd_topo2.dot
Normal file
73
tests/topotests/bfd-topo2/test_bfd_topo2.dot
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
## Color coding:
|
||||||
|
#########################
|
||||||
|
## Main FRR: #f08080 red
|
||||||
|
## Switches: #d0e0d0 gray
|
||||||
|
## RIP: #19e3d9 Cyan
|
||||||
|
## RIPng: #fcb314 dark yellow
|
||||||
|
## OSPFv2: #32b835 Green
|
||||||
|
## OSPFv3: #19e3d9 Cyan
|
||||||
|
## ISIS IPv4 #fcb314 dark yellow
|
||||||
|
## ISIS IPv6 #9a81ec purple
|
||||||
|
## BGP IPv4 #eee3d3 beige
|
||||||
|
## BGP IPv6 #fdff00 yellow
|
||||||
|
##### Colors (see http://www.color-hex.com/)
|
||||||
|
|
||||||
|
graph template {
|
||||||
|
label="bfd-topo2";
|
||||||
|
|
||||||
|
# Routers
|
||||||
|
r1 [
|
||||||
|
shape=doubleoctagon,
|
||||||
|
label="r1",
|
||||||
|
fillcolor="#f08080",
|
||||||
|
style=filled,
|
||||||
|
];
|
||||||
|
r2 [
|
||||||
|
shape=doubleoctagon
|
||||||
|
label="r2",
|
||||||
|
fillcolor="#f08080",
|
||||||
|
style=filled,
|
||||||
|
];
|
||||||
|
r3 [
|
||||||
|
shape=doubleoctagon
|
||||||
|
label="r3",
|
||||||
|
fillcolor="#f08080",
|
||||||
|
style=filled,
|
||||||
|
];
|
||||||
|
r4 [
|
||||||
|
shape=doubleoctagon
|
||||||
|
label="r4",
|
||||||
|
fillcolor="#f08080",
|
||||||
|
style=filled,
|
||||||
|
];
|
||||||
|
|
||||||
|
# Switches
|
||||||
|
sw1 [
|
||||||
|
shape=oval,
|
||||||
|
label="sw1\n2001:db8:1::/64",
|
||||||
|
fillcolor="#d0e0d0",
|
||||||
|
style=filled,
|
||||||
|
];
|
||||||
|
sw2 [
|
||||||
|
shape=oval,
|
||||||
|
label="sw2\n10.0.3.0/24",
|
||||||
|
fillcolor="#d0e0d0",
|
||||||
|
style=filled,
|
||||||
|
];
|
||||||
|
sw3 [
|
||||||
|
shape=oval,
|
||||||
|
label="sw3\n2001:db8:4::/64",
|
||||||
|
fillcolor="#d0e0d0",
|
||||||
|
style=filled,
|
||||||
|
];
|
||||||
|
|
||||||
|
# Connections
|
||||||
|
r1 -- sw1 [label="eth0"];
|
||||||
|
r2 -- sw1 [label="eth0"];
|
||||||
|
|
||||||
|
r2 -- sw2 [label="eth1"];
|
||||||
|
r3 -- sw2 [label="eth0"];
|
||||||
|
|
||||||
|
r2 -- sw3 [label="eth2"];
|
||||||
|
r4 -- sw3 [label="eth0"];
|
||||||
|
}
|
BIN
tests/topotests/bfd-topo2/test_bfd_topo2.jpg
Normal file
BIN
tests/topotests/bfd-topo2/test_bfd_topo2.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 24 KiB |
191
tests/topotests/bfd-topo2/test_bfd_topo2.py
Normal file
191
tests/topotests/bfd-topo2/test_bfd_topo2.py
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
#
|
||||||
|
# test_bfd_topo2.py
|
||||||
|
# Part of NetDEF Topology Tests
|
||||||
|
#
|
||||||
|
# Copyright (c) 2019 by
|
||||||
|
# Network Device Education Foundation, Inc. ("NetDEF")
|
||||||
|
#
|
||||||
|
# Permission to use, copy, modify, and/or distribute this software
|
||||||
|
# for any purpose with or without fee is hereby granted, provided
|
||||||
|
# that the above copyright notice and this permission notice appear
|
||||||
|
# in all copies.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
|
||||||
|
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
|
||||||
|
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
|
||||||
|
# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
|
||||||
|
# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
|
||||||
|
# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
|
||||||
|
# OF THIS SOFTWARE.
|
||||||
|
#
|
||||||
|
|
||||||
|
"""
|
||||||
|
test_bfd_topo2.py: Test the FRR/Quagga BFD daemon with multihop and BGP
|
||||||
|
unnumbered.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import json
|
||||||
|
from functools import partial
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
# Save the Current Working Directory to find configuration files.
|
||||||
|
CWD = os.path.dirname(os.path.realpath(__file__))
|
||||||
|
sys.path.append(os.path.join(CWD, '../'))
|
||||||
|
|
||||||
|
# pylint: disable=C0413
|
||||||
|
# Import topogen and topotest helpers
|
||||||
|
from lib import topotest
|
||||||
|
from lib.topogen import Topogen, TopoRouter, get_topogen
|
||||||
|
from lib.topolog import logger
|
||||||
|
|
||||||
|
# Required to instantiate the topology builder class.
|
||||||
|
from mininet.topo import Topo
|
||||||
|
|
||||||
|
|
||||||
|
class BFDTopo(Topo):
|
||||||
|
"Test topology builder"
|
||||||
|
def build(self, *_args, **_opts):
|
||||||
|
"Build function"
|
||||||
|
tgen = get_topogen(self)
|
||||||
|
|
||||||
|
# Create 4 routers.
|
||||||
|
for routern in range(1, 5):
|
||||||
|
tgen.add_router('r{}'.format(routern))
|
||||||
|
|
||||||
|
switch = tgen.add_switch('s1')
|
||||||
|
switch.add_link(tgen.gears['r1'])
|
||||||
|
switch.add_link(tgen.gears['r2'])
|
||||||
|
|
||||||
|
switch = tgen.add_switch('s2')
|
||||||
|
switch.add_link(tgen.gears['r2'])
|
||||||
|
switch.add_link(tgen.gears['r3'])
|
||||||
|
|
||||||
|
switch = tgen.add_switch('s3')
|
||||||
|
switch.add_link(tgen.gears['r2'])
|
||||||
|
switch.add_link(tgen.gears['r4'])
|
||||||
|
|
||||||
|
|
||||||
|
def setup_module(mod):
|
||||||
|
"Sets up the pytest environment"
|
||||||
|
tgen = Topogen(BFDTopo, mod.__name__)
|
||||||
|
tgen.start_topology()
|
||||||
|
|
||||||
|
router_list = tgen.routers()
|
||||||
|
for rname, router in router_list.iteritems():
|
||||||
|
router.load_config(
|
||||||
|
TopoRouter.RD_ZEBRA,
|
||||||
|
os.path.join(CWD, '{}/zebra.conf'.format(rname))
|
||||||
|
)
|
||||||
|
router.load_config(
|
||||||
|
TopoRouter.RD_BFD,
|
||||||
|
os.path.join(CWD, '{}/bfdd.conf'.format(rname))
|
||||||
|
)
|
||||||
|
router.load_config(
|
||||||
|
TopoRouter.RD_BGP,
|
||||||
|
os.path.join(CWD, '{}/bgpd.conf'.format(rname))
|
||||||
|
)
|
||||||
|
router.load_config(
|
||||||
|
TopoRouter.RD_OSPF,
|
||||||
|
os.path.join(CWD, '{}/ospfd.conf'.format(rname))
|
||||||
|
)
|
||||||
|
router.load_config(
|
||||||
|
TopoRouter.RD_OSPF6,
|
||||||
|
os.path.join(CWD, '{}/ospf6d.conf'.format(rname))
|
||||||
|
)
|
||||||
|
|
||||||
|
# Initialize all routers.
|
||||||
|
tgen.start_router()
|
||||||
|
|
||||||
|
# Verify that we are using the proper version and that the BFD
|
||||||
|
# daemon exists.
|
||||||
|
for router in router_list.values():
|
||||||
|
# Check for Version
|
||||||
|
if router.has_version('<', '5.1'):
|
||||||
|
tgen.set_error('Unsupported FRR version')
|
||||||
|
break
|
||||||
|
|
||||||
|
|
||||||
|
def teardown_module(_mod):
|
||||||
|
"Teardown the pytest environment"
|
||||||
|
tgen = get_topogen()
|
||||||
|
tgen.stop_topology()
|
||||||
|
|
||||||
|
|
||||||
|
def test_protocols_convergence():
|
||||||
|
"""
|
||||||
|
Assert that all protocols have converged before checking for the BFD
|
||||||
|
statuses as they depend on it.
|
||||||
|
"""
|
||||||
|
tgen = get_topogen()
|
||||||
|
if tgen.routers_have_failure():
|
||||||
|
pytest.skip(tgen.errors)
|
||||||
|
|
||||||
|
# Check IPv4 routing tables.
|
||||||
|
logger.info("Checking IPv4 routes for convergence")
|
||||||
|
for router in tgen.routers().values():
|
||||||
|
json_file = '{}/{}/ipv4_routes.json'.format(CWD, router.name)
|
||||||
|
if not os.path.isfile(json_file):
|
||||||
|
logger.info('skipping file {}'.format(json_file))
|
||||||
|
continue
|
||||||
|
|
||||||
|
expected = json.loads(open(json_file).read())
|
||||||
|
test_func = partial(topotest.router_json_cmp,
|
||||||
|
router, 'show ip route json', expected)
|
||||||
|
_, result = topotest.run_and_expect(test_func, None, count=160,
|
||||||
|
wait=0.5)
|
||||||
|
assertmsg = '"{}" JSON output mismatches'.format(router.name)
|
||||||
|
assert result is None, assertmsg
|
||||||
|
|
||||||
|
# Check IPv6 routing tables.
|
||||||
|
logger.info("Checking IPv6 routes for convergence")
|
||||||
|
for router in tgen.routers().values():
|
||||||
|
json_file = '{}/{}/ipv6_routes.json'.format(CWD, router.name)
|
||||||
|
if not os.path.isfile(json_file):
|
||||||
|
logger.info('skipping file {}'.format(json_file))
|
||||||
|
continue
|
||||||
|
|
||||||
|
expected = json.loads(open(json_file).read())
|
||||||
|
test_func = partial(topotest.router_json_cmp,
|
||||||
|
router, 'show ipv6 route json', expected)
|
||||||
|
_, result = topotest.run_and_expect(test_func, None, count=160,
|
||||||
|
wait=0.5)
|
||||||
|
assertmsg = '"{}" JSON output mismatches'.format(router.name)
|
||||||
|
assert result is None, assertmsg
|
||||||
|
|
||||||
|
|
||||||
|
def test_bfd_connection():
|
||||||
|
"Assert that the BFD peers can find themselves."
|
||||||
|
tgen = get_topogen()
|
||||||
|
if tgen.routers_have_failure():
|
||||||
|
pytest.skip(tgen.errors)
|
||||||
|
|
||||||
|
logger.info('waiting for bfd peers to go up')
|
||||||
|
|
||||||
|
for router in tgen.routers().values():
|
||||||
|
json_file = '{}/{}/peers.json'.format(CWD, router.name)
|
||||||
|
expected = json.loads(open(json_file).read())
|
||||||
|
|
||||||
|
test_func = partial(topotest.router_json_cmp,
|
||||||
|
router, 'show bfd peers json', expected)
|
||||||
|
_, result = topotest.run_and_expect(test_func, None, count=8, wait=0.5)
|
||||||
|
assertmsg = '"{}" JSON output mismatches'.format(router.name)
|
||||||
|
assert result is None, assertmsg
|
||||||
|
|
||||||
|
|
||||||
|
def test_memory_leak():
|
||||||
|
"Run the memory leak test and report results."
|
||||||
|
tgen = get_topogen()
|
||||||
|
if not tgen.is_memleak_enabled():
|
||||||
|
pytest.skip('Memory leak test/report is disabled')
|
||||||
|
|
||||||
|
tgen.report_memory_leaks()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
args = ["-s"] + sys.argv[1:]
|
||||||
|
sys.exit(pytest.main(args))
|
Loading…
Reference in New Issue
Block a user