]> git.proxmox.com Git - mirror_frr.git/blobdiff - bgpd/bgp_zebra.c
bgpd: fix issue with ipv6 ecmp with vrfs
[mirror_frr.git] / bgpd / bgp_zebra.c
index 4a39bc2b2e7ab26794557fe55a29de79af065550..1998dabc2444c580a8f4e54096ee0b047262665c 100644 (file)
@@ -33,6 +33,7 @@ Boston, MA 02111-1307, USA.  */
 #include "memory.h"
 #include "lib/json.h"
 #include "lib/bfd.h"
+#include "filter.h"
 
 #include "bgpd/bgpd.h"
 #include "bgpd/bgp_route.h"
@@ -44,6 +45,11 @@ Boston, MA 02111-1307, USA.  */
 #include "bgpd/bgp_mpath.h"
 #include "bgpd/bgp_nexthop.h"
 #include "bgpd/bgp_nht.h"
+#include "bgpd/bgp_bfd.h"
+#if ENABLE_BGP_VNC
+# include "bgpd/rfapi/rfapi_backend.h"
+# include "bgpd/rfapi/vnc_export_bgp.h"
+#endif
 
 /* All information about zebra. */
 struct zclient *zclient = NULL;
@@ -103,14 +109,14 @@ bgp_install_info_to_zebra (struct bgp *bgp)
   return 1;
 }
 
+int zclient_num_connects;
+
 /* Router-id update message from zebra. */
 static int
 bgp_router_id_update (int command, struct zclient *zclient, zebra_size_t length,
     vrf_id_t vrf_id)
 {
   struct prefix router_id;
-  struct listnode *node, *nnode;
-  struct bgp *bgp;
 
   zebra_router_id_update_read(zclient->ibuf,&router_id);
 
@@ -121,32 +127,7 @@ bgp_router_id_update (int command, struct zclient *zclient, zebra_size_t length,
       zlog_debug("Rx Router Id update VRF %u Id %s", vrf_id, buf);
     }
 
-  if (vrf_id == VRF_DEFAULT)
-    {
-      /* Router-id change for default VRF has to also update all views. */
-      for (ALL_LIST_ELEMENTS (bm->bgp, node, nnode, bgp))
-        {
-          if (bgp->inst_type == BGP_INSTANCE_TYPE_VRF)
-            continue;
-
-          bgp->router_id_zebra = router_id.u.prefix4;
-
-          if (!bgp->router_id_static.s_addr)
-            bgp_router_id_set (bgp, &router_id.u.prefix4);
-        }
-    }
-  else
-    {
-      bgp = bgp_lookup_by_vrf_id (vrf_id);
-      if (bgp)
-        {
-          bgp->router_id_zebra = router_id.u.prefix4;
-
-          if (!bgp->router_id_static.s_addr)
-            bgp_router_id_set (bgp, &router_id.u.prefix4);
-        }
-    }
-
+  bgp_router_id_zebra_bump (vrf_id, &router_id);
   return 0;
 }
 
@@ -385,7 +366,16 @@ bgp_interface_down (int command, struct zclient *zclient, zebra_size_t length,
 
     for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
       {
+#if defined(HAVE_CUMULUS)
+        /* Take down directly connected EBGP peers as well as 1-hop BFD
+         * tracked (directly connected) IBGP peers.
+         */
+        if ((peer->ttl != 1) && (peer->gtsm_hops != 1) &&
+            (!peer->bfd_info || bgp_bfd_is_peer_multihop(peer)))
+#else
+        /* Take down directly connected EBGP peers */
         if ((peer->ttl != 1) && (peer->gtsm_hops != 1))
+#endif
           continue;
 
         if (ifp == peer->nexthop.ifp)
@@ -617,13 +607,13 @@ zebra_read_ipv4 (int command, struct zclient *zclient, zebra_size_t length,
   /* Type, flags, message. */
   api.type = stream_getc (s);
   api.instance = stream_getw (s);
-  api.flags = stream_getc (s);
+  api.flags = stream_getl (s);
   api.message = stream_getc (s);
 
   /* IPv4 prefix. */
   memset (&p, 0, sizeof (struct prefix_ipv4));
   p.family = AF_INET;
-  p.prefixlen = stream_getc (s);
+  p.prefixlen = MIN(IPV4_MAX_PREFIXLEN, stream_getc (s));
   stream_get (&p.prefix, s, PSIZE (p.prefixlen));
 
   /* Nexthop, ifindex, distance, metric. */
@@ -652,7 +642,7 @@ zebra_read_ipv4 (int command, struct zclient *zclient, zebra_size_t length,
     api.metric = 0;
 
   if (CHECK_FLAG (api.message, ZAPI_MESSAGE_TAG))
-    api.tag = stream_getw (s);
+    api.tag = stream_getl (s);
   else
     api.tag = 0;
 
@@ -661,7 +651,7 @@ zebra_read_ipv4 (int command, struct zclient *zclient, zebra_size_t length,
       if (bgp_debug_zebra((struct prefix *)&p))
        {
          char buf[2][INET_ADDRSTRLEN];
-         zlog_debug("Rx IPv4 route add VRF %u %s[%d] %s/%d nexthop %s metric %u tag %d",
+         zlog_debug("Rx IPv4 route add VRF %u %s[%d] %s/%d nexthop %s metric %u tag %"ROUTE_TAG_PRI,
                      vrf_id,
                     zebra_route_string(api.type), api.instance,
                     inet_ntop(AF_INET, &p.prefix, buf[0], sizeof(buf[0])),
@@ -693,7 +683,7 @@ zebra_read_ipv4 (int command, struct zclient *zclient, zebra_size_t length,
        {
          char buf[2][INET_ADDRSTRLEN];
          zlog_debug("Rx IPv4 route delete VRF %u %s[%d] %s/%d "
-                    "nexthop %s metric %u tag %d",
+                    "nexthop %s metric %u tag %"ROUTE_TAG_PRI,
                      vrf_id,
                     zebra_route_string(api.type), api.instance,
                     inet_ntop(AF_INET, &p.prefix, buf[0], sizeof(buf[0])),
@@ -732,13 +722,13 @@ zebra_read_ipv6 (int command, struct zclient *zclient, zebra_size_t length,
   /* Type, flags, message. */
   api.type = stream_getc (s);
   api.instance = stream_getw (s);
-  api.flags = stream_getc (s);
+  api.flags = stream_getl (s);
   api.message = stream_getc (s);
 
   /* IPv6 prefix. */
   memset (&p, 0, sizeof (struct prefix_ipv6));
   p.family = AF_INET6;
-  p.prefixlen = stream_getc (s);
+  p.prefixlen = MIN(IPV6_MAX_PREFIXLEN, stream_getc (s));
   stream_get (&p.prefix, s, PSIZE (p.prefixlen));
 
   /* Nexthop, ifindex, distance, metric. */
@@ -769,7 +759,7 @@ zebra_read_ipv6 (int command, struct zclient *zclient, zebra_size_t length,
     api.metric = 0;
 
   if (CHECK_FLAG (api.message, ZAPI_MESSAGE_TAG))
-    api.tag = stream_getw (s);
+    api.tag = stream_getl (s);
   else
     api.tag = 0;
 
@@ -782,7 +772,7 @@ zebra_read_ipv6 (int command, struct zclient *zclient, zebra_size_t length,
       if (bgp_debug_zebra((struct prefix *)&p))
        {
          char buf[2][INET6_ADDRSTRLEN];
-         zlog_debug("Rx IPv6 route add VRF %u %s[%d] %s/%d nexthop %s metric %u tag %d",
+         zlog_debug("Rx IPv6 route add VRF %u %s[%d] %s/%d nexthop %s metric %u tag %"ROUTE_TAG_PRI,
                      vrf_id,
                     zebra_route_string(api.type), api.instance,
                     inet_ntop(AF_INET6, &p.prefix, buf[0], sizeof(buf[0])),
@@ -813,7 +803,7 @@ zebra_read_ipv6 (int command, struct zclient *zclient, zebra_size_t length,
        {
          char buf[2][INET6_ADDRSTRLEN];
          zlog_debug("Rx IPv6 route delete VRF %u %s[%d] %s/%d "
-                    "nexthop %s metric %u tag %d",
+                    "nexthop %s metric %u tag %"ROUTE_TAG_PRI,
                      vrf_id,
                     zebra_route_string(api.type), api.instance,
                     inet_ntop(AF_INET6, &p.prefix, buf[0], sizeof(buf[0])),
@@ -882,7 +872,7 @@ if_lookup_by_ipv4_exact (struct in_addr *addr, vrf_id_t vrf_id)
 
 #ifdef HAVE_IPV6
 struct interface *
-if_lookup_by_ipv6 (struct in6_addr *addr, unsigned int ifindex, vrf_id_t vrf_id)
+if_lookup_by_ipv6 (struct in6_addr *addr, ifindex_t ifindex, vrf_id_t vrf_id)
 {
   struct listnode *ifnode;
   struct listnode *cnode;
@@ -904,7 +894,7 @@ if_lookup_by_ipv6 (struct in6_addr *addr, unsigned int ifindex, vrf_id_t vrf_id)
          if (cp->family == AF_INET6)
            if (prefix_match (cp, (struct prefix *)&p))
              {
-               if (IN6_IS_ADDR_LINKLOCAL(&cp->u.prefix6.s6_addr32[0]))
+               if (IN6_IS_ADDR_LINKLOCAL(&cp->u.prefix6))
                  {
                    if (ifindex == ifp->ifindex)
                      return ifp;
@@ -918,7 +908,7 @@ if_lookup_by_ipv6 (struct in6_addr *addr, unsigned int ifindex, vrf_id_t vrf_id)
 }
 
 struct interface *
-if_lookup_by_ipv6_exact (struct in6_addr *addr, unsigned int ifindex, vrf_id_t vrf_id)
+if_lookup_by_ipv6_exact (struct in6_addr *addr, ifindex_t ifindex, vrf_id_t vrf_id)
 {
   struct listnode *ifnode;
   struct listnode *cnode;
@@ -1163,12 +1153,18 @@ bgp_info_to_ipv6_nexthop (struct bgp_info *info)
   /* If both global and link-local address present. */
   if (info->attr->extra->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL)
     {
-      /* Workaround for Cisco's nexthop bug.  */
-      if (IN6_IS_ADDR_UNSPECIFIED (&info->attr->extra->mp_nexthop_global)
-          && info->peer->su_remote->sa.sa_family == AF_INET6)
-        nexthop = &info->peer->su_remote->sin6.sin6_addr;
+      /* Check if route-map is set to prefer global over link-local */
+      if (info->attr->extra->mp_nexthop_prefer_global)
+        nexthop = &info->attr->extra->mp_nexthop_global;
       else
-        nexthop = &info->attr->extra->mp_nexthop_local;
+        {
+          /* Workaround for Cisco's nexthop bug.  */
+          if (IN6_IS_ADDR_UNSPECIFIED (&info->attr->extra->mp_nexthop_global)
+              && info->peer->su_remote->sa.sa_family == AF_INET6)
+            nexthop = &info->peer->su_remote->sin6.sin6_addr;
+          else
+            nexthop = &info->attr->extra->mp_nexthop_local;
+        }
     }
 
   return nexthop;
@@ -1209,7 +1205,7 @@ void
 bgp_zebra_announce (struct prefix *p, struct bgp_info *info, struct bgp *bgp,
                     afi_t afi, safi_t safi)
 {
-  int flags;
+  u_int32_t flags;
   u_char distance;
   struct peer *peer;
   struct bgp_info *mpinfo;
@@ -1217,7 +1213,7 @@ bgp_zebra_announce (struct prefix *p, struct bgp_info *info, struct bgp *bgp,
   u_int32_t nhcount, metric;
   struct bgp_info local_info;
   struct bgp_info *info_cp = &local_info;
-  u_short tag;
+  route_tag_t tag;
 
   /* Don't try to install if we're not connected to Zebra or Zebra doesn't
    * know of this instance.
@@ -1371,8 +1367,7 @@ bgp_zebra_announce (struct prefix *p, struct bgp_info *info, struct bgp *bgp,
           api.tag = tag;
         }
 
-      distance = bgp_distance_apply (p, info, bgp);
-
+      distance = bgp_distance_apply (p, info, afi, safi, bgp);
       if (distance)
        {
          SET_FLAG (api.message, ZAPI_MESSAGE_DISTANCE);
@@ -1382,7 +1377,7 @@ bgp_zebra_announce (struct prefix *p, struct bgp_info *info, struct bgp *bgp,
       if (bgp_debug_zebra(p))
         {
           int i;
-          zlog_debug("Tx IPv4 route %s VRF %u %s/%d metric %u tag %d"
+          zlog_debug("Tx IPv4 route %s VRF %u %s/%d metric %u tag %"ROUTE_TAG_PRI
                      " count %d", (valid_nh_count ? "add":"delete"),
                      bgp->vrf_id,
                      inet_ntop(AF_INET, &p->u.prefix4, buf[0], sizeof(buf[0])),
@@ -1401,7 +1396,7 @@ bgp_zebra_announce (struct prefix *p, struct bgp_info *info, struct bgp *bgp,
   if (p->family == AF_INET6 ||
       (p->family == AF_INET && BGP_ATTR_NEXTHOP_AFI_IP6(info->attr)))
     {
-      unsigned int ifindex;
+      ifindex_t ifindex;
       struct in6_addr *nexthop;
       struct zapi_ipv6 api;
       int valid_nh_count = 0;
@@ -1474,7 +1469,8 @@ bgp_zebra_announce (struct prefix *p, struct bgp_info *info, struct bgp *bgp,
           if (!ifindex)
            {
              if (info->peer->conf_if || info->peer->ifname)
-               ifindex = if_nametoindex (info->peer->conf_if ? info->peer->conf_if : info->peer->ifname);
+               ifindex = ifname2ifindex_vrf (info->peer->conf_if ? info->peer->conf_if :
+                                              info->peer->ifname, bgp->vrf_id);
              else if (info->peer->nexthop.ifp)
                ifindex = info->peer->nexthop.ifp->ifindex;
            }
@@ -1513,7 +1509,8 @@ bgp_zebra_announce (struct prefix *p, struct bgp_info *info, struct bgp *bgp,
           if (!ifindex)
            {
              if (mpinfo->peer->conf_if || mpinfo->peer->ifname)
-               ifindex = ifname2ifindex (mpinfo->peer->conf_if ? mpinfo->peer->conf_if : mpinfo->peer->ifname);
+                ifindex = ifname2ifindex_vrf (mpinfo->peer->conf_if ? mpinfo->peer->conf_if :
+                                              mpinfo->peer->ifname, bgp->vrf_id);
              else if (mpinfo->peer->nexthop.ifp)
                ifindex = mpinfo->peer->nexthop.ifp->ifindex;
            }
@@ -1547,7 +1544,7 @@ bgp_zebra_announce (struct prefix *p, struct bgp_info *info, struct bgp *bgp,
       api.nexthop = (struct in6_addr **)STREAM_DATA (bgp_nexthop_buf);
       SET_FLAG (api.message, ZAPI_MESSAGE_IFINDEX);
       api.ifindex_num = valid_nh_count;
-      api.ifindex = (unsigned int *)STREAM_DATA (bgp_ifindices_buf);
+      api.ifindex = (ifindex_t *)STREAM_DATA (bgp_ifindices_buf);
       SET_FLAG (api.message, ZAPI_MESSAGE_METRIC);
       api.metric = metric;
       api.tag = 0;
@@ -1558,12 +1555,19 @@ bgp_zebra_announce (struct prefix *p, struct bgp_info *info, struct bgp *bgp,
           api.tag = tag;
         }
 
+      distance = bgp_distance_apply (p, info, afi, safi, bgp);
+      if (distance)
+        {
+          SET_FLAG (api.message, ZAPI_MESSAGE_DISTANCE);
+          api.distance = distance;
+        }
+
       if (p->family == AF_INET)
         {
           if (bgp_debug_zebra(p))
             {
               int i;
-              zlog_debug("Tx IPv4 route %s VRF %u %s/%d metric %u tag %d",
+              zlog_debug("Tx IPv4 route %s VRF %u %s/%d metric %u tag %"ROUTE_TAG_PRI,
                          valid_nh_count ? "add" : "delete", bgp->vrf_id,
                          inet_ntop(AF_INET, &p->u.prefix4, buf[0], sizeof(buf[0])),
                          p->prefixlen, api.metric, api.tag);
@@ -1585,7 +1589,7 @@ bgp_zebra_announce (struct prefix *p, struct bgp_info *info, struct bgp *bgp,
           if (bgp_debug_zebra(p))
             {
               int i;
-              zlog_debug("Tx IPv6 route %s VRF %u %s/%d metric %u tag %d",
+              zlog_debug("Tx IPv6 route %s VRF %u %s/%d metric %u tag %"ROUTE_TAG_PRI,
                          valid_nh_count ? "add" : "delete", bgp->vrf_id,
                          inet_ntop(AF_INET6, &p->u.prefix6, buf[0], sizeof(buf[0])),
                          p->prefixlen, api.metric, api.tag);
@@ -1630,7 +1634,7 @@ bgp_zebra_announce_table (struct bgp *bgp, afi_t afi, safi_t safi)
 void
 bgp_zebra_withdraw (struct prefix *p, struct bgp_info *info, safi_t safi)
 {
-  int flags;
+  u_int32_t flags;
   struct peer *peer;
 
   peer = info->peer;
@@ -1689,7 +1693,7 @@ bgp_zebra_withdraw (struct prefix *p, struct bgp_info *info, safi_t safi)
       if (bgp_debug_zebra(p))
        {
          char buf[2][INET_ADDRSTRLEN];
-         zlog_debug("Tx IPv4 route delete VRF %u %s/%d metric %u tag %d",
+         zlog_debug("Tx IPv4 route delete VRF %u %s/%d metric %u tag %"ROUTE_TAG_PRI,
                      peer->bgp->vrf_id,
                     inet_ntop(AF_INET, &p->u.prefix4, buf[0], sizeof(buf[0])),
                     p->prefixlen, api.metric, api.tag);
@@ -1729,7 +1733,7 @@ bgp_zebra_withdraw (struct prefix *p, struct bgp_info *info, safi_t safi)
       if (bgp_debug_zebra(p))
        {
          char buf[2][INET6_ADDRSTRLEN];
-         zlog_debug("Tx IPv6 route delete VRF %u %s/%d metric %u tag %d",
+         zlog_debug("Tx IPv6 route delete VRF %u %s/%d metric %u tag %"ROUTE_TAG_PRI,
                      peer->bgp->vrf_id,
                     inet_ntop(AF_INET6, &p->u.prefix6, buf[0], sizeof(buf[0])),
                     p->prefixlen, api.metric, api.tag);
@@ -1790,6 +1794,7 @@ bgp_redist_del (struct bgp *bgp, afi_t afi, u_char type, u_short instance)
   if (red)
     {
       listnode_delete(bgp->redist[afi][type], red);
+      XFREE (MTYPE_BGP_REDIST, red);
       if (!bgp->redist[afi][type]->count)
         {
           list_free(bgp->redist[afi][type]);
@@ -1816,6 +1821,13 @@ bgp_redistribute_set (struct bgp *bgp, afi_t afi, int type, u_short instance)
       if (vrf_bitmap_check (zclient->redist[afi][type], bgp->vrf_id))
         return CMD_WARNING;
 
+#if ENABLE_BGP_VNC
+      if (bgp->vrf_id == VRF_DEFAULT &&
+          type == ZEBRA_ROUTE_VNC_DIRECT) {
+        vnc_export_bgp_enable(bgp, afi);       /* only enables if mode bits cfg'd */
+      }
+#endif
+
       vrf_bitmap_set (zclient->redist[afi][type], bgp->vrf_id);
     }
 
@@ -1891,16 +1903,30 @@ bgp_redistribute_metric_set (struct bgp *bgp, struct bgp_redist *red, afi_t afi,
   red->redist_metric_flag = 1;
   red->redist_metric = metric;
 
-  for (rn = bgp_table_top(bgp->rib[afi][SAFI_UNICAST]); rn; rn = bgp_route_next(rn)) {
-    for (ri = rn->info; ri; ri = ri->next) {
-      if (ri->sub_type == BGP_ROUTE_REDISTRIBUTE && ri->type == type &&
-         ri->instance == red->instance) {
-         ri->attr->med = red->redist_metric;
-         bgp_info_set_flag(rn, ri, BGP_INFO_ATTR_CHANGED);
-         bgp_process(bgp, rn, afi, SAFI_UNICAST);
-      }
+  for (rn = bgp_table_top(bgp->rib[afi][SAFI_UNICAST]); rn; rn = bgp_route_next(rn))
+    {
+      for (ri = rn->info; ri; ri = ri->next)
+        {
+          if (ri->sub_type == BGP_ROUTE_REDISTRIBUTE &&
+              ri->type == type &&
+              ri->instance == red->instance)
+            {
+              struct attr *old_attr;
+              struct attr new_attr;
+              struct attr_extra new_extra;
+
+              new_attr.extra = &new_extra;
+              bgp_attr_dup (&new_attr, ri->attr);
+              new_attr.med = red->redist_metric;
+              old_attr = ri->attr;
+              ri->attr = bgp_attr_intern (&new_attr);
+              bgp_attr_unintern (&old_attr);
+
+              bgp_info_set_flag(rn, ri, BGP_INFO_ATTR_CHANGED);
+              bgp_process(bgp, rn, afi, SAFI_UNICAST);
+            }
+        }
     }
-  }
 
   return 1;
 }
@@ -1929,6 +1955,13 @@ bgp_redistribute_unreg (struct bgp *bgp, afi_t afi, int type, u_short instance)
       vrf_bitmap_unset (zclient->redist[afi][type], bgp->vrf_id);
     }
 
+#if ENABLE_BGP_VNC
+  if (bgp->vrf_id == VRF_DEFAULT &&
+      type == ZEBRA_ROUTE_VNC_DIRECT) {
+    vnc_export_bgp_disable(bgp, afi);
+  }
+#endif
+
   if (bgp_install_info_to_zebra (bgp))
     {
       /* Send distribute delete message to zebra. */
@@ -1972,6 +2005,24 @@ bgp_redistribute_unset (struct bgp *bgp, afi_t afi, int type, u_short instance)
   return CMD_SUCCESS;
 }
 
+/* Update redistribute vrf bitmap during triggers like
+   restart networking or delete/add VRFs */
+void
+bgp_update_redist_vrf_bitmaps (struct bgp *bgp, vrf_id_t old_vrf_id)
+{
+  int i;
+  afi_t afi;
+
+  for (afi = AFI_IP; afi < AFI_MAX; afi++)
+    for (i = 0; i < ZEBRA_ROUTE_MAX; i++)
+      if (vrf_bitmap_check (zclient->redist[afi][i], old_vrf_id))
+        {
+          vrf_bitmap_unset (zclient->redist[afi][i], old_vrf_id);
+          vrf_bitmap_set (zclient->redist[afi][i], bgp->vrf_id);
+        }
+  return;
+}
+
 void
 bgp_zclient_reset (void)
 {
@@ -2047,6 +2098,8 @@ bgp_zebra_connected (struct zclient *zclient)
 {
   struct bgp *bgp;
 
+  zclient_num_connects++;       /* increment even if not responding */
+
   /* At this point, we may or may not have BGP instances configured, but
    * we're only interested in the default VRF (others wouldn't have learnt
    * the VRF from Zebra yet.)
@@ -2069,6 +2122,8 @@ bgp_zebra_connected (struct zclient *zclient)
 void
 bgp_zebra_init (struct thread_master *master)
 {
+  zclient_num_connects = 0;
+
   /* Set default values. */
   zclient = zclient_new (master);
   zclient_init (zclient, ZEBRA_ROUTE_BGP, 0);
@@ -2081,14 +2136,10 @@ bgp_zebra_init (struct thread_master *master)
   zclient->interface_nbr_address_add = bgp_interface_nbr_address_add;
   zclient->interface_nbr_address_delete = bgp_interface_nbr_address_delete;
   zclient->interface_vrf_update = bgp_interface_vrf_update;
-  zclient->ipv4_route_add = zebra_read_ipv4;
-  zclient->ipv4_route_delete = zebra_read_ipv4;
   zclient->redistribute_route_ipv4_add = zebra_read_ipv4;
   zclient->redistribute_route_ipv4_del = zebra_read_ipv4;
   zclient->interface_up = bgp_interface_up;
   zclient->interface_down = bgp_interface_down;
-  zclient->ipv6_route_add = zebra_read_ipv6;
-  zclient->ipv6_route_delete = zebra_read_ipv6;
   zclient->redistribute_route_ipv6_add = zebra_read_ipv6;
   zclient->redistribute_route_ipv6_del = zebra_read_ipv6;
   zclient->nexthop_update = bgp_read_nexthop_update;
@@ -2107,3 +2158,9 @@ bgp_zebra_destroy(void)
   zclient_free(zclient);
   zclient = NULL;
 }
+
+int
+bgp_zebra_num_connects(void)
+{
+  return zclient_num_connects;
+}