]> git.proxmox.com Git - mirror_frr.git/commitdiff
Merge pull request #3918 from opensourcerouting/bfd-bug-fixes
authorDonald Sharp <sharpd@cumulusnetworks.com>
Tue, 12 Mar 2019 23:08:17 +0000 (19:08 -0400)
committerGitHub <noreply@github.com>
Tue, 12 Mar 2019 23:08:17 +0000 (19:08 -0400)
bfdd: bug fixes

34 files changed:
bfdd/bfd.c
bfdd/bfd.h
bfdd/bfd_packet.c
bfdd/bfdd_vty.c
bfdd/config.c
bfdd/ptm_adapter.c
tests/topotests/bfd-topo2/__init__.py [new file with mode: 0644]
tests/topotests/bfd-topo2/r1/bfdd.conf [new file with mode: 0644]
tests/topotests/bfd-topo2/r1/bgpd.conf [new file with mode: 0644]
tests/topotests/bfd-topo2/r1/ipv4_routes.json [new file with mode: 0644]
tests/topotests/bfd-topo2/r1/ipv6_routes.json [new file with mode: 0644]
tests/topotests/bfd-topo2/r1/peers.json [new file with mode: 0644]
tests/topotests/bfd-topo2/r1/zebra.conf [new file with mode: 0644]
tests/topotests/bfd-topo2/r2/bgpd.conf [new file with mode: 0644]
tests/topotests/bfd-topo2/r2/ipv4_routes.json [new file with mode: 0644]
tests/topotests/bfd-topo2/r2/ipv6_routes.json [new file with mode: 0644]
tests/topotests/bfd-topo2/r2/ospf6d.conf [new file with mode: 0644]
tests/topotests/bfd-topo2/r2/ospfd.conf [new file with mode: 0644]
tests/topotests/bfd-topo2/r2/peers.json [new file with mode: 0644]
tests/topotests/bfd-topo2/r2/zebra.conf [new file with mode: 0644]
tests/topotests/bfd-topo2/r3/ipv4_routes.json [new file with mode: 0644]
tests/topotests/bfd-topo2/r3/ipv6_routes.json [new file with mode: 0644]
tests/topotests/bfd-topo2/r3/ospfd.conf [new file with mode: 0644]
tests/topotests/bfd-topo2/r3/peers.json [new file with mode: 0644]
tests/topotests/bfd-topo2/r3/zebra.conf [new file with mode: 0644]
tests/topotests/bfd-topo2/r4/bfdd.conf [new file with mode: 0644]
tests/topotests/bfd-topo2/r4/ipv4_routes.json [new file with mode: 0644]
tests/topotests/bfd-topo2/r4/ipv6_routes.json [new file with mode: 0644]
tests/topotests/bfd-topo2/r4/ospf6d.conf [new file with mode: 0644]
tests/topotests/bfd-topo2/r4/peers.json [new file with mode: 0644]
tests/topotests/bfd-topo2/r4/zebra.conf [new file with mode: 0644]
tests/topotests/bfd-topo2/test_bfd_topo2.dot [new file with mode: 0644]
tests/topotests/bfd-topo2/test_bfd_topo2.jpg [new file with mode: 0644]
tests/topotests/bfd-topo2/test_bfd_topo2.py [new file with mode: 0644]

index be6f2caa44862aa0d365518d158e85125d0ca5bb..c8adf82a83140e30c136fbdfbb1bd1ed1c520dfa 100644 (file)
@@ -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;
-
-                       mhop.vrfid = vrf->vrf_id;
-               }
+       gen_bfd_key(&key, &bpc->bpc_peer, &bpc->bpc_local, bpc->bpc_mhop,
+                   bpc->bpc_localif, bpc->bpc_vrfname);
 
-               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;
-
-               l_bfd = bfd_mhop_lookup(mhop);
-       } else {
-               memset(&shop, 0, sizeof(shop));
-               shop.peer = *peer;
-               shop.ifindex = ifindex;
+       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_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)
-{
-       struct bfd_session *bs = p;
-
-       return jhash(&bs->shop, sizeof(bs->shop), 0);
-}
-
-static bool bfd_shop_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;
-}
-
-/* BFD hash for multi hop. */
-static unsigned int bfd_mhop_hash_do(void *p)
+static unsigned int bfd_key_hash_do(void *p)
 {
        struct bfd_session *bs = p;
 
-       return jhash(&bs->mhop, sizeof(bs->mhop), 0);
+       return jhash(&bs->key, sizeof(bs->key), 0);
 }
 
-static bool bfd_mhop_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->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;
+       return memcmp(&bs1->key, &bs2->key, sizeof(bs1->key)) == 0;
 }
 
-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,32 +1318,33 @@ 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);
        }
 
-       return bsp;
-}
-
-struct bfd_session *bfd_mhop_lookup(struct bfd_mhop_key mhop)
-{
-       struct bfd_session 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);
 
-       _mhop_key(&bs, &mhop);
+               /* 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 hash_lookup(bfd_mhop_hash, &bs);
+       return bsp;
 }
 
 /*
@@ -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)
-{
-       hash_iterate(bfd_shop_hash, hif, arg);
-}
-
-void bfd_mhop_iterate(hash_iter_func hif, void *arg)
+void bfd_key_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)
-{
-       return (hash_get(bfd_shop_hash, bs, hash_alloc_intern) == bs);
-}
-
-bool bfd_mhop_insert(struct bfd_session *bs)
+bool bfd_key_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);
 }
index 7451ca82a387e4753591c3ec070c039fa696088c..a69ff9a1a7b9a33b7756d6ef6305a125904220d2 100644 (file)
@@ -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
index 8601bd2e4090ef32f79375b3e39b510364da8d53..69d27ed698080ff77893b245e5f8b2e8ef612f65 100644 (file)
@@ -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;
                }
index c77cd08be8033d514ffdcc43fbd52c173c8a0f36..c13949207642fe1e81673269291b48a957afda2f 100644 (file)
@@ -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;
 }
index 17e155e41df37208c6b8a846e3fddc7f8e0469f6..cd57ea9fe38b03ecd01aeb7dfe1cf4744b7299d9 100644 (file)
@@ -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)
index 5610c352fb3f1fdebedd7768d9f382eabd5dcfb8..b44d13f132a34b67df5216cccc19b8d3e587a4ab 100644 (file)
@@ -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 (file)
index 0000000..e69de29
diff --git a/tests/topotests/bfd-topo2/r1/bfdd.conf b/tests/topotests/bfd-topo2/r1/bfdd.conf
new file mode 100644 (file)
index 0000000..5c2571b
--- /dev/null
@@ -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 (file)
index 0000000..1623b45
--- /dev/null
@@ -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 (file)
index 0000000..8a2ec25
--- /dev/null
@@ -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 (file)
index 0000000..618853b
--- /dev/null
@@ -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 (file)
index 0000000..b14351c
--- /dev/null
@@ -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 (file)
index 0000000..7fe5eb2
--- /dev/null
@@ -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 (file)
index 0000000..bf42d21
--- /dev/null
@@ -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 (file)
index 0000000..b9d8afb
--- /dev/null
@@ -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 (file)
index 0000000..004e758
--- /dev/null
@@ -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 (file)
index 0000000..f1cdb50
--- /dev/null
@@ -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 (file)
index 0000000..8e0c459
--- /dev/null
@@ -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 (file)
index 0000000..29075fc
--- /dev/null
@@ -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 (file)
index 0000000..cccbf65
--- /dev/null
@@ -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 (file)
index 0000000..14dfc69
--- /dev/null
@@ -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 (file)
index 0000000..2c63c08
--- /dev/null
@@ -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 (file)
index 0000000..cf2a1bd
--- /dev/null
@@ -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 (file)
index 0000000..6698bff
--- /dev/null
@@ -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 (file)
index 0000000..96fd08c
--- /dev/null
@@ -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 (file)
index 0000000..fdb4412
--- /dev/null
@@ -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 (file)
index 0000000..ae1e97b
--- /dev/null
@@ -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 (file)
index 0000000..33608b4
--- /dev/null
@@ -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 (file)
index 0000000..756597d
--- /dev/null
@@ -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 (file)
index 0000000..83101eb
--- /dev/null
@@ -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 (file)
index 0000000..e4f8fd8
--- /dev/null
@@ -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 (file)
index 0000000..6b68fb3
--- /dev/null
@@ -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 (file)
index 0000000..35fe562
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 (file)
index 0000000..773db12
--- /dev/null
@@ -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))