Commit 21045b56 authored by Juliusz Chroboczek's avatar Juliusz Chroboczek

Implement protocol version 1.

This is not compatible with version 0.  Uses 24-byte messages and
doesn't distinguish routes from xroutes.
parent b9f132d5
......@@ -42,7 +42,7 @@ THE SOFTWARE.
#include "util.h"
#include "net.h"
#include "kernel.h"
#include "destination.h"
#include "source.h"
#include "neighbour.h"
#include "route.h"
#include "xroute.h"
......@@ -64,12 +64,13 @@ int wired_hello_interval = -1;
int idle_hello_interval = -1;
int update_interval = -1;
int max_request_hopcount = 65;
struct network nets[MAXNETS];
int numnets = 0;
const unsigned char zeroes[16] = {0};
const unsigned char ones[16] =
{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
char *state_file = "/var/lib/babel-state";
......@@ -83,8 +84,6 @@ static volatile sig_atomic_t exiting = 0, dumping = 0;
struct network *add_network(char *ifname, int ifindex, int bufsize,
int wired, unsigned int cost);
void expire_routes(void);
static int kernel_routes_callback(void *closure);
static void init_signals(void);
static void dump_tables(FILE *out);
......@@ -96,6 +95,7 @@ main(int argc, char **argv)
struct ipv6_mreq mreq;
int i, rc, fd;
static unsigned char *buf;
struct timeval check_neighbours_time;
int expiry_time, kernel_dump_time;
void *vrc;
unsigned int seed;
......@@ -135,26 +135,27 @@ main(int argc, char **argv)
protocol_port = atoi(*arg);
} else if(strcmp(*arg, "-x") == 0 || strcmp(*arg, "-X") == 0) {
int force = (strcmp(*arg, "-X") == 0);
if(nummyxroutes >= MAXMYXROUTES) {
fprintf(stderr, "Too many network routes.\n");
if(numxroutes >= MAXXROUTES) {
fprintf(stderr, "Too many exported routes.\n");
exit(1);
}
SHIFTE();
rc = parse_net(*arg,
myxroutes[nummyxroutes].prefix,
&myxroutes[nummyxroutes].plen);
xroutes[numxroutes].prefix,
&xroutes[numxroutes].plen);
if(rc < 0)
goto syntax;
SHIFTE();
if(strcmp(*arg, "infinity") == 0)
myxroutes[nummyxroutes].cost = INFINITY;
else
myxroutes[nummyxroutes].cost = atoi(*arg);
if(myxroutes[nummyxroutes].cost < 0 ||
myxroutes[nummyxroutes].cost > INFINITY)
goto syntax;
myxroutes[nummyxroutes].installed = force ? 2 : 0;
nummyxroutes++;
xroutes[numxroutes].metric = INFINITY;
else {
int metric = atoi(*arg);
if(metric < 0 || metric > INFINITY)
goto syntax;
xroutes[numxroutes].metric = metric;
}
xroutes[numxroutes].exported = force ? 2 : 0;
numxroutes++;
} else if(strcmp(*arg, "-h") == 0) {
SHIFTE();
wireless_hello_interval = atoi(*arg);
......@@ -182,7 +183,7 @@ main(int argc, char **argv)
} else if(strcmp(*arg, "-s") == 0) {
split_horizon = 0;
} else if(strcmp(*arg, "-b") == 0) {
broadcast_txcost = 1;
broadcast_ihu = 1;
} else if(strcmp(*arg, "-S") == 0) {
SHIFTE();
state_file = *arg;
......@@ -196,7 +197,7 @@ main(int argc, char **argv)
}
if(wireless_hello_interval <= 0)
wireless_hello_interval = 8;
wireless_hello_interval = 6;
if(wired_hello_interval <= 0)
wired_hello_interval = 30;
......@@ -207,7 +208,7 @@ main(int argc, char **argv)
70);
if(seqno_interval <= 0)
seqno_interval = MAX(2 * wireless_hello_interval - 1, 2);
seqno_interval = MAX(wireless_hello_interval - 1, 2);
jitter = MIN(wireless_hello_interval * 1000 / 4, 2000);
update_jitter = 2 * jitter;
......@@ -235,7 +236,7 @@ main(int argc, char **argv)
srandom(seed);
reboot_time = now.tv_sec;
seqno = (random() & 0xFF);
myseqno = (random() & 0xFFFF);
fd = open(state_file, O_RDONLY);
if(fd < 0 && errno != ENOENT)
......@@ -243,24 +244,35 @@ main(int argc, char **argv)
rc = unlink(state_file);
if(fd >= 0 && rc < 0) {
perror("unlink(babel-state)");
/* If we couldn't unlink it, it might be stale. */
/* If we couldn't unlink it, it's probably stale. */
close(fd);
fd = -1;
}
if(fd >= 0) {
char buf[100];
int s, t;
char buf2[100];
int s;
long t;
rc = read(fd, buf, 99);
if(rc < 0) {
perror("read(babel-state)");
} else {
buf[rc] = '\0';
rc = sscanf(buf, "%d %d\n", &s, &t);
if(rc == 2 && s >= 0 && s < 256) {
debugf("Got %d %d from babel-state.\n", s, t);
seqno = ((s + 1) & 0xFF);
if(t >= 1176800000 && t <= now.tv_sec)
reboot_time = t;
rc = sscanf(buf, "%99s %d %ld\n", buf2, &s, &t);
if(rc == 3 && s >= 0 && s <= 0xFFFF) {
unsigned char sid[16];
rc = parse_address(buf2, sid);
if(rc < 0) {
fprintf(stderr, "Couldn't parse babel-state.\n");
} else if(memcmp(sid, myid, 16) != 0) {
fprintf(stderr, "ID mismatch in babel-state.\n");
} else {
debugf("Got %s %d %ld from babel-state.\n",
format_address(sid), s, t);
myseqno = ((s + 1) & 0xFFFF);
if(t >= 1176800000L && t <= now.tv_sec)
reboot_time = t;
}
} else {
fprintf(stderr, "Couldn't parse babel-state.\n");
}
......@@ -315,14 +327,14 @@ main(int argc, char **argv)
if(mtu < 0) {
fprintf(stderr, "Warning: couldn't get MTU of interface %s (%d).\n",
*arg, ifindex);
mtu = 1204;
maxmtu = MAX(maxmtu, 16384);
mtu = 1280;
maxmtu = MAX(maxmtu, 0x10000);
} else if(mtu < 1280) {
fprintf(stderr,
"Warning: suspiciously low MTU %d on interface %s (%d).\n",
mtu, *arg, ifindex);
mtu = 1204;
maxmtu = MAX(maxmtu, 16384);
mtu = 1280;
maxmtu = MAX(maxmtu, 0x10000);
} else {
if(mtu >= 0x10000) {
fprintf(stderr,
......@@ -332,10 +344,10 @@ main(int argc, char **argv)
maxmtu = MAX(maxmtu, mtu);
mtu = 32768;
}
maxmtu = MAX(maxmtu, mtu);
/* 40 for IPv6 header, 8 for UDP header, 12 for good luck. */
mtu -= 60;
}
maxmtu = MAX(maxmtu, mtu);
/* 40 for IPv6 header, 8 for UDP header, 12 for good luck. */
mtu -= 60;
rc = kernel_interface_wireless(*arg, ifindex);
if(rc < 0) {
......@@ -347,7 +359,7 @@ main(int argc, char **argv)
}
debugf("Adding %s network %s (%d).\n",
rc ? "wireless" : "wired", *arg, ifindex);
vrc = add_network(*arg, ifindex, mtu, !rc, rc ? 0 : 128);
vrc = add_network(*arg, ifindex, mtu, !rc, rc ? 256 : 128);
if(vrc == NULL)
goto fail;
SHIFT();
......@@ -360,24 +372,28 @@ main(int argc, char **argv)
}
init_signals();
check_myxroutes();
check_xroutes();
kernel_routes_changed = 0;
kernel_dump_time = now.tv_sec + 20 + random() % 20;
timeval_plus_msec(&check_neighbours_time, &now, 5000 + random() % 5000);
expiry_time = now.tv_sec + 20 + random() % 20;
/* Make some noise so others notice us */
/* Make some noise so that others notice us */
for(i = 0; i < numnets; i++) {
gettimeofday(&now, NULL);
send_hello(&nets[i]);
send_request(&nets[i], NULL, 0, -1);
send_request(&nets[i], NULL, 0);
flushbuf(&nets[i]);
usleep(50000 + random() % 100000);
}
for(i = 0; i < numnets; i++) {
usleep(50000 + random() % 100000);
flushbuf(&nets[i]);
gettimeofday(&now, NULL);
send_hello(&nets[i]);
send_self_update(&nets[i], 0);
send_request(&nets[i], NULL, 0, -1);
send_request(&nets[i], NULL, 0);
flushbuf(&nets[i]);
usleep(50000 + random() % 100000);
}
debugf("Entering main loop.\n");
......@@ -387,19 +403,9 @@ main(int argc, char **argv)
fd_set readfds;
gettimeofday(&now, NULL);
if(update_flush_time.tv_sec != 0) {
if(now.tv_sec >= update_flush_time.tv_sec)
flushupdates();
}
for(i = 0; i < numnets; i++) {
if(nets[i].flush_time.tv_sec != 0) {
if(timeval_compare(&now, &nets[i].flush_time) >= 0)
flushbuf(&nets[i]);
}
}
tv.tv_sec = expiry_time;
tv.tv_usec = random() % 1000000;
tv = check_neighbours_time;
timeval_min_sec(&tv, expiry_time);
for(i = 0; i < numnets; i++) {
timeval_min(&tv, &nets[i].flush_time);
timeval_min_sec(&tv,
......@@ -420,12 +426,13 @@ main(int argc, char **argv)
rc = select(MAX(protocol_socket, kernel_socket) + 1,
&readfds, NULL, NULL, &tv);
if(rc < 0) {
if(errno != EINTR) {
if(errno == EINTR) {
rc = 0;
FD_ZERO(&readfds);
} else {
perror("select");
sleep(1);
continue;
} else {
FD_ZERO(&readfds);
}
}
}
......@@ -458,32 +465,40 @@ main(int argc, char **argv)
}
}
if(now.tv_sec >= expiry_time) {
expire_routes();
expiry_time = now.tv_sec + 20 + random() % 20;
}
if(kernel_routes_changed || now.tv_sec >= kernel_dump_time) {
rc = check_myxroutes();
rc = check_xroutes();
if(rc > 0)
send_self_update(NULL, 1);
else if(rc < 0)
fprintf(stderr, "Warning: couldn't check installed routes.\n");
fprintf(stderr, "Warning: couldn't check exported routes.\n");
kernel_routes_changed = 0;
if(kernel_socket >= 0)
kernel_dump_time = now.tv_sec + 200 + random() % 100;
kernel_dump_time = now.tv_sec + 200 + random() % 200;
else
kernel_dump_time = now.tv_sec + 20 + random() % 20;
}
if(timeval_compare(&check_neighbours_time, &now) < 0) {
int msecs;
msecs = check_neighbours();
msecs = MAX(msecs, 500);
timeval_plus_msec(&check_neighbours_time, &now,
msecs / 2 + random() % msecs);
}
if(now.tv_sec >= expiry_time) {
expire_routes();
expiry_time = now.tv_sec + 20 + random() % 20;
}
for(i = 0; i < numnets; i++) {
if(now.tv_sec >= nets[i].hello_time + nets[i].hello_interval)
send_hello(&nets[i]);
if(now.tv_sec >= nets[i].txcost_time + nets[i].txcost_interval)
send_txcost(NULL, &nets[i]);
if(now.tv_sec >= nets[i].ihu_time + nets[i].ihu_interval)
send_ihu(NULL, &nets[i]);
if(!network_idle(&nets[i])) {
if(now.tv_sec >= nets[i].update_time + update_interval)
send_update(NULL, &nets[i]);
send_update(&nets[i], NULL, 0);
if(now.tv_sec >=
nets[i].self_update_time + nets[i].self_update_interval) {
send_self_update(&nets[i], 0);
......@@ -491,6 +506,18 @@ main(int argc, char **argv)
}
}
if(update_flush_time.tv_sec != 0) {
if(now.tv_sec >= update_flush_time.tv_sec)
flushupdates();
}
for(i = 0; i < numnets; i++) {
if(nets[i].flush_time.tv_sec != 0) {
if(timeval_compare(&now, &nets[i].flush_time) >= 0)
flushbuf(&nets[i]);
}
}
if(debug || dumping) {
dump_tables(stdout);
dumping = 0;
......@@ -502,25 +529,25 @@ main(int argc, char **argv)
/* Uninstall and retract all routes. */
if(routes[i].installed) {
uninstall_route(&routes[i]);
send_update(routes[i].dest, NULL);
send_update(NULL, routes[i].src->prefix, routes[i].src->plen);
}
}
flushupdates();
usleep(50000 + random() % 100000);
for(i = 0; i < numnets; i++) {
/* Retract self routes. */
/* Retract exported routes. */
send_self_retract(&nets[i]);
/* Make sure that we expire quickly from our neighbours'
association caches. */
nets[i].hello_interval = 1;
send_hello(&nets[i]);
flushbuf(&nets[i]);
usleep(50000 + random() % 100000);
}
usleep(50000 + random() % 100000);
for(i = 0; i < numnets; i++) {
/* Make sure they got it. */
send_self_retract(&nets[i]);
send_hello(&nets[i]);
flushbuf(&nets[i]);
usleep(50000 + random() % 100000);
kernel_setup_interface(0, nets[i].ifname, nets[i].ifindex);
}
kernel_setup(0);
......@@ -531,7 +558,8 @@ main(int argc, char **argv)
unlink(state_file);
} else {
char buf[100];
rc = snprintf(buf, 100, "%d %d\n", seqno, (int)now.tv_sec);
rc = snprintf(buf, 100, "%s %d %ld\n",
format_address(myid), (int)myseqno, (long)now.tv_sec);
rc = write(fd, buf, rc);
if(rc < 0) {
perror("write(babel-state)");
......@@ -539,7 +567,7 @@ main(int argc, char **argv)
}
close(fd);
}
debugf("Done.");
debugf("Done.\n");
return 0;
syntax:
......@@ -551,7 +579,7 @@ main(int argc, char **argv)
" "
"[-u update] [-k metric] [-s] [-P] [-c cost]\n"
" "
"[-d level] [-x net cost] [-X net cost]... address interface...\n",
"[-d level] [-x net cost] [-X net cost]... id interface...\n",
argv[0]);
exit(1);
......@@ -622,16 +650,6 @@ dump_tables(FILE *out)
fprintf(out, "My id %s\n", format_address(myid));
for(i = 0; i < nummyxroutes; i++) {
fprintf(out, "External %s/%d cost %d%s\n",
format_address(myxroutes[i].prefix),
myxroutes[i].plen,
myxroutes[i].cost,
myxroutes[i].installed ?
(myxroutes[i].installed >= 2 ? " (forced)" : " (installed)") :
"");
}
for(i = 0; i < numneighs; i++) {
if(neighs[i].id[0] == 0xFF)
continue;
......@@ -643,27 +661,25 @@ dump_tables(FILE *out)
neighbour_rxcost(&neighs[i]),
neighs[i].txcost);
}
for(i = 0; i < numroutes; i++) {
fprintf(out, "%s metric %d refmetric %d seqno %d age %d ",
format_address(routes[i].dest->address),
routes[i].metric, routes[i].refmetric, routes[i].seqno,
(int)(now.tv_sec - routes[i].time));
fprintf(out, "via %s nexthop %s%s\n",
routes[i].nexthop->network->ifname,
format_address(routes[i].nexthop->address),
routes[i].installed ? " (installed)" :
route_feasible(&routes[i]) ? " (feasible)" : "");
}
for(i = 0; i < numxroutes; i++) {
fprintf(out, "%s/%d gw %s nexthop %s cost %d metric %d age %d%s\n",
format_address(xroutes[i].prefix),
xroutes[i].plen,
format_address(xroutes[i].gateway->address),
format_address(xroutes[i].nexthop->address),
xroutes[i].cost,
fprintf(out, "%s metric %d (%s)\n",
format_prefix(xroutes[i].prefix, xroutes[i].plen),
xroutes[i].metric,
(int)(now.tv_sec - xroutes[i].time),
xroutes[i].installed ? " (installed)" : "");
xroutes[i].exported ?
xroutes[i].exported > 1 ? "forced" : "exported" :
"not exported");
}
for(i = 0; i < numroutes; i++) {
fprintf(out, "%s via %s metric %d refmetric %d seqno %d age %d "
"via %s nexthop %s%s\n",
format_prefix(routes[i].src->prefix, routes[i].src->plen),
format_address(routes[i].src->address),
routes[i].metric, routes[i].refmetric, (int)routes[i].seqno,
(int)(now.tv_sec - routes[i].time),
routes[i].nexthop->network->ifname,
format_address(routes[i].nexthop->address),
routes[i].installed ? " (installed)" :
route_feasible(&routes[i]) ? " (feasible)" : "");
}
fflush(out);
}
......@@ -702,7 +718,7 @@ add_network(char *ifname, int ifindex, int mtu, int wired, unsigned int cost)
nets[numnets].buffered = 0;
nets[numnets].bucket_time = now.tv_sec;
nets[numnets].bucket = 0;
nets[numnets].hello_seqno = (random() & 0xFF);
nets[numnets].hello_seqno = (random() & 0xFFFF);
numnets++;
return &nets[numnets - 1];
}
......@@ -725,55 +741,7 @@ update_hello_interval(struct network *net)
net->hello_interval = wireless_hello_interval;
net->self_update_interval =
MAX(15 + nets[numnets].hello_interval / 2,
nets[numnets].hello_interval);
net->txcost_interval = 7 * net->hello_interval / 4;
}
MAX(15 + net->hello_interval / 2, net->hello_interval);
void
expire_routes(void)
{
int i;
debugf("Expiring old routes.\n");
for(i = 0; i < numneighs; i++) {
if(neighs[i].id[0] == 0xFF)
continue;
check_neighbour(&neighs[i]);
/* No need to update_neighbour_metric -- update_route_metric below. */
}
i = 0;
while(i < numroutes) {
struct route *route = &routes[i];
if(route->time < now.tv_sec - route_gc_delay) {
flush_route(route);
continue;
}
update_route_metric(route);
if(route->installed && route->refmetric < INFINITY) {
if(route->time < now.tv_sec - MAX(10, route_timeout_delay - 25))
send_unicast_request(route->nexthop, route->dest,
max_request_hopcount, -1);
}
i++;
}
i = 0;
while(i < numxroutes) {
struct xroute *xroute = &xroutes[i];
if(xroute->time < now.tv_sec - xroute_gc_delay ||
(xroute->cost >= INFINITY &&
xroute->time < now.tv_sec - xroute_hold_delay)) {
flush_xroute(xroute);
continue;
}
update_xroute_metric(xroute, xroute->cost);
i++;
}
net->ihu_interval = 10 * net->hello_interval / 4;
}
......@@ -21,12 +21,10 @@ THE SOFTWARE.
*/
#define MAXROUTES 512
#define MAXDESTS 1024
#define MAXSRCS 1024
#define MAXNEIGHBOURS 128
#define MAXNETS 8
#define MAXXROUTES 128
#define MAXMYXROUTES 64
#define MAX_BUFFERED_UPDATES 100
#define MAXXROUTES 64
#define INFINITY ((unsigned short)(~0))
......@@ -68,7 +66,7 @@ struct network {
int hello_time;
int self_update_time;
int update_time;
int txcost_time;
int ihu_time;
char ifname[IF_NAMESIZE];
int buffered;
struct timeval flush_time;
......@@ -77,10 +75,10 @@ struct network {
int bucket_time;
unsigned int bucket;
int activity_time;
unsigned char hello_seqno;
unsigned short hello_seqno;
unsigned int hello_interval;
unsigned int self_update_interval;
unsigned int txcost_interval;
unsigned int ihu_interval;
};
extern struct timeval now;
......@@ -92,7 +90,7 @@ extern unsigned char myid[16];
extern struct network nets[MAXNETS];
extern int numnets;
extern const unsigned char zeroes[16];
extern const unsigned char zeroes[16], ones[16];
extern int protocol_port;
extern unsigned char protocol_group[16];
......
......@@ -96,7 +96,7 @@ write_proc(char *filename, int value)
}
struct netlink {
unsigned int seqno;
unsigned short seqno;
int sock;
struct sockaddr_nl sockaddr;
socklen_t socklen;
......
......@@ -30,7 +30,7 @@ THE SOFTWARE.
#include "babel.h"
#include "util.h"
#include "net.h"
#include "destination.h"
#include "source.h"
#include "neighbour.h"
#include "route.h"
#include "xroute.h"
......@@ -38,21 +38,25 @@ THE SOFTWARE.
struct timeval update_flush_time = {0, 0};
const unsigned char packet_header[4] = {42, 0, 0, 0};
const unsigned char packet_header[8] = {42, 1};
unsigned int jitter;
unsigned int update_jitter;
int add_cost = 0;
int parasitic = 0;
int silent_time = 30;
int broadcast_txcost = 0;
int broadcast_ihu = 0;
int split_horizon = 1;
unsigned char seqno = 0;
unsigned short myseqno = 0;
int seqno_time = 0;
int seqno_interval = -1;
struct destination *buffered_updates[MAX_BUFFERED_UPDATES];
struct buffered_update {
unsigned char prefix[16];
unsigned char plen;
};
struct buffered_update buffered_updates[MAX_BUFFERED_UPDATES];
struct network *update_net = NULL;
int updates = 0;
......@@ -62,9 +66,13 @@ parse_packet(const unsigned char *from, struct network *net,
{
int i, j;
const unsigned char *message;
unsigned char type, plen;
unsigned short seqno;
unsigned short metric;
const unsigned char *address;
struct neighbour *neigh;
struct xroute pxroutes[40];
int numpxroutes = 0;
int have_current_source = 0;
unsigned char current_source[16];
if(from[0] != 0xFE || (from[1] & 0xC0) != 0x80) {
fprintf(stderr, "Received packet from non-local address %s.\n",
......@@ -72,148 +80,122 @@ parse_packet(const unsigned char *from, struct network *net,
return;
}
if(len % 20 != 4 || packet[0] != 42) {
if(packet[0] != 42) {
fprintf(stderr, "Received malformed packet on %s from %s.\n",
net->ifname, format_address(from));
return;
}
if(packet[1] != 0) {
if(packet[1] != 1) {
fprintf(stderr,
"Received packet with unknown version %d on %s from %s.\n",
packet[1], net->ifname, format_address(from));
return;
}
if(len % 24 != 8) {
fprintf(stderr, "Received malformed packet on %s from %s.\n",
net->ifname, format_address(from));
return;
}
j = 0;
for(i = 0; i < (len - 4) / 20; i++) {
message = packet + 4 + 20 * i;
if(message[0] != 4 && message[0] != 2) {
if(numpxroutes > 0) {
fprintf(stderr, "Received unexpected xroute on %s from %s.\n",
net->ifname, format_address(from));
}
numpxroutes = 0;
VALGRIND_MAKE_MEM_UNDEFINED(&pxroutes, sizeof(pxroutes));
}
if(message[0] == 0) {
if(memcmp(message + 4, myid, 16) == 0)
for(i = 0; i < (len - 8) / 24; i++) {
message = packet + 8 + 24 * i;
type = message[0];
plen = message[1];
seqno = ntohs(*(uint16_t*)(message + 4));
metric = ntohs(*(uint16_t*)(message + 6));
address = message + 8;
if(type == 0) {
int changed;
if(memcmp(address, myid, 16) == 0)
continue;
debugf("Received hello on %s from %s (%s).\n",
net->ifname,
format_address(message + 4),
format_address(address),
format_address(from));
net->activity_time = now.tv_sec;
neigh = add_neighbour(message + 4, from, net);
neigh = add_neighbour(address, from, net);
if(neigh == NULL)
continue;
update_neighbour(neigh, message[1],
((message[2] & 0x3) << 8) | message[3]);
update_neighbour_metric(neigh);
changed = update_neighbour(neigh, seqno, metric);
if(changed)
update_neighbour_metric(neigh);
} else {
neigh = find_neighbour(from, net);
if(neigh == NULL)
continue;
net->activity_time = now.tv_sec;
if(message[0] == 1) {
int hopcount = message[2];
int theirseqno = message[1];
if(type == 1) {
debugf("Received ihu for %s from %s (%s).\n",
format_address(address),
format_address(neigh->id),
format_address(from));
if(plen == 0xFF || memcmp(myid, address, 16) == 0) {
neigh->txcost = metric;
neigh->ihu_time = now.tv_sec;
update_neighbour_metric(neigh);
}
} else if(type == 2) {
debugf("Received request on %s from %s (%s) for %s "
"(%d hops for seqno %d).\n",
"(%d hops).\n",
net->ifname,
format_address(neigh->id),
format_address(from),
format_address(message + 4),
hopcount, theirseqno);
if(memcmp(message + 4, zeroes, 16) == 0) {
plen == 0xFF ?
"any" :
format_prefix(address, plen),
metric);
if(plen == 0xFF) {
/* If a neighbour is requesting a full route dump from us,
we might as well send its txcost. */
send_txcost(neigh, NULL);
send_update(NULL, neigh->network);
} else if(memcmp(message + 4, myid, 16) == 0) {
int new_seqno;
send_txcost(neigh, NULL);
if(hopcount > 0 && seqno_compare(theirseqno, seqno) <= 0)
/* Somebody's requesting an old seqno. */
new_seqno = 0;
else
new_seqno = 1;
send_self_update(neigh->network, new_seqno);
} else if(!parasitic) {
struct destination *dest;
dest = find_destination(message + 4, 0, 0);
if(dest) {
if(hopcount == 0) {
send_update(dest, neigh->network);
} else {
/* Request for a new seqno */
struct route *installed;
installed = find_installed_route(dest);
if(installed && installed->nexthop != neigh) {
if(seqno_compare(installed->seqno,
theirseqno) >= 0)
send_update(dest, neigh->network);
else if(!request_requested(dest,
theirseqno,
neigh->network)) {
if(hopcount >= 2)
send_unicast_request(installed->nexthop,
dest,
hopcount - 1,
theirseqno);
notice_request(dest, theirseqno,
neigh->network);
}
}
}
}
we might as well send it an ihu. */
send_ihu(neigh, NULL);
send_update(neigh->network, NULL, 0);
} else {
send_update(neigh->network, address, plen);
}
} else if(message[0] == 2) {
debugf("Received update on %s from %s (%s) for %s.\n",
} else if(type == 3) {
if(plen == 0xFF)
debugf("Received update for %s/none on %s from %s (%s).\n",
format_address(message + 8),
net->ifname,
format_address(neigh->id),
format_address(from));
else
debugf("Received update for %s on %s from %s (%s).\n",
format_prefix(message + 8, plen),
net->ifname,
format_address(neigh->id),
format_address(from));
memcpy(current_source, address, 16);
have_current_source = 1;
if(memcmp(address, myid, 16) == 0)
continue;
if(plen <= 128)
update_route(address, mask_prefix(address, plen), plen,
seqno, metric, neigh);
} else if(type == 4) {
debugf("Received prefix %s on %s from %s (%s).\n",
format_prefix(address, plen),
net->ifname,
format_address(neigh->id),
format_address(from),
format_address(message + 4));
if(memcmp(message + 4, myid, 16) == 0) {
int metric = ((message[2] << 8) | (message[3] & 0xFF));
int theirseqno = message[1];
if(metric >= INFINITY) {
/* Oh my, someone is retracting a route to me. */
send_txcost(neigh, NULL);
send_self_update(neigh->network,
seqno_compare(theirseqno, seqno) > 0);
}
continue;
}
update_route(message + 4,
message[1], (message[2] << 8 | message[3]),
neigh, pxroutes, numpxroutes);
numpxroutes = 0;
VALGRIND_MAKE_MEM_UNDEFINED(&pxroutes, sizeof(pxroutes));
} else if(message[0] == 3) {
debugf("Received txcost from %s.\n", format_address(from));
if(memcmp(myid, message + 4, 16) == 0 ||
memcmp(zeroes, message + 4, 16) == 0) {
neigh->txcost = (message[2] << 8 | message[3]);
neigh->txcost_time = now.tv_sec;
}
update_neighbour_metric(neigh);
} else if(message[0] == 4) {
debugf("Received xroute from %s.\n",
format_address(from));
if(numpxroutes >= 40) {
fprintf(stderr, "Too many xroutes in update.\n");
if(!have_current_source) {
fprintf(stderr, "Received prefix with no source "
"on %s from %s (%s).\n",
net->ifname,
format_address(neigh->id),
format_address(from));
continue;
}
memcpy(pxroutes[numpxroutes].prefix, message + 4, 16);
pxroutes[numpxroutes].plen = message[1];
pxroutes[numpxroutes].cost =
((message[2] << 8) | (message[3] & 0xFF));
numpxroutes++;
if(memcmp(current_source, myid, 16) == 0)
continue;
update_route(current_source, mask_prefix(address, plen), plen,
seqno, metric, neigh);
} else {
debugf("Received unknown packet type %d from %s.\n",
message[0], format_address(from));
debugf("Received unknown packet type %d from %s (%s).\n",
type, format_address(neigh->id), format_address(from));
}
}
}
......@@ -248,10 +230,10 @@ flushbuf(struct network *net)
int rc;
struct sockaddr_in6 sin6;
if(update_net == net) {
assert(net->buffered <= net->bufsize);
if(update_net == net)
flushupdates();
return;
}
if(net->buffered > 0) {
debugf(" (flushing %d buffered bytes on %s)\n",
......@@ -304,6 +286,7 @@ schedule_flush_now(struct network *net)
static void
start_message(struct network *net, int bytes)
{
assert(net->buffered % 8 == 0);
if(net->bufsize - net->buffered < bytes)
flushbuf(net);
}
......@@ -311,7 +294,6 @@ start_message(struct network *net, int bytes)
static void
accumulate_byte(struct network *net, unsigned char byte)
{
assert(net->bufsize - net->buffered >= 1);
net->sendbuf[net->buffered] = byte;
net->buffered++;
}
......@@ -319,9 +301,7 @@ accumulate_byte(struct network *net, unsigned char byte)
static void
accumulate_short(struct network *net, unsigned short s)
{
assert(net->bufsize - net->buffered >= 2);
net->sendbuf[net->buffered] = s >> 8;
net->sendbuf[net->buffered + 1] = (s & 0xFF);
*(uint16_t *)(net->sendbuf + net->buffered) = htons(s);
net->buffered += 2;
}
......@@ -329,61 +309,78 @@ static void
accumulate_data(struct network *net,
const unsigned char *data, unsigned int len)
{
assert(net->bufsize - net->buffered >= len);
memcpy(net->sendbuf + net->buffered, data, len);
net->buffered += len;
}
static void
send_message(struct network *net,
unsigned char type, unsigned char plen,
unsigned short seqno, unsigned short metric,
const unsigned char *address)
{
start_message(net, 24);
accumulate_byte(net, type);
accumulate_byte(net, plen);
accumulate_short(net, 0);
accumulate_short(net, seqno);
accumulate_short(net, metric);
accumulate_data(net, address, 16);
schedule_flush(net);
}
static const unsigned char *
message_source_id(struct network *net)
{
int i;
assert(net->buffered % 24 == 0);
i = net->buffered / 24 - 1;
while(i >= 0) {
const unsigned char *message;
message = (const unsigned char*)(net->sendbuf + i * 24);
if(message[0] == 3)
return message + 8;
else if(message[0] == 4)
i--;
else
break;
}
return NULL;
}
void
send_hello(struct network *net)
{
debugf("Sending hello to %s.\n", net->ifname);
update_hello_interval(net);
start_message(net, 20);
accumulate_byte(net, 0);
net->hello_seqno = ((net->hello_seqno + 1) & 0xFF);
accumulate_byte(net, net->hello_seqno);
if(net->hello_interval >= 1 << 10)
accumulate_short(net, 0);
else
accumulate_short(net, net->hello_interval);
accumulate_data(net, myid, 16);
schedule_flush(net);
net->hello_seqno = ((net->hello_seqno + 1) & 0xFFFF);
net->hello_time = now.tv_sec;
send_message(net, 0, 0, net->hello_seqno,
100 * net->hello_interval > 0xFFFF ?
0 : 100 * net->hello_interval,
myid);
}
void
send_request(struct network *net, struct destination *dest,
int hopcount, int seqno)
send_request(struct network *net,
const unsigned char *prefix, unsigned char plen)
{
int i;
if(net == NULL) {
for(i = 0; i < numnets; i++)
send_request(&nets[i], dest, hopcount, seqno);
send_request(&nets[i], prefix, plen);
return;
}
debugf("Sending request to %s for %s (%d hops for seqno %d).\n",
net->ifname, dest ? format_address(dest->address) : "::/0",
hopcount, seqno);
start_message(net, 20);
accumulate_byte(net, 1);
if(hopcount > 0 && dest) {
if(seqno >= 0)
accumulate_byte(net, seqno);
else
accumulate_byte(net,
dest->metric >= INFINITY ?
dest->seqno : ((dest->seqno + 1) & 0xFF));
accumulate_byte(net, hopcount);
} else {
accumulate_byte(net, 0);
accumulate_byte(net, 0);
}
accumulate_byte(net, 0);
accumulate_data(net, dest ? dest->address : zeroes, 16);
schedule_flush(net);
debugf("Sending request to %s for %s.\n",
net->ifname, prefix ? format_prefix(prefix, plen) : "any");
if(prefix)
send_message(net, 2, plen, 0, 0, prefix);
else
send_message(net, 2, 0xFF, 0, 0, ones);
}
static void
......@@ -411,44 +408,50 @@ send_unicast_packet(struct neighbour *neigh, unsigned char *buf, int buflen)
}
void
send_unicast_request(struct neighbour *neigh, struct destination *dest,
int hopcount, int seqno)
send_unicast_request(struct neighbour *neigh,
const unsigned char *prefix, unsigned char plen)
{
unsigned char buf[20];
unsigned char buf[24];
debugf("Sending unicast request to %s (%s) for %s "
"(%d hops for seqno %d).\n",
debugf("Sending unicast request to %s (%s) for %s.\n",
format_address(neigh->id),
format_address(neigh->address),
dest ? format_address(dest->address) : "::/0",
hopcount, seqno);
prefix ? format_prefix(prefix, plen) : "any");
memset(buf, 0, 24);
buf[0] = 1;
if(hopcount > 0 && dest) {
if(seqno >= 0)
buf[1] = seqno;
else
buf[1] =
dest->metric >= INFINITY ?
dest->seqno : ((dest->seqno + 1) & 0xFF);
buf[2] = hopcount;
if(prefix) {
memcpy(buf + 8, prefix, 16);
buf[7] = plen;
} else {
buf[1] = 0;
buf[2] = 0;
memcpy(buf + 8, ones, 16);
buf[7] = 0xFF;
}
buf[3] = 0;
if(dest == NULL)
memset(buf + 4, 0, 16);
else
memcpy(buf + 4, dest->address, 16);
send_unicast_packet(neigh, buf, 24);
}
send_unicast_packet(neigh, buf, 20);
static void
really_send_update(struct network *net,
const unsigned char *address,
const unsigned char *prefix, unsigned char plen,
unsigned short seqno, unsigned short metric)
{
if(in_prefix(address, prefix, plen)) {
send_message(net, 3, plen, seqno, metric, address);
} else {
unsigned const char *sid;
start_message(net, 48);
sid = message_source_id(net);
if(sid == NULL || memcmp(address, sid, 16) != 0)
send_message(net, 3, 0xFF, 0, 0xFFFF, address);
send_message(net, 4, plen, seqno, metric, prefix);
}
}
void
flushupdates(void)
{
int i, j;
int i;
if(updates > 0) {
/* Ensure that we won't be recursively called by flushbuf. */
......@@ -458,96 +461,44 @@ flushupdates(void)
update_net = NULL;
debugf(" (flushing %d buffered updates)\n", n);
for(i = 0; i < n; i++) {
if(buffered_updates[i] == NULL) {
int numpxroutes;
numpxroutes = 0;
for(j = 0; j < nummyxroutes; j++) {
if(myxroutes[j].installed)
numpxroutes++;
}
start_message(net, 20 + 20 * numpxroutes);
for(j = 0; j < nummyxroutes; j++) {
if(!myxroutes[j].installed)
continue;
if(net->bufsize - net->buffered < 40) {
/* We cannot just call start_message, as this would
split the xroutes from the update. */
start_message(net, 20);
accumulate_byte(net, 2);
accumulate_byte(net, seqno);
accumulate_short(net, 0);
accumulate_data(net, myid, 16);
flushbuf(net);
}
start_message(net, 20);
accumulate_byte(net, 4);
accumulate_byte(net, myxroutes[j].plen);
accumulate_short(net, myxroutes[j].cost);
accumulate_data(net, myxroutes[j].prefix, 16);
}
start_message(net, 20);
accumulate_byte(net, 2);
accumulate_byte(net, seqno);
accumulate_short(net, 0);
accumulate_data(net, myid, 16);
} else {
struct route *route;
int seqno;
int metric;
route = find_installed_route(buffered_updates[i]);
if(route) {
if(split_horizon && net->wired &&
route->nexthop->network == net)
continue;
seqno = route->seqno;
metric = MIN(route->metric + add_cost, INFINITY);
} else {
seqno = buffered_updates[i]->seqno;
metric = INFINITY;
}
update_destination(buffered_updates[i], seqno, metric);
satisfy_request(buffered_updates[i], seqno, net);
/* Don't send xroutes if the metric is infinite as the seqno
might be originated by us. */
if(metric < INFINITY) {
int numpxroutes;
numpxroutes = 0;
for(j = 0; j < numxroutes; j++) {
if(xroutes[j].installed &&
xroutes[j].gateway == buffered_updates[i])
numpxroutes++;
}
start_message(net, 20 + 20 * numpxroutes);
for(j = 0; j < numxroutes; j++) {
if(!xroutes[j].installed)
continue;
if(xroutes[j].gateway != buffered_updates[i])
continue;
/* See comment above */
if(net->bufsize - net->buffered < 40) {
start_message(net, 20);
accumulate_byte(net, 2);
accumulate_byte(net, seqno);
accumulate_short(net, metric);
accumulate_data(net,
buffered_updates[i]->address, 16);
flushbuf(net);
}
start_message(net, 20);
accumulate_byte(net, 4);
accumulate_byte(net, xroutes[j].plen);
accumulate_short(net, xroutes[j].cost);
accumulate_data(net, xroutes[j].prefix, 16);
}
}
start_message(net, 20);
accumulate_byte(net, 2);
accumulate_byte(net, seqno);
accumulate_short(net, metric);
accumulate_data(net, buffered_updates[i]->address, 16);
for(i = 0; i < n; i++) {
struct xroute *xroute;
struct route *route;
struct source *src;
unsigned short seqno;
unsigned short metric;
xroute = find_exported_xroute(buffered_updates[i].prefix,
buffered_updates[i].plen);
if(xroute) {
really_send_update(net, myid,
xroute->prefix, xroute->plen,
myseqno, xroute->metric);
continue;
}
route = find_installed_route(buffered_updates[i].prefix,
buffered_updates[i].plen);
if(route) {
if(split_horizon &&
net->wired && route->nexthop->network == net)
continue;
seqno = route->seqno;
metric = MIN((int)route->metric + add_cost, INFINITY);
really_send_update(net, route->src->address,
route->src->prefix,
route->src->plen,
seqno, metric);
update_source(route->src, seqno, metric);
continue;
}
src = find_recent_source(buffered_updates[i].prefix,
buffered_updates[i].plen);
if(src) {
really_send_update(net, src->address, src->prefix, src->plen,
src->metric >= INFINITY ?
src->seqno : (src->seqno + 1) & 0xFFFF,
INFINITY);
continue;
}
}
schedule_flush_now(net);
......@@ -570,7 +521,8 @@ schedule_update_flush(void)
}
static void
buffer_update(struct network *net, struct destination *dest)
buffer_update(struct network *net,
const unsigned char *prefix, unsigned char plen)
{
int i;
......@@ -579,64 +531,61 @@ buffer_update(struct network *net, struct destination *dest)
update_net = net;
for(i = 0; i < updates; i++)
if(buffered_updates[i] == dest)
for(i = 0; i < updates; i++) {
if(buffered_updates[i].plen == plen &&
memcmp(buffered_updates[i].prefix, prefix, 16) == 0)
return;
}
if(updates >= MAX_BUFFERED_UPDATES)
flushupdates();
buffered_updates[updates++] = dest;
memcpy(buffered_updates[updates].prefix, prefix, 16);
buffered_updates[updates].plen = plen;
updates++;
}
void
send_update(struct destination *dest, struct network *net)
send_update(struct network *net,
const unsigned char *prefix, unsigned char plen)
{
int i;
if(dest != NULL) {
/* The case of net == NULL is not handled by flushupdates.
The other case is, but it won't do any harm to record it early. */
struct route *installed = find_installed_route(dest);
satisfy_request(dest, installed ? installed->seqno : dest->seqno,
net);
}
if(net == NULL) {
for(i = 0; i < numnets; i++)
send_update(dest, &nets[i]);
send_update(&nets[i], prefix, plen);
return;
}
if(parasitic ||
(silent_time && now.tv_sec < reboot_time + silent_time)) {
net->update_time = now.tv_sec;
if(dest == NULL)
if(parasitic || (silent_time && now.tv_sec < reboot_time + silent_time)) {
if(prefix == NULL) {
send_self_update(net, 0);
net->update_time = now.tv_sec;
} else if(find_exported_xroute(prefix, plen)) {
buffer_update(net, prefix, plen);
}
return;
}
silent_time = 0;
if(dest) {
if(updates >= net->bufsize / 20) {
if(prefix) {
if(updates > net->bufsize / 24 - 2) {
/* Update won't fit in a single packet -- send a full dump. */
send_update(NULL, net);
send_update(net, NULL, 0);
return;
}
debugf("Sending update to %s for %s.\n",
net->ifname, format_address(dest->address));
buffer_update(net, dest);
net->ifname, format_prefix(prefix, plen));
buffer_update(net, prefix, plen);
} else {
if(now.tv_sec - net->update_time < 2) {
send_self_update(net, 0);
send_self_update(net, 0);
if(now.tv_sec - net->update_time < 1)
return;
}
debugf("Sending update to %s for ::/0.\n", net->ifname);
debugf("Sending update to %s for any.\n", net->ifname);
for(i = 0; i < numroutes; i++)
if(routes[i].installed)
buffer_update(net, routes[i].dest);
buffer_update(net, routes[i].src->prefix, routes[i].src->plen);
net->update_time = now.tv_sec;
send_self_update(net, 0);
}
schedule_update_flush();
}
......@@ -644,14 +593,13 @@ send_update(struct destination *dest, struct network *net)
void
send_self_update(struct network *net, int force_seqno)
{
if((force_seqno && seqno_time < now.tv_sec) ||
seqno_time + seqno_interval < now.tv_sec) {
seqno = ((seqno + 1) & 0xFF);
int i;
if(force_seqno || seqno_time + seqno_interval < now.tv_sec) {
myseqno = ((myseqno + 1) & 0xFFFF);
seqno_time = now.tv_sec;
}
if(net == NULL) {
int i;
for(i = 0; i < numnets; i++)
send_self_update(&nets[i], 0);
return;
......@@ -659,14 +607,20 @@ send_self_update(struct network *net, int force_seqno)
debugf("Sending self update to %s.\n", net->ifname);
buffer_update(net, NULL);
net->self_update_time = now.tv_sec;
for(i = 0; i < numxroutes; i++) {
if(xroutes[i].exported)
send_update(net, xroutes[i].prefix, xroutes[i].plen);
}
schedule_update_flush();
}
void
send_self_retract(struct network *net)
{
int i;
if(net == NULL) {
int i;
for(i = 0; i < numnets; i++)
......@@ -674,18 +628,18 @@ send_self_retract(struct network *net)
return;
}
flushupdates();
debugf("Retracting self on %s.\n", net->ifname);
seqno = ((seqno + 1) & 0xFF);
myseqno = ((myseqno + 1) & 0xFFFF);
seqno_time = now.tv_sec;
start_message(net, 20);
accumulate_byte(net, 2);
accumulate_byte(net, seqno);
accumulate_short(net, 0xFFFF);
accumulate_data(net, myid, 16);
schedule_flush(net);
net->self_update_time = now.tv_sec;
for(i = 0; i < numxroutes; i++) {
if(xroutes[i].exported)
really_send_update(net, myid, xroutes[i].prefix, xroutes[i].plen,
myseqno, 0xFFFF);
}
}
void
......@@ -694,40 +648,34 @@ send_neighbour_update(struct neighbour *neigh, struct network *net)
int i;
for(i = 0; i < numroutes; i++) {
if(routes[i].installed && routes[i].nexthop == neigh)
send_update(routes[i].dest, net);
send_update(net, routes[i].src->prefix, routes[i].src->plen);
}
schedule_update_flush();
}
void
send_txcost(struct neighbour *neigh, struct network *net)
send_ihu(struct neighbour *neigh, struct network *net)
{
int i;
if(neigh == NULL && net == NULL) {
for(i = 0; i < numnets; i++)
send_txcost(NULL, &nets[i]);
send_ihu(NULL, &nets[i]);
return;
}
if(neigh == NULL) {
if(broadcast_txcost && net->wired) {
debugf("Sending broadcast txcost to %s.\n", net->ifname);
start_message(net, 20);
accumulate_byte(net, 3);
accumulate_byte(net, 0);
accumulate_short(net, net->cost);
accumulate_data(net, zeroes, 16);
schedule_flush(net);
if(broadcast_ihu && net->wired) {
debugf("Sending broadcast ihu to %s.\n", net->ifname);
send_message(net, 1, 0xFF, 0, net->cost, ones);
} else {
for(i = 0; i < numneighs; i++) {
if(neighs[i].id[0] != 0xFF) {
if(neighs[i].network == net)
send_txcost(&neighs[i], net);
send_ihu(&neighs[i], net);
}
}
}
net->txcost_time = now.tv_sec;
net->ihu_time = now.tv_sec;
} else {
int rxcost;
......@@ -736,17 +684,12 @@ send_txcost(struct neighbour *neigh, struct network *net)
net = neigh->network;
debugf("Sending txcost on %s to %s (%s).\n",
debugf("Sending ihu on %s to %s (%s).\n",
neigh->network->ifname,
format_address(neigh->id),
format_address(neigh->address));
rxcost = neighbour_rxcost(neigh);
start_message(net, 20);
accumulate_byte(net, 3);
accumulate_byte(net, 0);
accumulate_short(net, rxcost);
accumulate_data(net, neigh->id, 16);
schedule_flush(net);
send_message(net, 1, 128, 0, rxcost, neigh->id);
}
}
......@@ -20,7 +20,9 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
extern unsigned char seqno;
#define MAX_BUFFERED_UPDATES 200
extern unsigned short myseqno;
extern int seqno_time;
extern int seqno_interval;
......@@ -29,25 +31,26 @@ extern unsigned int update_jitter;
extern int add_cost;
extern int parasitic;
extern int silent_time;
extern int broadcast_txcost;
extern int broadcast_ihu;
extern int split_horizon;
extern struct timeval update_flush_time;
extern const unsigned char packet_header[4];
extern const unsigned char packet_header[8];
void parse_packet(const unsigned char *from, struct network *net,
const unsigned char *packet, int len);
void flushbuf(struct network *net);
void send_hello(struct network *net);
void send_request(struct network *net, struct destination *dest,
int hopcount, int seqno);
void send_unicast_request(struct neighbour *neigh, struct destination *dest,
int hopcount, int seqno);
void send_update(struct destination *dest, struct network *net);
void send_request(struct network *net,
const unsigned char *prefix, unsigned char plen);
void send_unicast_request(struct neighbour *neigh,
const unsigned char *prefix, unsigned char plen);
void send_update(struct network *net,
const unsigned char *prefix, unsigned char plen);
void send_self_update(struct network *net, int force_seqno);
void send_self_retract(struct network *net);
void send_neighbour_update(struct neighbour *neigh, struct network *net);
void send_txcost(struct neighbour *neigh, struct network *net);
void send_ihu(struct neighbour *neigh, struct network *net);
void schedule_flush_now(struct network *net);
void flushupdates(void);
......
......@@ -29,9 +29,8 @@ THE SOFTWARE.
#include "babel.h"
#include "util.h"
#include "neighbour.h"
#include "destination.h"
#include "source.h"
#include "route.h"
#include "xroute.h"
#include "message.h"
struct neighbour neighs[MAXNEIGHBOURS];
......@@ -40,15 +39,14 @@ int numneighs = 0;
void
flush_neighbour(struct neighbour *neigh)
{
flush_neighbour_xroutes(neigh);
flush_neighbour_routes(neigh);
memset(neigh, 0, sizeof(*neigh));
if(neigh == &neighs[numneighs - 1]) {
VALGRIND_MAKE_MEM_UNDEFINED(neigh, sizeof(*neigh));
neigh->id[0] = 0xFF;
while(numneighs > 0 && neighs[numneighs - 1].id[0] == 0xFF) {
numneighs--;
VALGRIND_MAKE_MEM_UNDEFINED(neigh, sizeof(*neigh));
} else {
VALGRIND_MAKE_MEM_UNDEFINED(neigh, sizeof(*neigh));
neigh->id[0] = 0xFF;
VALGRIND_MAKE_MEM_UNDEFINED(&neighs[numneighs],
sizeof(neighs[numneighs]));
}
}
......@@ -71,6 +69,7 @@ add_neighbour(const unsigned char *id, const unsigned char *address,
struct network *net)
{
struct neighbour *neigh;
const struct timeval zero = {0, 0};
int i;
if(id[0] == 0xFF) {
......@@ -104,11 +103,10 @@ add_neighbour(const unsigned char *id, const unsigned char *address,
}
memcpy(neigh->id, id, 16);
memcpy(neigh->address, address, 16);
neigh->hello_interval = 0;
neigh->reach = 0;
neigh->txcost = INFINITY;
neigh->txcost_time = now.tv_sec;
neigh->hello_time = 0;
neigh->ihu_time = now.tv_sec;
neigh->hello_time = zero;
neigh->hello_interval = 0;
neigh->hello_seqno = -1;
neigh->network = net;
......@@ -116,19 +114,23 @@ add_neighbour(const unsigned char *id, const unsigned char *address,
return neigh;
}
void
/* Recompute a neighbour's rxcost. Return true if anything changed. */
int
update_neighbour(struct neighbour *neigh, int hello, int hello_interval)
{
int missed_hellos;
int rc = 0;
if(hello < 0) {
if(neigh->hello_interval <= 0)
return;
missed_hellos =
(now.tv_sec - 3 - neigh->hello_time) / neigh->hello_interval;
return rc;
missed_hellos = (timeval_minus_msec(&now, &neigh->hello_time) -
neigh->hello_interval * 6) /
(neigh->hello_interval * 10);
if(missed_hellos <= 0)
return;
neigh->hello_time += missed_hellos * neigh->hello_interval;
return rc;
timeval_plus_msec(&neigh->hello_time, &neigh->hello_time,
missed_hellos * neigh->hello_interval * 10);
} else {
if(neigh->hello_seqno >= 0 && neigh->reach > 0) {
missed_hellos = seqno_minus(hello, neigh->hello_seqno) - 1;
......@@ -137,97 +139,120 @@ update_neighbour(struct neighbour *neigh, int hello, int hello_interval)
didn't notice. */
neigh->reach <<= -missed_hellos;
missed_hellos = 0;
rc = 1;
}
} else {
missed_hellos = 0;
}
neigh->hello_time = now.tv_sec;
neigh->hello_time = now;
neigh->hello_interval = hello_interval;
}
neigh->reach >>= missed_hellos;
missed_hellos = 0;
if(missed_hellos > 0) {
neigh->reach >>= missed_hellos;
missed_hellos = 0;
rc = 1;
}
if(hello >= 0) {
neigh->hello_seqno = hello;
neigh->reach >>= 1;
neigh->reach |= 0x8000;
if((neigh->reach & 0xFC00) == 0xFC00)
return rc;
else
rc = 1;
}
/* Make sure to give neighbours some feedback early after association */
if((neigh->reach & 0xFC00) == 0x8000) {
/* A new neighbour */
send_hello(neigh->network);
send_txcost(neigh, NULL);
send_ihu(neigh, NULL);
} else {
/* Don't send hellos, in order to avoid a positive feedback loop. */
int a = (neigh->reach & 0xC000);
int b = (neigh->reach & 0x3000);
if((a == 0xC000 && b == 0) || (a == 0 && b == 0x3000)) {
/* Reachability is either 1100 or 0011 */
send_txcost(neigh, NULL);
send_ihu(neigh, NULL);
send_self_update(neigh->network, 0);
send_neighbour_update(neigh, NULL);
}
}
if((neigh->reach & 0xFF00) == 0xC000) {
if((neigh->reach & 0xFC00) == 0xC000) {
/* This is a newish neighbour. If we don't have another route to it,
request a full route dump. This assumes that the neighbour's ID
is also its IP address. */
struct destination *dest;
request a full route dump. This assumes that the neighbour's id
is also its IP address and that it is exporting a route to itself. */
struct route *route = NULL;
dest = find_destination(neigh->id, 0, 0);
if(dest)
route = find_installed_route(dest);
if(!martian_prefix(neigh->id, 128))
route = find_installed_route(neigh->id, 128);
if(!route || route->metric >= INFINITY || route->nexthop == neigh)
send_unicast_request(neigh, NULL, 0, -1);
send_unicast_request(neigh, NULL, 0);
}
return rc;
}
void
check_neighbour(struct neighbour *neigh)
int
check_neighbours()
{
update_neighbour(neigh, -1, 0);
if(neigh->reach == 0) {
flush_neighbour(neigh);
return;
}
int i, changed;
int msecs = 50000;
debugf("Checking neighbours.\n");
for(i = 0; i < numneighs; i++) {
if(neighs[i].id[0] == 0xFF)
continue;
if(!neigh->network->wired) {
while(neigh->txcost < INFINITY &&
neigh->txcost_time <= now.tv_sec - 40) {
neigh->txcost = MIN((int)neigh->txcost * 3 / 2, INFINITY);
neigh->txcost_time += 10;
changed = update_neighbour(&neighs[i], -1, 0);
if(neighs[i].reach == 0 ||
timeval_minus_msec(&now, &neighs[i].hello_time) > 300000) {
flush_neighbour(&neighs[i]);
continue;
}
if(changed)
update_neighbour_metric(&neighs[i]);
if(neighs[i].hello_interval > 0)
msecs = MIN(msecs, neighs[i].hello_interval * 10);
}
return msecs;
}
int
neighbour_rxcost(struct neighbour *neigh)
{
update_neighbour(neigh, -1, 0);
if((neigh->reach & 0xF800) == 0)
int delay;
unsigned short reach = neigh->reach;
delay = timeval_minus_msec(&now, &neigh->hello_time);
if((reach & 0xF800) == 0 || delay >= 180000) {
return INFINITY;
else if(neigh->network->wired) {
if((neigh->reach & 0xE000) != 0) {
int c = neigh->network->cost;
int d = now.tv_sec - neigh->hello_time;
if(d >= 60)
c = (c * (d - 40) + 19) / 20;
return MIN(c, INFINITY);
} else {
} else if(neigh->network->wired) {
/* To lose one hello is a misfortune, to lose two is carelessness. */
if((reach & 0xC000) == 0xC000)
return neigh->network->cost;
else if((reach & 0xC000) == 0)
return INFINITY;
else if((reach & 0x2000))
return neigh->network->cost;
else
return INFINITY;
}
} else {
int r = (neigh->reach & 0x7FFF) + ((neigh->reach & 0x8000) >> 1);
/* 0 <= r <= 0xBFFF */
int c = (0xC000 * 0x100) / r;
int d = now.tv_sec - neigh->hello_time;
if(d >= 40)
c = (c * (d - 20) + 10) / 20;
return MIN(c + neigh->network->cost, INFINITY);
int sreach = (reach & 0x7FFF) + ((reach & 0x8000) >> 1);
/* 0 <= sreach <= 0xBFFF */
int cost = (0xC000 * neigh->network->cost) / (sreach + 1);
/* cost >= network->cost */
if(delay >= 40000)
cost = (cost * (delay - 20000) + 10000) / 20000;
return MIN(cost, INFINITY);
}
}
......@@ -236,6 +261,9 @@ neighbour_cost(struct neighbour *neigh)
{
int a, b;
if(now.tv_sec - neigh->ihu_time >= 180)
return INFINITY;
a = neigh->txcost;
if(a >= INFINITY)
......
......@@ -26,10 +26,10 @@ struct neighbour {
unsigned short reach;
unsigned short txcost;
/* This is -1 when unknown, so don't make it unsigned */
short int hello_seqno;
int txcost_time;
int hello_time;
int hello_interval;
int hello_seqno;
int ihu_time;
struct timeval hello_time;
unsigned short hello_interval; /* in centiseconds */
struct network *network;
};
......@@ -42,7 +42,7 @@ struct neighbour *find_neighbour(const unsigned char *address,
struct neighbour *
add_neighbour(const unsigned char *id, const unsigned char *address,
struct network *net);
void update_neighbour(struct neighbour *neigh, int hello, int hello_interval);
void check_neighbour(struct neighbour *neigh);
int update_neighbour(struct neighbour *neigh, int hello, int hello_interval);
int check_neighbours(void);
int neighbour_rxcost(struct neighbour *neigh);
int neighbour_cost(struct neighbour *neigh);
......@@ -29,7 +29,7 @@ THE SOFTWARE.
#include "babel.h"
#include "util.h"
#include "kernel.h"
#include "destination.h"
#include "source.h"
#include "neighbour.h"
#include "route.h"
#include "xroute.h"
......@@ -42,22 +42,24 @@ int route_timeout_delay = 50;
int route_gc_delay = 95;
struct route *
find_route(struct destination *dest, struct neighbour *nexthop)
find_route(const unsigned char *prefix, unsigned char plen,
struct neighbour *nexthop)
{
int i;
for(i = 0; i < numroutes; i++) {
if(routes[i].nexthop == nexthop && routes[i].dest == dest)
if(routes[i].nexthop == nexthop &&
source_match(routes[i].src, prefix, plen))
return &routes[i];
}
return NULL;
}
struct route *
find_installed_route(struct destination *dest)
find_installed_route(const unsigned char *prefix, unsigned char plen)
{
int i;
for(i = 0; i < numroutes; i++) {
if(routes[i].installed && routes[i].dest == dest)
if(routes[i].installed && source_match(routes[i].src, prefix, plen))
return &routes[i];
}
return NULL;
......@@ -67,8 +69,8 @@ void
flush_route(struct route *route)
{
int n;
struct destination *dest;
int install = 0, oldmetric;
struct source *src;
int oldmetric, lost = 0;
n = route - routes;
assert(n >= 0 && n < numroutes);
......@@ -77,33 +79,18 @@ flush_route(struct route *route)
if(route->installed) {
uninstall_route(route);
install = 1;
lost = 1;
}
dest = route->dest;
src = route->src;
if(n != numroutes - 1)
memcpy(routes + n, routes + numroutes - 1, sizeof(struct route));
numroutes--;
VALGRIND_MAKE_MEM_UNDEFINED(routes + numroutes, sizeof(struct route));
if(install) {
struct route *new_route;
new_route = find_best_route(dest);
if(new_route) {
install_route(new_route);
send_triggered_update(new_route, oldmetric);
} else {
if(dest->metric < INFINITY) {
dest->metric = INFINITY;
dest->seqno = (dest->seqno + 1) & 0xFF;
}
send_update(dest, NULL);
}
if(oldmetric < INFINITY &&
(!new_route || new_route->metric >= INFINITY))
send_request(NULL, route->dest, max_request_hopcount, -1);
}
if(lost)
route_lost(src, oldmetric);
}
void
......@@ -135,17 +122,12 @@ metric_to_kernel(int metric)
void
install_route(struct route *route)
{
struct route *installed;
int rc;
if(route->installed)
return;
installed = find_installed_route(route->dest);
if(installed)
uninstall_route(installed);
rc = kernel_route(ROUTE_ADD, route->dest->address, 128,
rc = kernel_route(ROUTE_ADD, route->src->prefix, route->src->plen,
route->nexthop->address,
route->nexthop->network->ifindex,
metric_to_kernel(route->metric), 0);
......@@ -155,60 +137,85 @@ install_route(struct route *route)
return;
}
route->installed = 1;
consider_all_xroutes(route);
}
void
uninstall_route(struct route *route)
{
int i, rc;
int rc;
if(!route->installed)
return;
for(i = 0; i < numxroutes; i++) {
if(xroutes[i].installed &&
xroutes[i].gateway == route->dest &&
xroutes[i].nexthop == route->nexthop)
uninstall_xroute(&xroutes[i]);
}
rc = kernel_route(ROUTE_FLUSH, route->dest->address, 128,
rc = kernel_route(ROUTE_FLUSH, route->src->prefix, route->src->plen,
route->nexthop->address,
route->nexthop->network->ifindex,
metric_to_kernel(route->metric), 0);
if(rc < 0)
perror("kernel_route(FLUSH)");
route->installed = 0;
}
void
change_route_metric(struct route *route, int newmetric)
{
int rc;
if(route->installed) {
rc = kernel_route(ROUTE_MODIFY,
route->src->prefix, route->src->plen,
route->nexthop->address,
route->nexthop->network->ifindex,
metric_to_kernel(route->metric),
metric_to_kernel(newmetric));
if(rc < 0) {
perror("kernel_route(MODIFY)");
return;
}
}
route->metric = newmetric;
}
int
route_feasible(struct route *route)
{
return update_feasible(route->seqno, route->refmetric, route->dest);
return update_feasible(route->src->address,
route->src->prefix, route->src->plen,
route->seqno, route->refmetric);
}
int
update_feasible(unsigned char seqno, unsigned short refmetric,
struct destination *dest)
update_feasible(const unsigned char *a,
const unsigned char *p, unsigned char plen,
unsigned short seqno, unsigned short refmetric)
{
if(dest->time < now.tv_sec - 200) {
struct source *src = find_source(a, p, plen, 0, 0);
if(src == NULL)
return 1;
if(src->time < now.tv_sec - 200)
/* Never mind what is probably stale data */
return 1;
}
return (seqno_compare(dest->seqno, seqno) < 0 ||
(dest->seqno == seqno && refmetric < dest->metric));
if(refmetric >= INFINITY)
/* Retractions are always feasible */
return 1;
return (seqno_compare(seqno, src->seqno) > 0 ||
(src->seqno == seqno && refmetric < src->metric));
}
/* This returns a feasible route. The only condition it must satisfy if that
if there is a feasible route, then one will be found. */
struct route *
find_best_route(struct destination *dest)
find_best_route(const unsigned char *prefix, unsigned char plen)
{
struct route *route = NULL;
int i;
for(i = 0; i < numroutes; i++) {
if(routes[i].dest != dest)
if(!source_match(routes[i].src, prefix, plen))
continue;
if(routes[i].time < now.tv_sec - route_timeout_delay)
continue;
......@@ -229,19 +236,6 @@ find_best_route(struct destination *dest)
return route;
}
void
update_neighbour_metric(struct neighbour *neigh)
{
int i;
i = 0;
while(i < numroutes) {
if(routes[i].nexthop == neigh)
update_route_metric(&routes[i]);
i++;
}
}
void
update_route_metric(struct route *route)
{
......@@ -251,7 +245,7 @@ update_route_metric(struct route *route)
oldmetric = route->metric;
if(route->time < now.tv_sec - route_timeout_delay) {
if(route->refmetric < INFINITY) {
route->seqno = (route->dest->seqno + 1) & 0xFF;
route->seqno = (route->src->seqno + 1) & 0xFFFF;
route->refmetric = INFINITY;
}
newmetric = INFINITY;
......@@ -261,59 +255,73 @@ update_route_metric(struct route *route)
}
change_route_metric(route, newmetric);
trigger_route_change(route, route->src, oldmetric);
}
if(route->installed) {
if(newmetric > oldmetric) {
struct route *better_route;
better_route = find_best_route(route->dest);
if(better_route && better_route->metric <= route->metric - 96)
consider_route(better_route);
}
if(route->installed)
send_triggered_update(route, oldmetric);
} else if(newmetric < oldmetric) {
consider_route(route);
void
update_neighbour_metric(struct neighbour *neigh)
{
int i;
i = 0;
while(i < numroutes) {
if(routes[i].nexthop == neigh)
update_route_metric(&routes[i]);
i++;
}
}
/* This is called whenever we receive an update. */
struct route *
update_route(const unsigned char *d, int seqno, int refmetric,
struct neighbour *nexthop,
struct xroute *pxroutes, int numpxroutes)
update_route(const unsigned char *a, const unsigned char *p, unsigned char plen,
unsigned short seqno, unsigned short refmetric,
struct neighbour *nexthop)
{
struct route *route;
struct destination *dest;
int i, metric;
struct source *src;
int metric, feasible;
if(martian_prefix(d, 128)) {
fprintf(stderr, "Rejecting martian route to %s.\n",
format_address(d));
if(martian_prefix(p, plen)) {
fprintf(stderr, "Rejecting martian route to %s through %s.\n",
format_prefix(p, plen), format_address(a));
return NULL;
}
dest = find_destination(d, 1, seqno);
if(dest == NULL)
src = find_source(a, p, plen, 1, seqno);
if(src == NULL)
return NULL;
if(!update_feasible(seqno, refmetric, dest)) {
debugf("Rejecting unfeasible update from %s.\n",
format_address(nexthop->address));
/* It would be really nice to send a request at this point,
but that might get us into a positive feedback loop. */
return NULL;
}
metric = MIN(refmetric + neighbour_cost(nexthop), INFINITY);
route = find_route(dest, nexthop);
feasible = update_feasible(a, p, plen, seqno, refmetric);
route = find_route(p, plen, nexthop);
metric = MIN((int)refmetric + neighbour_cost(nexthop), INFINITY);
if(route) {
int oldseqno;
int oldmetric;
struct source *oldsrc;
unsigned short oldseqno;
unsigned short oldmetric;
int lost = 0;
oldsrc = route->src;
oldseqno = route->seqno;
oldmetric = route->metric;
if(refmetric < INFINITY) {
/* If a successor switches sources, we must accept his update even
if it makes a route unfeasible in order to break any routing loops.
It's not clear to me (jch) what is the best approach if the
successor sticks to the same source but increases its metric. */
if(!feasible && route->installed) {
debugf("Unfeasible update for installed route to %s "
"(%s %d %d -> %s %d %d).\n",
format_prefix(src->prefix, src->plen),
format_address(route->src->address),
route->seqno, route->refmetric,
format_address(src->address), seqno, refmetric);
uninstall_route(route);
lost = 1;
}
route->src = src;
if(feasible && refmetric < INFINITY) {
route->time = now.tv_sec;
if(route->refmetric >= INFINITY)
route->origtime = now.tv_sec;
......@@ -321,20 +329,14 @@ update_route(const unsigned char *d, int seqno, int refmetric,
route->seqno = seqno;
route->refmetric = refmetric;
change_route_metric(route, metric);
if(seqno_compare(oldseqno, seqno) <= 0) {
if(seqno_compare(oldseqno, seqno) < 0)
retract_xroutes(dest, nexthop, pxroutes, numpxroutes);
for(i = 0; i < numpxroutes; i++)
update_xroute(pxroutes[i].prefix,
pxroutes[i].plen,
dest, nexthop,
pxroutes[i].cost);
}
if(route->installed)
send_triggered_update(route, oldmetric);
else
consider_route(route);
if(feasible)
trigger_route_change(route, oldsrc, oldmetric);
else if(lost)
route_lost(oldsrc, oldmetric);
} else {
if(!feasible)
return NULL;
if(refmetric >= INFINITY)
/* Somebody's retracting a route we never saw. */
return NULL;
......@@ -343,7 +345,7 @@ update_route(const unsigned char *d, int seqno, int refmetric,
return NULL;
}
route = &routes[numroutes];
route->dest = dest;
route->src = src;
route->refmetric = refmetric;
route->seqno = seqno;
route->metric = metric;
......@@ -352,16 +354,14 @@ update_route(const unsigned char *d, int seqno, int refmetric,
route->origtime = now.tv_sec;
route->installed = 0;
numroutes++;
for(i = 0; i < numpxroutes; i++)
update_xroute(pxroutes[i].prefix,
pxroutes[i].plen,
dest, nexthop,
pxroutes[i].cost);
consider_route(route);
}
return route;
}
/* This takes a feasible route and decides whether to install it. The only
condition that it must satisfy is that if there is no currently installed
route, then one will be installed. */
void
consider_route(struct route *route)
{
......@@ -373,7 +373,10 @@ consider_route(struct route *route)
if(!route_feasible(route))
return;
installed = find_installed_route(route->dest);
if(find_exported_xroute(route->src->prefix, route->src->plen))
return;
installed = find_installed_route(route->src->prefix, route->src->plen);
if(installed == NULL)
goto install;
......@@ -381,62 +384,112 @@ consider_route(struct route *route)
if(installed->metric >= route->metric + 384)
goto install;
/* Avoid routes that haven't been around for some time */
if(route->origtime >= now.tv_sec - 30)
return;
if(installed->metric >= route->metric + 192)
goto install;
/* Avoid switching sources */
if(installed->src != route->src)
return;
if(installed->metric >= route->metric + 96)
goto install;
return;
install:
install_route(route);
if(installed)
send_triggered_update(route, installed->metric);
uninstall_route(installed);
install_route(route);
if(installed && route->installed)
send_triggered_update(route, installed->src, installed->metric);
else
send_update(route->dest, NULL);
send_update(NULL, route->src->prefix, route->src->plen);
return;
}
void
change_route_metric(struct route *route, int newmetric)
send_triggered_update(struct route *route, struct source *oldsrc, int oldmetric)
{
if(!route->installed)
return;
/* Switching sources can cause transient routing loops, so always send
updates in that case */
if(route->src != oldsrc ||
((route->metric >= INFINITY) != (oldmetric >= INFINITY)) ||
(route->metric >= oldmetric + 256 || oldmetric >= route->metric + 256))
send_update(NULL, route->src->prefix, route->src->plen);
if(oldmetric < INFINITY) {
if(route->metric >= INFINITY || route->metric - oldmetric >= 384)
send_request(NULL, route->src->prefix, route->src->plen);
}
}
/* A route has just changed. Decide whether to switch to a different route or
send an update. */
void
trigger_route_change(struct route *route,
struct source *oldsrc, unsigned short oldmetric)
{
int rc;
if(route->installed) {
rc = kernel_route(ROUTE_MODIFY,
route->dest->address, 128,
route->nexthop->address,
route->nexthop->network->ifindex,
metric_to_kernel(route->metric),
metric_to_kernel(newmetric));
if(rc < 0) {
perror("kernel_route(MODIFY)");
return;
if(route->metric > oldmetric) {
struct route *better_route;
better_route =
find_best_route(route->src->prefix, route->src->plen);
if(better_route && better_route->metric <= route->metric - 96)
consider_route(better_route);
}
if(route->installed)
send_triggered_update(route, oldsrc, oldmetric);
} else if(route->metric < oldmetric) {
consider_route(route);
}
route->metric = newmetric;
}
/* We just lost the installed route to a given destination. */
void
send_triggered_update(struct route *route, int oldmetric)
route_lost(struct source *src, int oldmetric)
{
if(!route->installed)
return;
struct route *new_route;
new_route = find_best_route(src->prefix, src->plen);
if(new_route) {
consider_route(new_route);
} else {
/* Complain loudly. */
send_update(NULL, src->prefix, src->plen);
if(oldmetric < INFINITY)
send_request(NULL, src->prefix, src->plen);
}
}
void
expire_routes(void)
{
int i;
if((route->metric >= INFINITY && oldmetric < INFINITY) ||
(route->metric - oldmetric >= 256 || oldmetric - route->metric >= 256))
send_update(route->dest, NULL);
else if(request_requested(route->dest, route->seqno, NULL))
send_update(route->dest, route->dest->requested_net);
debugf("Expiring old routes.\n");
if(oldmetric < INFINITY) {
if(route->metric >= INFINITY) {
/* We just lost a route, request a new seqno from the source */
send_request(NULL, route->dest, max_request_hopcount, -1);
} else if(route->metric - oldmetric >= 384) {
/* This route's metric has increased a lot -- let's hope we find
something better */
send_request(NULL, route->dest, 1, route->seqno);
i = 0;
while(i < numroutes) {
struct route *route = &routes[i];
if(route->time < now.tv_sec - route_gc_delay) {
flush_route(route);
continue;
}
update_route_metric(route);
if(route->installed && route->refmetric < INFINITY) {
if(route->time < now.tv_sec - MAX(10, route_timeout_delay - 25))
send_unicast_request(route->nexthop,
route->src->prefix, route->src->plen);
}
i++;
}
}
......@@ -20,13 +20,11 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
struct xroute;
struct route {
struct destination *dest;
struct source *src;
unsigned short metric;
unsigned short refmetric;
unsigned char seqno;
unsigned short seqno;
struct neighbour *nexthop;
int time;
int origtime;
......@@ -39,22 +37,33 @@ extern int kernel_metric;
extern int route_timeout_delay;
extern int route_gc_delay;
struct route *find_route(struct destination *dest, struct neighbour *nexthop);
struct route *find_installed_route(struct destination *dest);
struct route *find_route(const unsigned char *prefix, unsigned char plen,
struct neighbour *nexthop);
struct route *find_installed_route(const unsigned char *prefix,
unsigned char plen);
void flush_route(struct route *route);
void flush_neighbour_routes(struct neighbour *neigh);
unsigned int metric_to_kernel(int metric);
void install_route(struct route *route);
void uninstall_route(struct route *route);
void change_route_metric(struct route *route, int newmetric);
int route_feasible(struct route *route);
int update_feasible(unsigned char seqno, unsigned short refmetric,
struct destination *dest);
struct route *find_best_route(struct destination *dest);
int update_feasible(const unsigned char *a,
const unsigned char *p, unsigned char plen,
unsigned short seqno, unsigned short refmetric);
struct route *find_best_route(const unsigned char *prefix, unsigned char plen);
struct route *install_best_route(const unsigned char prefix[16],
unsigned char plen);
void update_neighbour_metric(struct neighbour *neigh);
void update_route_metric(struct route *route);
struct route *update_route(const unsigned char *d, int seqno, int refmetric,
struct neighbour *nexthop,
struct xroute *pxroutes, int numpxroutes);
struct route *update_route(const unsigned char *a,
const unsigned char *p, unsigned char plen,
unsigned short seqno, unsigned short refmetric,
struct neighbour *nexthop);
void consider_route(struct route *route);
void change_route_metric(struct route *route, int newmetric);
void send_triggered_update(struct route *route, int oldmetric);
void send_triggered_update(struct route *route,
struct source *oldsrc, int oldmetric);
void trigger_route_change(struct route *route,
struct source *oldsrc, unsigned short oldmetric);
void route_lost(struct source *src, int oldmetric);
void expire_routes(void);
......@@ -23,107 +23,86 @@ THE SOFTWARE.
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include "babel.h"
#include "util.h"
#include "destination.h"
#include "source.h"
struct destination dests[MAXDESTS];
int numdests = 0;
struct source srcs[MAXSRCS];
int numsrcs = 0;
struct destination *
find_destination(const unsigned char *d, int create, unsigned char seqno)
struct source *
find_source(const unsigned char *a, const unsigned char *p, unsigned char plen,
int create, unsigned short seqno)
{
int i;
for(i = 0; i < numdests; i++) {
for(i = 0; i < numsrcs; i++) {
/* This should really be a hash table. For now, check the
last byte first. */
if(dests[i].address[15] != d[15])
if(srcs[i].address[15] != a[15])
continue;
if(memcmp(dests[i].address, d, 16) == 0)
return &dests[i];
if(memcmp(srcs[i].address, a, 16) != 0)
continue;
if(source_match(&srcs[i], p, plen))
return &srcs[i];
}
if(!create)
return NULL;
if(numdests >= MAXDESTS) {
fprintf(stderr, "Too many destinations.\n");
if(numsrcs >= MAXSRCS) {
fprintf(stderr, "Too many sources.\n");
return NULL;
}
memcpy(dests[numdests].address, d, 16);
dests[numdests].seqno = seqno;
dests[numdests].metric = INFINITY;
dests[numdests].time = now.tv_sec;
dests[numdests].requested_seqno = -1;
dests[numdests].requested_net = NULL;
return &dests[numdests++];
memcpy(srcs[numsrcs].address, a, 16);
memcpy(srcs[numsrcs].prefix, p, 16);
srcs[numsrcs].plen = plen;
srcs[numsrcs].seqno = seqno;
srcs[numsrcs].metric = INFINITY;
srcs[numsrcs].time = now.tv_sec;
return &srcs[numsrcs++];
}
void
update_destination(struct destination *dest,
unsigned char seqno, unsigned short metric)
struct source *
find_recent_source(const unsigned char *p, unsigned char plen)
{
if(seqno_compare(dest->seqno, seqno) < 0 ||
(dest->seqno == seqno && dest->metric > metric)) {
dest->seqno = seqno;
dest->metric = metric;
}
dest->time = now.tv_sec;
if(dest->requested_seqno >= 0) {
if(seqno_minus(dest->requested_seqno, seqno) >= 16) {
/* Stale data? */
dest->requested_seqno = -1;
dest->requested_net = NULL;
}
}
}
int i;
struct source *src = NULL;
void
notice_request(struct destination *dest, unsigned char seqno,
struct network *net)
{
if(dest->requested_seqno < 0) {
dest->requested_seqno = seqno;
dest->requested_net = net;
} else {
if(seqno_compare(dest->requested_seqno, seqno) < 0)
dest->requested_seqno = seqno;
if(net == NULL || net != dest->requested_net)
dest->requested_net = NULL;
for(i = 0; i < numsrcs; i++) {
if(!source_match(&srcs[i], p, plen))
continue;
if(!src || src->time < srcs[i].time)
src = &srcs[i];
}
return src;
}
int
request_requested(struct destination *dest, unsigned char seqno,
struct network *net)
source_match(struct source *src,
const unsigned char *p, unsigned char plen)
{
if(dest->requested_seqno < 0)
if(src->plen != plen)
return 0;
if(seqno_compare(dest->requested_seqno, seqno) < 0)
if(src->prefix[15] != p[15])
return 0;
if(dest->requested_net == NULL)
return 1;
if(net == NULL || net != dest->requested_net)
if(memcmp(src->prefix, p, 16) != 0)
return 0;
return 1;
}
void
satisfy_request(struct destination *dest, unsigned char seqno,
struct network *net)
update_source(struct source *src,
unsigned short seqno, unsigned short metric)
{
if(dest->requested_seqno >= 0) {
if(net == NULL || net == dest->requested_net) {
if(seqno_compare(seqno, dest->requested_seqno) >= 0) {
dest->requested_seqno = -1;
dest->requested_net = NULL;
}
}
if(metric >= INFINITY)
return;
if(seqno_compare(src->seqno, seqno) < 0 ||
(src->seqno == seqno && src->metric > metric)) {
src->seqno = seqno;
src->metric = metric;
}
src->time = now.tv_sec;
}
......@@ -20,22 +20,22 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
struct destination {
struct source {
unsigned char address[16];
unsigned char seqno;
unsigned char prefix[16];
unsigned char plen;
unsigned short seqno;
unsigned short metric;
int time;
int requested_seqno;
struct network *requested_net;
};
struct destination *find_destination(const unsigned char *d,
int create, unsigned char seqno);
void update_destination(struct destination *dest,
unsigned char seqno, unsigned short metric);
void notice_request(struct destination *dest, unsigned char seqno,
struct network *net);
int request_requested(struct destination *dest, unsigned char seqno,
struct network *net);
void satisfy_request(struct destination *dest, unsigned char seqno,
struct network *net);
int source_match(struct source *src,
const unsigned char *p, unsigned char plen);
struct source *find_source(const unsigned char *a,
const unsigned char *p,
unsigned char plen,
int create, unsigned short seqno);
struct source *find_recent_source(const unsigned char *p,
unsigned char plen);
void update_source(struct source *src,
unsigned short seqno, unsigned short metric);
......@@ -38,7 +38,7 @@ seqno_compare(unsigned char s1, unsigned char s2)
{
if(s1 == s2)
return 0;
else if(((s2 - s1) & 0xFF) < 128)
else if(((s2 - s1) & 0xFFFF) < 0x8000)
return -1;
else
return 1;
......@@ -49,10 +49,10 @@ seqno_minus(unsigned char s1, unsigned char s2)
{
if(s1 == s2)
return 0;
else if(((s2 - s1) & 0xFF) < 128)
return -(int)((s2 - s1) & 0xFF);
else if(((s2 - s1) & 0xFFFF) < 0x8000)
return -(int)((s2 - s1) & 0xFFFF);
else
return ((s1 - s2) & 0xFF);
return ((s1 - s2) & 0xFFFF);
}
void
......@@ -75,6 +75,20 @@ timeval_minus_msec(const struct timeval *s1, const struct timeval *s2)
(s1->tv_usec - s2->tv_usec) / 1000;
}
void
timeval_plus_msec(struct timeval *d, const struct timeval *s, int msecs)
{
int usecs;
d->tv_sec = s->tv_sec + msecs / 1000;
usecs = s->tv_usec + (msecs % 1000) * 1000;
if(usecs < 1000000) {
d->tv_usec = usecs;
} else {
d->tv_usec = usecs - 1000000;
d->tv_sec++;
}
}
int
timeval_compare(const struct timeval *s1, const struct timeval *s2)
{
......@@ -122,6 +136,38 @@ do_debugf(const char *format, ...)
fflush(stderr);
}
int
in_prefix(const unsigned char *address,
const unsigned char *prefix, unsigned char plen)
{
unsigned char m;
if(plen > 128)
plen = 128;
if(memcmp(address, prefix, plen / 8) != 0)
return 0;
m = 0xFF << (8 - (plen % 8));
return ((address[plen / 8] & m) == (prefix[plen / 8] & m));
}
const unsigned char *
mask_prefix(const unsigned char *prefix, unsigned char plen)
{
static unsigned char ret[16];
if(plen > 128)
plen = 128;
memset(ret, 0, 16);
memcpy(ret, prefix, plen / 8);
if(plen % 8 != 0)
ret[plen / 8] = 0xFF << (8 - (plen % 8));
return (const unsigned char *)ret;
}
const char *
format_address(const unsigned char *address)
{
......@@ -131,6 +177,19 @@ format_address(const unsigned char *address)
return inet_ntop(AF_INET6, address, buf[i], INET6_ADDRSTRLEN);
}
const char *
format_prefix(const unsigned char *prefix, unsigned char plen)
{
static char buf[4][INET6_ADDRSTRLEN + 4];
static int i = 0;
int n;
i = (i + 1) % 4;
inet_ntop(AF_INET6, prefix, buf[i], INET6_ADDRSTRLEN);
n = strlen(buf[i]);
snprintf(buf[i] + n, INET6_ADDRSTRLEN + 4 - n, "/%d", plen);
return buf[i];
}
int
parse_address(const char *address, unsigned char *addr_r)
{
......@@ -146,12 +205,12 @@ parse_address(const char *address, unsigned char *addr_r)
}
int
parse_net(const char *net, unsigned char *prefix_r, unsigned short *plen_r)
parse_net(const char *net, unsigned char *prefix_r, unsigned char *plen_r)
{
char buf[INET6_ADDRSTRLEN];
char *slash, *end;
unsigned char prefix[16];
unsigned short plen;
long plen;
int rc;
if(strcmp(net, "default") == 0) {
......@@ -173,11 +232,11 @@ parse_net(const char *net, unsigned char *prefix_r, unsigned short *plen_r)
if(rc < 0)
return rc;
plen = strtol(slash + 1, &end, 0);
if(*end != '\0')
if(*end != '\0' || plen < 0 || plen > 128)
return -1;
}
}
memcpy(prefix_r, prefix, 16);
memcpy(prefix_r, mask_prefix(prefix, plen), 16);
*plen_r = plen;
return 0;
}
......
......@@ -20,28 +20,36 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
int seqno_compare(unsigned char s1, unsigned char s2)
int seqno_compare(unsigned short s1, unsigned short s2)
ATTRIBUTE ((const));
int seqno_minus(unsigned char s1, unsigned char s2)
int seqno_minus(unsigned short s1, unsigned short s2)
ATTRIBUTE ((const));
void timeval_minus(struct timeval *d,
const struct timeval *s1, const struct timeval *s2);
int timeval_minus_msec(const struct timeval *s1, const struct timeval *s2)
ATTRIBUTE ((pure));
void timeval_plus_msec(struct timeval *d,
const struct timeval *s, int msecs);
int timeval_compare(const struct timeval *s1, const struct timeval *s2)
ATTRIBUTE ((pure));
void timeval_min(struct timeval *d, const struct timeval *s);
void timeval_min_sec(struct timeval *d, int secs);
void do_debugf(const char *format, ...) ATTRIBUTE ((format (printf, 1, 2)));
int in_prefix(const unsigned char *address,
const unsigned char *prefix, unsigned char plen)
ATTRIBUTE ((pure));
const unsigned char *mask_prefix(const unsigned char *prefix,
unsigned char plen);
const char *format_address(const unsigned char *address);
const char *format_prefix(const unsigned char *address, unsigned char prefix);
int parse_address(const char *address, unsigned char *addr_r);
int parse_net(const char *net, unsigned char *prefix_r, unsigned short *plen_r);
int parse_net(const char *net, unsigned char *prefix_r, unsigned char *plen_r);
int wait_for_fd(int direction, int fd, int msecs);
int martian_prefix(const unsigned char *prefix, int plen);
/* If debugging is disabled, we want to avoid calling format_address
for every omitted debugging message. So debug is a macro. But
vararg macros are not portable... */
vararg macros are not portable. */
#if defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L
#define debugf(...) \
do { \
......
......@@ -36,322 +36,22 @@ THE SOFTWARE.
struct xroute xroutes[MAXXROUTES];
int numxroutes = 0;
struct xroute myxroutes[MAXMYXROUTES];
int nummyxroutes = 0;
int xroute_gc_delay = 180;
int xroute_hold_delay = 45;
static int
xroute_prefix(struct xroute *xroute, const unsigned char *prefix, int plen)
{
return (xroute->plen == plen &&
memcmp(xroute->prefix, prefix, 16) == 0);
}
static struct xroute *
find_installed_xroute(unsigned char *prefix, unsigned short plen)
{
int i;
for(i = 0; i < numxroutes; i++) {
if(xroutes[i].installed && xroute_prefix(&xroutes[i], prefix, plen))
return &xroutes[i];
}
return NULL;
}
static struct xroute *
find_installed_myxroute(unsigned char *prefix, unsigned short plen)
{
int i;
for(i = 0; i < nummyxroutes; i++) {
if(myxroutes[i].installed && xroute_prefix(&myxroutes[i], prefix, plen))
return &myxroutes[i];
}
return NULL;
}
static struct xroute *
find_best_xroute(unsigned char *prefix, unsigned short plen)
{
struct xroute *xroute = NULL;
struct route *route;
int i;
for(i = 0; i < numxroutes; i++) {
if(!xroute_prefix(&xroutes[i], prefix, plen))
continue;
if(xroutes[i].metric >= INFINITY && xroutes[i].cost < INFINITY)
continue;
route = find_installed_route(xroutes[i].gateway);
if(!route || route->nexthop != xroutes[i].nexthop)
continue;
if(!xroute || xroutes[i].metric < xroute->metric)
xroute = &xroutes[i];
}
return xroute;
}
void
install_xroute(struct xroute *xroute)
{
struct route *gwroute;
struct xroute *installed;
int rc;
if(xroute->installed)
return;
if(xroute->metric >= INFINITY && xroute->cost < INFINITY)
return;
gwroute = find_installed_route(xroute->gateway);
if(!gwroute || gwroute->nexthop != xroute->nexthop) {
fprintf(stderr,
"Attempted to install a blackhole xroute "
"(this shouldn't happen).\n");
return;
}
installed = find_installed_xroute(xroute->prefix, xroute->plen);
if(installed)
uninstall_xroute(installed);
rc = kernel_route(ROUTE_ADD, xroute->prefix, xroute->plen,
gwroute->nexthop->address,
gwroute->nexthop->network->ifindex,
metric_to_kernel(xroute->metric), 0);
if(rc < 0) {
perror("kernel_route(ADD)");
if(errno != EEXIST)
return;
}
xroute->installed = 1;
}
void
uninstall_xroute(struct xroute *xroute)
{
struct route *gwroute;
int rc;
if(!xroute->installed)
return;
gwroute = find_installed_route(xroute->gateway);
if(!gwroute) {
fprintf(stderr,
"Attempted to uninstall a blackhole xroute "
"(this shouldn't happen).\n");
return;
}
rc = kernel_route(ROUTE_FLUSH, xroute->prefix, xroute->plen,
gwroute->nexthop->address,
gwroute->nexthop->network->ifindex,
metric_to_kernel(xroute->metric), 0);
if(rc < 0)
perror("kernel_route(FLUSH)");
xroute->installed = 0;
}
void
consider_xroute(struct xroute *xroute)
{
struct xroute *installed;
struct route *route;
if(xroute->installed)
return;
route = find_installed_route(xroute->gateway);
if(xroute->nexthop != route->nexthop)
return;
update_xroute_metric(xroute, xroute->cost);
installed = find_installed_myxroute(xroute->prefix, xroute->plen);
if(!installed) {
installed = find_installed_xroute(xroute->prefix, xroute->plen);
if(!installed || installed->metric > xroute->metric + 64)
install_xroute(xroute);
}
}
void
consider_all_xroutes(struct route *route)
{
int i;
for(i = 0; i < numxroutes; i++) {
if(xroutes[i].gateway == route->dest &&
xroutes[i].nexthop == route->nexthop)
consider_xroute(&xroutes[i]);
}
}
void
flush_xroute(struct xroute *xroute)
{
int n;
int install = 0;
unsigned char prefix[16];
unsigned short plen = 0;
n = xroute - xroutes;
assert(n >= 0 && n < numxroutes);
if(xroute->installed) {
uninstall_xroute(xroute);
memcpy(prefix, xroute->prefix, 16);
plen = xroute->plen;
install = 1;
}
if(n != numxroutes - 1)
memcpy(xroutes + n, xroutes + numxroutes - 1,
sizeof(struct xroute));
numxroutes--;
VALGRIND_MAKE_MEM_UNDEFINED(xroutes + numxroutes, sizeof(struct xroute));
if(install) {
struct xroute *xroute;
xroute = find_best_xroute(prefix, plen);
if(xroute)
install_xroute(xroute);
}
}
void
flush_neighbour_xroutes(struct neighbour *neigh)
{
int i;
i = 0;
while(i < numxroutes) {
if(xroutes[i].nexthop == neigh) {
flush_xroute(xroutes + i);
continue;
}
i++;
}
}
void
retract_xroutes(struct destination *gateway, struct neighbour *nexthop,
const struct xroute *except, int numexcept)
{
int i, j;
for(i = 0; i < numxroutes; i++) {
if(xroutes[i].cost < INFINITY && xroutes[i].gateway == gateway &&
xroutes[i].nexthop == nexthop) {
for(j = 0; j < numexcept; j++) {
if(xroute_prefix(&xroutes[i], except[j].prefix, except[j].plen))
goto skip;
}
update_xroute_metric(&xroutes[i], INFINITY);
}
skip: ;
}
}
struct xroute *
update_xroute(const unsigned char *prefix, unsigned short plen,
struct destination *gateway, struct neighbour *nexthop, int cost)
find_exported_xroute(const unsigned char *prefix, unsigned char plen)
{
int i;
struct xroute *xroute = NULL;
struct route *gwroute;
if(martian_prefix(prefix, plen)) {
fprintf(stderr, "Ignoring martian xroute.\n");
return NULL;
}
if(gateway == NULL) {
fprintf(stderr, "Ignoring xroute through unknown destination.\n");
return NULL;
}
for(i = 0; i < numxroutes; i++) {
xroute = &xroutes[i];
if(xroute->gateway == gateway && xroute->nexthop == nexthop &&
xroute_prefix(xroute, prefix, plen)) {
update_xroute_metric(xroute, cost);
xroute->time = now.tv_sec;
return xroute;
if(xroutes[i].exported) {
if(xroutes[i].plen == plen &&
memcmp(xroutes[i].prefix, prefix, 16) == 0)
return &xroutes[i];
}
}
if(numxroutes >= MAXXROUTES) {
fprintf(stderr, "Too many xroutes.\n");
return NULL;
}
gwroute = find_installed_route(gateway);
xroute = &xroutes[numxroutes];
memcpy(&xroute->prefix, prefix, 16);
xroute->plen = plen;
xroute->gateway = gateway;
xroute->nexthop = nexthop;
xroute->cost = cost;
xroute->metric =
gwroute && gwroute->nexthop == xroute->nexthop ?
MIN(gwroute->metric + cost, INFINITY) :
INFINITY;
xroute->time = now.tv_sec;
xroute->installed = 0;
numxroutes++;
if(gwroute)
consider_xroute(xroute);
return xroute;
return NULL;
}
void
update_xroute_metric(struct xroute *xroute, int cost)
{
struct route *gwroute;
int oldmetric, newmetric;
int rc;
gwroute = find_installed_route(xroute->gateway);
oldmetric = xroute->metric;
newmetric = gwroute && gwroute->nexthop == xroute->nexthop ?
MIN(gwroute->metric + cost, INFINITY) :
INFINITY;
if(xroute->cost != cost || oldmetric != newmetric) {
xroute->cost = cost;
if(xroute->installed) {
if(gwroute == NULL) {
fprintf(stderr, "Found installed blackhole xroute!.\n");
return;
}
rc = kernel_route(ROUTE_MODIFY, xroute->prefix, xroute->plen,
gwroute->nexthop->address,
gwroute->nexthop->network->ifindex,
metric_to_kernel(oldmetric),
metric_to_kernel(newmetric));
if(rc < 0) {
perror("kernel_route(MODIFY)");
return;
}
}
xroute->metric = newmetric;
if(newmetric > oldmetric) {
struct xroute *best;
best = find_best_xroute(xroute->prefix, xroute->plen);
if(best)
consider_xroute(best);
}
}
}
int
check_myxroutes()
check_xroutes()
{
int i, j, n, change;
struct kernel_route routes[120];
......@@ -359,9 +59,9 @@ check_myxroutes()
debugf("Checking kernel routes.\n");
n = -1;
for(i = 0; i < nummyxroutes; i++)
if(myxroutes[i].installed < 2)
n = MAX(n, myxroutes[i].plen);
for(i = 0; i < numxroutes; i++)
if(xroutes[i].exported < 2)
n = MAX(n, xroutes[i].plen);
if(n < 0)
return 0;
......@@ -371,20 +71,32 @@ check_myxroutes()
return -1;
change = 0;
for(i = 0; i < nummyxroutes; i++) {
int installed;
if(myxroutes[i].installed == 2)
for(i = 0; i < numxroutes; i++) {
int export;
if(xroutes[i].exported == 2)
continue;
installed = 0;
export = 0;
for(j = 0; j < n; j++) {
if(xroute_prefix(&myxroutes[i],
routes[j].prefix, routes[j].plen)) {
installed = 1;
if(xroutes[i].plen == routes[j].plen &&
memcmp(xroutes[i].prefix, routes[j].prefix, 16) == 0) {
export = 1;
break;
}
}
if(myxroutes[i].installed != installed) {
myxroutes[i].installed = installed;
if(xroutes[i].exported != export) {
xroutes[i].exported = export;
if(export) {
struct route *route;
route = find_installed_route(xroutes[i].prefix,
xroutes[i].plen);
if(route)
uninstall_route(route);
} else {
struct route *route;
route = find_best_route(xroutes[i].prefix, xroutes[i].plen);
if(route)
consider_route(route);
}
change = 1;
}
}
......
......@@ -20,38 +20,17 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
struct route;
int check_xroutes(void);
struct xroute {
unsigned char prefix[16];
unsigned short plen;
struct destination *gateway;
struct neighbour *nexthop;
int cost;
int metric;
int time;
int installed;
unsigned char plen;
char exported;
unsigned short metric;
};
extern struct xroute xroutes[MAXXROUTES];
extern int numxroutes;
extern struct xroute myxroutes[MAXMYXROUTES];
extern int nummyxroutes;
extern int xroute_gc_delay;
extern int xroute_hold_delay;
void install_xroute(struct xroute *xroute);
void uninstall_xroute(struct xroute *xroute);
void consider_xroute(struct xroute *xroute);
void consider_all_xroutes(struct route *route);
void flush_xroute(struct xroute *xroute);
void flush_neighbour_xroutes(struct neighbour *neigh);
void retract_xroutes(struct destination *gateway, struct neighbour *nexthop,
const struct xroute *except, int numexcept);
struct xroute * update_xroute(const unsigned char *prefix, unsigned short plen,
struct destination *gateway,
struct neighbour *nexthop, int cost);
void update_xroute_metric(struct xroute *xroute, int cost);
int check_myxroutes(void);
struct xroute *find_exported_xroute(const unsigned char *prefix,
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