diff options
author | Ondrej Zajicek (work) <santiago@crfreenet.org> | 2018-07-03 19:21:42 +0200 |
---|---|---|
committer | Ondrej Zajicek (work) <santiago@crfreenet.org> | 2018-07-03 19:21:59 +0200 |
commit | cbfdf6ed057b993d7e107b4c39b8a5b81c081eee (patch) | |
tree | 0c6255bb81f037ceff0dfa9409a5829069ea24e1 | |
parent | 822a7ee6d5cd9bf38548026e0dd52fbc4634030d (diff) |
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.
-rw-r--r-- | nest/proto.c | 19 | ||||
-rw-r--r-- | nest/protocol.h | 1 | ||||
-rw-r--r-- | nest/rt-table.c | 6 | ||||
-rw-r--r-- | proto/pipe/pipe.c | 13 |
4 files changed, 26 insertions, 13 deletions
diff --git a/nest/proto.c b/nest/proto.c index 552d53ae..565b238f 100644 --- a/nest/proto.c +++ b/nest/proto.c @@ -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); diff --git a/nest/protocol.h b/nest/protocol.h index 5aca9a4e..4fb76e2b 100644 --- a/nest/protocol.h +++ b/nest/protocol.h @@ -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); diff --git a/nest/rt-table.c b/nest/rt-table.c index 28fe5baa..07bb0b12 100644 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@ -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) diff --git a/proto/pipe/pipe.c b/proto/pipe/pipe.c index 6ef80322..5d0e3c76 100644 --- a/proto/pipe/pipe.c +++ b/proto/pipe/pipe.c @@ -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; |