Commit cbfdf6ed authored by Ondřej Zajíček's avatar Ondřej Zajíček

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
Pipeline #37596 passed with stages
in 7 minutes and 3 seconds
......@@ -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);
......
......@@ -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);
......
......@@ -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)
......
......@@ -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;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment