summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOndrej Zajicek <santiago@crfreenet.org>2009-08-11 15:49:56 +0200
committerOndrej Zajicek <santiago@crfreenet.org>2009-08-11 15:49:56 +0200
commitc0973621bc1e06cb6176dc2dfd97bec637861edd (patch)
treef599119121114a38698386a41da9179d83c3ac8f
parentac07aacd2cdb5cf69a3bfdbc0e078cb0ae96c0db (diff)
downloadbird-c0973621bc1e06cb6176dc2dfd97bec637861edd.tar
bird-c0973621bc1e06cb6176dc2dfd97bec637861edd.zip
Fixes another bug in rte_recalculate().
Previous bugfix revealed another hidden bug here.
-rw-r--r--nest/rt-table.c104
1 files changed, 67 insertions, 37 deletions
diff --git a/nest/rt-table.c b/nest/rt-table.c
index 7213836..87bf0dc 100644
--- a/nest/rt-table.c
+++ b/nest/rt-table.c
@@ -427,57 +427,87 @@ rte_recalculate(rtable *table, net *net, struct proto *p, struct proto *src, rte
rte_announce(table, RA_ANY, net, new, old, tmpa);
- if (new && rte_better(new, old_best)) /* It's a new optimal route => announce and relink it */
+
+ if (new && rte_better(new, old_best))
{
+ /* The first case - the new route is cleary optimal, we link it
+ at the first position and announce it */
+
rte_trace_in(D_ROUTES, p, new, "added [best]");
rte_announce(table, RA_OPTIMAL, net, new, old_best, tmpa);
new->next = net->routes;
net->routes = new;
}
- else
+ else if (old == old_best)
{
- if (old == old_best) /* It has _replaced_ the old optimal route */
+ /* The second case - the old best route disappeared, we add the
+ new route (if we have any) to the list (we don't care about
+ position) and then we elect the new optimal route and relink
+ that route at the first position and announce it. New optimal
+ route might be NULL if there is no more routes */
+
+ /* Add the new route to the list */
+ if (new)
{
- r = new; /* Find new optimal route and announce it */
- for(s=net->routes; s; s=s->next)
- if (rte_better(s, r))
- r = s;
- rte_announce(table, RA_OPTIMAL, net, r, old_best, tmpa);
- if (r) /* Re-link the new optimal route */
+ rte_trace_in(D_ROUTES, p, new, "added");
+ new->next = net->routes;
+ net->routes = new;
+ }
+
+ /* Find new optimal route */
+ r = NULL;
+ for (s=net->routes; s; s=s->next)
+ if (rte_better(s, r))
+ r = s;
+
+ /* Announce optimal route */
+ rte_announce(table, RA_OPTIMAL, net, r, old_best, tmpa);
+
+ /* And relink it (if there is any) */
+ if (r)
+ {
+ k = &net->routes;
+ while (s = *k)
{
- k = &net->routes;
- while (s = *k)
+ if (s == r)
{
- if (s == r)
- {
- *k = r->next;
- break;
- }
- k = &s->next;
+ *k = r->next;
+ break;
}
- r->next = net->routes;
- net->routes = r;
+ k = &s->next;
}
- else if (table->gc_counter++ >= table->config->gc_max_ops &&
- table->gc_time + table->config->gc_min_time <= now)
- ev_schedule(table->gc_event);
- }
- if (new) /* Link in the new non-optimal route */
- {
- new->next = net->routes->next;
- net->routes->next = new;
- rte_trace_in(D_ROUTES, p, new, "added");
- }
- else if (old && (p->debug & D_ROUTES))
- {
- if (old != old_best)
- rte_trace_in(D_ROUTES, p, old, "removed");
- else if (net->routes)
- rte_trace_in(D_ROUTES, p, old, "removed [replaced]");
- else
- rte_trace_in(D_ROUTES, p, old, "removed [sole]");
+ r->next = net->routes;
+ net->routes = r;
}
+ else if (table->gc_counter++ >= table->config->gc_max_ops &&
+ table->gc_time + table->config->gc_min_time <= now)
+ ev_schedule(table->gc_event);
+ }
+ else if (new)
+ {
+ /* The third case - the new route is not better than the old
+ best route (therefore old_best != NULL) and the old best
+ route was not removed (therefore old_best == net->routes).
+ We just link the new route after the old best route. */
+
+ ASSERT(net->routes != NULL);
+ new->next = net->routes->next;
+ 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 */
+
+ if (old != old_best)
+ rte_trace_in(D_ROUTES, p, old, "removed");
+ else if (net->routes)
+ rte_trace_in(D_ROUTES, p, old, "removed [replaced]");
+ else
+ rte_trace_in(D_ROUTES, p, old, "removed [sole]");
+ }
+
if (old)
{
if (p->rte_remove)