summaryrefslogtreecommitdiff
path: root/nest/rt-table.c
diff options
context:
space:
mode:
Diffstat (limited to 'nest/rt-table.c')
-rw-r--r--nest/rt-table.c83
1 files changed, 70 insertions, 13 deletions
diff --git a/nest/rt-table.c b/nest/rt-table.c
index 87bf0dc7..df2834a6 100644
--- a/nest/rt-table.c
+++ b/nest/rt-table.c
@@ -158,13 +158,15 @@ rte_trace_out(unsigned int flag, struct proto *p, rte *e, char *msg)
}
static inline void
-do_rte_announce(struct announce_hook *a, int type, net *net, rte *new, rte *old, ea_list *tmpa, int class)
+do_rte_announce(struct announce_hook *a, int type, net *net, rte *new, rte *old, ea_list *tmpa, int class, int refeed)
{
struct proto *p = a->proto;
rte *new0 = new;
rte *old0 = old;
int ok;
+ int fast_exit_hack = 0;
+
if (new)
{
p->stats.exp_updates_received++;
@@ -174,6 +176,7 @@ do_rte_announce(struct announce_hook *a, int type, net *net, rte *new, rte *old,
{
p->stats.exp_updates_rejected++;
drop_reason = "out of scope";
+ fast_exit_hack = 1;
}
else if ((ok = p->import_control ? p->import_control(p, &new, &tmpa, rte_update_pool) : 0) < 0)
{
@@ -199,7 +202,32 @@ do_rte_announce(struct announce_hook *a, int type, net *net, rte *new, rte *old,
else
p->stats.exp_withdraws_received++;
- if (old)
+ /* Hack: This is here to prevent 'spurious withdraws'
+ for loopback addresses during reload. */
+ if (fast_exit_hack)
+ return;
+
+ /*
+ * This is a tricky part - we don't know whether route 'old' was
+ * exported to protocol 'p' or was filtered by the export filter.
+ * We try tu run the export filter to know this to have a correct
+ * value in 'old' argument of rt_update (and proper filter value)
+ *
+ * FIXME - this is broken because 'configure soft' may change
+ * filters but keep routes. Refeed is expected to be called after
+ * change of the filters and with old == new, therefore we do not
+ * even try to run the filter on an old route, This may lead to
+ * 'spurious withdraws' but ensure that there are no 'missing
+ * withdraws'.
+ *
+ * This is not completely safe as there is a window between
+ * 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).
+ */
+
+ if (old && !refeed)
{
if (p->out_filter == FILTER_REJECT)
old = NULL;
@@ -216,6 +244,7 @@ do_rte_announce(struct announce_hook *a, int type, net *net, rte *new, rte *old,
}
}
+ /* FIXME - This is broken because of incorrect 'old' value (see above) */
if (!new && !old)
return;
@@ -224,9 +253,11 @@ do_rte_announce(struct announce_hook *a, int type, net *net, rte *new, rte *old,
else
p->stats.exp_withdraws_accepted++;
+ /* Hack: We do not decrease exp_routes during refeed, we instead
+ reset exp_routes at the start of refeed. */
if (new)
p->stats.exp_routes++;
- if (old)
+ if (old && !refeed)
p->stats.exp_routes--;
if (p->debug & D_ROUTES)
@@ -304,7 +335,7 @@ rte_announce(rtable *tab, int type, net *net, rte *new, rte *old, ea_list *tmpa)
{
ASSERT(a->proto->core_state == FS_HAPPY || a->proto->core_state == FS_FEEDING);
if (a->proto->accept_ra_types == type)
- do_rte_announce(a, type, net, new, old, tmpa, class);
+ do_rte_announce(a, type, net, new, old, tmpa, class, 0);
}
}
@@ -394,6 +425,26 @@ rte_recalculate(rtable *table, net *net, struct proto *p, struct proto *src, rte
{
if (old->attrs->proto == src)
{
+ /* If there is the same route in the routing table but from
+ * a different sender, then there are two paths from the
+ * source protocol to this routing table through transparent
+ * pipes, which is not allowed.
+ *
+ * We log that and ignore the route. If it is withdraw, we
+ * ignore it completely (there might be 'spurious withdraws',
+ * see FIXME in do_rte_announce())
+ */
+ if (old->sender != p)
+ {
+ if (new)
+ {
+ log(L_ERR "Pipe collision detected when sending %I/%d to table %s",
+ net->n.prefix, net->n.pxlen, table->name);
+ rte_free_quick(new);
+ }
+ return;
+ }
+
if (new && rte_same(old, new))
{
/* No changes, ignore the new route */
@@ -495,11 +546,10 @@ rte_recalculate(rtable *table, net *net, struct proto *p, struct proto *src, rte
net->routes->next = new;
rte_trace_in(D_ROUTES, p, new, "added");
}
- else if (old && (p->debug & D_ROUTES))
- {
- /* Not really a case - the list of routes is correct, we just
- log the route removal */
+ /* Log the route removal */
+ if (!new && old && (p->debug & D_ROUTES))
+ {
if (old != old_best)
rte_trace_in(D_ROUTES, p, old, "removed");
else if (net->routes)
@@ -589,11 +639,12 @@ rte_update(rtable *table, net *net, struct proto *p, struct proto *src, rte *new
new->sender = p;
struct filter *filter = p->in_filter;
- /* Do not filter routes going to the secondary side of the pipe,
- that should only go through export filter.
- FIXME Make a better check whether p is really a pipe. */
- if (p->table != table)
+ /* Do not filter routes going through the pipe,
+ they are filtered in the export filter only. */
+#ifdef CONFIG_PIPE
+ if (p->proto == &proto_pipe)
filter = FILTER_ACCEPT;
+#endif
p->stats.imp_updates_received++;
if (!rte_validate(new))
@@ -636,6 +687,7 @@ rte_update(rtable *table, net *net, struct proto *p, struct proto *src, rte *new
drop:
rte_free(new);
+ rte_recalculate(table, net, p, src, NULL, NULL);
rte_update_unlock();
}
@@ -943,7 +995,7 @@ do_feed_baby(struct proto *p, int type, struct announce_hook *h, net *n, rte *e)
rte_update_lock();
tmpa = q->make_tmp_attrs ? q->make_tmp_attrs(e, rte_update_pool) : NULL;
- do_rte_announce(h, type, n, e, NULL, tmpa, ipa_classify(n->n.prefix));
+ do_rte_announce(h, type, n, e, p->refeeding ? e : NULL, tmpa, ipa_classify(n->n.prefix), p->refeeding);
rte_update_unlock();
}
@@ -1121,6 +1173,11 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
ok = 0;
else if (!ic && d->export_mode > 1)
{
+ /* FIXME - this shows what should be exported according
+ to current filters, but not what was really exported.
+ 'configure soft' command may change the export filter
+ and do not update routes */
+
if (p1->out_filter == FILTER_REJECT ||
p1->out_filter && f_run(p1->out_filter, &e, &tmpa, rte_update_pool, FF_FORCE_TMPATTR) > F_ACCEPT)
ok = 0;