diff --git a/bfdd/bfd.c b/bfdd/bfd.c index be6f2caa44..c8adf82a83 100644 --- a/bfdd/bfd.c +++ b/bfdd/bfd.c @@ -36,7 +36,9 @@ DEFINE_QOBJ_TYPE(bfd_session); /* * 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 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_up_handler(struct bfd_session *bs, int nstate); +/* Zeroed array with the size of an IPv6 address. */ +struct in6_addr zero_addr; /* * 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; - 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; - - break; - } - - if (strcmp(bs->vrfname, bpc->bpc_vrfname)) - continue; - if (memcmp(&bs->mhop.peer, &bpc->bpc_peer, - sizeof(bs->mhop.peer))) - continue; - if (memcmp(&bs->mhop.local, &bpc->bpc_local, - sizeof(bs->mhop.local))) - continue; + memset(key, 0, sizeof(*key)); + 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; } - if (bso == NULL) - bs = NULL; - return bs; + key->mhop = mhop; + if (ifname && ifname[0]) + strlcpy(key->ifname, ifname, sizeof(key->ifname)); + if (vrfname && vrfname[0]) + strlcpy(key->vrfname, vrfname, sizeof(key->vrfname)); } struct bfd_session *bs_peer_find(struct bfd_peer_cfg *bpc) { struct bfd_session *bs; struct peer_label *pl; - struct interface *ifp; - struct vrf *vrf; - struct bfd_mhop_key mhop; - struct bfd_shop_key shop; + struct bfd_key key; /* Try to find label first. */ 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. */ - if (bpc->bpc_mhop) { - memset(&mhop, 0, sizeof(mhop)); - 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; + gen_bfd_key(&key, &bpc->bpc_peer, &bpc->bpc_local, bpc->bpc_mhop, + bpc->bpc_localif, bpc->bpc_vrfname); - mhop.vrfid = vrf->vrf_id; - } - - 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); + return bfd_key_lookup(key); } /* @@ -165,7 +120,6 @@ struct bfd_session *bs_peer_find(struct bfd_peer_cfg *bpc) */ int bfd_session_enable(struct bfd_session *bs) { - struct sockaddr_in6 *sin6; struct interface *ifp = NULL; struct vrf *vrf = NULL; 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 * the session but delay its start. */ - if (bs->ifname[0] != 0) { - ifp = if_lookup_by_name_all_vrf(bs->ifname); + if (bs->key.ifname[0]) { + ifp = if_lookup_by_name_all_vrf(bs->key.ifname); if (ifp == NULL) { log_error( "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); if (vrf == NULL) { - log_error("session-enable: specified VRF doesn't exists."); + log_error( + "session-enable: specified VRF doesn't exists."); return 0; } } - if (bs->vrfname[0] != 0) { - vrf = vrf_lookup_by_name(bs->vrfname); + if (bs->key.vrfname[0]) { + vrf = vrf_lookup_by_name(bs->key.vrfname); if (vrf == NULL) { - log_error("session-enable: specified VRF doesn't exists."); + log_error( + "session-enable: specified VRF doesn't exists."); return 0; } } @@ -202,26 +158,15 @@ int bfd_session_enable(struct bfd_session *bs) if (bs->vrf == NULL) bs->vrf = vrf_lookup_by_id(VRF_DEFAULT); - if (bs->ifname[0] != 0 && - BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH) == 0) + if (bs->key.ifname[0] + && BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH) == 0) bs->ifp = ifp; - /* Set the IPv6 scope id for link-local addresses. */ - if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_IPV6)) { - sin6 = &bs->mhop.peer.sa_sin6; - if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) - sin6->sin6_scope_id = bs->ifp != NULL - ? 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; + /* Sanity check: don't leak open sockets. */ + if (bs->sock != -1) { + zlog_debug("session-enable: previous socket open"); + close(bs->sock); + bs->sock = -1; } /* @@ -232,11 +177,11 @@ int bfd_session_enable(struct bfd_session *bs) if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_IPV6) == 0) { psock = bp_peer_socket(bs); if (psock == -1) - return -1; + return 0; } else { psock = bp_peer_socketv6(bs); if (psock == -1) - return -1; + return 0; } /* @@ -247,25 +192,6 @@ int bfd_session_enable(struct bfd_session *bs) bfd_recvtimer_update(bs); 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; } @@ -288,13 +214,6 @@ void bfd_session_disable(struct bfd_session *bs) bfd_echo_recvtimer_delete(bs); bfd_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) @@ -438,21 +357,20 @@ static struct bfd_session *bfd_find_disc(struct sockaddr_any *sa, if (bs == NULL) return NULL; - /* Remove unused fields. */ - switch (sa->sa_sin.sin_family) { + switch (bs->key.family) { case AF_INET: - sa->sa_sin.sin_port = 0; - if (memcmp(sa, &bs->shop.peer, sizeof(sa->sa_sin)) == 0) - return bs; + if (memcmp(&sa->sa_sin.sin_addr, &bs->key.peer, + sizeof(sa->sa_sin.sin_addr))) + return NULL; break; case AF_INET6: - sa->sa_sin6.sin6_port = 0; - if (memcmp(sa, &bs->shop.peer, sizeof(sa->sa_sin6)) == 0) - return bs; + if (memcmp(&sa->sa_sin6.sin6_addr, &bs->key.peer, + sizeof(sa->sa_sin6.sin6_addr))) + return NULL; break; } - return NULL; + return bs; } 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, bool is_mhop) { - struct bfd_session *l_bfd = NULL; - struct bfd_mhop_key mhop; - struct bfd_shop_key shop; + struct interface *ifp; + struct vrf *vrf; + struct bfd_key key; /* Find our session using the ID signaled by the remote end. */ if (cp->discrs.remote_discr) return bfd_find_disc(peer, ntohl(cp->discrs.remote_discr)); /* Search for session without using discriminator. */ - if (is_mhop) { - memset(&mhop, 0, sizeof(mhop)); - mhop.peer = *peer; - mhop.local = *local; - mhop.vrfid = vrfid; + ifp = if_lookup_by_index(ifindex, vrfid); + if (vrfid == VRF_DEFAULT) { + /* + * Don't use the default vrf, otherwise we won't find + * sessions that doesn't specify it. + */ + vrf = NULL; + } else + vrf = vrf_lookup_by_id(vrfid); - l_bfd = bfd_mhop_lookup(mhop); - } else { - memset(&shop, 0, sizeof(shop)); - shop.peer = *peer; - shop.ifindex = ifindex; - - l_bfd = bfd_shop_lookup(shop); - } + gen_bfd_key(&key, peer, local, is_mhop, ifp ? ifp->name : NULL, + vrf ? vrf->name : NULL); /* XXX maybe remoteDiscr should be checked for remoteHeard cases. */ - return l_bfd; + return bfd_key_lookup(key); } 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_key_delete(bs->key); + bfd_id_delete(bs->discrs.my_discr); + /* Remove observer if any. */ TAILQ_FOREACH(bso, &bglobal.bg_obslist, bso_entry) { 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. */ 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) - strlcpy(bfd->vrfname, bpc->bpc_vrfname, sizeof(bfd->vrfname)); - - /* Add observer if we have moving parts. */ - if (bfd->ifname[0] || bfd->vrfname[0]) - bs_observer_add(bfd); + strlcpy(bfd->key.vrfname, bpc->bpc_vrfname, + sizeof(bfd->key.vrfname)); /* Copy remaining data. */ if (bpc->bpc_ipv4 == false) BFD_SET_FLAG(bfd->flags, BFD_SESS_FLAG_IPV6); - if (bpc->bpc_mhop) { - BFD_SET_FLAG(bfd->flags, BFD_SESS_FLAG_MH); - bfd->mhop.peer = bpc->bpc_peer; - bfd->mhop.local = bpc->bpc_local; - } else { - bfd->shop.peer = bpc->bpc_peer; + bfd->key.family = (bpc->bpc_ipv4) ? AF_INET : AF_INET6; + switch (bfd->key.family) { + case AF_INET: + memcpy(&bfd->key.peer, &bpc->bpc_peer.sa_sin.sin_addr, + sizeof(bpc->bpc_peer.sa_sin.sin_addr)); + 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; - bfd->local_address = bpc->bpc_local; + if (bpc->bpc_mhop) + 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. */ if (bfd_session_enable(bfd) == -1) { @@ -774,6 +711,10 @@ struct bfd_session *ptm_bfd_sess_new(struct bfd_peer_cfg *bpc) 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. */ _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); } -const char *bs_to_string(struct bfd_session *bs) +const char *bs_to_string(const struct bfd_session *bs) { static char buf[256]; + char addr_buf[INET6_ADDRSTRLEN]; int pos; bool is_mhop = BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH); pos = snprintf(buf, sizeof(buf), "mhop:%s", is_mhop ? "yes" : "no"); - if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)) { - pos += snprintf(buf + pos, sizeof(buf) - pos, - " peer:%s local:%s", satostr(&bs->mhop.peer), - satostr(&bs->mhop.local)); - - if (bs->mhop.vrfid != VRF_DEFAULT) - snprintf(buf + pos, sizeof(buf) - pos, " vrf:%u", - bs->mhop.vrfid); - } else { - pos += snprintf(buf + pos, sizeof(buf) - pos, " peer:%s", - satostr(&bs->shop.peer)); - - if (bs->local_address.sa_sin.sin_family) - pos += snprintf(buf + pos, sizeof(buf) - pos, - " local:%s", - satostr(&bs->local_address)); - - if (bs->shop.ifindex) - snprintf(buf + pos, sizeof(buf) - pos, " ifindex:%u", - bs->shop.ifindex); - } - + pos += snprintf(buf + pos, sizeof(buf) - pos, " peer:%s", + inet_ntop(bs->key.family, &bs->key.peer, addr_buf, + sizeof(addr_buf))); + pos += snprintf(buf + pos, sizeof(buf) - pos, " local:%s", + inet_ntop(bs->key.family, &bs->key.local, addr_buf, + sizeof(addr_buf))); + if (bs->key.vrfname[0]) + pos += snprintf(buf + pos, sizeof(buf) - pos, " vrf:%s", + bs->key.vrfname); + if (bs->key.ifname[0]) + pos += snprintf(buf + pos, sizeof(buf) - pos, " ifname:%s", + bs->key.ifname); return buf; } @@ -1259,12 +1191,20 @@ int bs_observer_add(struct bfd_session *bs) bso->bso_bs = bs; bso->bso_isinterface = !BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH); if (bso->bso_isinterface) - strlcpy(bso->bso_entryname, bs->ifname, + strlcpy(bso->bso_entryname, bs->key.ifname, sizeof(bso->bso_entryname)); else - strlcpy(bso->bso_entryname, bs->vrfname, + strlcpy(bso->bso_entryname, bs->key.vrfname, 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); return 0; @@ -1276,21 +1216,59 @@ void bs_observer_del(struct bfd_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. */ static struct hash *bfd_id_hash; -static struct hash *bfd_shop_hash; -static struct hash *bfd_mhop_hash; +static struct hash *bfd_key_hash; static unsigned int bfd_id_hash_do(void *p); -static unsigned int bfd_shop_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 unsigned int bfd_key_hash_do(void *p); static void _bfd_free(struct hash_bucket *hb, 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. */ -static unsigned int bfd_shop_hash_do(void *p) +static unsigned int bfd_key_hash_do(void *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; - 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. @@ -1393,34 +1318,35 @@ struct bfd_session *bfd_id_lookup(uint32_t id) 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; - _shop_key(&bs, &shop); + bs.key = key; + bsp = hash_lookup(bfd_key_hash, &bs); - bsp = hash_lookup(bfd_shop_hash, &bs); - if (bsp == NULL && bs.shop.ifindex != 0) { - /* - * Since the local interface spec is optional, try - * searching the key without it as well. - */ - _shop_key2(&bs, &shop); - bsp = hash_lookup(bfd_shop_hash, &bs); + /* Handle cases where local-address is 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); + } + + /* Handle cases where ifname is optional. */ + 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; } -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. * @@ -1440,31 +1366,18 @@ struct bfd_session *bfd_id_delete(uint32_t id) 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; - _shop_key(&bs, &shop); - bsp = hash_release(bfd_shop_hash, &bs); - if (bsp == NULL && shop.ifindex != 0) { - /* - * Since the local interface spec is optional, try - * searching the key without it as well. - */ - _shop_key2(&bs, &shop); - bsp = hash_release(bfd_shop_hash, &bs); + bs.key = key; + bsp = hash_lookup(bfd_key_hash, &bs); + if (bsp == NULL && key.ifname[0]) { + memset(bs.key.ifname, 0, sizeof(bs.key.ifname)); + bsp = hash_lookup(bfd_key_hash, &bs); } - return 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); + return hash_release(bfd_key_hash, bsp); } /* Iteration functions. */ @@ -1473,14 +1386,9 @@ void bfd_id_iterate(hash_iter_func hif, void *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); -} - -void bfd_mhop_iterate(hash_iter_func hif, void *arg) -{ - hash_iterate(bfd_mhop_hash, hif, arg); + hash_iterate(bfd_key_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); } -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); -} - -bool bfd_mhop_insert(struct bfd_session *bs) -{ - return (hash_get(bfd_mhop_hash, bs, hash_alloc_intern) == bs); + return (hash_get(bfd_key_hash, bs, hash_alloc_intern) == bs); } void bfd_initialize(void) { bfd_id_hash = hash_create(bfd_id_hash_do, bfd_id_hash_cmp, - "BFD discriminator hash"); - bfd_shop_hash = hash_create(bfd_shop_hash_do, bfd_shop_hash_cmp, - "BFD single hop hash"); - bfd_mhop_hash = hash_create(bfd_mhop_hash_do, bfd_mhop_hash_cmp, - "BFD multihop hop hash"); + "BFD session discriminator hash"); + bfd_key_hash = hash_create(bfd_key_hash_do, bfd_key_hash_cmp, + "BFD session hash"); } static void _bfd_free(struct hash_bucket *hb, @@ -1532,11 +1433,9 @@ void bfd_shutdown(void) * assert() here to make sure it really happened. */ bfd_id_iterate(_bfd_free, NULL); - assert(bfd_shop_hash->count == 0); - assert(bfd_mhop_hash->count == 0); + assert(bfd_key_hash->count == 0); /* Now free the hashes themselves. */ hash_free(bfd_id_hash); - hash_free(bfd_shop_hash); - hash_free(bfd_mhop_hash); + hash_free(bfd_key_hash); } diff --git a/bfdd/bfd.h b/bfdd/bfd.h index 7451ca82a3..a69ff9a1a7 100644 --- a/bfdd/bfd.h +++ b/bfdd/bfd.h @@ -173,15 +173,13 @@ enum bfd_session_flags { #define BFD_CHECK_FLAG(field, flag) (field & flag) /* BFD session hash keys */ -struct bfd_shop_key { - struct sockaddr_any peer; - ifindex_t ifindex; -}; - -struct bfd_mhop_key { - struct sockaddr_any peer; - struct sockaddr_any local; - vrf_id_t vrfid; +struct bfd_key { + uint16_t family; + uint8_t mhop; + struct in6_addr peer; + struct in6_addr local; + char ifname[MAXNAMELEN]; + char vrfname[MAXNAMELEN]; }; struct bfd_session_stats { @@ -227,19 +225,14 @@ struct bfd_session { uint8_t polling; /* This and the localDiscr are the keys to state info */ + struct bfd_key key; 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_ip; struct interface *ifp; struct vrf *vrf; - char ifname[MAXNAMELEN]; - char vrfname[MAXNAMELEN]; + + int sock; /* BFD session flags */ enum bfd_session_flags flags; @@ -281,7 +274,11 @@ struct bfd_state_str_list { struct bfd_session_observer { struct bfd_session *bso_bs; bool bso_isinterface; - char bso_entryname[MAXNAMELEN]; + bool bso_isaddress; + union { + char bso_entryname[MAXNAMELEN]; + struct prefix bso_addr; + }; TAILQ_ENTRY(bfd_session_observer) bso_entry; }; @@ -531,38 +528,28 @@ const char *satostr(struct sockaddr_any *sa); const char *diag2str(uint8_t diag); int strtosa(const char *addr, struct sockaddr_any *sa); 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); 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 */ void bfd_initialize(void); void bfd_shutdown(void); struct bfd_session *bfd_id_lookup(uint32_t id); -struct bfd_session *bfd_shop_lookup(struct bfd_shop_key shop); -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_key_lookup(struct bfd_key key); struct bfd_session *bfd_id_delete(uint32_t id); -struct bfd_session *bfd_shop_delete(struct bfd_shop_key shop); -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); +struct bfd_session *bfd_key_delete(struct bfd_key key); bool bfd_id_insert(struct bfd_session *bs); -bool bfd_shop_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); +bool bfd_key_insert(struct bfd_session *bs); typedef void (*hash_iter_func)(struct hash_bucket *hb, void *arg); void bfd_id_iterate(hash_iter_func hif, void *arg); -void bfd_shop_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); +void bfd_key_iterate(hash_iter_func hif, void *arg); /* Export callback functions for `event.c`. */ 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_echo_xmt_cb(struct thread *t); +extern struct in6_addr zero_addr; + /* * bfdd_vty.c diff --git a/bfdd/bfd_packet.c b/bfdd/bfd_packet.c index 8601bd2e40..69d27ed698 100644 --- a/bfdd/bfd_packet.c +++ b/bfdd/bfd_packet.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)) { memset(&sin6, 0, sizeof(sin6)); 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 = (port) ? *port : (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 { memset(&sin, 0, sizeof(sin)); 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 = (port) ? *port : (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) { - struct sockaddr_any *sa; + struct sockaddr *sa; socklen_t salen; int sd; 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.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)) { 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); #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN sin6.sin6_len = sizeof(sin6); #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */ - sa = (struct sockaddr_any *)&sin6; + sa = (struct sockaddr *)&sin6; salen = sizeof(sin6); } else { 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); #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN sin.sin_len = sizeof(sin); #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */ - sa = (struct sockaddr_any *)&sin; + sa = (struct sockaddr *)&sin; salen = sizeof(sin); } - if (bp_udp_send(sd, BFD_TTL_VAL, (uint8_t *)&bep, sizeof(bep), - (struct sockaddr *)sa, salen) + if (bp_udp_send(sd, BFD_TTL_VAL, (uint8_t *)&bep, sizeof(bep), sa, + salen) == -1) return; @@ -602,8 +608,8 @@ int bfd_recv_cb(struct thread *t) bfd->mh_ttl, BFD_TTL_VAL); return 0; } - } else if (bfd->local_ip.sa_sin.sin_family == AF_UNSPEC) { - bfd->local_ip = local; + } else if (bfd->local_address.sa_sin.sin_family == AF_UNSPEC) { + bfd->local_address = local; } /* @@ -917,25 +923,26 @@ int bp_peer_socket(const struct bfd_session *bs) return -1; } - if (bs->shop.ifindex != IFINDEX_INTERNAL) { - if (bp_bind_dev(sd, bs->ifp->name) != 0) { + if (bs->key.ifname[0]) { + if (bp_bind_dev(sd, bs->key.ifname) != 0) { close(sd); return -1; } - } else if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH) && - bs->mhop.vrfid != VRF_DEFAULT) { - if (bp_bind_dev(sd, bs->vrf->name) != 0) { + } else if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH) + && bs->key.vrfname[0]) { + if (bp_bind_dev(sd, bs->key.vrfname) != 0) { close(sd); return -1; } } /* 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; #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN sin.sin_len = sizeof(sin); #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) 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 */ - sin6 = bs->local_ip.sa_sin6; + memset(&sin6, 0, sizeof(sin6)); sin6.sin6_family = AF_INET6; #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN sin6.sin6_len = sizeof(sin6); #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 (bp_bind_dev(sd, bs->ifp->name) != 0) { + if (bs->key.ifname[0]) { + if (bp_bind_dev(sd, bs->key.ifname) != 0) { close(sd); return -1; } - } else if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH) && - bs->mhop.vrfid != VRF_DEFAULT) { - if (bp_bind_dev(sd, bs->vrf->name) != 0) { + } else if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH) + && bs->key.vrfname[0]) { + if (bp_bind_dev(sd, bs->key.vrfname) != 0) { close(sd); return -1; } diff --git a/bfdd/bfdd_vty.c b/bfdd/bfdd_vty.c index c77cd08be8..c139492076 100644 --- a/bfdd/bfdd_vty.c +++ b/bfdd/bfdd_vty.c @@ -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 *vrfname); - /* * 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) { - if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)) { - vty_out(vty, "\tpeer %s", satostr(&bs->mhop.peer)); + char addr_buf[INET6_ADDRSTRLEN]; + + 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, " local-address %s", satostr(&bs->mhop.local)); - if (bs->vrfname[0]) - 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", - satostr(&bs->local_address)); - if (bs->ifname[0]) - vty_out(vty, " interface %s", bs->ifname); - vty_out(vty, "\n"); - } + + if (memcmp(&bs->key.local, &zero_addr, sizeof(bs->key.local))) + vty_out(vty, " local-address %s", + inet_ntop(bs->key.family, &bs->key.local, addr_buf, + sizeof(addr_buf))); + + 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"); if (bs->pl) 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) { 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_string_add(jo, "peer", satostr(&bs->mhop.peer)); - json_object_string_add(jo, "local", satostr(&bs->mhop.local)); - if (bs->vrfname[0]) - json_object_string_add(jo, "vrf", bs->vrfname); - } else { + else 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, "local", - satostr(&bs->local_address)); - if (bs->ifname[0]) - json_object_string_add(jo, "interface", bs->ifname); - } + + 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", + inet_ntop(bs->key.family, &bs->key.local, + addr_buf, sizeof(addr_buf))); + + 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) 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) { - if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)) { - vty_out(vty, " peer %s", satostr(&bs->mhop.peer)); + char addr_buf[INET6_ADDRSTRLEN]; + + 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, " local-address %s", satostr(&bs->mhop.local)); - if (bs->vrfname[0]) - 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", - satostr(&bs->local_address)); - if (bs->ifname[0]) - vty_out(vty, " interface %s", bs->ifname); - vty_out(vty, "\n"); - } + + if (memcmp(&bs->key.local, &zero_addr, sizeof(bs->key.local))) + vty_out(vty, " local-address %s", + inet_ntop(bs->key.family, &bs->key.local, addr_buf, + sizeof(addr_buf))); + + 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"); 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) 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) { - struct bfd_session_observer *bso; - 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; } diff --git a/bfdd/config.c b/bfdd/config.c index 17e155e41d..cd57ea9fe3 100644 --- a/bfdd/config.c +++ b/bfdd/config.c @@ -309,24 +309,7 @@ static int parse_peer_label_config(struct json_object *jo, log_debug("\tpeer-label: %s", sval); /* Translate the label into BFD address keys. */ - bpc->bpc_ipv4 = !BFD_CHECK_FLAG(pl->pl_bs->flags, BFD_SESS_FLAG_IPV6); - 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)); - } - } + bs_to_bpc(pl->pl_bs, bpc); 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) { + char addr_buf[INET6_ADDRSTRLEN]; + /* Add peer 'key' information. */ if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_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)) { json_object_boolean_true_add(jo, "multihop"); 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", - satostr(&bs->mhop.local)); - if (bs->vrfname[0]) - json_object_string_add(jo, "vrf-name", bs->vrfname); + inet_ntop(bs->key.family, &bs->key.local, + addr_buf, sizeof(addr_buf))); + if (bs->key.vrfname[0]) + json_object_string_add(jo, "vrf-name", bs->key.vrfname); } else { json_object_boolean_false_add(jo, "multihop"); json_object_string_add(jo, "peer-address", - satostr(&bs->shop.peer)); - if (bs->local_address.sa_sin.sin_family != AF_UNSPEC) - json_object_string_add(jo, "local-address", - satostr(&bs->local_address)); - if (bs->ifname[0]) + 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-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", - bs->ifname); + bs->key.ifname); } if (bs->pl) diff --git a/bfdd/ptm_adapter.c b/bfdd/ptm_adapter.c index 5610c352fb..b44d13f132 100644 --- a/bfdd/ptm_adapter.c +++ b/bfdd/ptm_adapter.c @@ -55,7 +55,7 @@ static struct zclient *zclient; /* * 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 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) #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: - stream_putc(msg, sa->sa_sin.sin_family); - stream_put_in_addr(msg, &sa->sa_sin.sin_addr); + stream_put(msg, addr, sizeof(struct in_addr)); stream_putc(msg, 32); break; case AF_INET6: - stream_putc(msg, sa->sa_sin6.sin6_family); - stream_put(msg, &sa->sa_sin6.sin6_addr, - sizeof(sa->sa_sin6.sin6_addr)); + stream_put(msg, addr, sizeof(struct in6_addr)); stream_putc(msg, 128); break; default: - return -1; + assert(0); + break; } 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) { struct stream *msg; - struct sockaddr_any sac; bs->stats.znotification++; @@ -195,10 +194,7 @@ int ptm_bfd_notify(struct bfd_session *bs) stream_putl(msg, IFINDEX_INTERNAL); /* BFD destination prefix information. */ - if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)) - _ptm_msg_address(msg, &bs->mhop.peer); - else - _ptm_msg_address(msg, &bs->shop.peer); + _ptm_msg_address(msg, bs->key.family, &bs->key.peer); /* BFD status */ switch (bs->ses_state) { @@ -218,34 +214,7 @@ int ptm_bfd_notify(struct bfd_session *bs) } /* BFD source prefix information. */ - if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)) { - _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); - } - } + _ptm_msg_address(msg, bs->key.family, &bs->key.local); /* Write packet size. */ 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, struct bfd_peer_cfg *bpc, struct ptm_client **pc) { - struct interface *ifp; uint32_t pid; uint8_t ttl __attribute__((unused)); size_t ifnamelen; @@ -385,31 +353,6 @@ static int _ptm_msg_read(struct stream *msg, int command, if (bpc->bpc_has_localif) { STREAM_GET(bpc->bpc_localif, msg, ifnamelen); 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. */ bs = bso->bso_bs; - if (strcmp(ifp->name, bs->ifname)) + if (strcmp(ifp->name, bs->key.ifname)) continue; /* Skip enabled sessions. */ if (bs->sock != -1) @@ -630,7 +573,7 @@ static void bfdd_sessions_disable_interface(struct interface *ifp) /* Interface name mismatch. */ bs = bso->bso_bs; - if (strcmp(ifp->name, bs->ifname)) + if (strcmp(ifp->name, bs->key.ifname)) continue; /* Skip disabled sessions. */ if (bs->sock == -1) @@ -691,6 +634,48 @@ static int bfdd_interface_vrf_update(int command __attribute__((__unused__)), 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) { 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. */ 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) diff --git a/tests/topotests/bfd-topo2/__init__.py b/tests/topotests/bfd-topo2/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/bfd-topo2/r1/bfdd.conf b/tests/topotests/bfd-topo2/r1/bfdd.conf new file mode 100644 index 0000000000..5c2571bdbd --- /dev/null +++ b/tests/topotests/bfd-topo2/r1/bfdd.conf @@ -0,0 +1,5 @@ +bfd + peer 2001:db8:4::1 multihop local-address 2001:db8:1::1 + no shutdown + ! +! diff --git a/tests/topotests/bfd-topo2/r1/bgpd.conf b/tests/topotests/bfd-topo2/r1/bgpd.conf new file mode 100644 index 0000000000..1623b4578b --- /dev/null +++ b/tests/topotests/bfd-topo2/r1/bgpd.conf @@ -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 +! diff --git a/tests/topotests/bfd-topo2/r1/ipv4_routes.json b/tests/topotests/bfd-topo2/r1/ipv4_routes.json new file mode 100644 index 0000000000..8a2ec25baa --- /dev/null +++ b/tests/topotests/bfd-topo2/r1/ipv4_routes.json @@ -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 + } + ] + } + ] +} diff --git a/tests/topotests/bfd-topo2/r1/ipv6_routes.json b/tests/topotests/bfd-topo2/r1/ipv6_routes.json new file mode 100644 index 0000000000..618853bd42 --- /dev/null +++ b/tests/topotests/bfd-topo2/r1/ipv6_routes.json @@ -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 + } + ] + } + ] +} diff --git a/tests/topotests/bfd-topo2/r1/peers.json b/tests/topotests/bfd-topo2/r1/peers.json new file mode 100644 index 0000000000..b14351cd81 --- /dev/null +++ b/tests/topotests/bfd-topo2/r1/peers.json @@ -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 + } +] diff --git a/tests/topotests/bfd-topo2/r1/zebra.conf b/tests/topotests/bfd-topo2/r1/zebra.conf new file mode 100644 index 0000000000..7fe5eb218f --- /dev/null +++ b/tests/topotests/bfd-topo2/r1/zebra.conf @@ -0,0 +1,6 @@ +interface lo + ip address 10.254.254.1/32 +! +interface r1-eth0 + ipv6 address 2001:db8:1::1/64 +! diff --git a/tests/topotests/bfd-topo2/r2/bgpd.conf b/tests/topotests/bfd-topo2/r2/bgpd.conf new file mode 100644 index 0000000000..bf42d21812 --- /dev/null +++ b/tests/topotests/bfd-topo2/r2/bgpd.conf @@ -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 +! diff --git a/tests/topotests/bfd-topo2/r2/ipv4_routes.json b/tests/topotests/bfd-topo2/r2/ipv4_routes.json new file mode 100644 index 0000000000..b9d8afb430 --- /dev/null +++ b/tests/topotests/bfd-topo2/r2/ipv4_routes.json @@ -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" + } + ] + } + ] +} diff --git a/tests/topotests/bfd-topo2/r2/ipv6_routes.json b/tests/topotests/bfd-topo2/r2/ipv6_routes.json new file mode 100644 index 0000000000..004e7588aa --- /dev/null +++ b/tests/topotests/bfd-topo2/r2/ipv6_routes.json @@ -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 + } + ] + } + ] +} diff --git a/tests/topotests/bfd-topo2/r2/ospf6d.conf b/tests/topotests/bfd-topo2/r2/ospf6d.conf new file mode 100644 index 0000000000..f1cdb50285 --- /dev/null +++ b/tests/topotests/bfd-topo2/r2/ospf6d.conf @@ -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 +! diff --git a/tests/topotests/bfd-topo2/r2/ospfd.conf b/tests/topotests/bfd-topo2/r2/ospfd.conf new file mode 100644 index 0000000000..8e0c45980d --- /dev/null +++ b/tests/topotests/bfd-topo2/r2/ospfd.conf @@ -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 +! diff --git a/tests/topotests/bfd-topo2/r2/peers.json b/tests/topotests/bfd-topo2/r2/peers.json new file mode 100644 index 0000000000..29075fcc80 --- /dev/null +++ b/tests/topotests/bfd-topo2/r2/peers.json @@ -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" + } +] diff --git a/tests/topotests/bfd-topo2/r2/zebra.conf b/tests/topotests/bfd-topo2/r2/zebra.conf new file mode 100644 index 0000000000..cccbf6574a --- /dev/null +++ b/tests/topotests/bfd-topo2/r2/zebra.conf @@ -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 +! diff --git a/tests/topotests/bfd-topo2/r3/ipv4_routes.json b/tests/topotests/bfd-topo2/r3/ipv4_routes.json new file mode 100644 index 0000000000..14dfc692fe --- /dev/null +++ b/tests/topotests/bfd-topo2/r3/ipv4_routes.json @@ -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" + } + ] + } + ] +} diff --git a/tests/topotests/bfd-topo2/r3/ipv6_routes.json b/tests/topotests/bfd-topo2/r3/ipv6_routes.json new file mode 100644 index 0000000000..2c63c08510 --- /dev/null +++ b/tests/topotests/bfd-topo2/r3/ipv6_routes.json @@ -0,0 +1,2 @@ +{ +} diff --git a/tests/topotests/bfd-topo2/r3/ospfd.conf b/tests/topotests/bfd-topo2/r3/ospfd.conf new file mode 100644 index 0000000000..cf2a1bdf76 --- /dev/null +++ b/tests/topotests/bfd-topo2/r3/ospfd.conf @@ -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 +! diff --git a/tests/topotests/bfd-topo2/r3/peers.json b/tests/topotests/bfd-topo2/r3/peers.json new file mode 100644 index 0000000000..6698bff201 --- /dev/null +++ b/tests/topotests/bfd-topo2/r3/peers.json @@ -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" + } +] diff --git a/tests/topotests/bfd-topo2/r3/zebra.conf b/tests/topotests/bfd-topo2/r3/zebra.conf new file mode 100644 index 0000000000..96fd08c729 --- /dev/null +++ b/tests/topotests/bfd-topo2/r3/zebra.conf @@ -0,0 +1,6 @@ +interface lo + ip address 10.254.254.3/32 +! +interface r3-eth0 + ip address 10.0.3.1/24 +! diff --git a/tests/topotests/bfd-topo2/r4/bfdd.conf b/tests/topotests/bfd-topo2/r4/bfdd.conf new file mode 100644 index 0000000000..fdb4412446 --- /dev/null +++ b/tests/topotests/bfd-topo2/r4/bfdd.conf @@ -0,0 +1,5 @@ +bfd + peer 2001:db8:1::1 multihop local-address 2001:db8:4::1 + no shutdown + ! +! diff --git a/tests/topotests/bfd-topo2/r4/ipv4_routes.json b/tests/topotests/bfd-topo2/r4/ipv4_routes.json new file mode 100644 index 0000000000..ae1e97b017 --- /dev/null +++ b/tests/topotests/bfd-topo2/r4/ipv4_routes.json @@ -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 + } + ] + } + ] +} diff --git a/tests/topotests/bfd-topo2/r4/ipv6_routes.json b/tests/topotests/bfd-topo2/r4/ipv6_routes.json new file mode 100644 index 0000000000..33608b45aa --- /dev/null +++ b/tests/topotests/bfd-topo2/r4/ipv6_routes.json @@ -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" + } + ] + } + ] +} diff --git a/tests/topotests/bfd-topo2/r4/ospf6d.conf b/tests/topotests/bfd-topo2/r4/ospf6d.conf new file mode 100644 index 0000000000..756597d6f8 --- /dev/null +++ b/tests/topotests/bfd-topo2/r4/ospf6d.conf @@ -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 +! diff --git a/tests/topotests/bfd-topo2/r4/peers.json b/tests/topotests/bfd-topo2/r4/peers.json new file mode 100644 index 0000000000..83101eb47f --- /dev/null +++ b/tests/topotests/bfd-topo2/r4/peers.json @@ -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 + } +] diff --git a/tests/topotests/bfd-topo2/r4/zebra.conf b/tests/topotests/bfd-topo2/r4/zebra.conf new file mode 100644 index 0000000000..e4f8fd8514 --- /dev/null +++ b/tests/topotests/bfd-topo2/r4/zebra.conf @@ -0,0 +1,6 @@ +interface lo + ip address 10.254.254.4/32 +! +interface r4-eth0 + ipv6 address 2001:db8:4::1/64 +! diff --git a/tests/topotests/bfd-topo2/test_bfd_topo2.dot b/tests/topotests/bfd-topo2/test_bfd_topo2.dot new file mode 100644 index 0000000000..6b68fb398f --- /dev/null +++ b/tests/topotests/bfd-topo2/test_bfd_topo2.dot @@ -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"]; +} diff --git a/tests/topotests/bfd-topo2/test_bfd_topo2.jpg b/tests/topotests/bfd-topo2/test_bfd_topo2.jpg new file mode 100644 index 0000000000..35fe562a80 Binary files /dev/null and b/tests/topotests/bfd-topo2/test_bfd_topo2.jpg differ diff --git a/tests/topotests/bfd-topo2/test_bfd_topo2.py b/tests/topotests/bfd-topo2/test_bfd_topo2.py new file mode 100644 index 0000000000..773db129f0 --- /dev/null +++ b/tests/topotests/bfd-topo2/test_bfd_topo2.py @@ -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))