Commit 98bdd04e for quagga.net

commit 98bdd04e3fdd6790453e0dce13853c2472ce5d15
Author: Gerrie Roos <gerrie@xiplink.com>
Date:   Wed Jan 17 21:16:55 2018 +0000

    zebra/redistribute: Implicit withdraw needs to be explicit if update isn't sent

    * redistribute.{c,h}: (redistribute_add) update of redistributed route is an
      implicit withdraw of the old route. The RIB therefore doesn't bother
      deleting the old route, if doing a redistribute_add. However, if the
      updated route is /not/ sent to a client that received the previous route,
      then such a client is left with bogus state.

      This can happen when the new route is of a type that the client doesn't
      redistibute.

      Fix by passing in the old route, and adding an explicit delete of the old
      route where necessary.

    * zebra_rib.c: (rib_process) pass on the old route too, as per above.
    * redistribute_null.c: testing stub

    See bug #971

    Modification to fix the problem at the redistribute layer instead of the RIB
    suggested by paul@jakma.org.

diff --git a/zebra/redistribute.c b/zebra/redistribute.c
index a7a6b258..6d466acf 100644
--- a/zebra/redistribute.c
+++ b/zebra/redistribute.c
@@ -182,11 +182,11 @@ zebra_redistribute (struct zserv *client, int type, vrf_id_t vrf_id)
 }

 void
-redistribute_add (struct prefix *p, struct rib *rib)
+redistribute_add (struct prefix *p, struct rib *rib, struct rib *rib_old)
 {
   struct listnode *node, *nnode;
   struct zserv *client;
-
+
   for (ALL_LIST_ELEMENTS (zebrad.client_list, node, nnode, client))
     {
       if ((is_default (p) &&
@@ -204,6 +204,21 @@ redistribute_add (struct prefix *p, struct rib *rib)
 	      zsend_route_multipath (ZEBRA_IPV6_ROUTE_ADD, client, p, rib);
 	    }
         }
+      else if (rib_old && vrf_bitmap_check (client->redist[rib_old->type],
+                                            rib_old->vrf_id))
+        {
+          /* redistribute_add has implicit withdraw semantics, so there
+           * may be an old route already redistributed that is being updated.
+           *
+           * However, if the new route is of a type that is /not/ redistributed
+           * to the client, then we must ensure the old route is explicitly
+           * withdrawn.
+           */
+          if (p->family == AF_INET)
+            zsend_route_multipath (ZEBRA_IPV4_ROUTE_DELETE, client, p, rib_old);
+          if (p->family == AF_INET6)
+            zsend_route_multipath (ZEBRA_IPV6_ROUTE_DELETE, client, p, rib_old);
+        }
     }
 }

diff --git a/zebra/redistribute.h b/zebra/redistribute.h
index ce840094..196d7c30 100644
--- a/zebra/redistribute.h
+++ b/zebra/redistribute.h
@@ -34,7 +34,7 @@ extern void zebra_redistribute_default_add (int, struct zserv *, int,
 extern void zebra_redistribute_default_delete (int, struct zserv *, int,
     vrf_id_t);

-extern void redistribute_add (struct prefix *, struct rib *);
+extern void redistribute_add (struct prefix *, struct rib *new, struct rib *old);
 extern void redistribute_delete (struct prefix *, struct rib *);

 extern void zebra_interface_up_update (struct interface *);
diff --git a/zebra/redistribute_null.c b/zebra/redistribute_null.c
index 5584d12c..99272b6b 100644
--- a/zebra/redistribute_null.c
+++ b/zebra/redistribute_null.c
@@ -44,7 +44,7 @@ void zebra_redistribute_default_delete (int a, struct zserv *b, int c,
 { return; }
 #endif

-void redistribute_add (struct prefix *a, struct rib *b)
+void redistribute_add (struct prefix *a, struct rib *b, struct rib *c)
 { return; }
 #ifdef HAVE_SYS_WEAK_ALIAS_PRAGMA
 #pragma weak redistribute_delete = redistribute_add
diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c
index 9ca02904..857abcab 100644
--- a/zebra/zebra_rib.c
+++ b/zebra/zebra_rib.c
@@ -1320,7 +1320,7 @@ rib_process (struct route_node *rn)
         {
           /* Install new or replace existing redistributed entry */
           SET_FLAG (new_selected->flags, ZEBRA_FLAG_SELECTED);
-          redistribute_add (&rn->p, new_selected);
+          redistribute_add (&rn->p, new_selected, old_selected);
         }
      }