Commit b54949cb authored by Juliusz Chroboczek's avatar Juliusz Chroboczek

Initial import.

parents
Copyright (c) 2007 by Juliusz Chroboczek
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
PREFIX = /usr/local
CDEBUGFLAGS = -Os -g -Wall
DEFINES = $(PLATFORM_DEFINES)
CFLAGS = $(CDEBUGFLAGS) $(DEFINES) $(EXTRA_DEFINES)
SRCS = ariadne.c net.c kernel.c util.c destination.c neighbour.c \
route.c xroute.c message.c
OBJS = ariadne.o net.o kernel.o util.o destination.o neighbour.o \
route.o xroute.o message.o
ariadne: $(OBJS)
$(CC) $(CFLAGS) $(LDFLAGS) -o ariadne $(OBJS) $(LDLIBS)
ariadne.html: ariadne.man
groff -man -Thtml ariadne.man > ariadne.html
.PHONY: all install uninstall clean
all: ariadne
install: ariadne ariadne.man
-rm -f $(TARGET)$(PREFIX)/bin/ariadne
cp -f ariadne $(TARGET)$(PREFIX)/bin
mkdir -p $(TARGET)$(PREFIX)/man/man8
cp -f ariadne.man $(TARGET)$(PREFIX)/man/man8/ariadne.8
uninstall:
-rm -f $(TARGET)$(PREFIX)/bin/ariadne
-rm -f $(TARGET)$(PREFIX)/man/man8/ariadne.8
clean:
-rm -f ariadne ariadne.html *.o *~ core TAGS gmon.out
Ariadne
*******
Ariadne is a loop-avoiding distance-vector routing protocol roughly
based on HSDV and AODV, but with provisions for link cost estimation
and injection of external routes.
Installation
************
$ make
$ su -c 'make install'
Running Ariadne
***************
In order to run Ariadne on a node (wireless or wired), just give it an
IPv6 address, then run the routing daemon. Assuming your wireless
interface is eth1, and your IPv6 address is $IPv6, do
# ip -6 addr add $IPv6 dev eth1
# ariadne $IPv6 eth1
If your node has multiple interfaces which you want to participate in
the Ariadne netowk, just list them all:
# ariadne $IPv6 eth0 eth1 sit1
On an access point, you'll probably want to inject external routes
into the Ariadne network. First check that you have a default route
on your access point:
$ ip -6 route show default
default via ...
Then run the routing daemon as so:
# ariadne -n default 256 $IPv6 eth1
Juliusz
/*
Copyright (c) 2007 by Juliusz Chroboczek
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include <string.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/time.h>
#include <time.h>
#include <signal.h>
#include <assert.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <net/if.h>
#include <arpa/inet.h>
#include "ariadne.h"
#include "util.h"
#include "net.h"
#include "kernel.h"
#include "destination.h"
#include "neighbour.h"
#include "route.h"
#include "xroute.h"
#include "message.h"
struct timeval now;
unsigned char myid[16];
int debug = 0;
static int maxmtu;
int reboot_time;
int wireless_hello_interval = -1;
int wired_hello_interval = -1;
int update_interval = -1;
struct network nets[MAXNETS];
int numnets = 0;
static const unsigned char zeroes[16] = {0};
char *state_file = "/var/lib/ariadne-state";
int protocol_port;
unsigned char protocol_group[16];
int protocol_socket = -1;
static volatile sig_atomic_t exiting = 0;
struct network *add_network(char *ifname, int ifindex, int bufsize,
int wired, unsigned int cost, int hello_interval);
void expire_routes(void);
static void sigexit(int signo);
static void init_signals(void);
int
main(int argc, char **argv)
{
struct sockaddr_in6 sin6;
struct ipv6_mreq mreq;
int i, rc, fd;
static unsigned char *buf;
int expiry_time = 0;
void *vrc;
unsigned int seed;
char **arg;
parse_address("ff02::cca6:c0f9:e182:5373", protocol_group);
protocol_port = 8475;
#define SHIFT() do { arg++; } while(0);
#define SHIFTE() do { arg++; if(*arg == NULL) goto syntax; } while(0)
arg = argv;
SHIFTE();
while((*arg)[0] == '-') {
if(strcmp(*arg, "--") == 0) {
SHIFTE();
break;
} else if(strcmp(*arg, "-m") == 0) {
SHIFTE();
rc = parse_address(*arg, protocol_group);
if(rc < 0)
goto syntax;
if(protocol_group[0] != 0xff) {
fprintf(stderr,
"%s is not a multicast address\n", *arg);
goto syntax;
}
if(protocol_group[1] != 2) {
fprintf(stderr,
"Warning: %s is not a link-local multicast address\n",
*arg);
}
} else if(strcmp(*arg, "-p") == 0) {
SHIFTE();
protocol_port = atoi(*arg);
} else if(strcmp(*arg, "-n") == 0) {
if(nummyxroutes >= MAXMYXROUTES) {
fprintf(stderr, "Too many network routes.\n");
exit(1);
}
SHIFTE();
rc = parse_net(*arg,
myxroutes[nummyxroutes].prefix,
&myxroutes[nummyxroutes].plen);
if(rc < 0)
goto syntax;
SHIFTE();
myxroutes[nummyxroutes].cost = atoi(*arg);
if(myxroutes[nummyxroutes].cost <= 0 ||
myxroutes[nummyxroutes].cost >= 128 * 256)
goto syntax;
nummyxroutes++;
} else if(strcmp(*arg, "-h") == 0) {
SHIFTE();
wireless_hello_interval = atoi(*arg);
} else if(strcmp(*arg, "-H") == 0) {
SHIFTE();
wired_hello_interval = atoi(*arg);
} else if(strcmp(*arg, "-u") == 0) {
SHIFTE();
update_interval = atoi(*arg);
} else if(strcmp(*arg, "-k") == 0) {
SHIFTE();
kernel_metric = atoi(*arg);
if(kernel_metric < 0)
goto syntax;
} else if(strcmp(*arg, "-P") == 0) {
parasitic = 1;
} else if(strcmp(*arg, "-s") == 0) {
split_horizon = 0;
} else if(strcmp(*arg, "-b") == 0) {
broadcast_txcost = 0;
} else if(strcmp(*arg, "-S") == 0) {
SHIFTE();
state_file = *arg;
} else if(strcmp(*arg, "-d") == 0) {
SHIFTE();
debug = atoi(*arg);
} else {
goto syntax;
}
SHIFTE();
}
if(wireless_hello_interval <= 0)
wireless_hello_interval = 8;
if(wired_hello_interval <= 0)
wired_hello_interval = 30;
if(update_interval <= 0)
update_interval =
MIN(MAX(wireless_hello_interval * 5, wired_hello_interval),
150);
if(seqno_interval <= 0)
seqno_interval = MAX(2 * wireless_hello_interval - 1, 0);
jitter = MIN(wireless_hello_interval * 1000 / 4, 2000);
update_jitter = MIN(update_interval * 1000 / 4, 3000);
rc = parse_address(*arg, myid);
if(rc < 0)
goto syntax;
SHIFTE();
gettimeofday(&now, NULL);
reboot_time = now.tv_sec;
fd = open(state_file, O_RDONLY);
if(fd < 0 && errno != ENOENT)
perror("open(ariadne-state)");
rc = unlink(state_file);
if(fd >= 0 && rc < 0) {
perror("unlink(ariadne-state)");
/* If we couldn't unlink it, it might be stale. */
close(fd);
fd = -1;
}
if(fd >= 0) {
char buf[100];
int s, t;
rc = read(fd, buf, 99);
if(rc < 0) {
perror("read(ariadne-state)");
} else {
buf[rc] = '\0';
rc = sscanf(buf, "%d %d\n", &s, &t);
if(rc == 2 && s > 0 && s <= 256 &&
t >= 1176800000 && t <= now.tv_sec) {
debugf("Got %d %d from ariadne-state.\n", s, t);
seqno = ((s + 1) & 0xFF);
reboot_time = t;
} else {
fprintf(stderr, "Couldn't parse ariadne-state.\n");
}
}
close(fd);
}
fd = open("/dev/urandom", O_RDONLY);
if(fd < 0) {
perror("open(random)");
memcpy(&seed, myid + 12, 4);
seed ^= now.tv_sec ^ now.tv_usec;
} else {
rc = read(fd, &seed, sizeof(unsigned int));
if(rc < sizeof(unsigned int)) {
perror("read(random)");
exit(1);
}
close(fd);
}
srandom(seed);
rc = kernel_setup(1);
if(rc < 0) {
fprintf(stderr, "kernel_setup failed.\n");
exit(1);
}
protocol_socket = ariadne_socket(protocol_port);
if(protocol_socket < 0) {
perror("Couldn't create link local socket");
goto fail;
}
/* Just in case. */
maxmtu = 1500;
while(*arg) {
int ifindex;
int mtu;
ifindex = if_nametoindex(*arg);
if(ifindex < 0) {
perror("if_nametoindex");
goto fail;
}
rc = kernel_setup_interface(1, *arg, ifindex);
if(rc < 0) {
fprintf(stderr, "kernel_setup_interface(%s, %d) failed.\n",
*arg, ifindex);
goto fail;
}
memset(&mreq, 0, sizeof(mreq));
memcpy(&mreq.ipv6mr_multiaddr, protocol_group, 16);
mreq.ipv6mr_interface = ifindex;
rc = setsockopt(protocol_socket, IPPROTO_IPV6, IPV6_JOIN_GROUP,
(char*)&mreq, sizeof(mreq));
if(rc < 0) {
perror("setsockopt(IPV6_JOIN_GROUP)");
goto fail;
}
mtu = kernel_interface_mtu(*arg, ifindex);
if(mtu < 0) {
fprintf(stderr, "Warning: couldn't get MTU of interface %s (%d).\n",
*arg, ifindex);
mtu = 1004;
maxmtu = MAX(maxmtu, 16384);
} else if(mtu < 400) {
fprintf(stderr,
"Warning: suspiciously low MTU %d on interface %s (%d).\n",
mtu, *arg, ifindex);
mtu = 1004;
maxmtu = MAX(maxmtu, 16384);
} else {
if(mtu >= 0x10000) {
fprintf(stderr,
"Warning: "
"suspiciously high MTU %d on interface %s (%d).\n",
mtu, *arg, ifindex);
maxmtu = MAX(maxmtu, mtu);
mtu = 32768;
}
/* 40 for IPv6 header, 20 for UDP header. */
mtu -= 60;
}
maxmtu = MAX(maxmtu, mtu);
rc = kernel_interface_wireless(*arg, ifindex);
if(rc < 0) {
fprintf(stderr,
"Warning: "
"couldn't determine whether %s is a wireless interface.\n",
*arg);
rc = 1;
}
debugf("Adding %s network %s (%d).\n",
rc ? "wireless" : "wired", *arg, ifindex);
vrc = add_network(*arg, ifindex, mtu, !rc, rc ? 0 : 128,
rc ? wireless_hello_interval : wired_hello_interval);
if(vrc == NULL)
goto fail;
SHIFT();
}
buf = malloc(maxmtu);
if(buf == NULL) {
perror("malloc");
goto fail;
}
init_signals();
for(i = 0; i < numnets; i++) {
send_hello(&nets[i]);
send_self_update(&nets[i]);
send_request(&nets[i], NULL);
}
debugf("Entering main loop.\n");
while(1) {
struct timeval tv;
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;
for(i = 0; i < numnets; i++) {
timeval_min(&tv, &nets[i].flush_time);
timeval_min_sec(&tv,
nets[i].hello_time + nets[i].hello_interval);
timeval_min_sec(&tv, nets[i].self_update_time +
nets[i].self_update_interval);
timeval_min_sec(&tv, nets[i].update_time + update_interval);
}
timeval_min(&tv, &update_flush_time);
if(timeval_compare(&tv, &now) > 0) {
timeval_minus(&tv, &tv, &now);
FD_ZERO(&readfds);
FD_SET(protocol_socket, &readfds);
rc = select(protocol_socket + 1, &readfds, NULL, NULL, &tv);
if(rc < 0 && errno != EINTR) {
perror("select");
sleep(1);
continue;
}
}
gettimeofday(&now, NULL);
if(exiting)
break;
if(FD_ISSET(protocol_socket, &readfds)) {
rc = ariadne_recv(protocol_socket, buf, maxmtu,
(struct sockaddr*)&sin6, sizeof(sin6));
if(rc < 0) {
if(errno != EAGAIN && errno != EINTR) {
perror("recv");
sleep(1);
}
} else {
for(i = 0; i < numnets; i++) {
if(nets[i].ifindex == sin6.sin6_scope_id) {
parse_packet((unsigned char*)&sin6.sin6_addr, &nets[i],
buf, rc);
VALGRIND_MAKE_MEM_UNDEFINED(buf, maxmtu);
break;
}
}
}
}
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].update_time + update_interval) {
send_update(NULL, &nets[i]);
send_txcost(NULL, &nets[i]);
}
if(now.tv_sec >=
nets[i].self_update_time + nets[i].self_update_interval) {
send_self_update(&nets[i]);
}
}
if(!debug)
continue;
printf("\n");
for(i = 0; i < numneighs; i++) {
if(neighs[i].id[0] == 0)
continue;
printf("Neighbour %s ", format_address(neighs[i].id));
printf("at %s dev %s reach %04x cost %d txcost %d.\n",
format_address(neighs[i].address),
neighs[i].network->ifname,
neighs[i].reach,
neighbour_cost(&neighs[i]),
neighs[i].txcost);
}
for(i = 0; i < numroutes; i++) {
printf("%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));
printf("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++) {
printf("%s/%d gw %s cost %d metric %d age %d%s\n",
format_address(xroutes[i].prefix),
xroutes[i].plen,
format_address(xroutes[i].gateway->address),
xroutes[i].cost,
xroutes[i].metric,
(int)(now.tv_sec - xroutes[i].time),
xroutes[i].installed ? " (installed)" : "");
}
fflush(stdout);
}
debugf("Exiting...\n");
for(i = 0; i < numroutes; i++) {
/* Uninstall and retract all routes. */
if(routes[i].installed) {
uninstall_route(&routes[i]);
send_update(routes[i].dest, NULL);
}
}
flushupdates();
for(i = 0; i < numnets; i++) {
/* Retract self 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(5000 + random() % 10000);
for(i = 0; i < numnets; i++) {
/* They're not listening... */
send_hello(&nets[i]);
kernel_setup_interface(0, nets[i].ifname, nets[i].ifindex);
}
kernel_setup(0);
fd = open(state_file, O_WRONLY | O_TRUNC | O_CREAT, 0644);
if(fd < 0) {
perror("creat(ariadne-state)");
} else {
char buf[100];
rc = snprintf(buf, 100, "%d %d\n", seqno, (int)now.tv_sec);
rc = write(fd, buf, rc);
if(rc < 0)
perror("write(ariadne-state)");
close(fd);
}
debugf("Done.");
return 0;
syntax:
fprintf(stderr,
"Syntax: %s "
"[-m multicast_address] [-p port] [-S state-file]\n"
" "
"[-h hello_interval] [-H wired_hello_interval]\n"
" "
"[-u update_interval] [-k metric] [-s] [-P] [-d level]\n"
" "
"[-n net cost]... address interface...\n",
argv[0]);
exit(1);
fail:
for(i = 0; i < numnets; i++)
kernel_setup_interface(0, nets[i].ifname, nets[i].ifindex);
kernel_setup(0);
exit(1);
}
static void
sigexit(int signo)
{
exiting = 1;
}
static void
init_signals(void)
{
struct sigaction sa;
sigset_t ss;
sigemptyset(&ss);
sa.sa_handler = sigexit;
sa.sa_mask = ss;
sa.sa_flags = 0;
sigaction(SIGTERM, &sa, NULL);
sigemptyset(&ss);
sa.sa_handler = sigexit;
sa.sa_mask = ss;
sa.sa_flags = 0;
sigaction(SIGHUP, &sa, NULL);
sigemptyset(&ss);
sa.sa_handler = sigexit;
sa.sa_mask = ss;
sa.sa_flags = 0;
sigaction(SIGINT, &sa, NULL);
}
struct network *
add_network(char *ifname, int ifindex, int mtu,
int wired, unsigned int cost, int hello_interval)
{
void *p;
if(numnets >= MAXNETS) {
fprintf(stderr, "Too many networks.\n");
return NULL;
}
memset(nets + numnets, 0, sizeof(struct network));
nets[numnets].ifindex = ifindex;
nets[numnets].wired = wired;
nets[numnets].cost = cost;
nets[numnets].hello_interval = hello_interval;
nets[numnets].self_update_interval =
MAX(15 + hello_interval / 2 , hello_interval);
nets[numnets].bufsize = mtu - sizeof(packet_header);
strncpy(nets[numnets].ifname, ifname, IF_NAMESIZE);
p = malloc(nets[numnets].bufsize);
if(p == NULL) {
perror("malloc");
return NULL;
}
nets[numnets].sendbuf = p;
nets[numnets].buffered = 0;
numnets++;
return &nets[numnets - 1];
}
void
expire_routes(void)
{
int i;
for(i = 0; i < numneighs; i++) {
if(neighs[i].id[0] == 0)
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 - 180) {
flush_route(route);
continue;
}
update_route_metric(route);
if(route->refmetric >= INFINITY) {
flush_route(route);
continue;
}
if(route->installed) {
if(route->time <= now.tv_sec - 90)
send_unicast_request(route->nexthop, route->dest);
}
i++;
}
i = 0;
while(i < numxroutes) {
struct xroute *xroute = &xroutes[i];
if(xroute->time < now.tv_sec - 240) {
flush_xroute(xroute);
continue;
}
update_xroute_metric(xroute, xroute->cost);
i++;
}
}
/*
Copyright (c) 2007 by Juliusz Chroboczek
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#define MAXROUTES 512
#define MAXDESTS 1024
#define MAXNEIGHBOURS 128
#define MAXNETS 8
#define MAXXROUTES 64
#define MAXMYXROUTES 8
#define MAX_BUFFERED_UPDATES 100
#define INFINITY ((unsigned short)(~0))
#undef MAX
#undef MIN
#define MAX(x,y) ((x)<=(y)?(y):(x))
#define MIN(x,y) ((x)<=(y)?(x):(y))
#if defined(__GNUC__) && (__GNUC__ >= 3)
#define ATTRIBUTE(x) __attribute__(x)
#else
#define ATTRIBUTE(x) /**/
#endif
#ifndef IF_NAMESIZE
#include <net/if.h>
#endif
#ifdef HAVE_VALGRIND
#include <valgrind/memcheck.h>
#else
#ifndef VALGRIND_MAKE_MEM_UNDEFINED
#define VALGRIND_MAKE_MEM_UNDEFINED(a, b) do {} while(0)
#endif
#ifndef VALGRIND_CHECK_MEM_IS_DEFINED
#define VALGRIND_CHECK_MEM_IS_DEFINED(a, b) do {} while(0)
#endif
#endif
struct network {
unsigned int ifindex;
int wired;
unsigned short cost;
int hello_time;
int self_update_time;
int update_time;
char ifname[IF_NAMESIZE];
int buffered;
struct timeval flush_time;
int bufsize;
unsigned char *sendbuf;
unsigned char hello_seqno;
unsigned int hello_interval;
unsigned int self_update_interval;
};
extern struct timeval now;
extern int debug;
extern int reboot_time;
extern unsigned char myid[16];
extern struct network nets[MAXNETS];
extern int numnets;
extern int protocol_port;
extern unsigned char protocol_group[16];
extern int protocol_socket;
.TH ARIADNE 1
.SH NAME
ariadne \- ad-hoc network routing daemon
.SH SYNOPSIS
.B ariadne
[
.B \-m
.I multicast-address
]
[
.B \-p
.I port
] [
.B -S
.I state-file
] [
.B \-h
.I hello-interval
] [
.B \-H
.I wired-hello-interval
] [
.B \-u
.I update-interval
] [
.B \-k
.I kernel-metric
] [
.B \-s
] [
.B \-b
] [
.B \-p
] [
.B \-d
.I level
] [
.B \-n
.I
.I prefix cost
]...
.I address
.IR interface ...
.SH DESCRIPTION
Ariadne is a loop-avoiding distance-vector routing protocol roughly
based on HSDV and AODV, but with provisions for link cost estimation
and injection of external routes.
While it is optimised for wireless mesh networks, Ariadne will also
work efficiently on classical wired networks. In the worst case, it
will generate roughly double the amount of traffic that RIPng would
generate, while never counting to infinity.
.SH OPTIONS
.TP
.BI \-m " multicast-address"
Specify the link-local multicast address to be used by the protocol.
.TP
.BI \-p " port"
Specify the UDP port number to be used by the Ariadne protocol.
.TP
.BI \-S " state-file"
Set the name of the file used for preserving long-term information
between invocations of the
.B ariadne
daemon. If this file is deleted, the daemon will run in passive mode
for 3 minutes when it is next invoked (see
.B -P
below), and other hosts might initially not accept to route to it.
The default is
.BR /var/lib/ariadne-state .
.TP
.BI \-h " hello-interval"
Specify the interval in seconds at which periodic hello packets are
sent on wireless interfaces. The default is 8.
.TP
.BI \-H " wired-hello-interval"
Specify the interval in seconds at which periodic hello packets are
sent on wired interfaces. The default is 30.
.TP
.BI \-u " update-interval"
Specify the interval in seconds at which periodic routing table dumps
are made on all interfaces. The default is either the wired hello
interval, or 150, whichever is less.
.TP
.BI \-k " kernel-metric"
Specify a value that will be added to routes' metrics before
installing them in the kernel; this is useful when a single node
participates in multiple routing protocols. The default is 0.
.TP
.B \-s
Do not perform split-horizon processing on wired interfaces.
Split-horizon is never performed on wireless interfaces.
.TP
.B \-b
Generate messages for detecting bidirectional communication failures
on wired interfaces. By default, wired interfaces are assumed to be
symmetric.
.TP
.B \-P
Run in parasitic (passive) mode. The daemon will not announce any
routes except for itself.
.TP
.BI \-d " level"
Debug level. A value of 1 requests a routing table dump at every
iteration through the daemon's main loop. A value of 2 additionally
requests tracing every message sent or received. The default is 0.
.TP
.BI \-n " network cost"
Advertise an external route for the given network with the given cost.
We suggest a cost of 256 \[mu]
.RI ( n
+ 1) for a network
.I n
hops away.
.I Network
should be either the string
.BR default ,
or a nework specification in CIDR notation
.IB prefix / length.
If
.I length
is omitted, it defaults to 128 (a host route).
.TP
.I address
The IPv6 address that the node should advertise for itself.
(Additional addresses can be advertised as external routes with a cost
of 0.)
.TP
.IR interface ...
The list of interfaces on which the protocol should operate.
.SH WIRED INTERFACES
By default, the daemon optimises traffic on wired interfaces by
sending fewer periodic hello messages, performing split-horizon
processing and not generating any reverse reachability information.
These optimisations can be disabled using the
.BR -H ,
.B -s
and
.B -b
options respectively.
No link cost estimation is performed on wired interfaces: a wired
adjacency is assumed to be up if at least one of the last three hello
messages has been received, and down otherwise.
.SH FILES
.TP
.B /var/lib/ariadne-state
The default location of the file storing long-term state.
.SH SECURITY
Ariadne is a completely insecure protocol: any attacker able to
inject IP packets with a link-local source address can disrupt the
protocol's operation.
Since Ariadne uses link-local addresses only, there is no need to
update firewalls to allow forwarding of Ariadne protocol packets. If
ingress filtering is done on a host, the protocol port should be
opened. As Ariadne uses unicast packets in some cases, just allowing
packets destined to the multicast address is not enough.
.SH BUGS
Plenty. This is experimental software, run at your own risk.
.SH SEE ALSO
.BR routed (8),
.BR route6d (8),
.BR zebra (8),
.BR ahcpd (8).
.SH AUTHOR
Juliusz Chroboczek.
/*
Copyright (c) 2007 by Juliusz Chroboczek
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "ariadne.h"
#include "util.h"
#include "destination.h"
struct destination dests[MAXDESTS];
int numdests = 0;
struct destination *
find_destination(const unsigned char *d, int create, unsigned char seqno)
{
int i;
for(i = 0; i < numdests; i++) {
if(memcmp(dests[i].address, d, 16) == 0)
return &dests[i];
}
if(!create)
return NULL;
if(i >= numdests) {
if(numdests >= MAXDESTS) {
fprintf(stderr, "Too many destinations.\n");
return NULL;
}
memcpy(dests[numdests].address, d, 16);
numdests++;
}
dests[i].seqno = seqno;
dests[i].metric = INFINITY;
return &dests[i];
}
void
update_destination(struct destination *dest,
unsigned char seqno, unsigned short metric)
{
if(seqno_compare(dest->seqno, seqno) < 0 ||
(dest->seqno == seqno && dest->metric > metric)) {
dest->time = now.tv_sec;
dest->seqno = seqno;
dest->metric = metric;
}
}
/*
Copyright (c) 2007 by Juliusz Chroboczek
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
struct destination {
unsigned char address[16];
unsigned char seqno;
unsigned short metric;
int time;
};
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);
/*
Copyright (c) 2007 by Juliusz Chroboczek
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <net/route.h>
#include <net/if.h>
#include "kernel.h"
static int old_forwarding = -1;
static int old_accept_redirects = -1;
static int
read_proc(char *filename)
{
char buf[100];
int fd, rc;
fd = open(filename, O_RDONLY);
if(fd < 0)
return -1;
rc = read(fd, buf, 99);
if(rc < 0) {
int saved_errno = errno;
close(fd);
errno = saved_errno;
return -1;
}
close(fd);
if(rc == 0)
return -1;
buf[rc] = '\0';
return atoi(buf);
}
static int
write_proc(char *filename, int value)
{
char buf[100];
int fd, rc, n;
n = snprintf(buf, 100, "%d", value);
fd = open(filename, O_WRONLY);
if(fd < 0)
return -1;
rc = write(fd, buf, n);
if(rc < n) {
int saved_errno = errno;
close(fd);
errno = saved_errno;
return -1;
}
close(fd);
return 1;
}
int
kernel_setup(int setup)
{
int rc;
if(setup) {
old_forwarding = read_proc("/proc/sys/net/ipv6/conf/all/forwarding");
if(old_forwarding < 0) {
perror("Couldn't read forwarding knob.");
return -1;
}
rc = write_proc("/proc/sys/net/ipv6/conf/all/forwarding", 1);
if(rc < 0) {
perror("Couldn't write forwarding knob.");
return -1;
}
old_accept_redirects =
read_proc("/proc/sys/net/ipv6/conf/all/accept_redirects");
if(old_accept_redirects < 0) {
perror("Couldn't read accept_redirects knob.");
return -1;
}
rc = write_proc("/proc/sys/net/ipv6/conf/all/accept_redirects", 0);
if(rc < 0) {
perror("Couldn't write accept_redirects knob.");
return -1;
}
return 1;
} else {
if(old_forwarding >= 0) {
rc = write_proc("/proc/sys/net/ipv6/conf/all/forwarding",
old_forwarding);
if(rc < 0) {
perror("Couldn't write accept_redirects knob.\n");
return -1;
}
}
if(old_accept_redirects >= 0) {
rc = write_proc("/proc/sys/net/ipv6/conf/all/accept_redirects",
old_accept_redirects);
if(rc < 0) {
perror("Couldn't write accept_redirects knob.\n");
return -1;
}
}
return 1;
}
}
int
kernel_setup_interface(int setup, const char *ifname, int ifindex)
{
return 1;
}
int
kernel_interface_mtu(const char *ifname, int ifindex)
{
struct ifreq req;
int s, rc;
s = socket(PF_INET, SOCK_DGRAM, 0);
if(s < 0)
return -1;
memset(&req, 0, sizeof(req));
strncpy(req.ifr_name, ifname, sizeof(req.ifr_name));
rc = ioctl(s, SIOCGIFMTU, &req);
if(rc < 0) {
close(s);
return -1;
}
return req.ifr_mtu;
}
int
kernel_interface_wireless(const char *ifname, int ifindex)
{
#ifndef SIOCGIWNAME
#define SIOCGIWNAME 0x8B01
#endif
struct ifreq req;
int s, rc;
s = socket(PF_INET, SOCK_DGRAM, 0);
if(s < 0)
return -1;
memset(&req, 0, sizeof(req));
strncpy(req.ifr_name, ifname, sizeof(req.ifr_name));
rc = ioctl(s, SIOCGIWNAME, &req);
if(rc < 0) {
if(errno == EOPNOTSUPP || errno == EINVAL)
rc = 0;
else {
perror("ioctl(SIOCGIWNAME)");
rc = -1;
}
} else {
rc = 1;
}
close(s);
return rc;
}
static int route_socket = -1;
int
kernel_route(int add, const unsigned char *dest, unsigned short plen,
const unsigned char *gate, int ifindex, int metric)
{
struct in6_rtmsg msg;
int rc;
if(route_socket < 0) {
route_socket = socket(AF_INET6, SOCK_DGRAM, 0);
if(route_socket < 0)
return -1;
}
memset(&msg, 0, sizeof(msg));
msg.rtmsg_flags = RTF_UP;
if(plen < 128) {
msg.rtmsg_flags |= RTF_GATEWAY;
} else {
msg.rtmsg_flags |= RTF_HOST;
if(memcmp(dest, gate, 16) != 0)
msg.rtmsg_flags |= RTF_GATEWAY;
}
msg.rtmsg_metric = metric;
memcpy(&msg.rtmsg_dst, dest, sizeof(struct in6_addr));
msg.rtmsg_dst_len = plen;
memcpy(&msg.rtmsg_gateway, gate, sizeof(struct in6_addr));
msg.rtmsg_ifindex = ifindex;
rc = ioctl(route_socket, add ? SIOCADDRT : SIOCDELRT, &msg);
if(rc < 0)
return -1;
return 1;
}
/*
Copyright (c) 2007 by Juliusz Chroboczek
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
int kernel_setup(int setup);
int kernel_setup_interface(int setup, const char *ifname, int ifindex);
int kernel_interface_mtu(const char *ifname, int ifindex);
int kernel_interface_wireless(const char *ifname, int ifindex);
int kernel_route(int add, const unsigned char *dest, unsigned short plen,
const unsigned char *gate, int ifindex, int metric);
/*
Copyright (c) 2007 by Juliusz Chroboczek
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>
#include <arpa/inet.h>
#include "ariadne.h"
#include "util.h"
#include "net.h"
#include "destination.h"
#include "neighbour.h"
#include "route.h"
#include "xroute.h"
#include "message.h"
struct timeval update_flush_time = {0, 0};
static const unsigned char zeroes[16] = {0};
const unsigned char packet_header[4] = {42, 0, 0, 0};
unsigned int jitter;
unsigned int update_jitter;
int parasitic = 0;
int silent_time = 30;
int broadcast_txcost = 1;
int split_horizon = 1;
unsigned char seqno = 0;
int seqno_time = 0;
int seqno_interval = -1;
struct destination *buffered_updates[MAX_BUFFERED_UPDATES];
struct network *update_net = NULL;
int updates = 0;
void
parse_packet(const unsigned char *from, struct network *net,
const unsigned char *packet, int len)
{
int i, j;
const unsigned char *message;
struct neighbour *neigh;
struct xroute pxroutes[20];
int numpxroutes = 0;
if(len % 20 != 4 || packet[0] != 42) {
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)
continue;
debugf("Received hello on %s from %s (%s).\n",
net->ifname,
format_address(message + 4),
format_address(from));
neigh = add_neighbour(message + 4, from, net);
update_neighbour(neigh, message[1], (message[2] << 8 | message[3]));
update_neighbour_metric(neigh);
} else {
neigh = find_neighbour(from, net);
if(neigh == NULL)
continue;
if(message[0] == 1) {
debugf("Received request on %s from %s (%s) for %s.\n",
net->ifname,
format_address(neigh->id),
format_address(from),
format_address(message + 4));
if(memcmp(message + 4, zeroes, 16) == 0) {
send_update(NULL, neigh->network);
} else if(memcmp(message + 4, myid, 16) == 0) {
send_self_update(neigh->network);
} else {
struct destination *dest;
dest = find_destination(message + 4, 0, 0);
if(dest)
send_update(dest, neigh->network);
}
} else if(message[0] == 2) {
debugf("Received update on %s from %s (%s) for %s.\n",
net->ifname,
format_address(neigh->id),
format_address(from),
format_address(message + 4));
if(memcmp(message + 4, myid, 16) == 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 >= 20) {
fprintf(stderr, "Too many xroutes in update.\n");
continue;
}
memcpy(pxroutes[numpxroutes].prefix, message + 4, 16);
pxroutes[numpxroutes].plen = message[1];
pxroutes[numpxroutes].cost =
((message[2] << 8) | (message[3] & 0xFF));
numpxroutes++;
} else {
debugf("Received unknown packet type %d from %s.\n",
message[0], format_address(from));
}
}
}
return;
}
void
flushbuf(struct network *net)
{
int rc;
struct sockaddr_in6 sin6;
if(update_net == net) {
flushupdates();
return;
}
if(net->buffered > 0) {
debugf(" (flushing %d buffered bytes on %s)\n",
net->buffered, net->ifname);
memset(&sin6, 0, sizeof(sin6));
sin6.sin6_family = AF_INET6;
memcpy(&sin6.sin6_addr, protocol_group, 16);
sin6.sin6_port = htons(protocol_port);
sin6.sin6_scope_id = net->ifindex;
rc = ariadne_send(protocol_socket,
packet_header, sizeof(packet_header),
net->sendbuf, net->buffered,
(struct sockaddr*)&sin6, sizeof(sin6));
if(rc < 0)
perror("send");
}
VALGRIND_MAKE_MEM_UNDEFINED(net->sendbuf, net->bufsize);
net->buffered = 0;
net->flush_time.tv_sec = 0;
net->flush_time.tv_usec = 0;
}
static void
schedule_flush(struct network *net)
{
int msecs = jitter / 2 + random() % jitter;
if(net->flush_time.tv_sec != 0 &&
timeval_minus_msec(&net->flush_time, &now) < msecs)
return;
net->flush_time.tv_usec = (now.tv_usec + msecs * 1000) % 1000000;
net->flush_time.tv_sec = now.tv_sec + (now.tv_usec / 1000 + msecs) / 1000;
}
static void
start_message(struct network *net, int bytes)
{
if(net->bufsize - net->buffered < bytes)
flushbuf(net);
}
static void
accumulate_byte(struct network *net, unsigned char byte)
{
assert(net->bufsize - net->buffered >= 1);
net->sendbuf[net->buffered] = byte;
net->buffered++;
}
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);
net->buffered += 2;
}
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;
}
void
send_hello(struct network *net)
{
debugf("Sending hello to %s.\n", net->ifname);
start_message(net, 20);
accumulate_byte(net, 0);
accumulate_byte(net, net->hello_seqno++);
accumulate_short(net, net->hello_interval);
accumulate_data(net, myid, 16);
schedule_flush(net);
net->hello_time = now.tv_sec;
}
void
send_request(struct network *net, struct destination *dest)
{
int i;
if(net == NULL) {
for(i = 0; i < numnets; i++)
send_request(&nets[i], dest);
return;
}
debugf("Sending request to %s for %s.\n",
net->ifname, dest ? format_address(dest->address) : "::/0");
start_message(net, 20);
accumulate_byte(net, 1);
accumulate_byte(net, 0);
accumulate_short(net, 0);
accumulate_data(net, dest ? dest->address : zeroes, 16);
schedule_flush(net);
}
static void
send_unicast_packet(struct neighbour *neigh, unsigned char *buf, int buflen)
{
struct sockaddr_in6 sin6;
int rc;
memset(&sin6, 0, sizeof(sin6));
sin6.sin6_family = AF_INET6;
memcpy(&sin6.sin6_addr, neigh->address, 16);
sin6.sin6_port = htons(protocol_port);
sin6.sin6_scope_id = neigh->network->ifindex;
rc = ariadne_send(protocol_socket,
packet_header, sizeof(packet_header),
buf, buflen,
(struct sockaddr*)&sin6, sizeof(sin6));
if(rc < 0)
perror("send(unicast)");
}
void
send_unicast_request(struct neighbour *neigh, struct destination *dest)
{
unsigned char buf[20];
debugf("Sending unicast request to %s (%s) for %s.\n",
format_address(neigh->id),
format_address(neigh->address),
dest ? format_address(dest->address) : "::/0");
buf[0] = 1;
buf[1] = 0;
buf[2] = 0;
buf[3] = 0;
if(dest == NULL)
memset(buf + 4, 0, 16);
else
memcpy(buf + 4, dest->address, 16);
send_unicast_packet(neigh, buf, 20);
}
void
flushupdates(void)
{
int i, j;
if(updates > 0) {
/* Ensure that we won't be recursively called by flushbuf. */
int n = updates;
struct network *net = update_net;
updates = 0;
update_net = NULL;
debugf(" (flushing %d buffered updates)\n", n);
for(i = 0; i < n; i++) {
if(buffered_updates[i] == NULL) {
start_message(net, MIN(20 + 20 * nummyxroutes, 1000));
for(j = 0; j < nummyxroutes; j++) {
if(net->bufsize - net->buffered < 40)
/* We cannot just call start_message, as this would
split the xroutes from the update. Bail out
for now, and never mind the missing updates. */
break;
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 = route->metric;
} else {
seqno = buffered_updates[i]->seqno;
metric = INFINITY;
}
update_destination(buffered_updates[i], seqno, metric);
/* 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].gateway == buffered_updates[i])
numpxroutes++;
}
start_message(net, MIN(20 + 20 * numpxroutes, 1000));
for(j = 0; j < numxroutes; j++) {
if(xroutes[j].gateway != buffered_updates[i])
continue;
/* See comment above */
if(net->bufsize - net->buffered < 40)
break;
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);
}
}
schedule_flush_now(net);
VALGRIND_MAKE_MEM_UNDEFINED(buffered_updates,
MAX_BUFFERED_UPDATES *
sizeof(struct destination));
}
update_flush_time.tv_sec = 0;
update_flush_time.tv_usec = 0;
}
static void
schedule_update_flush(void)
{
int msecs = update_jitter / 2 + random() % update_jitter;
if(update_flush_time.tv_sec != 0 &&
timeval_minus_msec(&update_flush_time, &now) < msecs)
return;
update_flush_time.tv_usec = (now.tv_usec + msecs * 1000) % 1000000;
update_flush_time.tv_sec = now.tv_sec + (now.tv_usec / 1000 + msecs) / 1000;
}
static void
buffer_update(struct network *net, struct destination *dest)
{
int i;
if(update_net && update_net != net)
flushupdates();
update_net = net;
for(i = 0; i < updates; i++)
if(buffered_updates[i] == dest)
return;
if(updates >= MAX_BUFFERED_UPDATES)
flushupdates();
buffered_updates[updates++] = dest;
}
void
send_update(struct destination *dest, struct network *net)
{
int i;
if(net) {
if(parasitic ||
(silent_time && now.tv_sec < reboot_time + silent_time)) {
net->update_time = now.tv_sec;
if(dest == NULL) {
buffer_update(net, NULL);
net->self_update_time = now.tv_sec;
}
} else {
silent_time = 0;
}
}
if(net == NULL) {
for(i = 0; i < numnets; i++)
send_update(dest, &nets[i]);
return;
}
if(dest) {
if(updates >= net->bufsize / 20) {
/* Update won't fit in a single packet -- send a full dump. */
send_update(NULL, net);
return;
}
debugf("Sending update to %s for %s.\n",
net->ifname, format_address(dest->address));
buffer_update(net, dest);
} else {
debugf("Sending update to %s for ::/0.\n", net->ifname);
if(now.tv_sec - net->update_time < 2)
return;
for(i = 0; i < numroutes; i++)
if(routes[i].installed)
buffer_update(net, routes[i].dest);
buffer_update(net, NULL);
net->update_time = now.tv_sec;
net->self_update_time = now.tv_sec;
}
schedule_update_flush();
}
void
send_self_update(struct network *net)
{
debugf("Sending self update to %s.\n", net->ifname);
if(seqno_time + seqno_interval < now.tv_sec) {
seqno++;
seqno_time = now.tv_sec;
}
buffer_update(net, NULL);
schedule_update_flush();
net->self_update_time = now.tv_sec;
}
void
send_self_retract(struct network *net)
{
debugf("Retracting self on %s.\n", net->ifname);
seqno++;
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;
}
void
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);
}
schedule_update_flush();
}
void
send_txcost(struct neighbour *neigh, struct network *net)
{
int i;
if(neigh == NULL && net == NULL) {
for(i = 0; i < numnets; i++)
send_txcost(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);
} else {
for(i = 0; i < numneighs; i++) {
if(neighs[i].id[0] != 0) {
if(neighs[i].network == net)
send_txcost(&neighs[i], net);
}
}
}
} else {
int cost;
if(net && neigh->network != net)
return;
net = neigh->network;
debugf("Sending unicast txcost on %s to %s (%s).\n",
neigh->network->ifname,
format_address(neigh->id),
format_address(neigh->address));
cost = neighbour_cost(neigh);
start_message(net, 20);
accumulate_byte(net, 3);
accumulate_byte(net, 0);
accumulate_short(net, neighbour_cost(neigh));
accumulate_data(net, neigh->id, 16);
schedule_flush(net);
}
}
void
schedule_flush_now(struct network *net)
{
int msecs = random() % 10;
if(net->flush_time.tv_sec != 0 &&
timeval_minus_msec(&net->flush_time, &now) < msecs)
return;
net->flush_time.tv_usec = (now.tv_usec + msecs * 1000) % 1000000;
net->flush_time.tv_sec = now.tv_sec + (now.tv_usec / 1000 + msecs) / 1000;
}
/*
Copyright (c) 2007 by Juliusz Chroboczek
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
extern unsigned char seqno;
extern int seqno_time;
extern int seqno_interval;
extern unsigned int jitter;
extern unsigned int update_jitter;
extern int parasitic;
extern int silent_time;
extern int broadcast_txcost;
extern int split_horizon;
extern struct timeval update_flush_time;
extern const unsigned char packet_header[4];
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);
void send_unicast_request(struct neighbour *neigh, struct destination *dest);
void send_update(struct destination *dest, struct network *net);
void send_self_update(struct network *net);
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 schedule_flush_now(struct network *net);
void flushupdates(void);
/*
Copyright (c) 2007 by Juliusz Chroboczek
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <sys/time.h>
#include <time.h>
#include "ariadne.h"
#include "util.h"
#include "neighbour.h"
#include "route.h"
#include "message.h"
struct neighbour neighs[MAXNEIGHBOURS];
int numneighs = 0;
struct neighbour *find_neighbour(const unsigned char *address,
struct network *net);
struct neighbour *
add_neighbour(const unsigned char *id, const unsigned char *address,
struct network *net);
void
flush_neighbour(struct neighbour *neigh)
{
flush_neighbour_routes(neigh);
memset(neigh, 0, sizeof(*neigh));
if(neigh == &neighs[numneighs - 1]) {
numneighs--;
VALGRIND_MAKE_MEM_UNDEFINED(neigh, sizeof(*neigh));
} else {
/* The first byte is used to mark unused entries */
VALGRIND_MAKE_MEM_UNDEFINED((char*)neigh + 1, sizeof(*neigh) - 1);
}
}
struct neighbour *
find_neighbour(const unsigned char *address, struct network *net)
{
int i;
for(i = 0; i < numneighs; i++) {
if(neighs[i].id[0] == 0)
continue;
if(memcmp(address, neighs[i].address, 16) == 0 &&
neighs[i].network == net)
return &neighs[i];
}
return NULL;
}
struct neighbour *
add_neighbour(const unsigned char *id, const unsigned char *address,
struct network *net)
{
struct neighbour *neigh;
int i;
neigh = find_neighbour(address, net);
if(neigh) {
if(memcmp(neigh->id, id, 16) == 0) {
return neigh;
} else {
fprintf(stderr, "Neighbour changed id (%s -> %s)!\n",
format_address(neigh->id), format_address(id));
flush_neighbour(neigh);
neigh = NULL;
}
}
debugf("Creating neighbour %s (%s).\n",
format_address(id), format_address(address));
for(i = 0; i < numneighs; i++) {
if(neighs[i].id[0] == 0)
neigh = &neighs[i];
}
if(!neigh) {
if(numneighs >= MAXNEIGHBOURS) {
fprintf(stderr, "Too many neighbours.\n");
return NULL;
}
neigh = &neighs[numneighs++];
}
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->network = net;
send_hello(net);
return neigh;
}
void
update_neighbour(struct neighbour *neigh, int hello, int hello_interval)
{
int missed_hellos;
if(hello < 0) {
if(neigh->hello_interval <= 0)
return;
missed_hellos =
(now.tv_sec - 3 - neigh->hello_time) / neigh->hello_interval;
if(missed_hellos <= 0)
return;
neigh->hello_time += missed_hellos * neigh->hello_interval;
} else {
if(neigh->reach > 0) {
missed_hellos = seqno_minus(hello, neigh->hello_seqno) - 1;
while(missed_hellos < 0) {
/* This neighbour has increased its hello interval, and we
didn't notice. */
neigh->reach <<= 1;
missed_hellos++;
}
} else {
missed_hellos = 0;
}
neigh->hello_time = now.tv_sec;
neigh->hello_interval = hello_interval;
}
while(missed_hellos) {
neigh->reach >>= 1;
missed_hellos--;
}
if(hello >= 0) {
neigh->hello_seqno = hello;
neigh->reach >>= 1;
neigh->reach |= 0x8000;
}
/* Make sure to give neighbours some feedback early after association */
if(neigh->reach == 0x8000) {
send_hello(neigh->network);
send_txcost(neigh, NULL);
} else {
int a = (neigh->reach & 0xC000);
int b = (neigh->reach & 0x3000);
if((a == 0xC000 && b == 0) || (a == 0 && b == 0x3000)) {
send_txcost(neigh, NULL);
send_self_update(neigh->network);
send_neighbour_update(neigh, NULL);
} else {
if((neigh->reach & 0xF000) != 0 && (neigh->reach & 0x0F000) == 0)
send_txcost(neigh, NULL);
}
}
}
void
check_neighbour(struct neighbour *neigh)
{
update_neighbour(neigh, -1, 0);
if(neigh->reach == 0) {
flush_neighbour(neigh);
return;
}
if(!neigh->network->wired) {
while(neigh->txcost < INFINITY &&
neigh->txcost_time <= now.tv_sec - 30) {
neigh->txcost = MIN((int)neigh->txcost * 5 / 4, INFINITY);
neigh->txcost_time += 30;
}
}
}
int
neighbour_cost(struct neighbour *neigh)
{
update_neighbour(neigh, -1, 0);
if(neigh->reach == 0)
return INFINITY;
else if(neigh->network->wired) {
if((neigh->reach & 0xE000) != 0)
return neigh->network->cost;
else
return INFINITY;
} else {
int r = (neigh->reach & 0x7FFF) + ((neigh->reach & 0x8000) >> 1);
double c = (((double)0xC000 * 0x100) / r);
/* Avoid neighbours that give poor feedback */
if(neigh->hello_interval == 0)
c += 256 + (now.tv_sec - neigh->hello_time) * 10;
else if(neigh->hello_interval >= 20)
c *= (neigh->hello_interval / 20.0);
return MIN((int)(c + neigh->network->cost + 0.5), INFINITY);
}
}
int
neighbour_symmetric_cost(struct neighbour *neigh)
{
int c;
/* (1/(alpha * beta) + 1/beta) / 2, which is half the expected
number of transmissions, in both directions.
ETX uses 1/(alpha * beta), which is the expected number of
transmissions in the forward direction. */
if(neigh->txcost >= INFINITY)
return INFINITY;
c = neighbour_cost(neigh);
if(c >= INFINITY)
return INFINITY;
return (c + ((c * neigh->txcost + 128) >> 8) + 1) / 2;
}
/*
Copyright (c) 2007 by Juliusz Chroboczek
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
struct neighbour {
unsigned char id[16];
unsigned char address[16];
unsigned short reach;
unsigned short txcost;
int txcost_time;
int hello_time;
int hello_interval;
int hello_seqno;
struct network *network;
};
extern struct neighbour neighs[MAXNEIGHBOURS];
extern int numneighs;
void flush_neighbour(struct neighbour *neigh);
struct neighbour *find_neighbour(const unsigned char *address,
struct network *net);
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 neighbour_cost(struct neighbour *neigh);
int neighbour_symmetric_cost(struct neighbour *neigh);
/*
Copyright (c) 2007 by Juliusz Chroboczek
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>
#include "net.h"
int
ariadne_socket(int port)
{
struct sockaddr_in6 sin6;
int s, rc;
int saved_errno;
int one = 1, zero = 0;
s = socket(PF_INET6, SOCK_DGRAM, 0);
if(s < 0)
return -1;
rc = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
if(rc < 0)
goto fail;
memset(&sin6, 0, sizeof(sin6));
sin6.sin6_family = AF_INET6;
sin6.sin6_port = htons(port);
rc = bind(s, (struct sockaddr*)&sin6, sizeof(sin6));
if(rc < 0)
goto fail;
rc = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
&zero, sizeof(zero));
if(rc < 0)
goto fail;
rc = fcntl(s, F_GETFL, 0);
if(rc < 0)
goto fail;
rc = fcntl(s, F_SETFL, (rc | O_NONBLOCK));
if(rc < 0)
goto fail;
return s;
fail:
saved_errno = errno;
close(s);
errno = saved_errno;
return -1;
}
int
ariadne_recv(int s, void *buf, int buflen, struct sockaddr *sin, int slen)
{
struct iovec iovec;
struct msghdr msg;
int rc;
memset(&msg, 0, sizeof(msg));
iovec.iov_base = buf;
iovec.iov_len = buflen;
msg.msg_name = sin;
msg.msg_namelen = slen;
msg.msg_iov = &iovec;
msg.msg_iovlen = 1;
rc = recvmsg(s, &msg, 0);
return rc;
}
int
ariadne_send(int s,
const void *buf1, int buflen1, const void *buf2, int buflen2,
const struct sockaddr *sin, int slen)
{
struct iovec iovec[2];
struct msghdr msg;
int rc;
iovec[0].iov_base = (void*)buf1;
iovec[0].iov_len = buflen1;
iovec[1].iov_base = (void*)buf2;
iovec[1].iov_len = buflen2;
memset(&msg, 0, sizeof(msg));
msg.msg_name = (struct sockaddr*)sin;
msg.msg_namelen = slen;
msg.msg_iov = iovec;
msg.msg_iovlen = 2;
rc = sendmsg(s, &msg, 0);
return rc;
}
/*
Copyright (c) 2007 by Juliusz Chroboczek
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
int ariadne_socket(int port);
int ariadne_recv(int s, void *buf, int buflen, struct sockaddr *sin, int slen);
int ariadne_send(int s,
const void *buf1, int buflen1, const void *buf2, int buflen2,
const struct sockaddr *sin, int slen);
/*
Copyright (c) 2007 by Juliusz Chroboczek
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <assert.h>
#include "ariadne.h"
#include "util.h"
#include "kernel.h"
#include "destination.h"
#include "neighbour.h"
#include "route.h"
#include "xroute.h"
#include "message.h"
struct route routes[MAXROUTES];
int numroutes = 0;
int kernel_metric = 0;
struct route *
find_route(const unsigned char *dest, struct neighbour *nexthop)
{
int i;
for(i = 0; i < numroutes; i++) {
if(routes[i].nexthop == nexthop &&
memcmp(routes[i].dest->address, dest, 16) == 0)
return &routes[i];
}
return NULL;
}
struct route *
find_installed_route(struct destination *dest)
{
int i;
for(i = 0; i < numroutes; i++) {
if(routes[i].installed && routes[i].dest == dest)
return &routes[i];
}
return NULL;
}
void
flush_route(struct route *route)
{
int n;
unsigned char dest[16];
int install = 0, oldmetric = INFINITY;
n = route - routes;
assert(n >= 0 && n < numroutes);
if(route->installed) {
oldmetric = route->metric;
uninstall_route(route);
install = 1;
}
memcpy(dest, route->dest->address, 16);
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;
struct destination *destination = find_destination(dest, 0, 0);
if(destination == NULL) {
fprintf(stderr,
"Attempted to retract unknown destination "
"(this shouldn't happen).\n");
return;
}
new_route = find_best_route(destination);
if(new_route) {
install_route(new_route);
send_triggered_update(new_route, oldmetric);
} else {
if(destination->metric < INFINITY) {
destination->seqno++;
destination->metric = INFINITY;
}
send_update(route->dest, NULL);
send_request(NULL, route->dest);
}
}
}
void
flush_neighbour_routes(struct neighbour *neigh)
{
int i;
again:
for(i = 0; i < numroutes; i++) {
if(routes[i].nexthop == neigh) {
flush_route(routes + i);
goto again;
}
}
}
static struct route *
reinstall_route(struct route *route)
{
if(route->metric < INFINITY && route_feasible(route)) {
install_route(route);
return route;
} else {
struct route *new_route;
new_route = find_best_route(route->dest);
if(new_route) {
install_route(new_route);
return new_route;
} else {
/* We just lost a destination due to loop avoidance. */
if(route->dest->metric < INFINITY) {
/* Update seqno to make sure our predecessors accept
our retraction... */
route->dest->seqno++;
route->dest->metric = INFINITY;
}
/* ...and hope we get a new seqno soon. */
send_update(route->dest, NULL);
send_request(NULL, route->dest);
return NULL;
}
}
}
unsigned int
metric_to_kernel(int metric)
{
assert(metric >= 0 && metric < INFINITY);
return (metric + 255) / 256 + kernel_metric;
}
void
install_route(struct route *route)
{
struct route *installed;
int i, rc;
if(route->installed)
return;
installed = find_installed_route(route->dest);
if(installed)
uninstall_route(installed);
rc = kernel_route(1, route->dest->address, 128,
route->nexthop->address,
route->nexthop->network->ifindex,
metric_to_kernel(route->metric));
if(rc < 0) {
perror("kernel_route(1)");
if(errno != EEXIST)
return;
}
route->installed = 1;
for(i = 0; i < numxroutes; i++) {
if(xroutes[i].gateway == route->dest &&
xroutes[i].time >= now.tv_sec - 240) {
update_xroute_metric(&xroutes[i], xroutes[i].cost);
consider_xroute(&xroutes[i]);
}
}
}
void
uninstall_route(struct route *route)
{
int i, rc;
if(!route->installed)
return;
for(i = 0; i < numxroutes; i++) {
if(xroutes[i].installed && xroutes[i].gateway == route->dest)
uninstall_xroute(&xroutes[i]);
}
rc = kernel_route(0, route->dest->address, 128,
route->nexthop->address,
route->nexthop->network->ifindex,
metric_to_kernel(route->metric));
if(rc < 0)
perror("kernel_route(0)");
route->installed = 0;
}
int
route_feasible(struct route *route)
{
if(route->dest->time < now.tv_sec - 200) {
/* Never mind what is probably stale data */
return 1;
}
return
seqno_compare(route->dest->seqno, route->seqno) < 0 ||
(route->dest->seqno == route->seqno &&
route->refmetric < route->dest->metric);
}
int
update_feasible(unsigned char seqno, unsigned short metric,
struct destination *dest, struct route *route)
{
if(route) {
/* This allows us to keep unfeasible routes around in order to
keep statistics about their stability. */
if(seqno_compare(route->seqno, seqno) < 0 ||
(route->seqno == seqno && metric <= route->refmetric))
return 1;
}
if(seqno_compare(dest->seqno, seqno) < 0 ||
(dest->seqno == seqno && metric < dest->metric))
return 1;
return 0;
}
struct route *
find_best_route(struct destination *dest)
{
struct route *route = NULL;
int i;
for(i = 0; i < numroutes; i++) {
if(routes[i].dest != dest)
continue;
if(routes[i].time < now.tv_sec - 180)
continue;
if(routes[i].metric >= INFINITY)
continue;
if(!route_feasible(&routes[i]))
continue;
if(route && route->metric + 512 >= routes[i].metric) {
if(route->origtime <= now.tv_sec - 30 &&
routes[i].origtime >= now.tv_sec - 30)
continue;
if(route->metric < routes[i].metric)
continue;
if(route->origtime > routes[i].origtime)
continue;
}
route = &routes[i];
}
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)
{
int metric, oldmetric, install = 0;
metric = MIN(route->refmetric + neighbour_symmetric_cost(route->nexthop),
INFINITY);
if(route->installed &&
(metric >= INFINITY ||
metric_to_kernel(route->metric) != metric_to_kernel(metric))) {
uninstall_route(route);
install = 1;
}
oldmetric = route->metric;
route->metric = metric;
if(install)
route = reinstall_route(route);
if(route) {
if(route->installed) {
struct route *better_route;
better_route = find_best_route(route->dest);
if(better_route->metric < route->metric - 196)
consider_route(better_route);
else
send_triggered_update(route, oldmetric);
} else {
consider_route(route);
}
}
}
struct route *
update_route(const unsigned char *d, int seqno, int refmetric,
struct neighbour *nexthop,
struct xroute *pxroutes, int numpxroutes)
{
struct route *route;
struct destination *dest;
int i, metric;
if(d[0] == 0xFF || d[0] == 0) {
fprintf(stderr, "Ignoring martian route.\n");
return NULL;
}
dest = find_destination(d, 1, seqno);
if(dest == NULL)
return NULL;
metric = MIN(refmetric + neighbour_symmetric_cost(nexthop), INFINITY);
route = find_route(d, nexthop);
if(!update_feasible(seqno, metric, dest, route))
return NULL;
else
debugf("Rejecting unfeasible update.\n");
if(route) {
int oldseqno;
int oldmetric;
if(refmetric >= INFINITY) {
flush_route(route);
return NULL;
}
oldseqno = route->seqno;
oldmetric = route->metric;
route->time = now.tv_sec;
tweak_route(route, seqno, refmetric, metric);
if(seqno_compare(oldseqno, seqno) <= 0) {
if(seqno_compare(oldseqno, seqno) < 0) {
/* Flush retracted xroutes */
flush_xroutes(dest, pxroutes, numpxroutes);
}
for(i = 0; i < numpxroutes; i++)
update_xroute(pxroutes[i].prefix,
pxroutes[i].plen,
dest,
pxroutes[i].cost);
}
if(route->installed)
send_triggered_update(route, oldmetric);
else
consider_route(route);
} else {
if(refmetric >= INFINITY)
/* Somebody's retracting a route we never saw. */
return NULL;
if(numroutes >= MAXROUTES) {
fprintf(stderr, "Too many routes -- ignoring update.\n");
return NULL;
}
route = &routes[numroutes];
route->dest = dest;
route->refmetric = refmetric;
route->seqno = seqno;
route->metric = metric;
route->nexthop = nexthop;
route->time = now.tv_sec;
route->origtime = now.tv_sec;
route->installed = 0;
numroutes++;
for(i = 0; i < numpxroutes; i++)
update_xroute(pxroutes[i].prefix,
pxroutes[i].plen,
dest,
pxroutes[i].cost);
consider_route(route);
}
return route;
}
void
consider_route(struct route *route)
{
struct route *installed;
if(route->installed)
return;
if(route->metric >= INFINITY || !route_feasible(route))
return;
installed = find_installed_route(route->dest);
if(installed == NULL)
goto install;
if(installed->metric >= route->metric + 576)
goto install;
if(route->origtime >= now.tv_sec - 30)
return;
if(installed->metric >= route->metric + 96)
goto install;
return;
install:
install_route(route);
if(installed)
send_triggered_update(route, installed->metric);
else
send_update(route->dest, NULL);
return;
}
void
tweak_route(struct route *route, int newseqno, int newrefmetric, int newmetric)
{
int install = 0;
if(route->installed &&
(newmetric >= INFINITY ||
metric_to_kernel(route->metric) != metric_to_kernel(newmetric))) {
uninstall_route(route);
install = 1;
}
route->seqno = newseqno;
route->metric = newmetric;
route->refmetric = newrefmetric;
if(install)
reinstall_route(route);
}
void
send_triggered_update(struct route *route, int oldmetric)
{
if(!route->installed)
return;
if(route->metric - oldmetric >= 256 || oldmetric - route->metric >= 256)
send_update(route->dest, NULL);
}
/*
Copyright (c) 2007 by Juliusz Chroboczek
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
struct xroute;
struct route {
struct destination *dest;
unsigned short metric;
unsigned short refmetric;
unsigned char seqno;
struct neighbour *nexthop;
int time;
int origtime;
int installed;
};
extern struct route routes[MAXROUTES];
extern int numroutes;
extern int kernel_metric;
struct route *find_route(const unsigned char *dest, struct neighbour *nexthop);
struct route *find_installed_route(struct destination *dest);
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);
int route_feasible(struct route *route);
int update_feasible(unsigned char seqno, unsigned short metric,
struct destination *dest, struct route *route);
struct route *find_best_route(struct destination *dest);
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);
void consider_route(struct route *route);
void tweak_route(struct route *route,
int newseqno, int newrefmetric, int newmetric);
void send_triggered_update(struct route *route, int oldmetric);
/*
Copyright (c) 2007 by Juliusz Chroboczek
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <sys/time.h>
#include <time.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include "ariadne.h"
int
seqno_compare(unsigned char s1, unsigned char s2)
{
if(s1 == s2)
return 0;
else if(((s2 - s1) & 0xFF) < 128)
return -1;
else
return 1;
}
int
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
return ((s1 - s2) & 0xFF);
}
void
timeval_minus(struct timeval *d,
const struct timeval *s1, const struct timeval *s2)
{
if(s1->tv_usec > s2->tv_usec) {
d->tv_usec = s1->tv_usec - s2->tv_usec;
d->tv_sec = s1->tv_sec - s2->tv_sec;
} else {
d->tv_usec = s1->tv_usec + 1000000 - s2->tv_usec;
d->tv_sec = s1->tv_sec - s2->tv_sec - 1;
}
}
int
timeval_minus_msec(const struct timeval *s1, const struct timeval *s2)
{
return (s1->tv_sec - s2->tv_sec) * 1000 +
(s1->tv_usec - s2->tv_usec) / 1000;
}
int
timeval_compare(const struct timeval *s1, const struct timeval *s2)
{
if(s1->tv_sec < s2->tv_sec)
return -1;
else if(s1->tv_sec > s2->tv_sec)
return 1;
else if(s1->tv_usec < s2->tv_usec)
return -1;
else if(s1->tv_usec > s2->tv_usec)
return 1;
else
return 0;
}
/* {0, 0} represents infinity */
void
timeval_min(struct timeval *d, const struct timeval *s)
{
if(s->tv_sec == 0)
return;
if(d->tv_sec == 0 || timeval_compare(d, s) > 0) {
*d = *s;
}
}
void
timeval_min_sec(struct timeval *d, int secs)
{
if(d->tv_sec == 0 || d->tv_sec > secs) {
d->tv_sec = secs;
d->tv_usec = random() % 1000000;
}
}
void
do_debugf(const char *format, ...)
{
va_list args;
va_start(args, format);
if(debug >= 2)
vfprintf(stderr, format, args);
va_end(args);
fflush(stderr);
}
const char *
format_address(const unsigned char *address)
{
static char buf[4][INET6_ADDRSTRLEN];
static int i = 0;
i = (i + 1) % 4;
return inet_ntop(AF_INET6, address, buf[i], INET6_ADDRSTRLEN);
}
int
parse_address(const char *address, unsigned char *addr_r)
{
struct in6_addr ina6;
int rc;
rc = inet_pton(AF_INET6, address, &ina6);
if(rc > 0) {
memcpy(addr_r, &ina6, 16);
return 0;
}
return -1;
}
int
parse_net(const char *net, unsigned char *prefix_r, unsigned short *plen_r)
{
char buf[INET6_ADDRSTRLEN];
char *slash, *end;
unsigned char prefix[16];
unsigned short plen;
int rc;
if(strcmp(net, "default") == 0) {
memset(prefix, 0, 16);
plen = 0;
} else {
slash = strchr(net, '/');
if(slash == NULL) {
rc = parse_address(net, prefix);
if(rc < 0)
return rc;
plen = 128;
} else {
if(slash - net >= INET6_ADDRSTRLEN)
return -1;
memcpy(buf, net, slash - net);
buf[slash - net] = '\0';
rc = parse_address(buf, prefix);
if(rc < 0)
return rc;
plen = strtol(slash + 1, &end, 0);
if(*end != '\0')
return -1;
}
}
memcpy(prefix_r, prefix, 16);
*plen_r = plen;
return 0;
}
/*
Copyright (c) 2007 by Juliusz Chroboczek
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
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)
ATTRIBUTE ((const));
int seqno_minus(unsigned char s1, unsigned char 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));
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)));
const char *format_address(const unsigned char *address);
int parse_address(const char *address, unsigned char *addr_r);
int parse_net(const char *net, unsigned char *prefix_r, unsigned short *plen_r);
/* 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... */
#if defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L
#define debugf(...) \
do { \
if(debug >= 2) do_debugf(__VA_ARGS__); \
} while(0)
#elif defined(__GNUC__)
#define debugf(_args...) \
do { \
if(debug >= 2) do_debugf(_args); \
} while(0)
#else
#define debugf do_debugf
#endif
/*
Copyright (c) 2007 by Juliusz Chroboczek
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include "ariadne.h"
#include "kernel.h"
#include "neighbour.h"
#include "route.h"
#include "xroute.h"
struct xroute xroutes[MAXXROUTES];
int numxroutes = 0;
struct xroute myxroutes[MAXMYXROUTES];
int nummyxroutes = 0;
static struct xroute *
find_installed_xroute(unsigned char *prefix, unsigned short plen)
{
int i;
for(i = 0; i < numxroutes; i++) {
if(xroutes[i].plen == plen &&
memcmp(xroutes[i].prefix, prefix, 16) == 0) {
return &xroutes[i];
}
}
return NULL;
}
static struct xroute *
find_best_xroute(unsigned char *prefix, unsigned short plen)
{
struct xroute *xroute = NULL;
int i;
for(i = 0; i < numxroutes; i++) {
if(xroutes[i].plen == plen &&
memcmp(xroutes[i].prefix, prefix, 16) == 0) {
if((!xroute || xroutes[i].metric < xroute->metric) &&
find_installed_route(xroutes[i].gateway) != NULL)
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)
return;
if(xroute->plen >= 8 &&
(xroute->prefix[0] == 0 || xroute->prefix[0] == 0xFF)) {
fprintf(stderr, "Attempted to install martian xroute.\n");
return;
}
gwroute = find_installed_route(xroute->gateway);
if(!gwroute) {
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(1, xroute->prefix, xroute->plen,
gwroute->nexthop->address,
gwroute->nexthop->network->ifindex,
metric_to_kernel(xroute->metric));
if(rc < 0) {
perror("kernel_route(1)");
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(0, xroute->prefix, xroute->plen,
gwroute->nexthop->address,
gwroute->nexthop->network->ifindex,
metric_to_kernel(xroute->metric));
if(rc < 0)
perror("kernel_route(0)");
xroute->installed = 0;
}
void
consider_xroute(struct xroute *xroute)
{
struct xroute *installed;
if(xroute->installed)
return;
if(xroute->metric >= INFINITY)
return;
if(find_installed_route(xroute->gateway) == NULL)
return;
installed = find_installed_xroute(xroute->prefix, xroute->plen);
if(!installed || installed->metric >= xroute->metric)
install_xroute(xroute);
}
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_xroutes(struct destination *gateway,
const struct xroute *except, int numexcept)
{
int i, j;
i = 0;
while(i < numxroutes) {
if(xroutes[i].gateway == gateway) {
for(j = 0; j < numexcept; j++) {
if(memcmp(xroutes[i].prefix, except[j].prefix, 16) == 0 &&
xroutes[i].plen == except[j].plen)
goto skip;
}
flush_xroute(&xroutes[i]);
continue;
}
skip:
i++;
}
}
struct xroute *
update_xroute(const unsigned char *prefix, unsigned short plen,
struct destination *gateway, int cost)
{
int i;
struct xroute *xroute = NULL;
struct route *gwroute;
if(prefix[0] == 0xFF || (plen >= 8 && prefix[0] == 0)) {
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 &&
memcmp(xroute->prefix, prefix, 16) == 0 && xroute->plen == plen) {
update_xroute_metric(xroute, cost);
xroute->time = now.tv_sec;
return xroute;
}
}
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->cost = cost;
xroute->metric =
gwroute ? MIN(gwroute->metric + cost, INFINITY) : INFINITY;
xroute->time = now.tv_sec;
xroute->installed = 0;
numxroutes++;
if(gwroute)
install_xroute(xroute);
return xroute;
}
void
update_xroute_metric(struct xroute *xroute, int cost)
{
struct route *gwroute;
gwroute = find_installed_route(xroute->gateway);
if(!gwroute)
gwroute = find_best_route(xroute->gateway);
if(!gwroute)
return;
if(xroute->cost != cost || xroute->metric != gwroute->metric + cost) {
int install = 0;
if(xroute->installed) {
uninstall_xroute(xroute);
install = 1;
}
xroute->cost = cost;
xroute->metric = gwroute->metric + cost;
if(install) {
struct xroute *best;
best = find_best_xroute(xroute->prefix, xroute->plen);
if(best)
install_xroute(best);
}
}
}
/*
Copyright (c) 2007 by Juliusz Chroboczek
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
struct route;
struct xroute {
unsigned char prefix[16];
unsigned short plen;
struct destination *gateway;
int cost;
int metric;
int time;
int installed;
};
extern struct xroute xroutes[MAXXROUTES];
extern int numxroutes;
extern struct xroute myxroutes[MAXMYXROUTES];
extern int nummyxroutes;
void install_xroute(struct xroute *xroute);
void uninstall_xroute(struct xroute *xroute);
void consider_xroute(struct xroute *xroute);
void flush_xroute(struct xroute *xroute);
void flush_xroutes(struct destination *gateway,
const struct xroute *except, int numexcept);
struct xroute * update_xroute(const unsigned char *prefix, unsigned short plen,
struct destination *gateway, int cost);
void update_xroute_metric(struct xroute *xroute, int cost);
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