Browse Source

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.
Gerrie Roos 1 year ago
parent
commit
98bdd04e3f
4 changed files with 20 additions and 5 deletions
  1. 17 2
      zebra/redistribute.c
  2. 1 1
      zebra/redistribute.h
  3. 1 1
      zebra/redistribute_null.c
  4. 1 1
      zebra/zebra_rib.c

+ 17 - 2
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);
+        }
     }
 }
 

+ 1 - 1
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 *);

+ 1 - 1
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

+ 1 - 1
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);
         }
      }