Compare commits

...

4 commits

Author SHA1 Message Date
Toke Høiland-Jørgensen 4c582913ec babel: Add route metric smoothing
The Babel RTT extension employs metric smoothing to dampen route
oscillations in the face of varying RTT values between two peers[0].

This patch implements such dampening in Bird, roughly following the
implementation in babeld (i.e., using the same exponential function
definition). The main difference is that we calculate everything in the
native Bird microsecond time unit (and increase constants accordingly), and
that we split out the smoothed metric calculation in two function variants,
one that has no side effects and one that does.

  [0] https://arxiv.org/pdf/1403.3488.pdf

Signed-off-by: Toke Høiland-Jørgensen <toke@toke.dk>
2022-05-12 14:22:17 +02:00
Toke Høiland-Jørgensen 56841eecbc babel: Add support for the RTT extension
This adds support to the Babel protocol for the RTT extension specified in
draft-ietf-babel-rtt-extension. While this extension is not yet at the RFC
stage, it is one of the more useful extensions to Babel[0], so it seems
worth having in Bird as well.

The extension adds timestamps to Hello and IHU TLVs and uses these to
compute an RTT to each neighbour. An extra per-neighbour cost is then
computed from the RTT based on a minimum and maximum interval and cost
value specified in the configuration. The primary use case for this is
improving routing in a geographically distributed tunnel-based overlay
network.

The implementation follows the babeld implementation when picking constants
and default configuration values. It also uses the same RTT smoothing
algorithm as babeld, and follows it in adding a new 'tunnel' interface type
which enables RTT by default.

[0] https://alioth-lists.debian.net/pipermail/babel-users/2022-April/003932.html

Signed-off-by: Toke Høiland-Jørgensen <toke@toke.dk>
2022-05-12 14:22:17 +02:00
Toke Høiland-Jørgensen 44421ba7d4 lib/timer: Add current_time_now() function for immediate timestamp
Add a current_time_now() function which gets an immediate monotonic
timestamp instead of using the cached value from the event loop. This is
useful for callers that need precise times, such as the Babel RTT
measurement code.

Signed-off-by: Toke Høiland-Jørgensen <toke@toke.dk>
2022-05-12 14:22:17 +02:00
Toke Høiland-Jørgensen 8f9a091d30 babel: Don't try to remove multicast seqno request objects from neighbour list
The Babel seqno request code keeps track of which seqno requests are
outstanding for a neighbour by putting them onto a per-neighbour list. When
reusing a seqno request, it will try to remove this node, but if the seqno
request in question was a multicast request with no neighbour attached this
will result in a crash because it tries to remove a list node that wasn't
added to any list.

Fix this by making the list remove conditional. Also add a check so that
seqno requests are only reused if the neighbour also matches, allowing
multiple outstanding requests for the same router ID.

Fixes: ebd5751cde ("Babel: Seqno requests are properly decoupled from neighbors when the underlying interface disappears")
Reported-by: Stefan Haller <stefan.haller@stha.de>
Signed-off-by: Toke Høiland-Jørgensen <toke@toke.dk>
2022-05-12 14:09:59 +02:00
7 changed files with 467 additions and 35 deletions

View file

@ -1865,8 +1865,9 @@ protocol babel [<name>] {
ipv4 { <channel config> };
ipv6 [sadr] { <channel config> };
randomize router id <switch>;
metric decay <time>;
interface <interface pattern> {
type <wired|wireless>;
type <wired|wireless|tunnel>;
rxcost <number>;
limit <number>;
hello interval <time>;
@ -1879,6 +1880,11 @@ protocol babel [<name>] {
check link <switch>;
next hop ipv4 <address>;
next hop ipv6 <address>;
rtt cost <number>;
rtt min <time>;
rtt max <time>;
rtt decay <number>;
send timestamps <switch>;
authentication none|mac [permissive];
password "&lt;text&gt;";
password "&lt;text&gt;" {
@ -1909,15 +1915,27 @@ protocol babel [<name>] {
router ID every time it starts up, which avoids this problem at the cost
of not having stable router IDs in the network. Default: no.
<tag><label id="babel-type">type wired|wireless </tag>
This option specifies the interface type: Wired or wireless. On wired
interfaces a neighbor is considered unreachable after a small number of
Hello packets are lost, as described by <cf/limit/ option. On wireless
<tag><label id="babel-metric-decay">metric decay <m/time/ s</tag>
The metric smoothing decay time. When route metrics vary (because of
varying quality of a wireless link, or varying RTT when timestamps are
enabled), Babel applies an exponential smoothing procedure to the metric
to dampen route oscillations. This parameter specifies the half-life of
the convergence of the smoothed metric to the actual metric of the route.
I.e., the distance between the smoothed and the actual metric will be
halfed for each time period specified here (until they converge). Set to 0
to disable metric smoothing; if set, the value must be in the interval
1-180 s. Default: 4 s
<tag><label id="babel-type">type wired|wireless|tunnel </tag>
This option specifies the interface type: Wired, wireless or tunnel. On
wired interfaces a neighbor is considered unreachable after a small number
of Hello packets are lost, as described by <cf/limit/ option. On wireless
interfaces the ETX link quality estimation technique is used to compute
the metrics of routes discovered over this interface. This technique will
gradually degrade the metric of routes when packets are lost rather than
the more binary up/down mechanism of wired type links. Default:
<cf/wired/.
the more binary up/down mechanism of wired type links. A tunnel is like a
wired interface, but turns on RTT-based metrics with a default cost of 96.
Default: <cf/wired/.
<tag><label id="babel-rxcost">rxcost <m/num/</tag>
This option specifies the nominal RX cost of the interface. The effective
@ -1983,6 +2001,37 @@ protocol babel [<name>] {
source for Babel packets will be used. In normal operation, it should not
be necessary to set this option.
<tag><label id="babel-rtt-cost">rtt cost <m/number/</tag>
The RTT-based cost that will be applied to all routes from each neighbour
based on the measured RTT to that neighbour. If this value is set,
timestamps will be included in generated Babel Hello and IHU messages, and
(if the neighbours also have timestamps enabled), the RTT to each
neighbour will be computed. An additional cost is added to a neighbour if
its RTT is above the <ref id="babel-rtt-min" name="rtt min"> value
configured on the interface. The added cost scales linearly from 0 up to
the RTT cost configured in this option; the full cost is applied if the
neighbour RTT reaches the RTT configured in the <ref id="babel-rtt-max"
name="rtt max"> option (and for all RTTs above this value). Default: 0
(disabled), except for tunnel interfaces, where it is 96.
<tag><label id="babel-rtt-min">rtt min <m/time/ s|ms</tag>
The minimum RTT above which the RTT cost will start to be applied (scaling
linearly from zero up to the full cost). Default: 10 ms
<tag><label id="babel-rtt-max">rtt max <m/time/ s|ms</tag>
The maximum RTT above which the full RTT cost will start be applied.
Default: 120 ms
<tag><label id="babel-rtt-decay">rtt decay <m/number/</tag>
The decay factor used for the exponentional moving average of the RTT
samples from each neighbour, in units of 1/256. Higher values discards old
RTT samples faster. Must be between 1 and 256. Default: 42
<tag><label id="babel-send-timestamps">send timestamps <m/switch/</tag>
Whether to send the timestamps used for RTT calculation on this interface.
Sending the timestamps enables peers to calculate an RTT to this node,
even if no RTT cost is applied to the route metrics. Default: yes.
<tag><label id="babel-authentication">authentication none|mac [permissive]</tag>
Selects authentication method to be used. <cf/none/ means that packets
are not authenticated at all, <cf/mac/ means MAC authentication is

View file

@ -76,6 +76,19 @@ current_time(void)
return timeloop_current()->last_time;
}
btime
current_time_now(void)
{
struct timespec ts;
int rv;
rv = clock_gettime(CLOCK_MONOTONIC, &ts);
if (rv < 0)
die("clock_gettime: %m");
return ts.tv_sec S + ts.tv_nsec NS;
}
btime
current_real_time(void)
{

View file

@ -44,6 +44,7 @@ static inline timer *timers_first(struct timeloop *loop)
extern struct timeloop main_timeloop;
btime current_time(void);
btime current_time_now(void);
btime current_real_time(void);
//#define now (current_time() TO_S)

View file

@ -134,6 +134,103 @@ babel_expire_sources(struct babel_proto *p, struct babel_entry *e)
}
}
static u16
babel_calc_smoothed_metric(struct babel_proto *p, struct babel_route *r, u8 update)
{
struct babel_config *cf = (void *) p->p.cf;
uint metric = r->metric, smoothed_metric = r->smoothed_metric;
btime smoothed_time = r->smoothed_time, now = current_time();
if (!cf->metric_decay || metric == BABEL_INFINITY ||
metric == smoothed_metric || !smoothed_time)
{
smoothed_metric = metric;
smoothed_time = now;
goto out;
}
int diff = metric - smoothed_metric;
/*
* The decay defines the half-life of the metric convergence, so first iterate
* in halving steps
*/
while (smoothed_time < now - cf->metric_decay && diff) {
smoothed_metric += diff/2;
smoothed_time += cf->metric_decay;
diff = metric - smoothed_metric;
}
/*
* Then, update remainder in BABEL_SMOOTHING_STEP intervals using the
* exponential function (approximated via the pre-computed reciprocal).
*/
while (smoothed_time < now - BABEL_SMOOTHING_STEP && diff) {
smoothed_metric += (BABEL_SMOOTHING_STEP * diff *
(cf->smooth_recp - BABEL_SMOOTHING_UNIT) / BABEL_SMOOTHING_UNIT);
smoothed_time += BABEL_SMOOTHING_STEP;
diff = metric - smoothed_metric;
}
/* Consider the metric converged once we're close enough */
if (ABS(diff) < BABEL_SMOOTHING_MIN_DIFF)
smoothed_metric = metric;
out:
if (update) {
r->smoothed_metric = smoothed_metric;
r->smoothed_time = smoothed_time;
}
return smoothed_metric;
}
static u16
babel_update_smoothed_metric(struct babel_proto *p, struct babel_route *r)
{
if (!r->metric)
return 0;
if (!r->smoothed_metric) {
r->smoothed_metric = r->metric;
r->smoothed_time = current_time();
return r->smoothed_metric;
}
u16 smoothed = babel_calc_smoothed_metric(p, r, 1);
DBG("Updated smoothed metric for prefix %N: router-id %lR metric %d/%d\n",
r->e->n.addr, r->router_id, r->metric, smoothed);
return smoothed;
}
static u16
babel_smoothed_metric(struct babel_proto *p, struct babel_route *r)
{
return babel_calc_smoothed_metric(p, r, 0);
}
static void
babel_update_metric(struct babel_proto *p, struct babel_route *r, u16 metric)
{
babel_update_smoothed_metric(p, r);
r->metric = metric;
}
static inline u8
babel_route_better(struct babel_proto *p, struct babel_route *mod,
struct babel_route *best)
{
if (!mod->feasible)
return 0;
if (!best)
return mod->metric < BABEL_INFINITY;
return (mod->metric < best->metric &&
babel_smoothed_metric(p, mod) < babel_smoothed_metric(p, best));
}
static struct babel_route *
babel_find_route(struct babel_entry *e, struct babel_neighbor *n)
{
@ -151,8 +248,10 @@ babel_get_route(struct babel_proto *p, struct babel_entry *e, struct babel_neigh
{
struct babel_route *r = babel_find_route(e, nbr);
if (r)
if (r) {
babel_update_smoothed_metric(p, r);
return r;
}
r = sl_allocz(p->route_slab);
@ -304,7 +403,7 @@ babel_add_seqno_request(struct babel_proto *p, struct babel_entry *e,
struct babel_seqno_request *sr;
WALK_LIST(sr, e->requests)
if (sr->router_id == router_id)
if (sr->router_id == router_id && sr->nbr == nbr)
{
/* Found matching or newer */
if (ge_mod64k(sr->seqno, seqno) && seqno_request_valid(sr))
@ -312,7 +411,8 @@ babel_add_seqno_request(struct babel_proto *p, struct babel_entry *e,
/* Found older */
rem_node(NODE sr);
rem_node(&sr->nbr_node);
if (sr->nbr)
rem_node(&sr->nbr_node);
goto found;
}
@ -349,17 +449,20 @@ static int
babel_satisfy_seqno_request(struct babel_proto *p, struct babel_entry *e,
u64 router_id, u16 seqno)
{
struct babel_seqno_request *sr;
struct babel_seqno_request *sr, *srx;
int ret = 0;
WALK_LIST(sr, e->requests)
WALK_LIST_DELSAFE(sr, srx, e->requests)
if ((sr->router_id == router_id) && ge_mod64k(seqno, sr->seqno))
{
/* Found the request, remove it */
/* Found a matching request, remove it; there may be multiple outstanding
* requests, so continue looping
*/
babel_remove_seqno_request(p, sr);
return 1;
ret = 1;
}
return 0;
return ret;
}
static void
@ -570,6 +673,7 @@ babel_update_cost(struct babel_neighbor *nbr)
switch (cf->type)
{
case BABEL_IFACE_TYPE_WIRED:
case BABEL_IFACE_TYPE_TUNNEL:
/* k-out-of-j selection - Appendix 2.1 in the RFC. */
/* Link is bad if less than cf->limit/16 of expected hellos were received */
@ -598,6 +702,25 @@ babel_update_cost(struct babel_neighbor *nbr)
break;
}
if (cf->rtt_cost && nbr->srtt > cf->rtt_min)
{
uint rtt_cost = cf->rtt_cost;
if (nbr->srtt < cf->rtt_max)
{
uint rtt_interval = cf->rtt_max TO_US - cf->rtt_min TO_US;
uint rtt_diff = (nbr->srtt TO_US - cf->rtt_min TO_US);
rtt_cost = (rtt_cost * rtt_diff) / rtt_interval;
}
txcost = MIN(txcost + rtt_cost, BABEL_INFINITY);
rxcost = MIN(rxcost + rtt_cost, BABEL_INFINITY);
TRACE(D_EVENTS, "Added RTT cost %u to nbr %I on %s with srtt %u.%03u ms",
rtt_cost, nbr->addr, nbr->ifa->iface->name, nbr->srtt/1000, nbr->srtt%1000);
}
done:
/* If RX cost changed, send IHU with next Hello */
if (rxcost != nbr->rxcost)
@ -617,7 +740,7 @@ done:
struct babel_route *r; node *n;
WALK_LIST2(r, n, nbr->routes, neigh_route)
{
r->metric = babel_compute_metric(nbr, r->advert_metric);
babel_update_metric(p, r, babel_compute_metric(nbr, r->advert_metric));
babel_select_route(p, r->e, r);
}
}
@ -741,8 +864,7 @@ babel_select_route(struct babel_proto *p, struct babel_entry *e, struct babel_ro
/* Shortcut if only non-best was modified */
if (mod && (mod != best))
{
/* Either select modified route, or keep old best route */
if ((mod->metric < (best ? best->metric : BABEL_INFINITY)) && mod->feasible)
if (babel_route_better(p, mod, best))
best = mod;
else
return;
@ -753,17 +875,24 @@ babel_select_route(struct babel_proto *p, struct babel_entry *e, struct babel_ro
if (!best || (best->metric == BABEL_INFINITY) || !best->feasible)
best = NULL;
/* best will be compared to many routes below, make sure it's up-to-date */
if (best)
babel_update_smoothed_metric(p, best);
/* Find the best feasible route from all routes */
WALK_LIST(r, e->routes)
if ((r->metric < (best ? best->metric : BABEL_INFINITY)) && r->feasible)
if (babel_route_better(p, r, best))
best = r;
}
if (best)
{
if (best != e->selected)
TRACE(D_EVENTS, "Picked new route for prefix %N: router-id %lR metric %d",
e->n.addr, best->router_id, best->metric);
{
u16 smoothed = babel_update_smoothed_metric(p, best);
TRACE(D_EVENTS, "Picked new route for prefix %N: router-id %lR metric %d/%d",
e->n.addr, best->router_id, best->metric, smoothed);
}
}
else if (e->selected)
{
@ -812,6 +941,12 @@ babel_build_ihu(union babel_msg *msg, struct babel_iface *ifa, struct babel_neig
msg->ihu.rxcost = n->rxcost;
msg->ihu.interval = ifa->cf->ihu_interval;
if (n->last_tstamp_rcvd && ifa->cf->rtt_send)
{
msg->ihu.tstamp = n->last_tstamp;
msg->ihu.tstamp_rcvd = n->last_tstamp_rcvd TO_US;
}
TRACE(D_PACKETS, "Sending IHU for %I with rxcost %d interval %t",
msg->ihu.addr, msg->ihu.rxcost, (btime) msg->ihu.interval);
}
@ -851,6 +986,9 @@ babel_send_hello(struct babel_iface *ifa, uint interval)
msg.hello.seqno = ifa->hello_seqno++;
msg.hello.interval = interval ?: ifa->cf->hello_interval;
if (ifa->cf->rtt_send)
msg.hello.tstamp = 1; /* real timestamp will be set on TLV write */
TRACE(D_PACKETS, "Sending hello on %s with seqno %d interval %t",
ifa->ifname, msg.hello.seqno, (btime) msg.hello.interval);
@ -1146,14 +1284,26 @@ babel_handle_hello(union babel_msg *m, struct babel_iface *ifa)
msg->seqno, (btime) msg->interval);
struct babel_neighbor *n = babel_get_neighbor(ifa, msg->sender);
struct babel_iface_config *cf = n->ifa->cf;
int first_hello = !n->hello_cnt;
if (msg->tstamp)
{
n->last_tstamp = msg->tstamp;
n->last_tstamp_rcvd = msg->pkt_received;
}
babel_update_hello_history(n, msg->seqno, msg->interval);
babel_update_cost(n);
/* Speed up session establishment by sending IHU immediately */
if (first_hello)
babel_send_ihu(ifa, n);
{
/* if using RTT, all IHUs must be paired with hellos */
if(cf->rtt_send)
babel_send_hello(ifa, 0);
else
babel_send_ihu(ifa, n);
}
}
void
@ -1172,6 +1322,39 @@ babel_handle_ihu(union babel_msg *m, struct babel_iface *ifa)
struct babel_neighbor *n = babel_get_neighbor(ifa, msg->sender);
n->txcost = msg->rxcost;
n->ihu_expiry = current_time() + BABEL_IHU_EXPIRY_FACTOR(msg->interval);
if (msg->tstamp)
{
u32 rtt_sample = 0, pkt_received = msg->pkt_received TO_US;
int remote_time, full_time;
/* processing time reported by peer */
remote_time = (n->last_tstamp - msg->tstamp_rcvd);
/* time since we sent the last timestamp - RTT including remote time */
full_time = (pkt_received - msg->tstamp);
/* sanity checks */
if (remote_time < 0 || full_time < 0 ||
remote_time US_ > BABEL_RTT_MAX_VALUE || full_time US_ > BABEL_RTT_MAX_VALUE)
goto out;
if (remote_time < full_time)
rtt_sample = full_time - remote_time;
if (n->srtt)
{
uint decay = n->ifa->cf->rtt_decay;
n->srtt = (decay * rtt_sample + (256 - decay) * n->srtt) / 256;
}
else
n->srtt = rtt_sample;
TRACE(D_EVENTS, "RTT sample for neighbour %I on %s: %u us (srtt %u.%03u ms)",
n->addr, ifa->ifname, rtt_sample, n->srtt/1000, n->srtt%1000);
}
out:
babel_update_cost(n);
}
@ -1288,9 +1471,9 @@ babel_handle_update(union babel_msg *m, struct babel_iface *ifa)
return;
/* Last paragraph above - update the entry */
babel_update_metric(p, r, metric);
r->feasible = feasible;
r->seqno = msg->seqno;
r->metric = metric;
r->advert_metric = msg->metric;
r->router_id = msg->router_id;
r->next_hop = msg->next_hop;
@ -2087,8 +2270,8 @@ babel_show_neighbors(struct proto *P, const char *iff)
}
cli_msg(-1024, "%s:", p->p.name);
cli_msg(-1024, "%-25s %-10s %6s %6s %6s %7s %4s",
"IP address", "Interface", "Metric", "Routes", "Hellos", "Expires", "Auth");
cli_msg(-1024, "%-25s %-10s %6s %6s %6s %7s %4s %11s",
"IP address", "Interface", "Metric", "Routes", "Hellos", "Expires", "Auth", "RTT");
WALK_LIST(ifa, p->interfaces)
{
@ -2103,9 +2286,10 @@ babel_show_neighbors(struct proto *P, const char *iff)
uint hellos = u32_popcount(n->hello_map);
btime timer = (n->hello_expiry ?: n->init_expiry) - current_time();
cli_msg(-1024, "%-25I %-10s %6u %6u %6u %7t %-4s",
cli_msg(-1024, "%-25I %-10s %6u %6u %6u %7t %-4s %5u.%03ums",
n->addr, ifa->iface->name, n->cost, rts, hellos, MAX(timer, 0),
n->auth_passed ? "Yes" : "No");
n->auth_passed ? "Yes" : "No",
n->srtt/1000, n->srtt%1000);
}
}
}

View file

@ -50,10 +50,28 @@
#define BABEL_GARBAGE_INTERVAL (300 S_)
#define BABEL_RXCOST_WIRED 96
#define BABEL_RXCOST_WIRELESS 256
#define BABEL_RXCOST_RTT 96
#define BABEL_INITIAL_HOP_COUNT 255
#define BABEL_MAX_SEND_INTERVAL 5 /* Unused ? */
#define BABEL_INITIAL_NEIGHBOR_TIMEOUT (60 S_)
#define BABEL_RTT_MAX_VALUE (600 S_)
#define BABEL_RTT_MIN (10 MS_)
#define BABEL_RTT_MAX (120 MS_)
#define BABEL_RTT_DECAY 42
/*
* Constants for calculating metric smoothing. Chosen so that:
* log(2) = BABEL_SMOOTHING_CONSTANT / BABEL_SMOOTHING_UNIT, which means that
* log(2)/x can be calculated as BABEL_SMOOTHING_UNIT + BABEL_SMOOTHING_CONSTANT / x
*/
#define BABEL_SMOOTHING_UNIT 0x10000000
#define BABEL_SMOOTHING_CONSTANT 186065279
#define BABEL_SMOOTHING_STEP (1 S_) /* smoothing calculated in this step size */
#define BABEL_SMOOTHING_MIN_DIFF 4 /* metric diff beneath this is converged */
#define BABEL_SMOOTHING_DECAY (4 S_)
#define BABEL_SMOOTHING_DECAY_MAX (180 S_)
/* Max interval that will not overflow when carried as 16-bit centiseconds */
#define BABEL_TIME_UNITS 10000 /* On-wire times are counted in centiseconds */
#define BABEL_MIN_INTERVAL (0x0001 * BABEL_TIME_UNITS)
@ -93,6 +111,8 @@ enum babel_tlv_type {
enum babel_subtlv_type {
BABEL_SUBTLV_PAD1 = 0,
BABEL_SUBTLV_PADN = 1,
BABEL_SUBTLV_DIVERSITY = 2, /* we don't support this */
BABEL_SUBTLV_TIMESTAMP = 3,
/* Mandatory subtlvs */
BABEL_SUBTLV_SOURCE_PREFIX = 128,
@ -103,6 +123,7 @@ enum babel_iface_type {
BABEL_IFACE_TYPE_UNDEF = 0,
BABEL_IFACE_TYPE_WIRED = 1,
BABEL_IFACE_TYPE_WIRELESS = 2,
BABEL_IFACE_TYPE_TUNNEL = 3,
BABEL_IFACE_TYPE_MAX
};
@ -118,6 +139,8 @@ enum babel_ae_type {
struct babel_config {
struct proto_config c;
list iface_list; /* List of iface configs (struct babel_iface_config) */
btime metric_decay;
uint smooth_recp; /* Reciprocal for exponential metric smoothing */
uint hold_time; /* Time to hold stale entries and unreachable routes */
u8 randomize_router_id;
@ -137,6 +160,12 @@ struct babel_iface_config {
uint ihu_interval; /* IHU interval, in us */
uint update_interval; /* Update interval, in us */
btime rtt_min; /* rtt above which to start penalising metric */
btime rtt_max; /* max rtt metric penalty applied above this */
u16 rtt_cost; /* metric penalty to apply at rtt_max */
u16 rtt_decay; /* decay of neighbour RTT (units of 1/256) */
u8 rtt_send; /* whether to send timestamps on this interface */
u16 rx_buffer; /* RX buffer size, 0 for MTU */
u16 tx_length; /* TX packet length limit (including headers), 0 for MTU */
int tx_tos;
@ -224,6 +253,10 @@ struct babel_neighbor {
u16 next_hello_seqno;
uint last_hello_int;
u32 last_tstamp;
btime last_tstamp_rcvd;
btime srtt;
u32 auth_pc;
u8 auth_passed;
u8 auth_index_len;
@ -261,9 +294,11 @@ struct babel_route {
u16 seqno;
u16 metric;
u16 advert_metric;
u16 smoothed_metric;
u64 router_id;
ip_addr next_hop;
btime refresh_time;
btime smoothed_time;
btime expires;
};
@ -321,6 +356,8 @@ struct babel_msg_hello {
u16 seqno;
uint interval;
ip_addr sender;
u32 tstamp;
btime pkt_received;
};
struct babel_msg_ihu {
@ -330,6 +367,9 @@ struct babel_msg_ihu {
uint interval;
ip_addr addr;
ip_addr sender;
u32 tstamp;
u32 tstamp_rcvd;
btime pkt_received;
};
struct babel_msg_update {

View file

@ -23,9 +23,10 @@ CF_DEFINES
CF_DECLS
CF_KEYWORDS(BABEL, INTERFACE, METRIC, RXCOST, HELLO, UPDATE, INTERVAL, PORT,
TYPE, WIRED, WIRELESS, RX, TX, BUFFER, PRIORITY, LENGTH, CHECK, LINK,
NEXT, HOP, IPV4, IPV6, BABEL_METRIC, SHOW, INTERFACES, NEIGHBORS,
ENTRIES, RANDOMIZE, ROUTER, ID, AUTHENTICATION, NONE, MAC, PERMISSIVE)
TYPE, WIRED, WIRELESS, TUNNEL, RX, TX, BUFFER, PRIORITY, LENGTH, CHECK,
LINK, NEXT, HOP, IPV4, IPV6, BABEL_METRIC, SHOW, INTERFACES, NEIGHBORS,
ENTRIES, RANDOMIZE, ROUTER, ID, AUTHENTICATION, NONE, MAC, PERMISSIVE,
RTT, MIN, MAX, DECAY, SEND, TIMESTAMPS)
CF_GRAMMAR
@ -36,6 +37,13 @@ babel_proto_start: proto_start BABEL
this_proto = proto_config_new(&proto_babel, $1);
init_list(&BABEL_CFG->iface_list);
BABEL_CFG->hold_time = 1 S_;
BABEL_CFG->metric_decay = BABEL_SMOOTHING_DECAY;
};
babel_proto_finish:
{
if (BABEL_CFG->metric_decay)
BABEL_CFG->smooth_recp = BABEL_SMOOTHING_UNIT + BABEL_SMOOTHING_CONSTANT / BABEL_CFG->metric_decay;
};
babel_proto_item:
@ -43,6 +51,7 @@ babel_proto_item:
| proto_channel
| INTERFACE babel_iface
| RANDOMIZE ROUTER ID bool { BABEL_CFG->randomize_router_id = $4; }
| METRIC DECAY expr_us { BABEL_CFG->metric_decay = $3; if ($3 && (($3 < BABEL_SMOOTHING_STEP) || ($3 > BABEL_SMOOTHING_DECAY_MAX))) cf_error("Metric decay must be 0, or between 1-180s"); }
;
babel_proto_opts:
@ -51,7 +60,7 @@ babel_proto_opts:
;
babel_proto:
babel_proto_start proto_name '{' babel_proto_opts '}';
babel_proto_start proto_name '{' babel_proto_opts '}' babel_proto_finish;
babel_iface_start:
@ -66,6 +75,10 @@ babel_iface_start:
BABEL_IFACE->limit = BABEL_HELLO_LIMIT;
BABEL_IFACE->tx_tos = IP_PREC_INTERNET_CONTROL;
BABEL_IFACE->tx_priority = sk_priority_control;
BABEL_IFACE->rtt_min = BABEL_RTT_MIN;
BABEL_IFACE->rtt_max = BABEL_RTT_MAX;
BABEL_IFACE->rtt_decay = BABEL_RTT_DECAY;
BABEL_IFACE->rtt_send = 1;
BABEL_IFACE->check_link = 1;
};
@ -85,8 +98,16 @@ babel_iface_finish:
BABEL_IFACE->hello_interval = BABEL_HELLO_INTERVAL_WIRED;
if (!BABEL_IFACE->rxcost)
BABEL_IFACE->rxcost = BABEL_RXCOST_WIRED;
if (BABEL_IFACE->type == BABEL_IFACE_TYPE_TUNNEL && !BABEL_IFACE->rtt_cost)
BABEL_IFACE->rtt_cost = BABEL_RXCOST_RTT;
}
if (BABEL_IFACE->rtt_cost && !BABEL_IFACE->rtt_send)
cf_error("Can't set RTT cost when sending timestamps is disabled");
if (BABEL_IFACE->rtt_min >= BABEL_IFACE->rtt_max)
cf_error("Min RTT must be smaller than max RTT");
/* Make sure we do not overflow the 16-bit centisec fields */
if (!BABEL_IFACE->update_interval)
BABEL_IFACE->update_interval = MIN_(BABEL_IFACE->hello_interval*BABEL_UPDATE_INTERVAL_FACTOR, BABEL_MAX_INTERVAL);
@ -134,6 +155,7 @@ babel_iface_item:
| LIMIT expr { BABEL_IFACE->limit = $2; if (($2<1) || ($2>16)) cf_error("Limit must be in range 1-16"); }
| TYPE WIRED { BABEL_IFACE->type = BABEL_IFACE_TYPE_WIRED; }
| TYPE WIRELESS { BABEL_IFACE->type = BABEL_IFACE_TYPE_WIRELESS; }
| TYPE TUNNEL { BABEL_IFACE->type = BABEL_IFACE_TYPE_TUNNEL; }
| HELLO INTERVAL expr_us { BABEL_IFACE->hello_interval = $3; if (($3<BABEL_MIN_INTERVAL) || ($3>BABEL_MAX_INTERVAL)) cf_error("Hello interval must be in range 10 ms - 655 s"); }
| UPDATE INTERVAL expr_us { BABEL_IFACE->update_interval = $3; if (($3<BABEL_MIN_INTERVAL) || ($3>BABEL_MAX_INTERVAL)) cf_error("Update interval must be in range 10 ms - 655 s"); }
| RX BUFFER expr { BABEL_IFACE->rx_buffer = $3; if (($3<256) || ($3>65535)) cf_error("RX buffer must be in range 256-65535"); }
@ -146,6 +168,11 @@ babel_iface_item:
| AUTHENTICATION NONE { BABEL_IFACE->auth_type = BABEL_AUTH_NONE; }
| AUTHENTICATION MAC { BABEL_IFACE->auth_type = BABEL_AUTH_MAC; BABEL_IFACE->auth_permissive = 0; }
| AUTHENTICATION MAC PERMISSIVE { BABEL_IFACE->auth_type = BABEL_AUTH_MAC; BABEL_IFACE->auth_permissive = 1; }
| RTT MIN expr_us { BABEL_IFACE->rtt_min = $3; }
| RTT MAX expr_us { BABEL_IFACE->rtt_max = $3; }
| RTT COST expr { BABEL_IFACE->rtt_cost = $3; if ($3 >= BABEL_INFINITY) cf_error("RTT cost must be < 65535"); }
| RTT DECAY expr { BABEL_IFACE->rtt_decay = $3; if (($3 < 1) || ($3 > 256)) cf_error("RTT decay must be between 1-256"); }
| SEND TIMESTAMPS bool { BABEL_IFACE->rtt_send = $3; }
| password_list
;

View file

@ -58,6 +58,13 @@ struct babel_tlv_ihu {
u8 addr[0];
} PACKED;
struct babel_subtlv_timestamp {
u8 type;
u8 length;
u32 tstamp;
u32 tstamp_rcvd; /* only used in IHU */
} PACKED;
struct babel_tlv_router_id {
u8 type;
u8 length;
@ -161,6 +168,7 @@ struct babel_parse_state {
const struct babel_tlv_data* (*get_subtlv_data)(u8 type);
struct babel_proto *proto;
struct babel_iface *ifa;
btime received_time;
ip_addr saddr;
ip_addr next_hop_ip4;
ip_addr next_hop_ip6;
@ -170,6 +178,7 @@ struct babel_parse_state {
u8 router_id_seen; /* router_id field is valid */
u8 def_ip6_prefix_seen; /* def_ip6_prefix is valid */
u8 def_ip4_prefix_seen; /* def_ip4_prefix is valid */
u8 hello_tstamp_seen; /* pkt contains a hello timestamp */
u8 current_tlv_endpos; /* End of self-terminating TLVs (offset from start) */
u8 sadr_enabled;
u8 is_unicast;
@ -334,6 +343,7 @@ static int babel_read_update(struct babel_tlv *hdr, union babel_msg *msg, struct
static int babel_read_route_request(struct babel_tlv *hdr, union babel_msg *msg, struct babel_parse_state *state);
static int babel_read_seqno_request(struct babel_tlv *hdr, union babel_msg *msg, struct babel_parse_state *state);
static int babel_read_source_prefix(struct babel_tlv *hdr, union babel_msg *msg, struct babel_parse_state *state);
static int babel_read_timestamp(struct babel_tlv *hdr, union babel_msg *msg, struct babel_parse_state *state);
static uint babel_write_ack(struct babel_tlv *hdr, union babel_msg *msg, struct babel_write_state *state, uint max_len);
static uint babel_write_hello(struct babel_tlv *hdr, union babel_msg *msg, struct babel_write_state *state, uint max_len);
@ -342,6 +352,7 @@ static uint babel_write_update(struct babel_tlv *hdr, union babel_msg *msg, stru
static uint babel_write_route_request(struct babel_tlv *hdr, union babel_msg *msg, struct babel_write_state *state, uint max_len);
static uint babel_write_seqno_request(struct babel_tlv *hdr, union babel_msg *msg, struct babel_write_state *state, uint max_len);
static int babel_write_source_prefix(struct babel_tlv *hdr, net_addr *net, uint max_len);
static int babel_write_timestamp(struct babel_tlv *hdr, u32 tstamp, u32 tstamp_rcvd, uint max_len);
static const struct babel_tlv_data tlv_data[BABEL_TLV_MAX] = {
[BABEL_TLV_ACK_REQ] = {
@ -417,6 +428,13 @@ static const struct babel_tlv_data *get_packet_tlv_data(u8 type)
return type < sizeof(tlv_data) / sizeof(*tlv_data) ? &tlv_data[type] : NULL;
}
static const struct babel_tlv_data timestamp_tlv_data = {
sizeof(struct babel_subtlv_timestamp),
babel_read_timestamp,
NULL,
NULL
};
static const struct babel_tlv_data source_prefix_tlv_data = {
sizeof(struct babel_subtlv_source_prefix),
babel_read_source_prefix,
@ -428,6 +446,8 @@ static const struct babel_tlv_data *get_packet_subtlv_data(u8 type)
{
switch (type)
{
case BABEL_SUBTLV_TIMESTAMP:
return &timestamp_tlv_data;
case BABEL_SUBTLV_SOURCE_PREFIX:
return &source_prefix_tlv_data;
@ -489,16 +509,34 @@ babel_read_hello(struct babel_tlv *hdr, union babel_msg *m,
static uint
babel_write_hello(struct babel_tlv *hdr, union babel_msg *m,
struct babel_write_state *state UNUSED, uint max_len UNUSED)
struct babel_write_state *state UNUSED, uint max_len)
{
struct babel_tlv_hello *tlv = (void *) hdr;
struct babel_msg_hello *msg = &m->hello;
uint len = sizeof(struct babel_tlv_hello);
TLV_HDR0(tlv, BABEL_TLV_HELLO);
put_u16(&tlv->seqno, msg->seqno);
put_time16(&tlv->interval, msg->interval);
return sizeof(struct babel_tlv_hello);
if (msg->tstamp)
{
/*
* There can be a substantial delay between when the babel_msg was created
* and when it is serialised. We don't want this included in the RTT
* measurement, so replace the timestamp with the current time to get as
* close as possible to on-wire time for the packet.
*/
u32 tstamp = current_time_now() TO_US;
int l = babel_write_timestamp(hdr, tstamp, 0, max_len);
if (l < 0)
return 0;
len += l;
}
return len;
}
static int
@ -556,6 +594,7 @@ babel_write_ihu(struct babel_tlv *hdr, union babel_msg *m,
{
struct babel_tlv_ihu *tlv = (void *) hdr;
struct babel_msg_ihu *msg = &m->ihu;
uint len = sizeof(*tlv);
if (ipa_is_link_local(msg->addr) && max_len < sizeof(struct babel_tlv_ihu) + 8)
return 0;
@ -567,12 +606,24 @@ babel_write_ihu(struct babel_tlv *hdr, union babel_msg *m,
if (!ipa_is_link_local(msg->addr))
{
tlv->ae = BABEL_AE_WILDCARD;
return sizeof(struct babel_tlv_ihu);
goto out;
}
put_ip6_ll(&tlv->addr, msg->addr);
tlv->ae = BABEL_AE_IP6_LL;
hdr->length += 8;
return sizeof(struct babel_tlv_ihu) + 8;
len += 8;
out:
if (msg->tstamp)
{
int l = babel_write_timestamp(hdr, msg->tstamp, msg->tstamp_rcvd, max_len);
if (l < 0)
return 0;
len += l;
}
return len;
}
static int
@ -1200,6 +1251,66 @@ babel_write_source_prefix(struct babel_tlv *hdr, net_addr *n, uint max_len)
return len;
}
static int
babel_read_timestamp(struct babel_tlv *hdr, union babel_msg *msg,
struct babel_parse_state *state)
{
struct babel_subtlv_timestamp *tlv = (void *) hdr;
switch (msg->type)
{
case BABEL_TLV_HELLO:
if (tlv->length < 4)
return PARSE_ERROR;
msg->hello.tstamp = get_u32(&tlv->tstamp);
msg->hello.pkt_received = state->received_time;
state->hello_tstamp_seen = 1;
break;
case BABEL_TLV_IHU:
if (tlv->length < 8)
return PARSE_ERROR;
/* RTT calculation relies on a Hello always being present with an IHU */
if (!state->hello_tstamp_seen)
break;
msg->ihu.tstamp = get_u32(&tlv->tstamp);
msg->ihu.tstamp_rcvd = get_u32(&tlv->tstamp_rcvd);
msg->ihu.pkt_received = state->received_time;
break;
default:
return PARSE_ERROR;
}
return PARSE_SUCCESS;
}
static int
babel_write_timestamp(struct babel_tlv *hdr, u32 tstamp, u32 tstamp_rcvd, uint max_len)
{
struct babel_subtlv_timestamp *tlv = (void *) NEXT_TLV(hdr);
uint len = sizeof(*tlv);
if (hdr->type == BABEL_TLV_HELLO)
len -= 4;
if (len > max_len)
return -1;
TLV_HDR(tlv, BABEL_SUBTLV_TIMESTAMP, len);
hdr->length += len;
put_u32(&tlv->tstamp, tstamp);
if (hdr->type == BABEL_TLV_IHU)
put_u32(&tlv->tstamp_rcvd, tstamp_rcvd);
return len;
}
static inline int
babel_read_subtlvs(struct babel_tlv *hdr,
union babel_msg *msg,
@ -1470,6 +1581,13 @@ babel_process_packet(struct babel_iface *ifa,
.saddr = saddr,
.next_hop_ip6 = saddr,
.sadr_enabled = babel_sadr_enabled(p),
/*
* The core updates current_time() after returning from poll(), so this is
* actually the time the packet was received, even though there may have
* been a bit of delay before we got to process it
*/
.received_time = current_time(),
};
if ((pkt->magic != BABEL_MAGIC) || (pkt->version != BABEL_VERSION))