]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Nest: Fix race condition during reconfiguration
authorOndrej Zajicek (work) <santiago@crfreenet.org>
Tue, 3 Jul 2018 17:21:42 +0000 (19:21 +0200)
committerOndrej Zajicek (work) <santiago@crfreenet.org>
Tue, 3 Jul 2018 17:21:59 +0000 (19:21 +0200)
If export filter is changed during reconfiguration and a route disappears
between reconfiguration and refeed (e.g., if the route is a static route
also removed during the reconfiguration), the route is not withdrawn.
The patch fixes that by adding tx reconfiguration timestamp.

nest/proto.c
nest/protocol.h
nest/rt-table.c
proto/pipe/pipe.c

index 552d53ae110b0e6e2edc1239f3159d0c6c0e1351..565b238f4a77e03883050c5adcb8ed4f55da7fb1 100644 (file)
@@ -433,10 +433,17 @@ proto_reconfigure(struct proto *p, struct proto_config *oc, struct proto_config
   if (p->proto->multitable)
     return 1;
 
+  int import_changed = ! filter_same(nc->in_filter, oc->in_filter);
+  int export_changed = ! filter_same(nc->out_filter, oc->out_filter);
+
+  /* We treat a change in preferences by reimporting routes */
+  if (nc->preference != oc->preference)
+    import_changed = 1;
+
   /* Update filters and limits in the main announce hook
      Note that this also resets limit state */
   if (p->main_ahook)
-    {  
+    {
       struct announce_hook *ah = p->main_ahook;
       ah->in_filter = nc->in_filter;
       ah->out_filter = nc->out_filter;
@@ -445,6 +452,9 @@ proto_reconfigure(struct proto *p, struct proto_config *oc, struct proto_config
       ah->out_limit = nc->out_limit;
       ah->in_keep_filtered = nc->in_keep_filtered;
       proto_verify_limits(ah);
+
+      if (export_changed)
+       ah->last_out_filter_change = now;
     }
 
   /* Update routes when filters changed. If the protocol in not UP,
@@ -452,13 +462,6 @@ proto_reconfigure(struct proto *p, struct proto_config *oc, struct proto_config
   if ((p->proto_state != PS_UP) || (type == RECONFIG_SOFT))
     return 1;
 
-  int import_changed = ! filter_same(nc->in_filter, oc->in_filter);
-  int export_changed = ! filter_same(nc->out_filter, oc->out_filter);
-
-  /* We treat a change in preferences by reimporting routes */
-  if (nc->preference != oc->preference)
-    import_changed = 1;
-
   if (import_changed || export_changed)
     log(L_INFO "Reloading protocol %s", p->name);
 
index 5aca9a4eafcfae0f8d9adc0c1232248e102d499b..4fb76e2b231e1c2edd22ccacb212625673bba0ff 100644 (file)
@@ -471,6 +471,7 @@ struct announce_hook {
   struct proto_stats *stats;           /* Per-table protocol statistics */
   struct announce_hook *next;          /* Next hook for the same protocol */
   int in_keep_filtered;                        /* Routes rejected in import filter are kept */
+  bird_clock_t last_out_filter_change; /* Last time when out_filter _changed_ */
 };
 
 struct announce_hook *proto_add_announce_hook(struct proto *p, struct rtable *t, struct proto_stats *stats);
index 28fe5baaec26212027b335e8cb508e9853b1a8e5..07bb0b127e51d91eb2168c1aa163e97d073bcfd2 100644 (file)
@@ -426,13 +426,15 @@ rt_notify_basic(struct announce_hook *ah, net *net, rte *new0, rte *old0, int re
    * reconfiguration and the end of refeed - if a newly filtered
    * route disappears during this period, proper withdraw is not
    * sent (because old would be also filtered) and the route is
-   * not refeeded (because it disappeared before that).
+   * not refeeded (because it disappeared before that). Therefore,
+   * we also do not try to run the filter on old routes that are
+   * older than the last filter change.
    */
 
   if (new)
     new = export_filter(ah, new, &new_free, &tmpa, 0);
 
-  if (old && !refeed)
+  if (old && !(refeed || (old->lastmod <= ah->last_out_filter_change)))
     old = export_filter(ah, old, &old_free, NULL, 1);
 
   if (!new && !old)
index 6ef803222329d82854d37777609891e70eaa4bcb..5d0e3c768b522f2b4fb0911d6da2125e04f1de9e 100644 (file)
@@ -230,12 +230,18 @@ pipe_reconfigure(struct proto *P, struct proto_config *new)
   if ((oc->peer->table != nc->peer->table) || (oc->mode != nc->mode))
     return 0;
 
+  int import_changed = ! filter_same(new->in_filter, old->in_filter);
+  int export_changed = ! filter_same(new->out_filter, old->out_filter);
+
   /* Update output filters in ahooks */
   if (P->main_ahook)
     {
       P->main_ahook->out_filter = new->out_filter;
       P->main_ahook->in_limit = new->in_limit;
       proto_verify_limits(P->main_ahook);
+
+      if (export_changed)
+       P->main_ahook->last_out_filter_change = now;
     }
 
   if (p->peer_ahook)
@@ -243,14 +249,15 @@ pipe_reconfigure(struct proto *P, struct proto_config *new)
       p->peer_ahook->out_filter = new->in_filter;
       p->peer_ahook->in_limit = new->out_limit;
       proto_verify_limits(p->peer_ahook);
+
+      if (import_changed)
+       p->peer_ahook->last_out_filter_change = now;
     }
 
   if ((P->proto_state != PS_UP) || (proto_reconfig_type == RECONFIG_SOFT))
     return 1;
 
-  if ((new->preference != old->preference)
-      || ! filter_same(new->in_filter, old->in_filter)
-      || ! filter_same(new->out_filter, old->out_filter))
+  if (import_changed || export_changed || (new->preference != old->preference))
     proto_request_feeding(P);
 
   return 1;