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)
parse_address("ff02:0:0:0:0:0:1:6", protocol_group, NULL);
protocol_port = 6696;
change_smoothing_half_life(4);
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)
break;
......@@ -212,6 +213,13 @@ main(int argc, char **argv)
goto usage;
}
break;
case 'M': {
int l = parse_nat(optarg);
if(l < 0 || l > 3600)
goto usage;
change_smoothing_half_life(l);
break;
}
case 't':
export_table = parse_nat(optarg);
if(export_table < 0 || export_table > 0xFFFF)
......@@ -939,10 +947,10 @@ dump_route_callback(struct babel_route *route, void *closure)
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",
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),
(int)route->seqno,
channels,
......
......@@ -62,6 +62,11 @@ The value
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).
.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"
Specify the priority value used when installing routes into the kernel.
The default is 0.
......
......@@ -48,6 +48,9 @@ int diversity_kind = DIVERSITY_NONE;
int diversity_factor = 256; /* in units of 1/256 */
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
contains a linked list of the routes to this prefix, with the
installed route, if any, at the head of the list. */
......@@ -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->cost = cost;
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);
}
......@@ -570,6 +582,65 @@ update_feasible(struct source *src,
(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
route_acceptable(struct babel_route *route, int feasible,
struct neighbour *exclude)
......@@ -603,7 +674,7 @@ find_best_route(const unsigned char *prefix, unsigned char plen, int feasible,
r = route->next;
while(r) {
if(route_acceptable(r, feasible, exclude) &&
(route_metric(r) < route_metric(route)))
(route_smoothed_metric(r) < route_smoothed_metric(route)))
route = r;
r = r->next;
}
......@@ -615,6 +686,7 @@ void
update_route_metric(struct babel_route *route)
{
int oldmetric = route_metric(route);
int old_smoothed_metric = route_smoothed_metric(route);
if(route_expired(route)) {
if(route->refmetric < INFINITY) {
......@@ -631,7 +703,8 @@ update_route_metric(struct babel_route *route)
neigh->ifp->ifindex);
change_route_metric(route, route->refmetric,
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);
}
}
......@@ -784,6 +857,8 @@ update_route(const unsigned char *id,
memcpy(route->nexthop, nexthop, 16);
route->time = now.tv_sec;
route->hold_time = hold_time;
route->smoothed_metric = MAX(route_metric(route), INFINITY / 2);
route->smoothed_metric_time = now.tv_sec;
route->installed = 0;
memset(&route->channels, 0, sizeof(route->channels));
if(channels_len > 0)
......@@ -854,7 +929,8 @@ consider_route(struct babel_route *route)
if(route_metric(installed) >= INFINITY)
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;
return;
......@@ -960,9 +1036,10 @@ route_changed(struct babel_route *route,
find_best_route(route->src->prefix, route->src->plen, 1, NULL);
if(better_route && route_metric(better_route) < route_metric(route))
consider_route(better_route);
}
if(route->installed)
/* We didn't switch to the better route. */
if(route->installed) {
/* We didn't change routes after all. */
send_triggered_update(route, oldsrc, oldmetric);
} else {
/* Reconsider routes even when their metric didn't decrease,
......
......@@ -37,6 +37,8 @@ struct babel_route {
unsigned char nexthop[16];
time_t time;
unsigned short hold_time; /* in seconds */
unsigned short smoothed_metric; /* for route selection */
time_t smoothed_metric_time;
short installed;
unsigned char channels[DIVERSITY_HOPS];
struct babel_route *next;
......@@ -85,6 +87,8 @@ int route_expired(struct babel_route *route);
int route_interferes(struct babel_route *route, struct interface *ifp);
int update_feasible(struct source *src,
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,
int feasible, struct neighbour *exclude);
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