From: Dinesh Dutt <[email protected]>

Use NHT to support static routes with NH derived from protocols.

Signed-off-by: Dinesh Dutt <[email protected]>
---
 lib/nexthop.h          |   1 +
 zebra/rib.h            |  11 +-
 zebra/zebra_rib.c      | 272 +++++++++++++++++++++++++++++++-------
 zebra/zebra_rnh.c      | 348 +++++++++++++++++++++++++++++++------------------
 zebra/zebra_rnh.h      |   4 +
 zebra/zebra_rnh_null.c |   9 ++
 zebra/zserv.c          |   2 +-
 7 files changed, 466 insertions(+), 181 deletions(-)

diff --git a/lib/nexthop.h b/lib/nexthop.h
index 69ebb35..2c317bf 100644
--- a/lib/nexthop.h
+++ b/lib/nexthop.h
@@ -64,6 +64,7 @@ struct nexthop
 #define NEXTHOP_FLAG_RECURSIVE  (1 << 2) /* Recursive nexthop. */
 #define NEXTHOP_FLAG_ONLINK     (1 << 3) /* Nexthop should be installed 
onlink. */
 #define NEXTHOP_FLAG_MATCHED    (1 << 4) /* Already matched vs a nexthop */
+#define NEXTHOP_FLAG_FILTERED   (1 << 5) /* rmap filtered, used by static only 
*/
 
   /* Nexthop address */
   union g_addr gate;
diff --git a/zebra/rib.h b/zebra/rib.h
index 94c7d1f..f10e315 100644
--- a/zebra/rib.h
+++ b/zebra/rib.h
@@ -76,8 +76,9 @@ struct rib
 
   /* RIB internal status */
   u_char status;
-#define RIB_ENTRY_REMOVED      (1 << 0)
-#define RIB_ENTRY_CHANGED      (1 << 1)
+#define RIB_ENTRY_REMOVED              (1 << 0)
+  /* to simplify NHT logic when NHs change, instead of doing a NH by NH cmp */
+#define RIB_ENTRY_NEXTHOPS_CHANGED     (1 << 1)
 
   /* Nexthop information. */
   u_char nexthop_num;
@@ -383,6 +384,7 @@ extern struct nexthop *rib_nexthop_ipv4_ifindex_add (struct 
rib *,
                                                     unsigned int);
 
 extern void rib_nexthop_add (struct rib *rib, struct nexthop *nexthop);
+extern void rib_copy_nexthops (struct rib *rib, struct nexthop *nexthop);
 
 extern int nexthop_has_fib_child(struct nexthop *);
 extern void rib_lookup_and_dump (struct prefix_ipv4 *);
@@ -422,7 +424,7 @@ extern int rib_delete_ipv4 (int type, int flags, struct 
prefix_ipv4 *p,
                            vrf_id_t, safi_t safi);
 
 extern struct rib *rib_match_ipv4_safi (struct in_addr addr, safi_t safi,
-                                       int skip_bgp, struct route_node 
**rn_out,
+                                       struct route_node **rn_out,
                                        vrf_id_t);
 extern struct rib *rib_match_ipv4_multicast (struct in_addr addr,
                                             struct route_node **rn_out,
@@ -437,6 +439,9 @@ extern void rib_close_table (struct route_table *);
 extern void rib_close (void);
 extern void rib_init (void);
 extern unsigned long rib_score_proto (u_char proto);
+struct zebra_t;
+extern void rib_queue_add (struct zebra_t *zebra, struct route_node *rn);
+
 
 extern int
 static_add_ipv4_safi (safi_t safi, struct prefix *p, struct in_addr *gate,
diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c
index 474b13c..819b7bc 100644
--- a/zebra/zebra_rib.c
+++ b/zebra/zebra_rib.c
@@ -120,6 +120,27 @@ rib_nexthop_add (struct rib *rib, struct nexthop *nexthop)
   rib->nexthop_num++;
 }
 
+/**
+ * copy_nexthop - copy a nexthop to the rib structure.
+ */
+void
+rib_copy_nexthops (struct rib *rib, struct nexthop *nh)
+{
+  struct nexthop *nexthop;
+
+  nexthop = nexthop_new();
+  nexthop->flags = nh->flags;
+  nexthop->type = nh->type;
+  nexthop->ifindex = nh->ifindex;
+  if (nh->ifname)
+    nexthop->ifname = XSTRDUP(0, nh->ifname);
+  memcpy(&(nexthop->gate), &(nh->gate), sizeof(union g_addr));
+  memcpy(&(nexthop->src), &(nh->src), sizeof(union g_addr));
+  rib_nexthop_add(rib, nexthop);
+  if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_RECURSIVE))
+    copy_nexthops(&nexthop->resolved, nh->resolved);
+}
+
 /* Delete specified nexthop from the list. */
 static void
 rib_nexthop_delete (struct rib *rib, struct nexthop *nexthop)
@@ -284,8 +305,9 @@ nexthop_active_ipv4 (struct rib *rib, struct nexthop 
*nexthop, int set,
   struct route_node *rn;
   struct rib *match;
   int resolved;
-  struct nexthop *newhop;
+  struct nexthop *newhop, *tnewhop;
   struct nexthop *resolved_hop;
+  int recursing = 0;
 
   if (nexthop->type == NEXTHOP_TYPE_IPV4)
     nexthop->ifindex = 0;
@@ -293,11 +315,18 @@ nexthop_active_ipv4 (struct rib *rib, struct nexthop 
*nexthop, int set,
   if (set)
     {
       UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE);
+      zebra_deregister_rnh_static_nexthops (nexthop->resolved, top);
       nexthops_free(nexthop->resolved);
       nexthop->resolved = NULL;
       rib->nexthop_mtu = 0;
     }
 
+  /* Skip nexthops that have been filtered out due to route-map */
+  /* The nexthops are specific to this route and so the same */
+  /* nexthop for a different route may not have this flag set */
+  if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FILTERED))
+    return 0;
+
   /* Make lookup prefix. */
   memset (&p, 0, sizeof (struct prefix_ipv4));
   p.family = AF_INET;
@@ -329,8 +358,7 @@ nexthop_active_ipv4 (struct rib *rib, struct nexthop 
*nexthop, int set,
 
       /* If there is no selected route or matched route is EGP, go up
          tree. */
-      if (! match 
-         || match->type == ZEBRA_ROUTE_BGP)
+      if (! match)
        {
          do {
            rn = rn->parent;
@@ -365,6 +393,7 @@ nexthop_active_ipv4 (struct rib *rib, struct nexthop 
*nexthop, int set,
                    if (set)
                      {
                        SET_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE);
+                       SET_FLAG(rib->status, RIB_ENTRY_NEXTHOPS_CHANGED);
 
                        resolved_hop = XCALLOC(MTYPE_NEXTHOP, sizeof (struct 
nexthop));
                        SET_FLAG (resolved_hop->flags, NEXTHOP_FLAG_ACTIVE);
@@ -408,6 +437,57 @@ nexthop_active_ipv4 (struct rib *rib, struct nexthop 
*nexthop, int set,
                 rib->nexthop_mtu = match->mtu;
              return resolved;
            }
+         else if (rib->type == ZEBRA_ROUTE_STATIC)
+           {
+             resolved = 0;
+             for (ALL_NEXTHOPS_RO(match->nexthop, newhop, tnewhop, recursing))
+               if (CHECK_FLAG (newhop->flags, NEXTHOP_FLAG_FIB))
+                 {
+                   if (set)
+                     {
+                       SET_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE);
+
+                       resolved_hop = XCALLOC(MTYPE_NEXTHOP, sizeof (struct 
nexthop));
+                       SET_FLAG (resolved_hop->flags, NEXTHOP_FLAG_ACTIVE);
+                       /* If the resolving route specifies a gateway, use it */
+                       if (newhop->type == NEXTHOP_TYPE_IPV4
+                           || newhop->type == NEXTHOP_TYPE_IPV4_IFINDEX
+                           || newhop->type == NEXTHOP_TYPE_IPV4_IFNAME)
+                         {
+                           resolved_hop->type = newhop->type;
+                           resolved_hop->gate.ipv4 = newhop->gate.ipv4;
+
+                           if (newhop->ifindex)
+                             {
+                               resolved_hop->type = NEXTHOP_TYPE_IPV4_IFINDEX;
+                               resolved_hop->ifindex = newhop->ifindex;
+                             }
+                         }
+
+                       /* If the resolving route is an interface route,
+                        * it means the gateway we are looking up is connected
+                        * to that interface. (The actual network is _not_ 
onlink).
+                        * Therefore, the resolved route should have the 
original
+                        * gateway as nexthop as it is directly connected.
+                        *
+                        * On Linux, we have to set the onlink netlink flag 
because
+                        * otherwise, the kernel won't accept the route.
+                        */
+                       if (newhop->type == NEXTHOP_TYPE_IFINDEX
+                           || newhop->type == NEXTHOP_TYPE_IFNAME)
+                         {
+                           resolved_hop->flags |= NEXTHOP_FLAG_ONLINK;
+                           resolved_hop->type = NEXTHOP_TYPE_IPV4_IFINDEX;
+                           resolved_hop->gate.ipv4 = nexthop->gate.ipv4;
+                           resolved_hop->ifindex = newhop->ifindex;
+                         }
+
+                       nexthop_add(&nexthop->resolved, resolved_hop);
+                     }
+                   resolved = 1;
+                 }
+             return resolved;
+           }
          else
            {
              return 0;
@@ -428,7 +508,8 @@ nexthop_active_ipv6 (struct rib *rib, struct nexthop 
*nexthop, int set,
   struct route_node *rn;
   struct rib *match;
   int resolved;
-  struct nexthop *newhop;
+  struct nexthop *newhop, *tnewhop;
+  int recursing = 0;
   struct nexthop *resolved_hop;
 
   if (nexthop->type == NEXTHOP_TYPE_IPV6)
@@ -437,10 +518,17 @@ nexthop_active_ipv6 (struct rib *rib, struct nexthop 
*nexthop, int set,
   if (set)
     {
       UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE);
+      zebra_deregister_rnh_static_nexthops (nexthop->resolved, top);
       nexthops_free(nexthop->resolved);
       nexthop->resolved = NULL;
     }
 
+  /* Skip nexthops that have been filtered out due to route-map */
+  /* The nexthops are specific to this route and so the same */
+  /* nexthop for a different route may not have this flag set */
+  if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FILTERED))
+    return 0;
+
   /* Make lookup prefix. */
   memset (&p, 0, sizeof (struct prefix_ipv6));
   p.family = AF_INET6;
@@ -472,8 +560,7 @@ nexthop_active_ipv6 (struct rib *rib, struct nexthop 
*nexthop, int set,
 
       /* If there is no selected route or matched route is EGP, go up
          tree. */
-      if (! match
-         || match->type == ZEBRA_ROUTE_BGP)
+      if (! match)
        {
          do {
            rn = rn->parent;
@@ -509,6 +596,50 @@ nexthop_active_ipv6 (struct rib *rib, struct nexthop 
*nexthop, int set,
                    if (set)
                      {
                        SET_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE);
+                       SET_FLAG(rib->status, RIB_ENTRY_NEXTHOPS_CHANGED);
+
+                       resolved_hop = XCALLOC(MTYPE_NEXTHOP, sizeof (struct 
nexthop));
+                       SET_FLAG (resolved_hop->flags, NEXTHOP_FLAG_ACTIVE);
+                       /* See nexthop_active_ipv4 for a description how the
+                        * resolved nexthop is constructed. */
+                       if (newhop->type == NEXTHOP_TYPE_IPV6
+                           || newhop->type == NEXTHOP_TYPE_IPV6_IFINDEX
+                           || newhop->type == NEXTHOP_TYPE_IPV6_IFNAME)
+                         {
+                           resolved_hop->type = newhop->type;
+                           resolved_hop->gate.ipv6 = newhop->gate.ipv6;
+
+                           if (newhop->ifindex)
+                             {
+                               resolved_hop->type = NEXTHOP_TYPE_IPV6_IFINDEX;
+                               resolved_hop->ifindex = newhop->ifindex;
+                             }
+                         }
+
+                       if (newhop->type == NEXTHOP_TYPE_IFINDEX
+                           || newhop->type == NEXTHOP_TYPE_IFNAME)
+                         {
+                               resolved_hop->flags |= NEXTHOP_FLAG_ONLINK;
+                               resolved_hop->type = NEXTHOP_TYPE_IPV6_IFINDEX;
+                               resolved_hop->gate.ipv6 = nexthop->gate.ipv6;
+                               resolved_hop->ifindex = newhop->ifindex;
+                         }
+
+                       nexthop_add(&nexthop->resolved, resolved_hop);
+                     }
+                   resolved = 1;
+                 }
+             return resolved;
+           }
+         else if (rib->type == ZEBRA_ROUTE_STATIC)
+           {
+             resolved = 0;
+             for (ALL_NEXTHOPS_RO(match->nexthop, newhop, tnewhop, recursing))
+               if (CHECK_FLAG (newhop->flags, NEXTHOP_FLAG_FIB))
+                 {
+                   if (set)
+                     {
+                       SET_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE);
 
                        resolved_hop = XCALLOC(MTYPE_NEXTHOP, sizeof (struct 
nexthop));
                        SET_FLAG (resolved_hop->flags, NEXTHOP_FLAG_ACTIVE);
@@ -553,7 +684,7 @@ nexthop_active_ipv6 (struct rib *rib, struct nexthop 
*nexthop, int set,
 }
 
 struct rib *
-rib_match_ipv4_safi (struct in_addr addr, safi_t safi, int skip_bgp,
+rib_match_ipv4_safi (struct in_addr addr, safi_t safi,
                     struct route_node **rn_out, vrf_id_t vrf_id)
 {
   struct route_table *table;
@@ -584,7 +715,7 @@ rib_match_ipv4_safi (struct in_addr addr, safi_t safi, int 
skip_bgp,
 
       /* If there is no selected route or matched route is EGP, go up
          tree. */
-      if (!match || (skip_bgp && (match->type == ZEBRA_ROUTE_BGP)))
+      if (! match)
        {
          do {
            rn = rn->parent;
@@ -621,29 +752,22 @@ rib_match_ipv4_multicast (struct in_addr addr, struct 
route_node **rn_out,
 {
   struct rib *rib = NULL, *mrib = NULL, *urib = NULL;
   struct route_node *m_rn = NULL, *u_rn = NULL;
-  int skip_bgp = 0; /* bool */
 
   switch (ipv4_multicast_mode)
     {
     case MCAST_MRIB_ONLY:
-      return rib_match_ipv4_safi (addr, SAFI_MULTICAST, skip_bgp, rn_out,
-                                  vrf_id);
+      return rib_match_ipv4_safi (addr, SAFI_MULTICAST, rn_out, vrf_id);
     case MCAST_URIB_ONLY:
-      return rib_match_ipv4_safi (addr, SAFI_UNICAST, skip_bgp, rn_out,
-                                  vrf_id);
+      return rib_match_ipv4_safi (addr, SAFI_UNICAST, rn_out, vrf_id);
     case MCAST_NO_CONFIG:
     case MCAST_MIX_MRIB_FIRST:
-      rib = mrib = rib_match_ipv4_safi (addr, SAFI_MULTICAST, skip_bgp, &m_rn,
-                                        vrf_id);
+      rib = mrib = rib_match_ipv4_safi (addr, SAFI_MULTICAST, &m_rn, vrf_id);
       if (!mrib)
-        rib = urib = rib_match_ipv4_safi (addr, SAFI_UNICAST, skip_bgp, &u_rn,
-                                          vrf_id);
+        rib = urib = rib_match_ipv4_safi (addr, SAFI_UNICAST, &u_rn, vrf_id);
       break;
     case MCAST_MIX_DISTANCE:
-      mrib = rib_match_ipv4_safi (addr, SAFI_MULTICAST, skip_bgp, &m_rn,
-                                  vrf_id);
-      urib = rib_match_ipv4_safi (addr, SAFI_UNICAST, skip_bgp, &u_rn,
-                                  vrf_id);
+      mrib = rib_match_ipv4_safi (addr, SAFI_MULTICAST, &m_rn, vrf_id);
+      urib = rib_match_ipv4_safi (addr, SAFI_UNICAST, &u_rn, vrf_id);
       if (mrib && urib)
        rib = urib->distance < mrib->distance ? urib : mrib;
       else if (mrib)
@@ -652,10 +776,8 @@ rib_match_ipv4_multicast (struct in_addr addr, struct 
route_node **rn_out,
        rib = urib;
       break;
     case MCAST_MIX_PFXLEN:
-      mrib = rib_match_ipv4_safi (addr, SAFI_MULTICAST, skip_bgp, &m_rn,
-                                  vrf_id);
-      urib = rib_match_ipv4_safi (addr, SAFI_UNICAST, skip_bgp, &u_rn,
-                                  vrf_id);
+      mrib = rib_match_ipv4_safi (addr, SAFI_MULTICAST, &m_rn, vrf_id);
+      urib = rib_match_ipv4_safi (addr, SAFI_UNICAST, &u_rn, vrf_id);
       if (mrib && urib)
        rib = u_rn->p.prefixlen > m_rn->p.prefixlen ? urib : mrib;
       else if (mrib)
@@ -727,7 +849,7 @@ rib_lookup_ipv4 (struct prefix_ipv4 *p, vrf_id_t vrf_id)
        break;
     }
 
-  if (! match || match->type == ZEBRA_ROUTE_BGP)
+  if (! match)
     return NULL;
 
   if (match->type == ZEBRA_ROUTE_CONNECT)
@@ -855,8 +977,7 @@ rib_match_ipv6 (struct in6_addr *addr, vrf_id_t vrf_id)
 
       /* If there is no selected route or matched route is EGP, go up
          tree. */
-      if (! match 
-         || match->type == ZEBRA_ROUTE_BGP)
+      if (! match)
        {
          do {
            rn = rn->parent;
@@ -1016,10 +1137,12 @@ static int
 nexthop_active_update (struct route_node *rn, struct rib *rib, int set)
 {
   struct nexthop *nexthop;
-  unsigned int prev_active, prev_index, new_active;
+  unsigned int prev_active, prev_index, new_active, old_num_nh;
+
+  old_num_nh = rib->nexthop_active_num;
 
   rib->nexthop_active_num = 0;
-  UNSET_FLAG (rib->status, RIB_ENTRY_CHANGED);
+  UNSET_FLAG (rib->status, RIB_ENTRY_NEXTHOPS_CHANGED);
 
   for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next)
   {
@@ -1029,8 +1152,12 @@ nexthop_active_update (struct route_node *rn, struct rib 
*rib, int set)
       rib->nexthop_active_num++;
     if (prev_active != new_active ||
        prev_index != nexthop->ifindex)
-      SET_FLAG (rib->status, RIB_ENTRY_CHANGED);
+      SET_FLAG (rib->status, RIB_ENTRY_NEXTHOPS_CHANGED);
   }
+
+  if (old_num_nh != rib->nexthop_active_num)
+    SET_FLAG (rib->status, RIB_ENTRY_NEXTHOPS_CHANGED);
+
   return rib->nexthop_active_num;
 }
 
@@ -1214,6 +1341,8 @@ rib_process (struct route_node *rn)
 
   RNODE_FOREACH_RIB_SAFE (rn, rib, next)
     {
+      UNSET_FLAG(rib->status, RIB_ENTRY_NEXTHOPS_CHANGED);
+
       /* Currently installed rib. */
       if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED))
         {
@@ -1240,7 +1369,14 @@ rib_process (struct route_node *rn)
         }
       
       /* Skip unreachable nexthop. */
-      if (! nexthop_active_update (rn, rib, 0))
+      /* With static routes that may have recursive nexthops, calling
+       * nexthop_active_update will clear the ZEBRA_FLAG_CHANGED flag
+       * as the top level NH may not have changed. Those flags are set
+       * by the NHT evaluation. So, we skip an active_update_check here
+       * for static routes as its job has already been done.
+       */
+      if (rib->type != ZEBRA_ROUTE_STATIC &&
+         ! nexthop_active_update (rn, rib, 0))
         continue;
 
       /* Infinit distance. */
@@ -1303,7 +1439,7 @@ rib_process (struct route_node *rn)
       if (IS_ZEBRA_DEBUG_RIB)
        rnode_debug (rn, "Updating existing route, select %p, fib %p",
                      (void *)select, (void *)fib);
-      if (CHECK_FLAG (select->status, RIB_ENTRY_CHANGED))
+      if (CHECK_FLAG (select->status, RIB_ENTRY_NEXTHOPS_CHANGED))
         {
           if (info->safi == SAFI_UNICAST)
            zfpm_trigger_update (rn, "updating existing route");
@@ -1313,12 +1449,20 @@ rib_process (struct route_node *rn)
             rib_uninstall_kernel (rn, select);
 
           /* Set real nexthop. */
-          nexthop_active_update (rn, select, 1);
-  
-          if (! RIB_SYSTEM_ROUTE (select))
-            rib_install_kernel (rn, select);
-          redistribute_add (&rn->p, select);
-        }
+         /* Need to check if any NHs are active to clear the
+          * the selected flag
+          */
+          if (nexthop_active_update (rn, select, 1))
+           {
+             if (! RIB_SYSTEM_ROUTE (select))
+               rib_install_kernel (rn, select);
+             redistribute_add (&rn->p, select);
+           }
+         else
+           {
+             UNSET_FLAG (select->flags, ZEBRA_FLAG_SELECTED);
+           }
+       }
       else if (! RIB_SYSTEM_ROUTE (select))
         {
           /* Housekeeping code to deal with 
@@ -1374,12 +1518,13 @@ rib_process (struct route_node *rn)
         zfpm_trigger_update (rn, "new route selected");
 
       /* Set real nexthop. */
-      nexthop_active_update (rn, select, 1);
-
-      if (! RIB_SYSTEM_ROUTE (select))
-        rib_install_kernel (rn, select);
-      SET_FLAG (select->flags, ZEBRA_FLAG_SELECTED);
-      redistribute_add (&rn->p, select);
+      if (nexthop_active_update (rn, select, 1))
+       {
+         if (! RIB_SYSTEM_ROUTE (select))
+           rib_install_kernel (rn, select);
+         SET_FLAG (select->flags, ZEBRA_FLAG_SELECTED);
+         redistribute_add (&rn->p, select);
+       }
     }
 
   /* FIB route was removed, should be deleted */
@@ -1515,7 +1660,7 @@ rib_meta_queue_add (struct meta_queue *mq, struct 
route_node *rn)
 }
 
 /* Add route_node to work queue and schedule processing */
-static void
+void
 rib_queue_add (struct zebra_t *zebra, struct route_node *rn)
 {
   assert (zebra && rn);
@@ -1729,7 +1874,8 @@ rib_unlink (struct route_node *rn, struct rib *rib)
     }
 
   /* free RIB and nexthops */
-  nexthops_free(rib->nexthop);
+  zebra_deregister_rnh_static_nexthops (rib->nexthop, rn);
+  nexthops_free (rib->nexthop);
   XFREE (MTYPE_RIB, rib);
 
 }
@@ -2235,6 +2381,7 @@ static_install_route (afi_t afi, safi_t safi, struct 
prefix *p, struct static_ro
   struct rib *rib;
   struct route_node *rn;
   struct route_table *table;
+  struct prefix nh_p;
 
   /* Lookup table.  */
   table = zebra_vrf_table (afi, safi, si->vrf_id);
@@ -2265,6 +2412,10 @@ static_install_route (afi_t afi, safi_t safi, struct 
prefix *p, struct static_ro
         {
        case STATIC_IPV4_GATEWAY:
          rib_nexthop_ipv4_add (rib, &si->addr.ipv4, NULL);
+         nh_p.family = AF_INET;
+         nh_p.prefixlen = IPV4_MAX_BITLEN;
+         nh_p.u.prefix4 = si->addr.ipv4;
+         zebra_register_rnh_static_nh(&nh_p, rn);
          break;
        case STATIC_IPV4_IFNAME:
          rib_nexthop_ifname_add (rib, si->ifname);
@@ -2274,6 +2425,10 @@ static_install_route (afi_t afi, safi_t safi, struct 
prefix *p, struct static_ro
          break;
        case STATIC_IPV6_GATEWAY:
          rib_nexthop_ipv6_add (rib, &si->addr.ipv6);
+         nh_p.family = AF_INET6;
+         nh_p.prefixlen = IPV6_MAX_BITLEN;
+         nh_p.u.prefix6 = si->addr.ipv6;
+         zebra_register_rnh_static_nh(&nh_p, rn);
          break;
        case STATIC_IPV6_IFNAME:
          rib_nexthop_ifname_add (rib, si->ifname);
@@ -2301,6 +2456,10 @@ static_install_route (afi_t afi, safi_t safi, struct 
prefix *p, struct static_ro
         {
        case STATIC_IPV4_GATEWAY:
          rib_nexthop_ipv4_add (rib, &si->addr.ipv4, NULL);
+         nh_p.family = AF_INET;
+         nh_p.prefixlen = IPV4_MAX_BITLEN;
+         nh_p.u.prefix4 = si->addr.ipv4;
+         zebra_register_rnh_static_nh(&nh_p, rn);
          break;
        case STATIC_IPV4_IFNAME:
          rib_nexthop_ifname_add (rib, si->ifname);
@@ -2310,6 +2469,10 @@ static_install_route (afi_t afi, safi_t safi, struct 
prefix *p, struct static_ro
          break;
        case STATIC_IPV6_GATEWAY:
          rib_nexthop_ipv6_add (rib, &si->addr.ipv6);
+         nh_p.family = AF_INET6;
+         nh_p.prefixlen = IPV6_MAX_BITLEN;
+         nh_p.u.prefix6 = si->addr.ipv6;
+         zebra_register_rnh_static_nh(&nh_p, rn);
          break;
        case STATIC_IPV6_IFNAME:
          rib_nexthop_ifname_add (rib, si->ifname);
@@ -2365,6 +2528,7 @@ static_uninstall_route (afi_t afi, safi_t safi, struct 
prefix *p, struct static_
   struct rib *rib;
   struct nexthop *nexthop;
   struct route_table *table;
+  struct prefix nh_p;
 
   /* Lookup table.  */
   table = zebra_vrf_table (afi, safi, si->vrf_id);
@@ -2411,7 +2575,21 @@ static_uninstall_route (afi_t afi, safi_t safi, struct 
prefix *p, struct static_
     {
       if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB))
         rib_uninstall (rn, rib);
+
+      if (afi == AF_INET)
+       {
+         nh_p.family = AF_INET;
+         nh_p.prefixlen = IPV4_MAX_BITLEN;
+         nh_p.u.prefix4 = nexthop->gate.ipv4;
+       }
+      else
+       {
+         nh_p.family = AF_INET6;
+         nh_p.prefixlen = IPV6_MAX_BITLEN;
+         nh_p.u.prefix6 = nexthop->gate.ipv6;
+       }
       rib_nexthop_delete (rib, nexthop);
+      zebra_deregister_rnh_static_nh(&nh_p, rn);
       nexthop_free (nexthop);
       rib_queue_add (&zebrad, rn);
     }
diff --git a/zebra/zebra_rnh.c b/zebra/zebra_rnh.c
index e623409..d2061ca 100644
--- a/zebra/zebra_rnh.c
+++ b/zebra/zebra_rnh.c
@@ -54,8 +54,11 @@
   t;                                             \
 })
 
-static void free_state(struct rib *rib);
-static void copy_state(struct rnh *rnh, struct rib *rib);
+/* Default rtm_table for all clients */
+extern struct zebra_t zebrad;
+
+static void rib_free_state(struct rib *rib, struct route_node *rn);
+static void copy_state(struct rnh *rnh, struct rib *rib, struct route_node 
*rn);
 static int compare_state(struct rib *r1, struct rib *r2);
 static int send_client(struct rnh *rnh, struct zserv *client, vrf_id_t vrf_id);
 static void print_rnh(struct route_node *rn, struct vty *vty);
@@ -97,6 +100,7 @@ zebra_add_rnh (struct prefix *p, vrf_id_t vrfid)
     {
       rnh = XCALLOC(MTYPE_RNH, sizeof(struct rnh));
       rnh->client_list = list_new();
+      rnh->zebra_static_route_list = list_new();
       route_lock_node (rn);
       rn->info = rnh;
       rnh->node = rn;
@@ -143,7 +147,8 @@ zebra_delete_rnh (struct rnh *rnh)
     }
 
   list_free(rnh->client_list);
-  free_state(rnh->state);
+  list_free(rnh->zebra_static_route_list);
+  rib_free_state(rnh->state, rn);
   XFREE(MTYPE_RNH, rn->info);
   rn->info = NULL;
   route_unlock_node (rn);
@@ -178,10 +183,94 @@ zebra_remove_rnh_client (struct rnh *rnh, struct zserv 
*client)
                 rnh_str(rnh, buf, INET6_ADDRSTRLEN));
     }
   listnode_delete(rnh->client_list, client);
-  if (list_isempty(rnh->client_list))
+  if (list_isempty(rnh->client_list) &&
+      list_isempty(rnh->zebra_static_route_list))
+    zebra_delete_rnh(rnh);
+}
+
+void
+zebra_register_rnh_static_nh(struct prefix *nh, struct route_node *static_rn)
+{
+  struct rnh *rnh;
+
+  rnh = zebra_add_rnh(nh, 0);
+  if (rnh && !listnode_lookup(rnh->zebra_static_route_list, static_rn))
+    {
+      listnode_add(rnh->zebra_static_route_list, static_rn);
+    }
+}
+
+void
+zebra_deregister_rnh_static_nh(struct prefix *nh, struct route_node *static_rn)
+{
+  struct rnh *rnh;
+
+  rnh = zebra_lookup_rnh(nh, 0);
+  if (!rnh)
+    return;
+
+  listnode_delete(rnh->zebra_static_route_list, static_rn);
+
+  if (list_isempty(rnh->client_list) &&
+      list_isempty(rnh->zebra_static_route_list))
     zebra_delete_rnh(rnh);
 }
 
+void
+zebra_deregister_rnh_static_nexthops (struct nexthop *nexthop, struct 
route_node *rn)
+{
+  struct nexthop *nh;
+  struct prefix nh_p;
+
+  for (nh = nexthop; nh ; nh = nh->next)
+    {
+      if (nh->type == NEXTHOP_TYPE_IPV4)
+        {
+          nh_p.family = AF_INET;
+          nh_p.prefixlen = IPV4_MAX_BITLEN;
+          nh_p.u.prefix4 = nh->gate.ipv4;
+        }
+      else if (nh->type == NEXTHOP_TYPE_IPV6)
+        {
+          nh_p.family = AF_INET6;
+          nh_p.prefixlen = IPV6_MAX_BITLEN;
+          nh_p.u.prefix6 = nh->gate.ipv6;
+        }
+      zebra_deregister_rnh_static_nh(&nh_p, rn);
+    }
+}
+
+static int
+zebra_evaluate_rnh_nexthops(int family, struct rib *rib, struct route_node 
*prn,
+                           int proto)
+{
+  int at_least_one = 0;
+  int rmap_family;            /* Route map has diff AF family enum */
+  struct nexthop *nexthop;
+  int ret;
+
+  rmap_family = (family == AF_INET) ? AFI_IP : AFI_IP6;
+
+  if (prn && rib)
+    {
+      for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next)
+       {
+         ret = zebra_nht_route_map_check(rmap_family, proto, &prn->p, rib,
+                                         nexthop);
+         if (ret != RMAP_DENYMATCH)
+           {
+             SET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+             at_least_one++; /* at least one valid NH */
+           }
+         else
+           {
+             UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+           }
+       }
+    }
+  return (at_least_one);
+}
+
 int
 zebra_evaluate_rnh_table (vrf_id_t vrfid, int family, int force)
 {
@@ -192,17 +281,14 @@ zebra_evaluate_rnh_table (vrf_id_t vrfid, int family, int 
force)
   struct rnh *rnh;
   struct zserv *client;
   struct listnode *node;
-  struct rib *rib;
-  int rmap_family;            /* Route map has diff AF family enum */
-  route_map_result_t ret = RMAP_MATCH;
-  struct nexthop *nexthop;
+  struct rib *rib, *srib;
   int state_changed = 0;
   int at_least_one = 0;
   char bufn[INET6_ADDRSTRLEN];
   char bufp[INET6_ADDRSTRLEN];
-
-  rmap_family = (family == AF_INET) ? AFI_IP : AFI_IP6;
-
+  char bufs[INET6_ADDRSTRLEN];
+  struct route_node *static_rn;
+  struct nexthop *nexthop;
   ntable = lookup_rnh_table(vrfid, family);
   if (!ntable)
     {
@@ -223,6 +309,8 @@ zebra_evaluate_rnh_table (vrf_id_t vrfid, int family, int 
force)
          continue;
 
       rnh = nrn->info;
+      at_least_one = 0;
+
       prn = route_node_match(ptable, &nrn->p);
       if (!prn)
        rib = NULL;
@@ -249,7 +337,10 @@ zebra_evaluate_rnh_table (vrf_id_t vrfid, int family, int 
force)
 
       if (compare_state(rib, rnh->state))
        {
-         copy_state(rnh, rib);
+         if (rib)
+           UNSET_FLAG(rib->status, RIB_ENTRY_NEXTHOPS_CHANGED);
+
+         copy_state(rnh, rib, nrn);
          state_changed = 1;
        }
 
@@ -260,56 +351,116 @@ zebra_evaluate_rnh_table (vrf_id_t vrfid, int family, 
int force)
            prefix2str(&prn->p, bufp, INET6_ADDRSTRLEN);
          else
            strcpy(bufp, "null");
+
+         zlog_debug("%s: State changed for %s/%s", __FUNCTION__, bufn, bufp);
+
        }
 
       /* Notify registered clients */
+      rib = rnh->state;
+
       if (state_changed || force)
-       for (ALL_LIST_ELEMENTS_RO(rnh->client_list, node, client))
-         {
-           rib = rnh->state;
-           if (prn && rib)
-             {
-               at_least_one = 0;
-               for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next)
-                 {
-                   ret = zebra_nht_route_map_check(rmap_family, client->proto,
-                                                   &prn->p, rib, nexthop);
-                   if (ret == RMAP_DENYMATCH)
-                     {
-                       UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
-                     }
-                   else
-                     {
-                       SET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
-                       at_least_one++; /* at least one valid NH */
-                     }
-                 }
-               if (at_least_one)
-                 rnh->filtered[client->proto] = 0;
-               else
-                 rnh->filtered[client->proto] = 1;
-
-               if (IS_ZEBRA_DEBUG_NHT && (state_changed || force))
-                 zlog_debug("%srnh %s resolved through route %s - sending "
-                            "nexthop %s event to clients",
-                            at_least_one ? "":"(filtered)", bufn, bufp,
-                            rib ? "reachable" : "unreachable");
-
-               send_client(rnh, client, vrfid); /* Route-map passed */
-             }
-           else if (state_changed)
-             {
-               if (IS_ZEBRA_DEBUG_NHT && (state_changed || force))
-                 zlog_debug("rnh %s resolved through route %s - sending "
-                            "nexthop %s event to clients", bufn, bufp,
-                            rib ? "reachable" : "unreachable");
+       {
+         for (ALL_LIST_ELEMENTS_RO(rnh->client_list, node, client))
+           {
+             if (prn && rib)
+               {
+                 at_least_one = zebra_evaluate_rnh_nexthops(family, rib, prn,
+                                                            client->proto);
+                 if (at_least_one)
+                   rnh->filtered[client->proto] = 0;
+                 else
+                   rnh->filtered[client->proto] = 1;
+               }
+             else if (state_changed)
                rnh->filtered[client->proto] = 0;
-               send_client(rnh, client, vrfid);
-               /* We don't need to send a RNH update on force since no
-                * update really happened, just a route-map change.
-                */
-             }
-         }
+
+             if (IS_ZEBRA_DEBUG_NHT && (state_changed || force))
+               zlog_debug("%srnh %s resolved through route %s - sending "
+                          "nexthop %s event to clients",
+                          at_least_one ? "":"(filtered)", bufn, bufp,
+                          rib ? "reachable" : "unreachable");
+
+             send_client(rnh, client, vrfid); /* Route-map passed */
+           }
+
+         /* Now evaluate static client */
+         if (prn && rib)
+           {
+             at_least_one = zebra_evaluate_rnh_nexthops(family, rib, prn,
+                                                        ZEBRA_ROUTE_STATIC);
+             if (at_least_one)
+               rnh->filtered[ZEBRA_ROUTE_STATIC] = 0;
+             else
+               rnh->filtered[ZEBRA_ROUTE_STATIC] = 1;
+           }
+         else if (state_changed)
+           rnh->filtered[ZEBRA_ROUTE_STATIC] = 0;
+
+         for (ALL_LIST_ELEMENTS_RO(rnh->zebra_static_route_list, node,
+                                   static_rn))
+           {
+             RNODE_FOREACH_RIB(static_rn, srib)
+               {
+                 break;        /* pick the first and only(?) rib for static */
+               }
+
+             if (!srib)
+               {
+                 if (IS_ZEBRA_DEBUG_NHT)
+                   {
+                     prefix2str(&static_rn->p, bufs, INET6_ADDRSTRLEN);
+                     zlog_debug("%s: Unable to find RIB for static route %s, 
skipping NH resolution",
+                                __FUNCTION__, bufs);
+                     continue;
+                   }
+               }
+
+             /* Mark the appropriate static route's NH as filtered */
+             for (nexthop = srib->nexthop; nexthop; nexthop = nexthop->next)
+               {
+                 switch (nexthop->type)
+                   {
+                   case NEXTHOP_TYPE_IPV4:
+                   case NEXTHOP_TYPE_IPV4_IFINDEX:
+                     /* Don't see a use case for *_IFNAME */
+                     if (nexthop->gate.ipv4.s_addr == nrn->p.u.prefix4.s_addr)
+                       {
+                         if (at_least_one)
+                           UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FILTERED);
+                         else
+                           SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FILTERED);
+                       }
+                     break;
+                   case NEXTHOP_TYPE_IPV6:
+                   case NEXTHOP_TYPE_IPV6_IFINDEX:
+                     /* Don't see a use case for *_IFNAME */
+                     if (memcmp(&nexthop->gate.ipv6,&nrn->p.u.prefix6, 16) == 
0)
+                       {
+                         if (at_least_one)
+                           UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FILTERED);
+                         else
+                           SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FILTERED);
+                       }
+                     break;
+                   default:
+                     break;
+                   }
+               }
+
+             if (IS_ZEBRA_DEBUG_NHT && (state_changed || force))
+               zlog_debug("%srnh %s resolved through route %s - sending "
+                          "nexthop %s event to zebra",
+                          at_least_one ? "":"(filtered)", bufn, bufp,
+                          rib ? "reachable" : "unreachable");
+
+             if (srib && (state_changed || force))
+               {
+                 SET_FLAG(srib->status, RIB_ENTRY_NEXTHOPS_CHANGED);
+                 rib_queue_add(&zebrad, static_rn);
+               }
+           }
+       }
     }
   return 1;
 }
@@ -401,51 +552,27 @@ zebra_cleanup_rnh_client (vrf_id_t vrfid, int family, 
struct zserv *client)
  * free_state - free up the rib structure associated with the rnh.
  */
 static void
-free_state (struct rib *rib)
+rib_free_state (struct rib *rib, struct route_node *rn)
 {
-  struct nexthop *nexthop, *next;
 
   if (!rib)
     return;
 
   /* free RIB and nexthops */
-  for (nexthop = rib->nexthop; nexthop; nexthop = next)
-    {
-      next = nexthop->next;
-      nexthop_free (nexthop);
-    }
+  zebra_deregister_rnh_static_nexthops (rib->nexthop, rn);
+  nexthops_free(rib->nexthop);
   XFREE (MTYPE_RIB, rib);
 }
 
-/**
- * copy_nexthop - copy a nexthop to the rib structure.
- */
 static void
-rib_copy_nexthop (struct rib *state, struct nexthop *nh)
-{
-  struct nexthop *nexthop;
-
-  nexthop = nexthop_new();
-  nexthop->flags = nh->flags;
-  nexthop->type = nh->type;
-  nexthop->ifindex = nh->ifindex;
-  if (nh->ifname)
-    nexthop->ifname = XSTRDUP(0, nh->ifname);
-  memcpy(&(nexthop->gate), &(nh->gate), sizeof(union g_addr));
-  memcpy(&(nexthop->src), &(nh->src), sizeof(union g_addr));
-
-  rib_nexthop_add(state, nexthop);
-}
-
-static void
-copy_state (struct rnh *rnh, struct rib *rib)
+copy_state (struct rnh *rnh, struct rib *rib, struct route_node *rn)
 {
   struct rib *state;
   struct nexthop *nh;
 
   if (rnh->state)
     {
-      free_state(rnh->state);
+      rib_free_state(rnh->state, rn);
       rnh->state = NULL;
     }
 
@@ -457,16 +584,13 @@ copy_state (struct rnh *rnh, struct rib *rib)
   state->metric = rib->metric;
 
   for (nh = rib->nexthop; nh; nh = nh->next)
-    rib_copy_nexthop(state, nh);
+    rib_copy_nexthops(state, nh);
   rnh->state = state;
 }
 
 static int
 compare_state (struct rib *r1, struct rib *r2)
 {
-  struct nexthop *nh1;
-  struct nexthop *nh2;
-  u_char found_nh = 0;
 
   if (!r1 && !r2)
     return 0;
@@ -480,46 +604,8 @@ compare_state (struct rib *r1, struct rib *r2)
   if (r1->nexthop_num != r2->nexthop_num)
       return 1;
 
-  /* We need to verify that the nexthops for r1 match the nexthops for r2.
-   * Since it is possible for a rib entry to have the same nexthop multiple
-   * times (Example: [a,a]) we need to keep track of which r2 nexthops we have
-   * already used as a match against a r1 nexthop.  We track this
-   * via NEXTHOP_FLAG_MATCHED. Clear this flag for all r2 nexthops when you
-   * are finished.
-   *
-   * TRUE:  r1 [a,b], r2 [a,b]
-   * TRUE:  r1 [a,b], r2 [b,a]
-   * FALSE: r1 [a,b], r2 [a,c]
-   * FALSE: r1 [a,a], r2 [a,b]
-   */
-  for (nh1 = r1->nexthop; nh1; nh1 = nh1->next)
-    {
-      found_nh = 0;
-      for (nh2 = r2->nexthop; nh2; nh2 = nh2->next)
-        {
-          if (CHECK_FLAG (nh2->flags, NEXTHOP_FLAG_MATCHED))
-            continue;
-
-          if (nexthop_same_no_recurse(nh1, nh2))
-            {
-              SET_FLAG (nh2->flags, NEXTHOP_FLAG_MATCHED);
-              found_nh = 1;
-              break;
-            }
-        }
-
-      if (!found_nh)
-        {
-          for (nh2 = r2->nexthop; nh2; nh2 = nh2->next)
-            if (CHECK_FLAG (nh2->flags, NEXTHOP_FLAG_MATCHED))
-              UNSET_FLAG (nh2->flags, NEXTHOP_FLAG_MATCHED);
-          return 1;
-        }
-    }
-
-  for (nh2 = r2->nexthop; nh2; nh2 = nh2->next)
-    if (CHECK_FLAG (nh2->flags, NEXTHOP_FLAG_MATCHED))
-      UNSET_FLAG (nh2->flags, NEXTHOP_FLAG_MATCHED);
+  if (CHECK_FLAG(r1->status, RIB_ENTRY_NEXTHOPS_CHANGED))
+    return 1;
 
   return 0;
 }
@@ -665,5 +751,7 @@ print_rnh (struct route_node *rn, struct vty *vty)
   for (ALL_LIST_ELEMENTS_RO(rnh->client_list, node, client))
     vty_out(vty, " %s(fd %d)%s", zebra_route_string(client->proto),
            client->sock, rnh->filtered[client->proto] ? "(filtered)" : "");
+  if (!list_isempty(rnh->zebra_static_route_list))
+    vty_out(vty, " zebra%s", rnh->filtered[ZEBRA_ROUTE_STATIC] ? "(filtered)" 
: "");
   vty_out(vty, "%s", VTY_NEWLINE);
 }
diff --git a/zebra/zebra_rnh.h b/zebra/zebra_rnh.h
index 1f52cf5..77a913a 100644
--- a/zebra/zebra_rnh.h
+++ b/zebra/zebra_rnh.h
@@ -33,6 +33,7 @@ struct rnh
 #define ZEBRA_NHT_CONNECTED    0x1
   struct rib *state;
   struct list *client_list;
+  struct list *zebra_static_route_list; /* static routes dependent on this NH 
*/
   struct route_node *node;
   int filtered[ZEBRA_ROUTE_MAX]; /* if this has been filtered for client */
 };
@@ -41,6 +42,9 @@ extern struct rnh *zebra_add_rnh(struct prefix *p, vrf_id_t 
vrfid);
 extern struct rnh *zebra_lookup_rnh(struct prefix *p, vrf_id_t vrfid);
 extern void zebra_delete_rnh(struct rnh *rnh);
 extern void zebra_add_rnh_client(struct rnh *rnh, struct zserv *client, 
vrf_id_t vrf_id_t);
+extern void zebra_register_rnh_static_nh(struct prefix *, struct route_node *);
+extern void zebra_deregister_rnh_static_nh(struct prefix *, struct route_node 
*);
+extern void zebra_deregister_rnh_static_nexthops (struct nexthop *, struct 
route_node *); 
 extern void zebra_remove_rnh_client(struct rnh *rnh, struct zserv *client);
 extern int zebra_evaluate_rnh_table(vrf_id_t vrfid, int family, int force);
 extern int zebra_dispatch_rnh_table(vrf_id_t vrfid, int family, struct zserv 
*cl);
diff --git a/zebra/zebra_rnh_null.c b/zebra/zebra_rnh_null.c
index f45cba3..c30e3e4 100644
--- a/zebra/zebra_rnh_null.c
+++ b/zebra/zebra_rnh_null.c
@@ -10,3 +10,12 @@ int zebra_evaluate_rnh_table (vrf_id_t vrfid, int family, 
int force)
 
 void zebra_print_rnh_table (vrf_id_t vrfid, int family, struct vty *vty)
 {}
+
+void zebra_register_rnh_static_nh(struct prefix *p, struct route_node *rn)
+{}
+
+void zebra_deregister_rnh_static_nh(struct prefix *p, struct route_node *rn)
+{}
+
+void zebra_deregister_rnh_static_nexthops (struct nexthop *nexthop, struct 
route_node *rn)
+{}
diff --git a/zebra/zserv.c b/zebra/zserv.c
index 8f2a3f3..f887af9 100644
--- a/zebra/zserv.c
+++ b/zebra/zserv.c
@@ -697,7 +697,7 @@ zsend_ipv4_nexthop_lookup (struct zserv *client, struct 
in_addr addr,
   struct nexthop *nexthop;
 
   /* Lookup nexthop - eBGP excluded */
-  rib = rib_match_ipv4_safi (addr, SAFI_UNICAST, 1, NULL, vrf_id);
+  rib = rib_match_ipv4_safi (addr, SAFI_UNICAST, NULL, vrf_id);
 
   /* Get output stream. */
   s = client->obuf;
-- 
1.9.1


_______________________________________________
Quagga-dev mailing list
[email protected]
https://lists.quagga.net/mailman/listinfo/quagga-dev

Reply via email to