Commit cbfdf6ed authored by Ondrej Zajicek (work)'s avatar Ondrej Zajicek (work)
Browse files

Nest: Fix race condition during reconfiguration

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.
parent 822a7ee6
Loading
Loading
Loading
Loading
+11 −8
Original line number Diff line number Diff line
@@ -433,6 +433,13 @@ 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)
@@ -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);

+1 −0
Original line number Diff line number Diff line
@@ -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);
+4 −2
Original line number Diff line number Diff line
@@ -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)
+10 −3
Original line number Diff line number Diff line
@@ -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;