Commit c3d2a632 authored by Juliusz Chroboczek's avatar Juliusz Chroboczek

Implement route selection using a smoothed metric.

This is a systematic way of implementing hyseresis in route
selection, and should solve the issues we're seeing with ETX
jitter and feedback.  The default half-time is 4s, which might
be too small.
parent 021cc482
...@@ -120,9 +120,10 @@ main(int argc, char **argv) ...@@ -120,9 +120,10 @@ main(int argc, char **argv)
parse_address("ff02:0:0:0:0:0:1:6", protocol_group, NULL); parse_address("ff02:0:0:0:0:0:1:6", protocol_group, NULL);
protocol_port = 6696; protocol_port = 6696;
change_smoothing_half_life(4);
while(1) { while(1) {
opt = getopt(argc, argv, "m:p:h:H:i:k:A:PsuS:d:g:lwz:t:T:c:C:DL:I:"); opt = getopt(argc, argv, "m:p:h:H:i:k:A:PsuS:d:g:lwz:M:t:T:c:C:DL:I:");
if(opt < 0) if(opt < 0)
break; break;
...@@ -212,6 +213,13 @@ main(int argc, char **argv) ...@@ -212,6 +213,13 @@ main(int argc, char **argv)
goto usage; goto usage;
} }
break; break;
case 'M': {
int l = parse_nat(optarg);
if(l < 0 || l > 3600)
goto usage;
change_smoothing_half_life(l);
break;
}
case 't': case 't':
export_table = parse_nat(optarg); export_table = parse_nat(optarg);
if(export_table < 0 || export_table > 0xFFFF) if(export_table < 0 || export_table > 0xFFFF)
...@@ -939,10 +947,10 @@ dump_route_callback(struct babel_route *route, void *closure) ...@@ -939,10 +947,10 @@ dump_route_callback(struct babel_route *route, void *closure)
channels[0] = '\0'; channels[0] = '\0';
} }
fprintf(out, "%s metric %d refmetric %d id %s seqno %d%s age %d " fprintf(out, "%s metric %d (%d) refmetric %d id %s seqno %d%s age %d "
"via %s neigh %s%s%s%s\n", "via %s neigh %s%s%s%s\n",
format_prefix(route->src->prefix, route->src->plen), format_prefix(route->src->prefix, route->src->plen),
route_metric(route), route->refmetric, route_metric(route), route_smoothed_metric(route), route->refmetric,
format_eui64(route->src->id), format_eui64(route->src->id),
(int)route->seqno, (int)route->seqno,
channels, channels,
......
...@@ -62,6 +62,11 @@ The value ...@@ -62,6 +62,11 @@ The value
specifies by how much the cost of non-interfering routes is multiplied, specifies by how much the cost of non-interfering routes is multiplied,
in units of 1/256; the default is 128 (i.e. division by 2). in units of 1/256; the default is 128 (i.e. division by 2).
.TP .TP
.BI \-M " half-time"
Specify the half-time in seconds of the exponential decay used for
smoothing metrics for performing route selection; the value 0 disables
smoothing. The default is 4s.
.TP
.BI \-k " priority" .BI \-k " priority"
Specify the priority value used when installing routes into the kernel. Specify the priority value used when installing routes into the kernel.
The default is 0. The default is 0.
......
...@@ -48,6 +48,9 @@ int diversity_kind = DIVERSITY_NONE; ...@@ -48,6 +48,9 @@ int diversity_kind = DIVERSITY_NONE;
int diversity_factor = 256; /* in units of 1/256 */ int diversity_factor = 256; /* in units of 1/256 */
int keep_unfeasible = 0; int keep_unfeasible = 0;
static int smoothing_half_life = 0;
static int two_to_the_one_over_hl = 0; /* 2^(1/hl) * 0x10000 */
/* We maintain a list of "slots", ordered by prefix. Every slot /* We maintain a list of "slots", ordered by prefix. Every slot
contains a linked list of the routes to this prefix, with the contains a linked list of the routes to this prefix, with the
installed route, if any, at the head of the list. */ installed route, if any, at the head of the list. */
...@@ -481,9 +484,18 @@ change_route_metric(struct babel_route *route, ...@@ -481,9 +484,18 @@ change_route_metric(struct babel_route *route,
} }
} }
/* Update route->smoothed_metric using the old metric. */
route_smoothed_metric(route);
route->refmetric = refmetric; route->refmetric = refmetric;
route->cost = cost; route->cost = cost;
route->add_metric = add; route->add_metric = add;
if(smoothing_half_life == 0) {
route->smoothed_metric = route_metric(route);
route->smoothed_metric_time = now.tv_sec;
}
local_notify_route(route, LOCAL_CHANGE); local_notify_route(route, LOCAL_CHANGE);
} }
...@@ -570,6 +582,65 @@ update_feasible(struct source *src, ...@@ -570,6 +582,65 @@ update_feasible(struct source *src,
(src->seqno == seqno && refmetric < src->metric)); (src->seqno == seqno && refmetric < src->metric));
} }
void
change_smoothing_half_life(int half_life)
{
if(half_life <= 0) {
smoothing_half_life = 0;
two_to_the_one_over_hl = 0;
return;
}
smoothing_half_life = half_life;
switch(smoothing_half_life) {
case 1: two_to_the_one_over_hl = 131072; break;
case 2: two_to_the_one_over_hl = 92682; break;
case 3: two_to_the_one_over_hl = 82570; break;
case 4: two_to_the_one_over_hl = 77935;
default:
/* 1/2^(1/x) is 1 + log(2)/x + O(1/x^2) at infinity. */
two_to_the_one_over_hl = 0x10000 + 45426 / half_life;
}
}
/* Update the smoothed metric, return the new value. */
int
route_smoothed_metric(struct babel_route *route)
{
int metric = route_metric(route);
if(smoothing_half_life <= 0 ||
/* Protect against clock stepping. */
route->smoothed_metric_time >= now.tv_sec ||
route->smoothed_metric == metric) {
route->smoothed_metric = metric;
route->smoothed_metric_time = now.tv_sec;
} else {
int diff;
/* We randomise the computation, to minimise global synchronisation
and hence oscillations. */
while(route->smoothed_metric_time <= now.tv_sec - smoothing_half_life) {
diff = metric - route->smoothed_metric;
route->smoothed_metric += roughly(diff) / 2;
route->smoothed_metric_time += smoothing_half_life;
}
while(route->smoothed_metric_time < now.tv_sec) {
diff = metric - route->smoothed_metric;
route->smoothed_metric +=
roughly(diff) / two_to_the_one_over_hl / 0x10000;
route->smoothed_metric_time++;
}
diff = metric - route->smoothed_metric;
if(diff > -4 && diff < 4)
route->smoothed_metric = metric;
}
/* change_route_metric relies on this */
assert(route->smoothed_metric_time == now.tv_sec);
return route->smoothed_metric;
}
static int static int
route_acceptable(struct babel_route *route, int feasible, route_acceptable(struct babel_route *route, int feasible,
struct neighbour *exclude) struct neighbour *exclude)
...@@ -603,7 +674,7 @@ find_best_route(const unsigned char *prefix, unsigned char plen, int feasible, ...@@ -603,7 +674,7 @@ find_best_route(const unsigned char *prefix, unsigned char plen, int feasible,
r = route->next; r = route->next;
while(r) { while(r) {
if(route_acceptable(r, feasible, exclude) && if(route_acceptable(r, feasible, exclude) &&
(route_metric(r) < route_metric(route))) (route_smoothed_metric(r) < route_smoothed_metric(route)))
route = r; route = r;
r = r->next; r = r->next;
} }
...@@ -615,6 +686,7 @@ void ...@@ -615,6 +686,7 @@ void
update_route_metric(struct babel_route *route) update_route_metric(struct babel_route *route)
{ {
int oldmetric = route_metric(route); int oldmetric = route_metric(route);
int old_smoothed_metric = route_smoothed_metric(route);
if(route_expired(route)) { if(route_expired(route)) {
if(route->refmetric < INFINITY) { if(route->refmetric < INFINITY) {
...@@ -631,7 +703,8 @@ update_route_metric(struct babel_route *route) ...@@ -631,7 +703,8 @@ update_route_metric(struct babel_route *route)
neigh->ifp->ifindex); neigh->ifp->ifindex);
change_route_metric(route, route->refmetric, change_route_metric(route, route->refmetric,
neighbour_cost(route->neigh), add_metric); neighbour_cost(route->neigh), add_metric);
if(route_metric(route) != oldmetric) if(route_metric(route) != oldmetric ||
route_smoothed_metric(route) != old_smoothed_metric)
route_changed(route, route->src, oldmetric); route_changed(route, route->src, oldmetric);
} }
} }
...@@ -784,6 +857,8 @@ update_route(const unsigned char *id, ...@@ -784,6 +857,8 @@ update_route(const unsigned char *id,
memcpy(route->nexthop, nexthop, 16); memcpy(route->nexthop, nexthop, 16);
route->time = now.tv_sec; route->time = now.tv_sec;
route->hold_time = hold_time; route->hold_time = hold_time;
route->smoothed_metric = MAX(route_metric(route), INFINITY / 2);
route->smoothed_metric_time = now.tv_sec;
route->installed = 0; route->installed = 0;
memset(&route->channels, 0, sizeof(route->channels)); memset(&route->channels, 0, sizeof(route->channels));
if(channels_len > 0) if(channels_len > 0)
...@@ -854,7 +929,8 @@ consider_route(struct babel_route *route) ...@@ -854,7 +929,8 @@ consider_route(struct babel_route *route)
if(route_metric(installed) >= INFINITY) if(route_metric(installed) >= INFINITY)
goto install; goto install;
if(route_metric(installed) >= route_metric(route) + 64) if(route_metric(installed) >= route_metric(route) &&
route_smoothed_metric(installed) > route_smoothed_metric(route))
goto install; goto install;
return; return;
...@@ -960,10 +1036,11 @@ route_changed(struct babel_route *route, ...@@ -960,10 +1036,11 @@ route_changed(struct babel_route *route,
find_best_route(route->src->prefix, route->src->plen, 1, NULL); find_best_route(route->src->prefix, route->src->plen, 1, NULL);
if(better_route && route_metric(better_route) < route_metric(route)) if(better_route && route_metric(better_route) < route_metric(route))
consider_route(better_route); consider_route(better_route);
}
if(route->installed) if(route->installed) {
/* We didn't switch to the better route. */ /* We didn't change routes after all. */
send_triggered_update(route, oldsrc, oldmetric); send_triggered_update(route, oldsrc, oldmetric);
} else { } else {
/* Reconsider routes even when their metric didn't decrease, /* Reconsider routes even when their metric didn't decrease,
they may not have been feasible before. */ they may not have been feasible before. */
......
...@@ -37,6 +37,8 @@ struct babel_route { ...@@ -37,6 +37,8 @@ struct babel_route {
unsigned char nexthop[16]; unsigned char nexthop[16];
time_t time; time_t time;
unsigned short hold_time; /* in seconds */ unsigned short hold_time; /* in seconds */
unsigned short smoothed_metric; /* for route selection */
time_t smoothed_metric_time;
short installed; short installed;
unsigned char channels[DIVERSITY_HOPS]; unsigned char channels[DIVERSITY_HOPS];
struct babel_route *next; struct babel_route *next;
...@@ -85,6 +87,8 @@ int route_expired(struct babel_route *route); ...@@ -85,6 +87,8 @@ int route_expired(struct babel_route *route);
int route_interferes(struct babel_route *route, struct interface *ifp); int route_interferes(struct babel_route *route, struct interface *ifp);
int update_feasible(struct source *src, int update_feasible(struct source *src,
unsigned short seqno, unsigned short refmetric); unsigned short seqno, unsigned short refmetric);
void change_smoothing_half_life(int half_life);
int route_smoothed_metric(struct babel_route *route);
struct babel_route *find_best_route(const unsigned char *prefix, unsigned char plen, struct babel_route *find_best_route(const unsigned char *prefix, unsigned char plen,
int feasible, struct neighbour *exclude); int feasible, struct neighbour *exclude);
struct babel_route *install_best_route(const unsigned char prefix[16], struct babel_route *install_best_route(const unsigned char prefix[16],
......
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