]> git.puffer.fish Git - mirror/frr.git/commitdiff
zebra: zebra-static-route-nht.patch
authorDonald Sharp <sharpd@cumulusnetworks.com>
Wed, 20 May 2015 00:47:22 +0000 (17:47 -0700)
committerDonald Sharp <sharpd@cumulusnetworks.com>
Wed, 20 May 2015 00:47:22 +0000 (17:47 -0700)
Use NHT to support static routes with NH derived from protocols.

lib/nexthop.h
zebra/rib.h
zebra/zebra_rib.c
zebra/zebra_rnh.c
zebra/zebra_rnh.h
zebra/zebra_rnh_null.c

index bddac6554f2bb87635e19811cfadaa379d613ac9..b3a128a2a86744a205421d891219e09d31338ce8 100644 (file)
@@ -63,6 +63,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;
index c2b28d196705b8382d383790da38d92e7cd71a9f..65d1667b54bd709d0cd199bafa4e47fbef657c4f 100644 (file)
@@ -68,7 +68,9 @@ struct rib
 
   /* RIB internal status */
   u_char status;
-#define RIB_ENTRY_REMOVED      (1 << 0)
+#define RIB_ENTRY_REMOVED         0x1
+  /* to simplify NHT logic when NHs change, instead of doing a NH by NH cmp */
+#define RIB_ENTRY_NEXTHOPS_CHANGED 0x2
 
   /* Nexthop information. */
   u_char nexthop_num;
@@ -342,9 +344,10 @@ extern struct nexthop *nexthop_ipv4_ifindex_add (struct rib *,
                                                  struct in_addr *,
                                                  struct in_addr *,
                                                  unsigned int);
-extern void nexthop_free (struct nexthop *nexthop);
-extern void nexthops_free (struct nexthop *nexthop);
+extern void nexthop_free (struct nexthop *nexthop, struct route_node *);
+extern void nexthops_free (struct nexthop *nexthop, struct route_node *);
 extern void nexthop_add (struct rib *rib, struct nexthop *nexthop);
+extern void copy_nexthops (struct rib *rib, struct nexthop *nh);
 
 extern int nexthop_has_fib_child(struct nexthop *);
 extern void rib_lookup_and_dump (struct prefix_ipv4 *);
@@ -399,6 +402,9 @@ extern void rib_sweep_route (void);
 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 (struct prefix *p, struct in_addr *gate, const char *ifname,
index ee50ad70306647fa034f3f262e453956729c13ea..9499e80422b53ec9b715aac3fe3932aafe8e0abf 100644 (file)
@@ -182,7 +182,7 @@ vrf_static_table (afi_t afi, safi_t safi, u_int32_t id)
 }
 
 /* Add nexthop to the end of a nexthop list.  */
-static void
+void
 _nexthop_add (struct nexthop **target, struct nexthop *nexthop)
 {
   struct nexthop *last;
@@ -204,6 +204,50 @@ nexthop_add (struct rib *rib, struct nexthop *nexthop)
   rib->nexthop_num++;
 }
 
+static void
+_copy_nexthops (struct nexthop **tnh, struct nexthop *nh)
+{
+  struct nexthop *nexthop;
+  struct nexthop *nh1;
+
+  for (nh1 = nh; nh1; nh1 = nh1->next)
+    {
+      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));
+      _nexthop_add(tnh, nexthop);
+
+      if (CHECK_FLAG(nh1->flags, NEXTHOP_FLAG_RECURSIVE))
+       _copy_nexthops(&nexthop->resolved, nh1->resolved);
+    }
+}
+
+/**
+ * copy_nexthop - copy a nexthop to the rib structure.
+ */
+void
+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));
+  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
 nexthop_delete (struct rib *rib, struct nexthop *nexthop)
@@ -219,25 +263,39 @@ nexthop_delete (struct rib *rib, struct nexthop *nexthop)
 
 /* Free nexthop. */
 void
-nexthop_free (struct nexthop *nexthop)
+nexthop_free (struct nexthop *nexthop, struct route_node *rn)
 {
   if (nexthop->ifname)
     XFREE (0, nexthop->ifname);
   if (nexthop->resolved)
-    nexthops_free(nexthop->resolved);
+    nexthops_free(nexthop->resolved, rn);
   XFREE (MTYPE_NEXTHOP, nexthop);
 }
 
 /* Frees a list of nexthops */
 void
-nexthops_free (struct nexthop *nexthop)
+nexthops_free (struct nexthop *nexthop, struct route_node *rn)
 {
   struct nexthop *nh, *next;
+  struct prefix nh_p;
 
   for (nh = nexthop; nh; nh = next)
     {
       next = nh->next;
-      nexthop_free (nh);
+      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);
+      nexthop_free (nh, rn);
     }
 }
 
@@ -394,8 +452,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;
@@ -403,10 +462,16 @@ nexthop_active_ipv4 (struct rib *rib, struct nexthop *nexthop, int set,
   if (set)
     {
       UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE);
-      nexthops_free(nexthop->resolved);
+      nexthops_free(nexthop->resolved, top);
       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_ipv4));
   p.family = AF_INET;
@@ -438,8 +503,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;
@@ -474,6 +538,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);
@@ -509,6 +574,57 @@ nexthop_active_ipv4 (struct rib *rib, struct nexthop *nexthop, int set,
                            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);
+                       /* 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;
@@ -536,7 +652,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)
@@ -545,10 +662,16 @@ nexthop_active_ipv6 (struct rib *rib, struct nexthop *nexthop, int set,
   if (set)
     {
       UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE);
-      nexthops_free(nexthop->resolved);
+      nexthops_free(nexthop->resolved, top);
       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;
@@ -580,8 +703,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;
@@ -613,6 +735,50 @@ nexthop_active_ipv6 (struct rib *rib, struct nexthop *nexthop, int set,
              for (newhop = match->nexthop; newhop; newhop = newhop->next)
                if (CHECK_FLAG (newhop->flags, NEXTHOP_FLAG_FIB)
                    && ! CHECK_FLAG (newhop->flags, NEXTHOP_FLAG_RECURSIVE))
+                 {
+                   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)
                      {
@@ -698,8 +864,7 @@ rib_match_ipv4 (struct in_addr addr)
 
       /* 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;
@@ -755,7 +920,7 @@ rib_lookup_ipv4 (struct prefix_ipv4 *p)
        break;
     }
 
-  if (! match || match->type == ZEBRA_ROUTE_BGP)
+  if (! match)
     return NULL;
 
   if (match->type == ZEBRA_ROUTE_CONNECT)
@@ -883,8 +1048,7 @@ rib_match_ipv6 (struct in6_addr *addr)
 
       /* 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;
@@ -1047,7 +1211,9 @@ 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->flags, ZEBRA_FLAG_CHANGED);
@@ -1060,8 +1226,20 @@ 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->flags, ZEBRA_FLAG_CHANGED);
+      {
+       SET_FLAG (rib->flags, ZEBRA_FLAG_CHANGED);
+       SET_FLAG (rib->status, RIB_ENTRY_NEXTHOPS_CHANGED);
+      }
   }
+
+  if (old_num_nh != rib->nexthop_active_num)
+    SET_FLAG (rib->flags, ZEBRA_FLAG_CHANGED);
+
+  if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_CHANGED))
+    {
+      SET_FLAG (rib->status, RIB_ENTRY_NEXTHOPS_CHANGED);
+    }
+
   return rib->nexthop_active_num;
 }
 
@@ -1232,6 +1410,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))
         {
@@ -1258,7 +1438,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. */
@@ -1330,12 +1517,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 
@@ -1391,12 +1586,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 */
@@ -1537,7 +1733,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)
 {
   char buf[INET_ADDRSTRLEN];
@@ -1772,7 +1968,7 @@ rib_unlink (struct route_node *rn, struct rib *rib)
     }
 
   /* free RIB and nexthops */
-  nexthops_free(rib->nexthop);
+  nexthops_free(rib->nexthop, rn);
   XFREE (MTYPE_RIB, rib);
 
 }
@@ -2284,6 +2480,7 @@ static_install_ipv4 (struct prefix *p, struct static_ipv4 *si)
   struct rib *rib;
   struct route_node *rn;
   struct route_table *table;
+  struct prefix nh_p;
 
   /* Lookup table.  */
   table = vrf_table (AFI_IP, SAFI_UNICAST, 0);
@@ -2314,6 +2511,10 @@ static_install_ipv4 (struct prefix *p, struct static_ipv4 *si)
         {
           case STATIC_IPV4_GATEWAY:
             nexthop_ipv4_add (rib, &si->gate.ipv4, NULL);
+           nh_p.family = AF_INET;
+           nh_p.prefixlen = IPV4_MAX_BITLEN;
+           nh_p.u.prefix4 = si->gate.ipv4;
+           zebra_register_rnh_static_nh(&nh_p, rn);
             break;
           case STATIC_IPV4_IFNAME:
             nexthop_ifname_add (rib, si->gate.ifname);
@@ -2340,6 +2541,10 @@ static_install_ipv4 (struct prefix *p, struct static_ipv4 *si)
         {
           case STATIC_IPV4_GATEWAY:
             nexthop_ipv4_add (rib, &si->gate.ipv4, NULL);
+           nh_p.family = AF_INET;
+           nh_p.prefixlen = IPV4_MAX_BITLEN;
+           nh_p.u.prefix4 = si->gate.ipv4;
+           zebra_register_rnh_static_nh(&nh_p, rn);
             break;
           case STATIC_IPV4_IFNAME:
             nexthop_ifname_add (rib, si->gate.ifname);
@@ -2382,6 +2587,7 @@ static_uninstall_ipv4 (struct prefix *p, struct static_ipv4 *si)
   struct rib *rib;
   struct nexthop *nexthop;
   struct route_table *table;
+  struct prefix nh_p;
 
   /* Lookup table.  */
   table = vrf_table (AFI_IP, SAFI_UNICAST, 0);
@@ -2428,8 +2634,13 @@ static_uninstall_ipv4 (struct prefix *p, struct static_ipv4 *si)
     {
       if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB))
         rib_uninstall (rn, rib);
+
+      nh_p.family = AF_INET;
+      nh_p.prefixlen = IPV4_MAX_BITLEN;
+      nh_p.u.prefix4 = nexthop->gate.ipv4;
       nexthop_delete (rib, nexthop);
-      nexthop_free (nexthop);
+      zebra_deregister_rnh_static_nh(&nh_p, rn);
+      nexthop_free (nexthop, rn);
       rib_queue_add (&zebrad, rn);
     }
   /* Unlock node. */
@@ -2737,11 +2948,8 @@ rib_add_ipv6_multipath (struct prefix_ipv6 *p, struct rib *rib, safi_t safi,
   struct rib *same = NULL;
   struct nexthop *nexthop;
   int ret = 0;
-  int table_id = 0;
 
-  if (rib)
-    table_id = rib->table;
-  else
+  if (!rib)
     return 0;                  /* why are we getting called with NULL rib */
 
   /* Lookup table.  */
@@ -2961,6 +3169,7 @@ static_install_ipv6 (struct prefix *p, struct static_ipv6 *si)
   struct rib *rib;
   struct route_table *table;
   struct route_node *rn;
+  struct prefix nh_p;
 
   /* Lookup table.  */
   table = vrf_table (AFI_IP6, SAFI_UNICAST, 0);
@@ -2992,6 +3201,10 @@ static_install_ipv6 (struct prefix *p, struct static_ipv6 *si)
        {
        case STATIC_IPV6_GATEWAY:
          nexthop_ipv6_add (rib, &si->ipv6);
+         nh_p.family = AF_INET6;
+         nh_p.prefixlen = IPV6_MAX_BITLEN;
+         nh_p.u.prefix6 = si->ipv6;
+         zebra_register_rnh_static_nh(&nh_p, rn);
          break;
        case STATIC_IPV6_IFNAME:
          nexthop_ifname_add (rib, si->ifname);
@@ -3018,6 +3231,10 @@ static_install_ipv6 (struct prefix *p, struct static_ipv6 *si)
        {
        case STATIC_IPV6_GATEWAY:
          nexthop_ipv6_add (rib, &si->ipv6);
+         nh_p.family = AF_INET6;
+         nh_p.prefixlen = IPV6_MAX_BITLEN;
+         nh_p.u.prefix6 = si->ipv6;
+         zebra_register_rnh_static_nh(&nh_p, rn);
          break;
        case STATIC_IPV6_IFNAME:
          nexthop_ifname_add (rib, si->ifname);
@@ -3061,6 +3278,7 @@ static_uninstall_ipv6 (struct prefix *p, struct static_ipv6 *si)
   struct route_node *rn;
   struct rib *rib;
   struct nexthop *nexthop;
+  struct prefix nh_p;
 
   /* Lookup table.  */
   table = vrf_table (AFI_IP6, SAFI_UNICAST, 0);
@@ -3109,8 +3327,13 @@ static_uninstall_ipv6 (struct prefix *p, struct static_ipv6 *si)
     {
       if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB))
         rib_uninstall (rn, rib);
+
+      nh_p.family = AF_INET6;
+      nh_p.prefixlen = IPV6_MAX_BITLEN;
+      nh_p.u.prefix6 = nexthop->gate.ipv6;
       nexthop_delete (rib, nexthop);
-      nexthop_free (nexthop);
+      zebra_deregister_rnh_static_nh(&nh_p, rn);
+      nexthop_free (nexthop, rn);
       rib_queue_add (&zebrad, rn);
     }
   /* Unlock node. */
index 85ab0910a48935c322eeee41e2a24906872e4f50..67d27c8823d1985ec59169a39e2e3e997bd06b62 100644 (file)
   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 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);
 static void print_rnh(struct route_node *rn, struct vty *vty);
@@ -97,6 +100,7 @@ zebra_add_rnh (struct prefix *p, u_int32_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);
+  free_state(rnh->state, rn);
   XFREE(MTYPE_RNH, rn->info);
   rn->info = NULL;
   route_unlock_node (rn);
@@ -178,10 +183,70 @@ 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);
 }
 
+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 (int vrfid, int family, int force)
 {
@@ -192,17 +257,14 @@ zebra_evaluate_rnh_table (int 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 +285,8 @@ zebra_evaluate_rnh_table (int 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 +313,10 @@ zebra_evaluate_rnh_table (int 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 +327,117 @@ zebra_evaluate_rnh_table (int 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); /* 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);
-               /* 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); /* 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->flags, ZEBRA_FLAG_CHANGED);
+                 SET_FLAG(srib->status, RIB_ENTRY_NEXTHOPS_CHANGED);
+                 rib_queue_add(&zebrad, static_rn);
+               }
+           }
+       }
     }
   return 1;
 }
@@ -401,51 +529,26 @@ zebra_cleanup_rnh_client (int vrfid, int family, struct zserv *client)
  * free_state - free up the rib structure associated with the rnh.
  */
 static void
-free_state (struct 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);
-    }
+  nexthops_free(rib->nexthop, rn);
   XFREE (MTYPE_RIB, rib);
 }
 
-/**
- * copy_nexthop - copy a nexthop to the rib structure.
- */
-static void
-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));
-
-  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);
+      free_state(rnh->state, rn);
       rnh->state = NULL;
     }
 
@@ -457,16 +560,13 @@ copy_state (struct rnh *rnh, struct rib *rib)
   state->metric = rib->metric;
 
   for (nh = rib->nexthop; nh; nh = nh->next)
-    copy_nexthop(state, nh);
+    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 +580,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 +727,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);
 }
index 92e4c7a88c7b69906000a9d4b649cae7947b4dc8..53e932aa4962d886071c2e89641c86de8e89cefa 100644 (file)
@@ -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,8 @@ extern struct rnh *zebra_add_rnh(struct prefix *p, u_int32_t vrfid);
 extern struct rnh *zebra_lookup_rnh(struct prefix *p, u_int32_t vrfid);
 extern void zebra_delete_rnh(struct rnh *rnh);
 extern void zebra_add_rnh_client(struct rnh *rnh, struct zserv *client);
+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_remove_rnh_client(struct rnh *rnh, struct zserv *client);
 extern int zebra_evaluate_rnh_table(int vrfid, int family, int force);
 extern int zebra_dispatch_rnh_table(int vrfid, int family, struct zserv *cl);
index 40f0c87288dd98990ea49393c677d3326fb05e81..da1bab876ae163e93e0a9ebc71cbc44677e72761 100644 (file)
@@ -8,3 +8,9 @@ int zebra_evaluate_rnh_table (int vrfid, int family, int force)
 
 void zebra_print_rnh_table (int 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)
+{}