diff options
author | Matthieu Boutier <boutier@pps.jussieu.fr> | 2012-01-23 23:46:32 +0100 |
---|---|---|
committer | Paul Jakma <paul@quagga.net> | 2012-03-25 17:06:53 +0100 |
commit | c35fafdf887aa32c5be6ad738d3a3b0140cea6e8 (patch) | |
tree | 4aa21a41dcd82247e467e5b955a6f7813bfd7ba7 /babeld/route.c | |
parent | 16e51b246be6b18641327685f44bd4f5f6649367 (diff) |
babeld: babelz merge.
Babelz is the last version of the stand-alone babel daemon. In
particular, it use multiple channels to diminuate
interferences. Please refer to this one for more details.
Diffstat (limited to 'babeld/route.c')
-rw-r--r-- | babeld/route.c | 574 |
1 files changed, 422 insertions, 152 deletions
diff --git a/babeld/route.c b/babeld/route.c index aadf80f2..a9ffc5d9 100644 --- a/babeld/route.c +++ b/babeld/route.c @@ -53,36 +53,161 @@ THE SOFTWARE. static void consider_route(struct babel_route *route); -struct babel_route *routes = NULL; -int numroutes = 0, maxroutes = 0; +struct babel_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 babel_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 babel_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 babel_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 babel_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; } +/* Returns an overestimate of the number of installed routes. */ +int +installed_routes_estimate(void) +{ + return route_slots; +} + +static int +resize_route_table(int new_slots) +{ + struct babel_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 babel_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 babel_route * +insert_route(struct babel_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 babel_route*)); + route_slots++; + routes[n] = route; + } else { + struct babel_route *r; + r = routes[i]; + while(r->next) + r = r->next; + r->next = route; + route->next = NULL; + } + + return route; +} + void flush_route(struct babel_route *route) { @@ -91,39 +216,67 @@ flush_route(struct babel_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; } - 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 babel_route)); - numroutes--; - VALGRIND_MAKE_MEM_UNDEFINED(routes + numroutes, sizeof(struct babel_route)); + if(route == routes[i]) { + routes[i] = route->next; + route->next = NULL; + free(route); - if(numroutes == 0) { - free(routes); - routes = NULL; - maxroutes = 0; - } else if(maxroutes > 8 && numroutes < maxroutes / 4) { - struct babel_route *new_routes; - int n = maxroutes / 2; - new_routes = realloc(routes, n * sizeof(struct babel_route)); - if(new_routes != NULL) { - routes = new_routes; - maxroutes = n; + if(routes[i] == NULL) { + if(i < route_slots - 1) + memmove(routes + i, routes + i + 1, + (route_slots - i - 1) * sizeof(struct babel_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 babel_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); + + release_source(src); +} + +void +flush_all_routes() +{ + int i; + + /* Start from the end, to avoid shifting the table. */ + 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--; + } + + check_sources_released(); } void @@ -132,12 +285,19 @@ flush_neighbour_routes(struct neighbour *neigh) int i; i = 0; - while(i < numroutes) { - if(routes[i].neigh == neigh) { - flush_route(&routes[i]); - continue; + while(i < route_slots) { + struct babel_route *r; + r = routes[i]; + while(r) { + if(r->neigh == neigh) { + flush_route(r); + goto again; + } + r = r->next; } i++; + again: + ; } } @@ -147,13 +307,46 @@ 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; + while(i < route_slots) { + struct babel_route *r; + r = routes[i]; + while(r) { + if(r->neigh->ifp == ifp && + (!v4only || v4mapped(r->nexthop))) { + flush_route(r); + goto again; + } + r = r->next; } i++; + again: + ; + } +} + +/* Iterate a function over all routes. */ +void +for_all_routes(void (*f)(struct babel_route*, void*), void *closure) +{ + int i; + + for(i = 0; i < route_slots; i++) { + struct babel_route *r = routes[i]; + while(r) { + (*f)(r, closure); + r = r->next; + } + } +} + +void +for_all_installed_routes(void (*f)(struct babel_route*, void*), void *closure) +{ + int i; + + for(i = 0; i < route_slots; i++) { + if(routes[i]->installed) + (*f)(routes[i], closure); } } @@ -163,10 +356,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 babel_route *route, int i) +{ + assert(i >= 0 && i < route_slots); + assert(route->installed); + + if(route != routes[i]) { + struct babel_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 babel_route *route) { - int rc; + int i, rc; if(route->installed) return; @@ -175,6 +386,15 @@ install_route(struct babel_route *route) zlog_err("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, @@ -186,6 +406,8 @@ install_route(struct babel_route *route) return; } route->installed = 1; + move_installed_route(route, i); + } void @@ -239,15 +461,16 @@ switch_routes(struct babel_route *old, struct babel_route *new) old->installed = 0; new->installed = 1; + move_installed_route(new, find_route_slot(new->src->prefix, new->src->plen, + NULL)); } static void -change_route_metric(struct babel_route *route, unsigned newmetric) +change_route_metric(struct babel_route *route, + unsigned refmetric, unsigned cost, unsigned add) { int old, new; - - if(route_metric(route) == newmetric) - return; + int newmetric = MIN(refmetric + cost + add, INFINITY); old = metric_to_kernel(route_metric(route)); new = metric_to_kernel(newmetric); @@ -265,14 +488,15 @@ change_route_metric(struct babel_route *route, unsigned newmetric) } } - route->metric = newmetric; + route->refmetric = refmetric; + route->cost = cost; + route->add_metric = add; } static void retract_route(struct babel_route *route) { - route->refmetric = INFINITY; - change_route_metric(route, INFINITY); + change_route_metric(route, INFINITY, INFINITY, 0); } int @@ -293,6 +517,51 @@ route_expired(struct babel_route *route) return route->time < babel_now.tv_sec - route->hold_time; } +static int +channels_interfere(int ch1, int ch2) +{ + if(ch1 == BABEL_IF_CHANNEL_NONINTERFERING + || ch2 == BABEL_IF_CHANNEL_NONINTERFERING) + return 0; + if(ch1 == BABEL_IF_CHANNEL_INTERFERING + || ch2 == BABEL_IF_CHANNEL_INTERFERING) + return 1; + return ch1 == ch2; +} + +int +route_interferes(struct babel_route *route, struct interface *ifp) +{ + struct babel_interface *babel_ifp = NULL; + switch(diversity_kind) { + case DIVERSITY_NONE: + return 1; + case DIVERSITY_INTERFACE_1: + return route->neigh->ifp == ifp; + case DIVERSITY_CHANNEL_1: + case DIVERSITY_CHANNEL: + if(route->neigh->ifp == ifp) + return 1; + babel_ifp = babel_get_if_nfo(ifp); + if(channels_interfere(babel_ifp->channel, + babel_get_if_nfo(route->neigh->ifp)->channel)) + return 1; + if(diversity_kind == DIVERSITY_CHANNEL) { + int i; + for(i = 0; i < DIVERSITY_HOPS; i++) { + if(route->channels[i] == 0) + break; + if(channels_interfere(babel_ifp->channel, route->channels[i])) + return 1; + } + } + return 0; + default: + fprintf(stderr, "Unknown kind of diversity.\n"); + return 1; + } +} + int update_feasible(struct source *src, unsigned short seqno, unsigned short refmetric) @@ -317,21 +586,22 @@ struct babel_route * find_best_route(const unsigned char *prefix, unsigned char plen, int feasible, struct neighbour *exclude) { - struct babel_route *route = NULL; - int i; + struct babel_route *route = NULL, *r = NULL; + 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; } @@ -354,31 +624,29 @@ update_route_metric(struct babel_route *route) route->src->prefix, route->src->plen, neigh->address, neigh->ifp->ifindex); - int newmetric = MIN(route->refmetric + - add_metric + - neighbour_cost(route->neigh), - INFINITY); - - if(newmetric != oldmetric) { - change_route_metric(route, newmetric); + change_route_metric(route, route->refmetric, + neighbour_cost(route->neigh), add_metric); + if(route_metric(route) != oldmetric) route_changed(route, route->src, oldmetric); - } } } /* Called whenever a neighbour's cost changes, to update the metric of - all routes through that neighbour. */ + all routes through that neighbour. Calls local_notify_neighbour. */ void 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 babel_route *r = routes[i]; + while(r) { + if(r->neigh == neigh) + update_route_metric(r); + r = r->next; + } } } } @@ -388,11 +656,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 babel_route *r = routes[i]; + while(r) { + if(r->neigh->ifp == ifp) + update_route_metric(r); + r = r->next; + } } } @@ -402,7 +672,8 @@ update_route(const unsigned char *router_id, const unsigned char *prefix, unsigned char plen, unsigned short seqno, unsigned short refmetric, unsigned short interval, - struct neighbour *neigh, const unsigned char *nexthop) + struct neighbour *neigh, const unsigned char *nexthop, + const unsigned char *channels, int channels_len) { struct babel_route *route; struct source *src; @@ -424,12 +695,18 @@ update_route(const unsigned char *router_id, if(add_metric >= INFINITY) return NULL; - src = find_source(router_id, prefix, plen, 1, seqno); + route = find_route(prefix, plen, neigh, nexthop); + + if(route && memcmp(route->src->id, router_id, 8) == 0) + /* Avoid scanning the source table. */ + src = route->src; + else + src = find_source(router_id, prefix, plen, 1, seqno); + if(src == NULL) return NULL; feasible = update_feasible(src, seqno, refmetric); - route = find_route(prefix, plen, neigh, nexthop); metric = MIN((int)refmetric + neighbour_cost(neigh) + add_metric, INFINITY); if(route) { @@ -457,12 +734,12 @@ update_route(const unsigned char *router_id, } } - route->src = src; - if(feasible && refmetric < INFINITY) + route->src = retain_source(src); + if((feasible || keep_unfeasible) && refmetric < INFINITY) route->time = babel_now.tv_sec; route->seqno = seqno; - route->refmetric = refmetric; - change_route_metric(route, metric); + change_route_metric(route, + refmetric, neighbour_cost(neigh), add_metric); route->hold_time = hold_time; route_changed(route, oldsrc, oldmetric); @@ -472,36 +749,46 @@ update_route(const unsigned char *router_id, if(!feasible) send_unfeasible_request(neigh, route->installed && route_old(route), seqno, metric, src); + release_source(oldsrc); } else { + struct babel_route *new_route; + if(refmetric >= INFINITY) /* Somebody's retracting a route we never saw. */ return NULL; if(!feasible) { send_unfeasible_request(neigh, 0, seqno, metric, src); - return NULL; - } - if(numroutes >= maxroutes) { - struct babel_route *new_routes; - int n = maxroutes < 1 ? 8 : 2 * maxroutes; - new_routes = routes == NULL ? - malloc(n * sizeof(struct babel_route)) : - realloc(routes, n * sizeof(struct babel_route)); - if(new_routes == NULL) + if(!keep_unfeasible) return NULL; - maxroutes = n; - routes = new_routes; } - route = &routes[numroutes]; - route->src = src; + + route = malloc(sizeof(struct babel_route)); + if(route == NULL) { + perror("malloc(route)"); + return NULL; + } + + route->src = retain_source(src); route->refmetric = refmetric; + route->cost = neighbour_cost(neigh); + route->add_metric = add_metric; route->seqno = seqno; - route->metric = metric; route->neigh = neigh; memcpy(route->nexthop, nexthop, 16); route->time = babel_now.tv_sec; route->hold_time = hold_time; route->installed = 0; - numroutes++; + memset(&route->channels, 0, sizeof(route->channels)); + if(channels_len > 0) + memcpy(&route->channels, channels, + MIN(channels_len, DIVERSITY_HOPS)); + route->next = NULL; + new_route = insert_route(route); + if(new_route == NULL) { + fprintf(stderr, "Couldn't insert route.\n"); + free(route); + return NULL; + } consider_route(route); } return route; @@ -558,13 +845,6 @@ consider_route(struct babel_route *route) if(route_metric(installed) >= INFINITY) goto install; - if(route_metric(installed) >= route_metric(route) + 192) - goto install; - - /* Avoid switching sources */ - if(installed->src != route->src) - return; - if(route_metric(installed) >= route_metric(route) + 64) goto install; @@ -584,15 +864,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 babel_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++; } @@ -699,51 +982,38 @@ 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 babel_route *r; int i; debugf(BABEL_DEBUG_COMMON,"Expiring old routes."); i = 0; - while(i < numroutes) { - struct babel_route *route = &routes[i]; - - if(route->time > babel_now.tv_sec || /* clock stepped */ - route_old(route)) { - flush_route(route); - continue; - } + while(i < route_slots) { + r = routes[i]; + while(r) { + /* Protect against clock being stepped. */ + if(r->time > babel_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++; + again: + ; } } - -void -babel_uninstall_all_routes(void) -{ - while(numroutes > 0) { - uninstall_route(&routes[0]); - /* We need to flush the route so network_up won't reinstall it */ - flush_route(&routes[0]); - } -} - -struct babel_route * -babel_route_get_by_source(struct source *src) -{ - int i; - for(i = 0; i < numroutes; i++) { - if(routes[i].src == src) - return &routes[i]; - } - return NULL; -} |