Commit a08dde85 authored by Juliusz Chroboczek's avatar Juliusz Chroboczek

Make route operations execute in O(log n).

This changes the route table to be a sorted table of linked lists of
routes to a given prefix, which makes most route operations behave in
O(log n).  Insertion and flushing of a prefix is still O(n), but these
are fairly rare operations.

A nice side-effect is that the route table is now private to route.c,
which should make it easy to switch to a different data structure in
the future.
parent a19cdda2
......@@ -722,13 +722,8 @@ main(int argc, char **argv)
usleep(roughly(10000));
gettime(&now);
/* Uninstall and flush all routes. */
while(numroutes > 0) {
if(routes[0].installed)
uninstall_route(&routes[0]);
/* We need to flush the route so interface_up won't reinstall it */
flush_route(&routes[0]);
}
/* We need to flush so interface_up won't try to reinstall. */
flush_all_routes();
FOR_ALL_INTERFACES(ifp) {
if(!if_up(ifp))
......@@ -923,6 +918,50 @@ init_signals(void)
#endif
}
static void
dump_route_callback(struct route *route, void *closure)
{
FILE *out = (FILE*)closure;
const unsigned char *nexthop =
memcmp(route->nexthop, route->neigh->address, 16) == 0 ?
NULL : route->nexthop;
char channels[100];
if(route->channels[0] == 0)
channels[0] = '\0';
else {
int k, j = 0;
snprintf(channels, 100, " chan (");
j = strlen(channels);
for(k = 0; k < DIVERSITY_HOPS; k++) {
if(route->channels[k] == 0)
break;
if(k > 0)
channels[j++] = ',';
snprintf(channels + j, 100 - j, "%d", route->channels[k]);
j = strlen(channels);
}
snprintf(channels + j, 100 - j, ")");
if(k == 0)
channels[0] = '\0';
}
fprintf(out, "%s metric %d refmetric %d id %s seqno %d%s age %d "
"via %s neigh %s%s%s%s\n",
format_prefix(route->src->prefix, route->src->plen),
route_metric(route), route->refmetric,
format_eui64(route->src->id),
(int)route->seqno,
channels,
(int)(now.tv_sec - route->time),
route->neigh->ifp->name,
format_address(route->neigh->address),
nexthop ? " nexthop " : "",
nexthop ? format_address(nexthop) : "",
route->installed ? " (installed)" :
route_feasible(route) ? " (feasible)" : "");
}
static void
dump_tables(FILE *out)
{
......@@ -948,45 +987,7 @@ dump_tables(FILE *out)
format_prefix(xroutes[i].prefix, xroutes[i].plen),
xroutes[i].metric);
}
for(i = 0; i < numroutes; i++) {
const unsigned char *nexthop =
memcmp(routes[i].nexthop, routes[i].neigh->address, 16) == 0 ?
NULL : routes[i].nexthop;
char channels[100];
if(routes[i].channels[0] == 0)
channels[0] = '\0';
else {
int k, j = 0;
snprintf(channels, 100, " chan (");
j = strlen(channels);
for(k = 0; k < DIVERSITY_HOPS; k++) {
if(routes[i].channels[k] == 0)
break;
if(k > 0)
channels[j++] = ',';
snprintf(channels + j, 100 - j, "%d", routes[i].channels[k]);
j = strlen(channels);
}
snprintf(channels + j, 100 - j, ")");
if(k == 0)
channels[0] = '\0';
}
fprintf(out, "%s metric %d refmetric %d id %s seqno %d%s age %d "
"via %s neigh %s%s%s%s\n",
format_prefix(routes[i].src->prefix, routes[i].src->plen),
route_metric(&routes[i]), routes[i].refmetric,
format_eui64(routes[i].src->id),
(int)routes[i].seqno,
channels,
(int)(now.tv_sec - routes[i].time),
routes[i].neigh->ifp->name,
format_address(routes[i].neigh->address),
nexthop ? " nexthop " : "",
nexthop ? format_address(nexthop) : "",
routes[i].installed ? " (installed)" :
route_feasible(&routes[i]) ? " (feasible)" : "");
}
for_all_routes(dump_route_callback, out);
fflush(out);
}
......
......@@ -222,6 +222,12 @@ local_notify_route(struct route *route, int kind)
return;
}
static void
local_notify_route_callback(struct route *route, void *closure)
{
local_notify_route(route, LOCAL_ADD);
}
void
local_notify_all()
{
......@@ -242,8 +248,7 @@ local_notify_all()
}
for(i = 0; i < numxroutes; i++)
local_notify_xroute(&xroutes[i], LOCAL_ADD);
for(i = 0; i < numroutes; i++)
local_notify_route(&routes[i], LOCAL_ADD);
for_all_routes(local_notify_route_callback, NULL);
return;
fail:
......
......@@ -1082,12 +1082,17 @@ buffer_update(struct interface *ifp,
ifp->num_buffered_updates++;
}
void
buffer_update_callback(struct route *route, void *closure)
{
buffer_update((struct interface*)closure,
route->src->prefix, route->src->plen);
}
void
send_update(struct interface *ifp, int urgent,
const unsigned char *prefix, unsigned char plen)
{
int i;
if(ifp == NULL) {
struct interface *ifp_aux;
struct route *route;
......@@ -1118,11 +1123,7 @@ send_update(struct interface *ifp, int urgent,
send_self_update(ifp);
if(!parasitic) {
debugf("Sending update to %s for any.\n", ifp->name);
for(i = 0; i < numroutes; i++)
if(routes[i].installed)
buffer_update(ifp,
routes[i].src->prefix,
routes[i].src->plen);
for_all_installed_routes(buffer_update_callback, ifp);
}
}
set_timeout(&ifp->update_timeout, ifp->update_interval);
......
/*
Copyright (c) 2007, 2008 by Juliusz Chroboczek
Copyright (c) 2007-2011 by Juliusz Chroboczek
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
......@@ -40,39 +40,154 @@ THE SOFTWARE.
#include "configuration.h"
#include "local.h"
struct route *routes = NULL;
int numroutes = 0, maxroutes = 0;
struct route **routes = NULL;
static int route_slots = 0, max_route_slots = 0;
int kernel_metric = 0;
int allow_duplicates = -1;
int diversity_kind = DIVERSITY_NONE;
int diversity_factor = 256; /* in units of 1/256 */
int keep_unfeasible = 0;
/* We maintain a list of "slots", ordered by prefix. Every slot
contains a linked list of the routes to this prefix, with the
installed route, if any, at the head of the list. */
static int
route_compare(const unsigned char *prefix, unsigned char plen,
struct route *route)
{
int i = memcmp(prefix, route->src->prefix, 16);
if(i != 0)
return i;
if(plen < route->src->plen)
return -1;
else if(plen > route->src->plen)
return 1;
else
return 0;
}
/* Performs binary search, returns -1 in case of failure. In the latter
case, new_return is the place where to insert the new element. */
static int
find_route_slot(const unsigned char *prefix, unsigned char plen,
int *new_return)
{
int p, m, g, c;
if(route_slots < 1) {
if(new_return)
*new_return = 0;
return -1;
}
p = 0; g = route_slots - 1;
do {
m = (p + g) / 2;
c = route_compare(prefix, plen, routes[m]);
if(c == 0)
return m;
else if(c < 0)
g = m - 1;
else
p = m + 1;
} while(p <= g);
if(new_return)
*new_return = p;
return -1;
}
struct route *
find_route(const unsigned char *prefix, unsigned char plen,
struct neighbour *neigh, const unsigned char *nexthop)
{
int i;
for(i = 0; i < numroutes; i++) {
if(routes[i].neigh == neigh &&
memcmp(routes[i].nexthop, nexthop, 16) == 0 &&
source_match(routes[i].src, prefix, plen))
return &routes[i];
struct route *route;
int i = find_route_slot(prefix, plen, NULL);
if(i < 0)
return NULL;
route = routes[i];
while(route) {
if(route->neigh == neigh && memcmp(route->nexthop, nexthop, 16) == 0)
return route;
route = route->next;
}
return NULL;
}
struct route *
find_installed_route(const unsigned char *prefix, unsigned char plen)
{
int i;
for(i = 0; i < numroutes; i++) {
if(routes[i].installed && source_match(routes[i].src, prefix, plen))
return &routes[i];
}
int i = find_route_slot(prefix, plen, NULL);
if(i >= 0 && routes[i]->installed)
return routes[i];
return NULL;
}
static int
resize_route_table(int new_slots)
{
struct route **new_routes;
assert(new_slots >= route_slots);
if(new_slots == 0) {
new_routes = NULL;
free(routes);
} else {
new_routes = realloc(routes, new_slots * sizeof(struct route*));
if(new_routes == NULL)
return -1;
}
max_route_slots = new_slots;
routes = new_routes;
return 1;
}
/* Insert a route into the table. If successful, retains the route.
On failure, caller must free the route. */
static struct route *
insert_route(struct route *route)
{
int i, n;
assert(!route->installed);
i = find_route_slot(route->src->prefix, route->src->plen, &n);
if(i < 0) {
if(route_slots >= max_route_slots)
resize_route_table(max_route_slots < 1 ? 8 : 2 * max_route_slots);
if(route_slots >= max_route_slots)
return NULL;
route->next = NULL;
if(n < route_slots)
memmove(routes + n + 1, routes + n,
(route_slots - n) * sizeof(struct route*));
route_slots++;
routes[n] = route;
} else {
struct route *r;
r = routes[i];
while(r->next)
r = r->next;
r->next = route;
route->next = NULL;
}
return route;
}
void
flush_route(struct route *route)
{
......@@ -81,53 +196,80 @@ flush_route(struct route *route)
unsigned oldmetric;
int lost = 0;
i = route - routes;
assert(i >= 0 && i < numroutes);
oldmetric = route_metric(route);
src = route->src;
if(route->installed) {
uninstall_route(route);
lost = 1;
}
local_notify_route(route, LOCAL_FLUSH);
src = route->src;
i = find_route_slot(route->src->prefix, route->src->plen, NULL);
assert(i >= 0 && i < route_slots);
if(i != numroutes - 1)
memcpy(routes + i, routes + numroutes - 1, sizeof(struct route));
numroutes--;
VALGRIND_MAKE_MEM_UNDEFINED(routes + numroutes, sizeof(struct route));
local_notify_route(route, LOCAL_FLUSH);
if(numroutes == 0) {
free(routes);
routes = NULL;
maxroutes = 0;
} else if(maxroutes > 8 && numroutes < maxroutes / 4) {
struct route *new_routes;
int n = maxroutes / 2;
new_routes = realloc(routes, n * sizeof(struct route));
if(new_routes != NULL) {
routes = new_routes;
maxroutes = n;
if(route == routes[i]) {
routes[i] = route->next;
route->next = NULL;
free(route);
if(routes[i] == NULL) {
if(i < route_slots - 1)
memmove(routes + i + 1, routes + i,
(route_slots - i - 1) * sizeof(struct route*));
routes[route_slots - 1] = NULL;
route_slots--;
}
if(route_slots == 0)
resize_route_table(0);
else if(max_route_slots > 8 && route_slots < max_route_slots / 4)
resize_route_table(max_route_slots / 2);
} else {
struct route *r = routes[i];
while(r->next != route)
r = r->next;
r->next = route->next;
route->next = NULL;
free(route);
}
if(lost)
route_lost(src, oldmetric);
}
void
flush_all_routes()
{
/* Start from the end, to avoid shifting the table. */
int i = route_slots - 1;
while(i >= 0) {
while(i < route_slots) {
/* Uninstall first, to avoid calling route_lost. */
if(routes[i]->installed)
uninstall_route(routes[0]);
flush_route(routes[i]);
}
i--;
}
}
void
flush_neighbour_routes(struct neighbour *neigh)
{
int i;
i = 0;
while(i < numroutes) {
if(routes[i].neigh == neigh) {
flush_route(&routes[i]);
continue;
for(i = 0; i < route_slots; i++) {
struct route *r;
again:
r = routes[i];
while(r) {
if(r->neigh == neigh) {
flush_route(r);
goto again;
}
r = r->next;
}
i++;
}
......@@ -138,15 +280,63 @@ flush_interface_routes(struct interface *ifp, int v4only)
{
int i;
i = 0;
while(i < numroutes) {
if(routes[i].neigh->ifp == ifp &&
(!v4only || v4mapped(routes[i].nexthop))) {
flush_route(&routes[i]);
continue;
for(i = 0; i < route_slots; i++) {
struct route *r;
again:
r = routes[i];
while(r) {
if(r->neigh->ifp == ifp &&
(!v4only || v4mapped(r->nexthop))) {
flush_route(r);
goto again;
}
r = r->next;
}
}
}
/* Iterate a function over all routes. */
void
for_all_routes(void (*f)(struct route*, void*), void *closure)
{
int i;
for(i = 0; i < route_slots; i++) {
struct route *r = routes[i];
while(r) {
(*f)(r, closure);
r = r->next;
}
}
}
void
for_all_installed_routes(void (*f)(struct route*, void*), void *closure)
{
int i;
for(i = 0; i < route_slots; i++) {
if(routes[i]->installed)
(*f)(routes[i], closure);
}
}
/* Find any route with a given source. This should go when we fix our
data structures. */
struct route *
find_route_with_source(struct source *src)
{
int i;
for(i = 0; i < route_slots; i++) {
struct route *r = routes[i];
while(r) {
if(r->src == src)
return r;
r = r->next;
}
i++;
}
return NULL;
}
static int
......@@ -155,10 +345,28 @@ metric_to_kernel(int metric)
return metric < INFINITY ? kernel_metric : KERNEL_INFINITY;
}
/* This is used to maintain the invariant that the installed route is at
the head of the list. */
static void
move_installed_route(struct route *route, int i)
{
assert(i >= 0 && i < route_slots);
assert(route->installed);
if(route != routes[i]) {
struct route *r = routes[i];
while(r->next != route)
r = r->next;
r->next = route->next;
route->next = routes[i];
routes[i] = route;
}
}
void
install_route(struct route *route)
{
int rc;
int i, rc;
if(route->installed)
return;
......@@ -167,6 +375,15 @@ install_route(struct route *route)
fprintf(stderr, "WARNING: installing unfeasible route "
"(this shouldn't happen).");
i = find_route_slot(route->src->prefix, route->src->plen, NULL);
assert(i >= 0 && i < route_slots);
if(routes[i] != route && routes[i]->installed) {
fprintf(stderr, "WARNING: attempting to install duplicate route "
"(this shouldn't happen).");
return;
}
rc = kernel_route(ROUTE_ADD, route->src->prefix, route->src->plen,
route->nexthop,
route->neigh->ifp->ifindex,
......@@ -178,6 +395,8 @@ install_route(struct route *route)
return;
}
route->installed = 1;
move_installed_route(route, i);
local_notify_route(route, LOCAL_CHANGE);
}
......@@ -233,7 +452,8 @@ switch_routes(struct route *old, struct route *new)
old->installed = 0;
new->installed = 1;
move_installed_route(new, find_route_slot(new->src->prefix, new->src->plen,
NULL));
local_notify_route(old, LOCAL_CHANGE);
local_notify_route(new, LOCAL_CHANGE);
}
......@@ -355,21 +575,22 @@ struct route *
find_best_route(const unsigned char *prefix, unsigned char plen, int feasible,
struct neighbour *exclude)
{
struct route *route = NULL;
int i;
struct route *route, *r;
int i = find_route_slot(prefix, plen, NULL);
if(i < 0)
return NULL;
route = routes[i];
for(i = 0; i < numroutes; i++) {
if(!source_match(routes[i].src, prefix, plen))
continue;
if(route_expired(&routes[i]))
continue;
if(feasible && !route_feasible(&routes[i]))
continue;
if(exclude && routes[i].neigh == exclude)
continue;
if(route && route_metric(route) <= route_metric(&routes[i]))
continue;
route = &routes[i];
r = route->next;
while(r) {
if(!route_expired(r) &&
(!feasible || route_feasible(r)) &&
(!exclude || r->neigh != exclude) &&
(route_metric(r) < route_metric(route)))
route = r;
r = r->next;
}
return route;
}
......@@ -408,11 +629,13 @@ update_neighbour_metric(struct neighbour *neigh, int changed)
if(changed) {
int i;
i = 0;
while(i < numroutes) {
if(routes[i].neigh == neigh)
update_route_metric(&routes[i]);
i++;
for(i = 0; i < route_slots; i++) {
struct route *r = routes[i];
while(r) {
if(r->neigh == neigh)
update_route_metric(r);
r = r->next;
}
}
}
......@@ -425,10 +648,13 @@ update_interface_metric(struct interface *ifp)
int i;
i = 0;
while(i < numroutes) {
if(routes[i].neigh->ifp == ifp)
update_route_metric(&routes[i]);
i++;
for(i = 0; i < route_slots; i++) {
struct route *r = routes[i];
while(r) {
if(r->neigh->ifp == ifp)
update_route_metric(r);
r = r->next;
}
}
}
......@@ -508,6 +734,8 @@ update_route(const unsigned char *a, const unsigned char *p, unsigned char plen,
send_unfeasible_request(neigh, route->installed && route_old(route),
seqno, metric, src);
} else {
struct route *new_route;
if(refmetric >= INFINITY)
/* Somebody's retracting a route we never saw. */
return NULL;
......@@ -517,18 +745,12 @@ update_route(const unsigned char *a, const unsigned char *p, unsigned char plen,
return NULL;
}
if(numroutes >= maxroutes) {
struct route *new_routes;
int n = maxroutes < 1 ? 8 : 2 * maxroutes;
new_routes = routes == NULL ?
malloc(n * sizeof(struct route)) :
realloc(routes, n * sizeof(struct route));
if(new_routes == NULL)
return NULL;
maxroutes = n;
routes = new_routes;
route = malloc(sizeof(struct route));
if(route == NULL) {
perror("malloc(route)");
return NULL;
}
route = &routes[numroutes];
route->src = src;
route->refmetric = refmetric;
route->cost = neighbour_cost(neigh);
......@@ -543,7 +765,13 @@ update_route(const unsigned char *a, const unsigned char *p, unsigned char plen,
if(channels_len > 0)
memcpy(&route->channels, channels,
MIN(channels_len, DIVERSITY_HOPS));
numroutes++;
route->next = NULL;
new_route = insert_route(route);
if(new_route == NULL) {
fprintf(stderr, "Couldn't insert route.\n");
free(route);
return NULL;
}
local_notify_route(route, LOCAL_ADD);
consider_route(route);
}
......@@ -628,15 +856,18 @@ retract_neighbour_routes(struct neighbour *neigh)
{
int i;
i = 0;
while(i < numroutes) {
if(routes[i].neigh == neigh) {
if(routes[i].refmetric != INFINITY) {
unsigned short oldmetric = route_metric(&routes[i]);
retract_route(&routes[i]);
for(i = 0; i < route_slots; i++) {
struct route *r = routes[i];
while(r) {
if(r->neigh == neigh) {
if(r->refmetric != INFINITY) {
unsigned short oldmetric = route_metric(r);
retract_route(r);
if(oldmetric != INFINITY)
route_changed(&routes[i], routes[i].src, oldmetric);
route_changed(r, r->src, oldmetric);
}
}
r = r->next;
}
i++;
}
......@@ -743,30 +974,35 @@ route_lost(struct source *src, unsigned oldmetric)
}
}
/* This is called periodically to flush old routes. It will also send
requests for routes that are about to expire. */
void
expire_routes(void)
{
struct route *r;
int i;
debugf("Expiring old routes.\n");
i = 0;
while(i < numroutes) {
struct route *route = &routes[i];
if(route->time > now.tv_sec || /* clock stepped */
route_old(route)) {
flush_route(route);
continue;
}
for(i = 0; i < route_slots; i++) {
again:
r = routes[i];
while(r) {
/* Protect against clock being stepped. */
if(r->time > now.tv_sec || route_old(r)) {
flush_route(r);
goto again;
}
update_route_metric(route);
update_route_metric(r);
if(route->installed && route->refmetric < INFINITY) {
if(route_old(route))
send_unicast_request(route->neigh,
route->src->prefix, route->src->plen);
if(r->installed && r->refmetric < INFINITY) {
if(route_old(r))
/* Route about to expire, send a request. */
send_unicast_request(r->neigh,
r->src->prefix, r->src->plen);
}
r = r->next;
}
i++;
}
}
/*
Copyright (c) 2007, 2008 by Juliusz Chroboczek
Copyright (c) 2007-2011 by Juliusz Chroboczek
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
......@@ -39,10 +39,10 @@ struct route {
unsigned short hold_time; /* in seconds */
short installed;
unsigned char channels[DIVERSITY_HOPS];
struct route *next;
};
extern struct route *routes;
extern int numroutes, maxroutes;
extern struct route **routes;
extern int kernel_metric, allow_duplicates;
extern int diversity_kind, diversity_factor;
extern int keep_unfeasible;
......@@ -70,8 +70,12 @@ struct route *find_route(const unsigned char *prefix, unsigned char plen,
struct route *find_installed_route(const unsigned char *prefix,
unsigned char plen);
void flush_route(struct route *route);
void flush_all_routes(void);
void flush_neighbour_routes(struct neighbour *neigh);
void flush_interface_routes(struct interface *ifp, int v4only);
void for_all_routes(void (*f)(struct route*, void*), void *closure);
void for_all_installed_routes(void (*f)(struct route*, void*), void *closure);
struct route *find_route_with_source(struct source *src);
void install_route(struct route *route);
void uninstall_route(struct route *route);
void switch_route(struct route *old, struct route *new);
......
......@@ -46,8 +46,10 @@ find_source(const unsigned char *id, const unsigned char *p, unsigned char plen,
continue;
if(memcmp(src->id, id, 8) != 0)
continue;
if(source_match(src, p, plen))
return src;
if(src->plen != plen)
continue;
if(memcmp(src->prefix, p, 16) == 0)
return src;
}
if(!create)
......@@ -73,15 +75,10 @@ find_source(const unsigned char *id, const unsigned char *p, unsigned char plen,
int
flush_source(struct source *src)
{
int i;
/* This is absolutely horrible -- it makes expire_sources quadratic.
But it's not called very often. */
for(i = 0; i < numroutes; i++) {
if(routes[i].src == src)
return 0;
}
if(find_route_with_source(src))
return 0;
if(srcs == src) {
srcs = src->next;
......@@ -96,19 +93,6 @@ flush_source(struct source *src)
return 1;
}
int
source_match(struct source *src,
const unsigned char *p, unsigned char plen)
{
if(src->plen != plen)
return 0;
if(src->prefix[15] != p[15])
return 0;
if(memcmp(src->prefix, p, 16) != 0)
return 0;
return 1;
}
void
update_source(struct source *src,
unsigned short seqno, unsigned short metric)
......
......@@ -32,9 +32,6 @@ struct source {
time_t time;
};
int source_match(struct source *src,
const unsigned char *p, unsigned char plen)
ATTRIBUTE ((pure));
struct source *find_source(const unsigned char *id,
const unsigned char *p,
unsigned char plen,
......
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