Commit a1b68b4d authored by Jondy Zhao's avatar Jondy Zhao

Add snapshot of babeld-1.3.4

parent 9f9ea659
8 August 2012: babeld-1.3.4
* Disable atomic route changes on Linux; this used to cause stuck
unreachable routes on non-multipath kernels.
* Improve error checking in command-line and configuration parser.
12 July 2012: babeld-1.3.3
* More fixes to IPv4 support on BSD -- pure meshes are now
supported.
* Fixed a very rare bug where an unfeasible route could be
selected.
30 June 2012: babeld-1.3.2
* INCOMPATIBLE CHANGE: removed parasitic mode (-P).
* Fixes to IPv4 support on BSD.
* More reduction to the rate of sending requests.
11 February 2012: babeld-1.3.1
* Made the resend logic less aggressive. This should lead to fewer
request messages upon link failure, at the cost of somewhat worse
behaviour in the presence of heavy packet loss.
* INCOMPATIBLE CHANGE: removed the idle detection functionality (-i).
This feature was little used and complicated the code somewhat.
* Various internal tweaks to bring babeld closer to the Quagga version.
8 December 2011: babeld-1.3.0
* Made the route table into a sorted array, and use binary sort for
searching for routes. This makes most route operations O(log n), at
a slight cost in memory usage.
* Changed the update sending strategy to use buffers large enough for
a full update. This makes the duplicate suppression mechanism
effective in large networks, at a small cost in memory usage.
* Rate-limit the reaction to wildcard requests. This avoids an update
storm at boot in large networks.
* Fixed a bug that prevented usage of the "default" keyword in
configuration files.
16 October 2011: babeld-1.2.1
* Fixed an incorrect assertion that would cause a crash when -w was
being used (reported by Thomas McLure).
9 September 2011: babeld 1.2.0
* Merged the interference-aware branch ("babelz"). Please see the
"-z" flag in the manual page.
* Fixed a memory leak when expiring resent messages.
* Fixed a buffer overflow when parsing MAC addresses (Matthieu Boutier).
* Implemented MAC address parsing for BSD (Matthieu Boutier).
27 August 2011: babeld 1.1.4
* Change the default port number to 6696, as allocated by IANA.
3 August 2011: babeld 1.1.3
* Implemented an option -u to keep unfeasible routes; this is useful
for giving more data to front-end interfaces.
* Fixed a number of minor bugs in the front-end interface.
* Fixed incorrect handling of interfaces with multiple link-local
addresses (thanks to Matthieu Boutier).
27 July 2011: babeld 1.1.2:
* Changed the strategy used to tweak an installed route in a way that
should avoid packet loss (thanks to Dave Taht).
* Fixed the handling of duplicate interface definitions in the config
file (thanks to Matthieu Boutier).
16 May 2011: babeld 1.1.1:
* Fixed two bugs in the message parser that could cause IPv4 updates to
get lost.
* Fixed a bug in the monitoring interface that could cause route ids
to change (thanks to Gabriel Kerneis).
* INCOMPATIBLE CHANGE: the default wired hello interval is now 4 seconds.
* Ported to Bionic libc.
30 January 2011: babeld 1.1.0:
* INCOMPATIBLE CHANGE: the UDP port number and multicast group have
been changed to be the ones allocated by IANA.
* Initial port to OpenBSD, by Vincent Gross.
1 October 2010: babeld 1.0.2:
* Worked around a gcc bug that would cause assertion failures on MIPS.
2 May 2010: babeld 1.0.1:
* Fixed a bug that could cause input filters to be ignored.
22 April 2010: babeld 1.0:
* Minor portability fixes.
8 February 2010: babeld 0.98:
* Implement the ability to prefer Babel routes to external routes
according to the kernel priority (-A).
* Implement the ability to redistribute "boot" routes when the protocol
is explicitly specified on the "redistribute" line.
* Allow trailing whitespace in config file.
5 November 2009: babeld 0.97:
* INCOMPATIBLE CHANGE: rename babel.{conf,log} to babeld.*.
* Use getopt for parsing command-line options.
11 August 2009: babeld 0.96
* Renamed babel to babeld.
* Routes are now automatically flushed when an interface goes down or an
IPv4 address changes, which avoids desynchronisation between Babel and
the kernel.
21 April 2009: babel 0.95
* Fixed a bug that broke link-quality estimation, and could cause
severe instability when we had both good and marginal neighbours.
* We now send retractions after a redistributed route is retracted.
* Fixed a bug that could cause reliable messages (retractions and
router-id switches) to only be sent twice.
* We no longer obey a silent time at startup, instead sending a bunch of
retractions. The silent time is inconvenient, but seldom useful.
* Updates for routes to self are now sent together with other updates
(they used to be sent more frequently).
* Fixes the configuration parser to interpret hello-interval as a number
of seconds, as specified in the documentation (it used to be interpreted
as a number of milliseconds).
* INCOMPATIBLE CHANGE: the update interval is now a per-interface value,
may be configured manually in the configuraton file, and defaults to
4 times the hello interval. The -u flag is gone.
10 April 2009: babel 0.94
* Fixed a bug introduced in 0.17 that caused recently retracted routes to
remain until the routing table entry was flushed.
* Implemented per-interface configuration of parameters such as link
cost, hello interval etc. The command-line flags are now only used to
set defaults.
15 March 2009: babel 0.93
* No longer update seqno periodically, rely on explicit seqno requests.
21 January 2009: babel 0.92
* Fixed a bug that could cause a crash if an interface was repeatedly
brought down and then back up.
* Implemented some protection against time stepping when POSIX clocks are
not available.
10 November 2008: babel 0.91
* Maintain buffered updates per-interface, which makes multi-interface
nodes significantly less noisy.
* Changed the strategy for dealing with unfeasible routes to be slightly
more generous while still avoiding loops.
* Fixed a bug that would cause multi-hop requests to be spuriously resent.
* Made a number of micro-optimisations throughout.
23 October 2008: babel 0.90
* INCOMPATIBLE CHANGE: all new Babel version 2 protocol, which is both
more robust and less chatty than version 1.
* Tweaked the strategies for sending triggered updates and unfeasible
requests to be more conservative.
* Minor optimisations all over the place.
* Removed the protocol specification -- the version 2 spec is maintained
separately.
18 October 2008: babel 0.17
* INCOMPATIBLE CHANGE: removed support for ``inherit'' in redistribution.
* INCOMPATIBLE CHANGE: a pidfile is now created by default.
* Increased the default seqno interval.
* Use a fixed kernel priority for routes installed by babel.
29 September 2008: babel 0.16
* Tweaked cost computation to be slightly slower.
* Implemented a local interface for GUIs.
* INCOMPATIBLE CHANGE: the -X command-line option is no more.
8 July 2008: babel 0.15
* Fixed a bug that could break link-quality estimation on yo-yo links.
* Protect against duplicate neighbour ids on the same interface.
* More tweaks to improve scaling with the number of kernel routes.
* Tweaked the default update interval.
1 July 2008: babel 0.14
* Use POSIX clocks if available to protect against clock stepping.
* Made babel use available internal routes straight away when the
set of redistributed routes changes.
* Lifted the arbitrary limit on the number of kernel routes.
* Changed the routing metric used on wireless links to plain ETX.
* Bridges are now automatically detected and treated as potential
wireless interfaces.
* Reduced the default hello interval.
24 May 2008: babel 0.13
* Removed all arbitrary limits (interfaces, neighbours, routes,
xroutes and sources).
* Fixed a bug that prevented expiration of stale sources.
* Updated the kernel interface to work with recent Linux kernels.
* More tweaks to the order in which updates are sent.
7 April 2008: babel 0.12
* Retractions are now sent multiple times, which should speed up
convergence in presence of packet loss.
* Optimised the sending of updates to make them smaller.
* Don't forward requests multiple times; this should reduce the
noise due to requests with no increase in convergence time.
* Fixed a bug that could cause a crash when resending requests.
* Added some protection against clock stepping.
29 March 2008: babel 0.11
* Implemented sub-second hello and update intervals.
* Fixed a bug that could prevent the best route from being selected
for extended periods of time.
* Implemented protection against out-of-date requests being sent and
forwarded when a node loses its sequence number.
* INCOMPATIBLE CHANGE: reduced the cost of wired networks down to 96
from 128.
* Tweaked the frequency at which a router's seqno increases, to make
it more likely that a feasible route will be available when needed.
* Implemented garbage collection of old sources.
* Implemented coalescing of unicast messages.
* Fixed a bug that could cause a crash when a link's MTU changes.
* Fixed a bug that could delay noticing that a network is no longer
idle when running Babel with the -i flag.
* Fixed a bug that could cause incorrect metrics to be advertised
when output filtering was used.
* Fixed a bug that could cause incorrect link costs to be computed when
a neighbour reduces its hello interval.
* Fixed some minor issues with the ordering of outgoing messages.
11 March 2008: babel 0.10
* Implemented the ability to automatically export local addresses (see
the ``local'' keyword in redistribute specifications). This should
avoid the need to explicitly specify -X on the command line
(Julien Cristau and Juliusz Chroboczek).
* INCOMPATIBLE CHANGE: local routes (local interface addresses) are
now exported by default. Specify ``redistribute local deny'' to
avoid that.
* Babel will now automatically choose a router id if none is
specified on the command line.
* Automatically adapt to interfaces appearing or disappearing at runtime,
as is usually the case when running over tunnels or VPNs.
* Changed the link quality computation algorithm to not discard very
lossy links.
* Multi-hop requests will now be forwarded to an unfeasible successor
under some circumstances.
* Send multi-hop requests more aggressively.
* Send requests for a new seqno upon receiving an unfeasible update
if it's better than what we have.
* No longer consider the age of routes in route selection.
* Added ability to run as a daemon.
14 February 2008: babel 0.9
* Implemented a proper configuration language to specify input and
output filters and redistribution policies.
* INCOMPATIBLE CHANGE: the flags -4, -x and -c are no longer supported.
8 February 2008: babel 0.8
* Babel will now automatically check for interfaces' up/down status,
IPv4 address, and optionally for carrier sense.
* Implemented the -w option, which disables all optimisations for
wired interfaces.
* Implemented support for non-default routing tables.
* Fixed a bug that could spuriously remove IPv4 routes (thanks to
Julien Cristau).
3 January 2008: babel 0.7
* Implemented support for IPv4.
* Fixed sending of unicast requests.
* Don't send poison when receiving a request for an unknown route.
* Basic filtering infrastructure.
* Removed support for broadcast IHU.
* Changed the behaviour of -d.
16 October 2007: babel 0.6
* Implemented resending of unsatisfied requests, with exponential backoff.
* Fixed a potential crash in the request handling code.
* Send IHUs more aggressively.
9 October 2007: babel 0.5
* Implemented forwarding of requests and replies.
* Fixed a bug that prevented requests from being parsed correctly.
* Fixed a bug that prevented IHU intervals from being sent.
* Respect reboot_time even after an id change.
* Deal with neighbours rebooting and losing their hello seqno when
computing link quality.
23 September 2007: babel 0.4
* Fixed incorrect expiration of old sources. This could prevent
convergence in some cases.
16 September 2007: babel 0.3
* Fixes to Mac OS X support (Grégoire Henry).
29 August 2007: babel 0.2
* Made jitter computation depend on how urgent a given message is.
This dramatically improves convergence speed, without increasing
network load.
* Fixed a bug that prevented neighbour associations from being
discarded at shutdown.
22 August 2007: babel 0.1
* Initial public release.
Copyright (c) 2007, 2008 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)
LDLIBS = -lrt
SRCS = babeld.c net.c kernel.c util.c interface.c source.c neighbour.c \
route.c xroute.c message.c resend.c configuration.c local.c
OBJS = babeld.o net.o kernel.o util.o interface.o source.o neighbour.o \
route.o xroute.o message.o resend.o configuration.o local.o
babeld: $(OBJS)
$(CC) $(CFLAGS) $(LDFLAGS) -o babeld $(OBJS) $(LDLIBS)
.SUFFIXES: .man .html
.man.html:
rman -f html $< | \
sed -e "s|<a href='babeld.8'|<a href=\"babeld.html\"|" \
-e "s|<a href='\\(ahcp[-a-z]*\\).8'|<a href=\"../ahcp/\1.html\"|" \
-e "s|<a href='[^']*8'>\\(.*(8)\\)</a>|\1|" \
> $@
babeld.html: babeld.man
.PHONY: all install install.minimal uninstall clean
all: babeld babeld.man
install.minimal: babeld
-rm -f $(TARGET)$(PREFIX)/bin/babeld
mkdir -p $(TARGET)$(PREFIX)/bin
cp -f babeld $(TARGET)$(PREFIX)/bin
install: install.minimal all
mkdir -p $(TARGET)$(PREFIX)/man/man8
cp -f babeld.man $(TARGET)$(PREFIX)/man/man8/babeld.8
uninstall:
-rm -f $(TARGET)$(PREFIX)/bin/babeld
-rm -f $(TARGET)$(PREFIX)/man/man8/babeld.8
clean:
-rm -f babeld babeld.html *.o *~ core TAGS gmon.out
kernel.o: kernel_netlink.c kernel_socket.c
Babel
=====
Babel is a loop-avoiding distance-vector routing protocol roughly
based on HSDV and AODV, but with provisions for link cost estimation
and redistribution of routes from other routing protocols.
Installation
============
$ make
$ su -c 'make install'
If compiling for OpenWRT, you will probably want to say something like
$ make CC=mipsel-linux-gcc PLATFORM_DEFINES='-march=mips32'
On Mac OS X, you'll need to do
$ make LDLIBS=''
Setting up a network for use with Babel
=======================================
1. Set up every node's interface
--------------------------------
On every node, set up the wireless interface:
# iwconfig eth1 mode ad-hoc channel 11 essid "my-mesh-network"
# ip link set up dev eth1
2. Set up every node's IP addresses
-----------------------------------
You will need to make sure that all of your nodes have a unique IPv6
address, and/or a unique IPv4 address.
On every node, run something like:
# ip addr add 192.168.13.33/32 dev eth1
# ip -6 addr add $(generate-ipv6-address -r)/128 dev eth1
You will find the generate-ipv6-address utility, which can generate random
IPv6 addresses according to RFC 4193, on
http://www.pps.jussieu.fr/~jch/software/files/
A note about tunnels and VPNs
-----------------------------
Some VPN implementations (notably OpenVPN and Linux GRE) do not
automatically add an IPv6 link-local address to the tunnel interface.
If you attempt to run Babel over such an interface, it will complain
that it ``couldn't allocate requested address''.
The solution is to manually add the link-local address to the
interface. This can be done by running e.g.
# ip -6 addr add $(ahcp-generate-address fe80::) dev gre0
3. Start the routing daemon
---------------------------
Run Babel on every node, specifying the set of interfaces that it
should consider:
# babeld eth1
If your node has multiple interfaces which you want to participate in
the Babel network, just list them all:
# babeld eth0 eth1 sit1
4. Setting up an Internet gateway
---------------------------------
If you have one or more Internet gateways on your mesh network, you
will want to set them up so that they redistribute the default route.
Babel will only redistribute routes with an explicit protocol
attached, so you must say something like:
# ip route add 0.0.0.0/0 via 1.2.3.4 dev eth0 proto static
In order to redistribute all routes, you will say:
# babeld -C 'redistribute metric 128' eth1
You may also be more selective in the routes you redistribute, for
instance by specifying the interface over which the route goes out:
# babeld -C 'redistribute if eth0 metric 128' eth1
or by constraining the prefix length:
# babeld -C 'redistribute ip ::/0 le 64 metric 128' \
-C 'redistribute ip 0.0.0.0/0 le 28 metric 128' \
eth1
You may also want to constrain which local routes (routes to local
interface addresses) you advertise:
# babeld -C 'redistribute local if eth1' -C 'redistribute local deny' \
-C 'redistribute metric 128' \
eth1
If you find all of this too complicated and error-prone (as I do), you
may want to consider autoconfiguring your routing domain using AHCP:
http://www.pps.jussieu.fr/~jch/software/ahcp/
-- Juliusz Chroboczek
/*
Copyright (c) 2007, 2008 by Juliusz Chroboczek
Copyright (c) 2010 by Vincent Gross
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 "babeld.h"
#include "util.h"
#include "net.h"
#include "kernel.h"
#include "interface.h"
#include "source.h"
#include "neighbour.h"
#include "route.h"
#include "xroute.h"
#include "message.h"
#include "resend.h"
#include "configuration.h"
#include "local.h"
struct timeval now;
unsigned char myid[8];
int debug = 0;
int link_detect = 0;
int all_wireless = 0;
int default_wireless_hello_interval = -1;
int default_wired_hello_interval = -1;
int resend_delay = -1;
int do_daemonise = 0;
char *logfile = NULL, *pidfile = "/var/run/babeld.pid";
unsigned char *receive_buffer = NULL;
int receive_buffer_size = 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";
int protocol_port;
unsigned char protocol_group[16];
int protocol_socket = -1;
int kernel_socket = -1;
static int kernel_routes_changed = 0;
static int kernel_link_changed = 0;
static int kernel_addr_changed = 0;
struct timeval check_neighbours_timeout;
static volatile sig_atomic_t exiting = 0, dumping = 0, reopening = 0;
int local_server_socket = -1, local_socket = -1;
int local_server_port = -1;
static int kernel_routes_callback(int changed, void *closure);
static void init_signals(void);
static void dump_tables(FILE *out);
static int reopen_logfile(void);
int
main(int argc, char **argv)
{
struct sockaddr_in6 sin6;
int rc, fd, i, opt;
time_t expiry_time, source_expiry_time, kernel_dump_time;
char *config_file = NULL;
void *vrc;
unsigned int seed;
struct interface *ifp;
gettime(&now);
rc = read_random_bytes(&seed, sizeof(seed));
if(rc < 0) {
perror("read(random)");
seed = 42;
}
seed ^= (now.tv_sec ^ now.tv_usec);
srandom(seed);
parse_address("ff02:0:0:0:0:0:1:6", protocol_group, NULL);
protocol_port = 6696;
while(1) {
opt = getopt(argc, argv, "m:p:h:H:i:k:A:PsuS:d:g:lwz:t:T:c:C:DL:I:");
if(opt < 0)
break;
switch(opt) {
case 'm':
rc = parse_address(optarg, protocol_group, NULL);
if(rc < 0)
goto usage;
if(protocol_group[0] != 0xff) {
fprintf(stderr,
"%s is not a multicast address\n", optarg);
goto usage;
}
if(protocol_group[1] != 2) {
fprintf(stderr,
"Warning: %s is not a link-local multicast address\n",
optarg);
}
break;
case 'p':
protocol_port = parse_nat(optarg);
if(protocol_port <= 0 || protocol_port > 0xFFFF)
goto usage;
break;
case 'h':
default_wireless_hello_interval = parse_msec(optarg);
if(default_wireless_hello_interval <= 0 ||
default_wireless_hello_interval > 0xFFFF * 10)
goto usage;
break;
case 'H':
default_wired_hello_interval = parse_msec(optarg);
if(default_wired_hello_interval <= 0 ||
default_wired_hello_interval > 0xFFFF * 10)
goto usage;
break;
case 'k':
kernel_metric = parse_nat(optarg);
if(kernel_metric < 0 || kernel_metric > 0xFFFF)
goto usage;
break;
case 'A':
allow_duplicates = parse_nat(optarg);
if(allow_duplicates < 0 || allow_duplicates > 0xFFFF)
goto usage;
break;
case 's':
split_horizon = 0;
break;
case 'u':
keep_unfeasible = 1;
break;
case 'S':
state_file = optarg;
break;
case 'd':
debug = parse_nat(optarg);
if(debug < 0)
goto usage;
break;
case 'g':
#ifdef NO_LOCAL_INTERFACE
fprintf(stderr, "Warning: no local interface in this version.\n");
#else
local_server_port = parse_nat(optarg);
if(local_server_port <= 0 || local_server_port > 0xFFFF)
goto usage;
#endif
break;
case 'l':
link_detect = 1;
break;
case 'w':
all_wireless = 1;
break;
case 'z':
{
char *comma;
diversity_kind = (int)strtol(optarg, &comma, 0);
if(*comma == '\0')
diversity_factor = 128;
else if(*comma == ',')
diversity_factor = parse_nat(comma + 1);
else
goto usage;
if(diversity_factor <= 0 || diversity_factor > 256)
goto usage;
}
break;
case 't':
export_table = parse_nat(optarg);
if(export_table < 0 || export_table > 0xFFFF)
goto usage;
break;
case 'T':
import_table = parse_nat(optarg);
if(import_table < 0 || import_table > 0xFFFF)
goto usage;
break;
case 'c':
config_file = optarg;
break;
case 'C':
rc = parse_config_from_string(optarg);
if(rc < 0) {
fprintf(stderr,
"Couldn't parse configuration from command line.\n");
exit(1);
}
break;
case 'D':
do_daemonise = 1;
break;
case 'L':
logfile = optarg;
break;
case 'I':
pidfile = optarg;
break;
default:
goto usage;
}
}
if(!config_file) {
if(access("/etc/babeld.conf", F_OK) >= 0)
config_file = "/etc/babeld.conf";
}
if(config_file) {
rc = parse_config_from_file(config_file);
if(rc < 0) {
fprintf(stderr,
"Couldn't parse configuration from file %s.\n",
config_file);
exit(1);
}
} else {
if(access("/etc/babel.conf", F_OK) >= 0)
fprintf(stderr,
"Warning: /etc/babel.conf exists, it will be ignored.\n");
}
if(default_wireless_hello_interval <= 0)
default_wireless_hello_interval = 4000;
default_wireless_hello_interval = MAX(default_wireless_hello_interval, 5);
if(default_wired_hello_interval <= 0)
default_wired_hello_interval = 4000;
default_wired_hello_interval = MAX(default_wired_hello_interval, 5);
resend_delay = 2000;
resend_delay = MIN(resend_delay, default_wireless_hello_interval / 2);
resend_delay = MIN(resend_delay, default_wired_hello_interval / 2);
resend_delay = MAX(resend_delay, 20);
if(do_daemonise) {
if(logfile == NULL)
logfile = "/var/log/babeld.log";
}
rc = reopen_logfile();
if(rc < 0) {
perror("reopen_logfile()");
exit(1);
}
fd = open("/dev/null", O_RDONLY);
if(fd < 0) {
perror("open(null)");
exit(1);
}
rc = dup2(fd, 0);
if(rc < 0) {
perror("dup2(null, 0)");
exit(1);
}
close(fd);
if(do_daemonise) {
rc = daemonise();
if(rc < 0) {
perror("daemonise");
exit(1);
}
}
if(pidfile && pidfile[0] != '\0') {
int pfd, len;
char buf[100];
len = snprintf(buf, 100, "%lu", (unsigned long)getpid());
if(len < 0 || len >= 100) {
perror("snprintf(getpid)");
exit(1);
}
pfd = open(pidfile, O_WRONLY | O_CREAT | O_EXCL, 0644);
if(pfd < 0) {
char buf[40];
snprintf(buf, 40, "creat(%s)", pidfile);
buf[39] = '\0';
perror(buf);
exit(1);
}
rc = write(pfd, buf, len);
if(rc < len) {
perror("write(pidfile)");
goto fail_pid;
}
close(pfd);
}
rc = kernel_setup(1);
if(rc < 0) {
fprintf(stderr, "kernel_setup failed.\n");
goto fail_pid;
}
rc = kernel_setup_socket(1);
if(rc < 0) {
fprintf(stderr, "kernel_setup_socket failed.\n");
kernel_setup(0);
goto fail_pid;
}
rc = finalise_config();
if(rc < 0) {
fprintf(stderr, "Couldn't finalise configuration.\n");
goto fail;
}
for(i = optind; i < argc; i++) {
vrc = add_interface(argv[i], NULL);
if(vrc == NULL)
goto fail;
}
if(interfaces == NULL) {
fprintf(stderr, "Eek... asked to run on no interfaces!\n");
goto fail;
}
FOR_ALL_INTERFACES(ifp) {
/* ifp->ifindex is not necessarily valid at this point */
int ifindex = if_nametoindex(ifp->name);
if(ifindex > 0) {
unsigned char eui[8];
rc = if_eui64(ifp->name, ifindex, eui);
if(rc < 0)
continue;
memcpy(myid, eui, 8);
goto have_id;
}
}
/* We failed to get a global EUI64 from the interfaces we were given.
Let's try to find an interface with a MAC address. */
for(i = 1; i < 256; i++) {
char buf[IF_NAMESIZE], *ifname;
unsigned char eui[8];
ifname = if_indextoname(i, buf);
if(ifname == NULL)
continue;
rc = if_eui64(ifname, i, eui);
if(rc < 0)
continue;
memcpy(myid, eui, 8);
goto have_id;
}
fprintf(stderr,
"Warning: couldn't find router id -- using random value.\n");
rc = read_random_bytes(myid, 8);
if(rc < 0) {
perror("read(random)");
goto fail;
}
/* Clear group and global bits */
myid[0] &= ~3;
have_id:
myseqno = (random() & 0xFFFF);
fd = open(state_file, O_RDONLY);
if(fd < 0 && errno != ENOENT)
perror("open(babel-state)");
rc = unlink(state_file);
if(fd >= 0 && rc < 0) {
perror("unlink(babel-state)");
/* If we couldn't unlink it, it's probably stale. */
close(fd);
fd = -1;
}
if(fd >= 0) {
char buf[100];
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, "%99s %d %ld\n", buf2, &s, &t);
if(rc == 3 && s >= 0 && s <= 0xFFFF) {
unsigned char sid[8];
rc = parse_eui64(buf2, sid);
if(rc < 0) {
fprintf(stderr, "Couldn't parse babel-state.\n");
} else {
struct timeval realnow;
debugf("Got %s %d %ld from babel-state.\n",
format_eui64(sid), s, t);
gettimeofday(&realnow, NULL);
if(memcmp(sid, myid, 8) == 0)
myseqno = seqno_plus(s, 1);
else
fprintf(stderr, "ID mismatch in babel-state.\n");
}
} else {
fprintf(stderr, "Couldn't parse babel-state.\n");
}
}
close(fd);
fd = -1;
}
protocol_socket = babel_socket(protocol_port);
if(protocol_socket < 0) {
perror("Couldn't create link local socket");
goto fail;
}
#ifndef NO_LOCAL_INTERFACE
if(local_server_port >= 0) {
local_server_socket = tcp_server_socket(local_server_port, 1);
if(local_server_socket < 0) {
perror("local_server_socket");
goto fail;
}
}
#endif
init_signals();
rc = resize_receive_buffer(1500);
if(rc < 0)
goto fail;
check_interfaces();
if(receive_buffer == NULL)
goto fail;
rc = check_xroutes(0);
if(rc < 0)
fprintf(stderr, "Warning: couldn't check exported routes.\n");
kernel_routes_changed = 0;
kernel_link_changed = 0;
kernel_addr_changed = 0;
kernel_dump_time = now.tv_sec + roughly(30);
schedule_neighbours_check(5000, 1);
expiry_time = now.tv_sec + roughly(30);
source_expiry_time = now.tv_sec + roughly(300);
/* Make some noise so that others notice us, and send retractions in
case we were restarted recently */
FOR_ALL_INTERFACES(ifp) {
if(!if_up(ifp))
continue;
/* Apply jitter before we send the first message. */
usleep(roughly(10000));
gettime(&now);
send_hello(ifp);
send_wildcard_retraction(ifp);
}
FOR_ALL_INTERFACES(ifp) {
if(!if_up(ifp))
continue;
usleep(roughly(10000));
gettime(&now);
send_hello(ifp);
send_wildcard_retraction(ifp);
send_self_update(ifp);
send_request(ifp, NULL, 0);
flushupdates(ifp);
flushbuf(ifp);
}
debugf("Entering main loop.\n");
while(1) {
struct timeval tv;
fd_set readfds;
gettime(&now);
tv = check_neighbours_timeout;
timeval_min_sec(&tv, expiry_time);
timeval_min_sec(&tv, source_expiry_time);
timeval_min_sec(&tv, kernel_dump_time);
timeval_min(&tv, &resend_time);
FOR_ALL_INTERFACES(ifp) {
if(!if_up(ifp))
continue;
timeval_min(&tv, &ifp->flush_timeout);
timeval_min(&tv, &ifp->hello_timeout);
timeval_min(&tv, &ifp->update_timeout);
timeval_min(&tv, &ifp->update_flush_timeout);
}
timeval_min(&tv, &unicast_flush_timeout);
FD_ZERO(&readfds);
if(timeval_compare(&tv, &now) > 0) {
int maxfd = 0;
timeval_minus(&tv, &tv, &now);
FD_SET(protocol_socket, &readfds);
maxfd = MAX(maxfd, protocol_socket);
if(kernel_socket < 0) kernel_setup_socket(1);
if(kernel_socket >= 0) {
FD_SET(kernel_socket, &readfds);
maxfd = MAX(maxfd, kernel_socket);
}
#ifndef NO_LOCAL_INTERFACE
if(local_socket >= 0) {
FD_SET(local_socket, &readfds);
maxfd = MAX(maxfd, local_socket);
} else if(local_server_socket >= 0) {
FD_SET(local_server_socket, &readfds);
maxfd = MAX(maxfd, local_server_socket);
}
#endif
rc = select(maxfd + 1, &readfds, NULL, NULL, &tv);
if(rc < 0) {
if(errno != EINTR) {
perror("select");
sleep(1);
}
rc = 0;
FD_ZERO(&readfds);
}
}
gettime(&now);
if(exiting)
break;
if(kernel_socket >= 0 && FD_ISSET(kernel_socket, &readfds))
kernel_callback(kernel_routes_callback, NULL);
if(FD_ISSET(protocol_socket, &readfds)) {
rc = babel_recv(protocol_socket,
receive_buffer, receive_buffer_size,
(struct sockaddr*)&sin6, sizeof(sin6));
if(rc < 0) {
if(errno != EAGAIN && errno != EINTR) {
perror("recv");
sleep(1);
}
} else {
FOR_ALL_INTERFACES(ifp) {
if(!if_up(ifp))
continue;
if(ifp->ifindex == sin6.sin6_scope_id) {
parse_packet((unsigned char*)&sin6.sin6_addr, ifp,
receive_buffer, rc);
VALGRIND_MAKE_MEM_UNDEFINED(receive_buffer,
receive_buffer_size);
break;
}
}
}
}
#ifndef NO_LOCAL_INTERFACE
if(local_server_socket >= 0 &&
FD_ISSET(local_server_socket, &readfds)) {
if(local_socket >= 0) {
close(local_socket);
local_socket = -1;
}
local_socket = accept(local_server_socket, NULL, NULL);
if(local_socket < 0) {
if(errno != EINTR && errno != EAGAIN)
perror("accept(local_server_socket)");
} else {
local_notify_all();
}
}
if(local_socket >= 0 && FD_ISSET(local_socket, &readfds)) {
rc = local_read(local_socket);
if(rc <= 0) {
if(rc < 0)
perror("read(local_socket)");
close(local_socket);
local_socket = -1;
}
}
#endif
if(reopening) {
kernel_dump_time = now.tv_sec;
check_neighbours_timeout = now;
expiry_time = now.tv_sec;
rc = reopen_logfile();
if(rc < 0) {
perror("reopen_logfile");
break;
}
reopening = 0;
}
if(kernel_link_changed || kernel_addr_changed) {
check_interfaces();
kernel_link_changed = 0;
}
if(kernel_routes_changed || kernel_addr_changed ||
now.tv_sec >= kernel_dump_time) {
rc = check_xroutes(1);
if(rc < 0)
fprintf(stderr, "Warning: couldn't check exported routes.\n");
kernel_routes_changed = kernel_addr_changed = 0;
if(kernel_socket >= 0)
kernel_dump_time = now.tv_sec + roughly(300);
else
kernel_dump_time = now.tv_sec + roughly(30);
}
if(timeval_compare(&check_neighbours_timeout, &now) < 0) {
int msecs;
msecs = check_neighbours();
msecs = MAX(msecs, 10);
schedule_neighbours_check(msecs, 1);
}
if(now.tv_sec >= expiry_time) {
check_interfaces();
expire_routes();
expire_resend();
expiry_time = now.tv_sec + roughly(30);
}
if(now.tv_sec >= source_expiry_time) {
expire_sources();
source_expiry_time = now.tv_sec + roughly(300);
}
FOR_ALL_INTERFACES(ifp) {
if(!if_up(ifp))
continue;
if(timeval_compare(&now, &ifp->hello_timeout) >= 0)
send_hello(ifp);
if(timeval_compare(&now, &ifp->update_timeout) >= 0)
send_update(ifp, 0, NULL, 0);
if(timeval_compare(&now, &ifp->update_flush_timeout) >= 0)
flushupdates(ifp);
}
if(resend_time.tv_sec != 0) {
if(timeval_compare(&now, &resend_time) >= 0)
do_resend();
}
if(unicast_flush_timeout.tv_sec != 0) {
if(timeval_compare(&now, &unicast_flush_timeout) >= 0)
flush_unicast(1);
}
FOR_ALL_INTERFACES(ifp) {
if(!if_up(ifp))
continue;
if(ifp->flush_timeout.tv_sec != 0) {
if(timeval_compare(&now, &ifp->flush_timeout) >= 0)
flushbuf(ifp);
}
}
if(UNLIKELY(debug || dumping)) {
dump_tables(stdout);
dumping = 0;
}
}
debugf("Exiting...\n");
usleep(roughly(10000));
gettime(&now);
/* We need to flush so interface_up won't try to reinstall. */
flush_all_routes();
FOR_ALL_INTERFACES(ifp) {
if(!if_up(ifp))
continue;
send_wildcard_retraction(ifp);
/* Make sure that we expire quickly from our neighbours'
association caches. */
send_hello_noupdate(ifp, 10);
flushbuf(ifp);
usleep(roughly(1000));
gettime(&now);
}
FOR_ALL_INTERFACES(ifp) {
if(!if_up(ifp))
continue;
/* Make sure they got it. */
send_wildcard_retraction(ifp);
send_hello_noupdate(ifp, 1);
flushbuf(ifp);
usleep(roughly(10000));
gettime(&now);
interface_up(ifp, 0);
}
kernel_setup_socket(0);
kernel_setup(0);
fd = open(state_file, O_WRONLY | O_TRUNC | O_CREAT, 0644);
if(fd < 0) {
perror("creat(babel-state)");
unlink(state_file);
} else {
struct timeval realnow;
char buf[100];
gettimeofday(&realnow, NULL);
rc = snprintf(buf, 100, "%s %d %ld\n",
format_eui64(myid), (int)myseqno,
(long)realnow.tv_sec);
if(rc < 0 || rc >= 100) {
fprintf(stderr, "write(babel-state): overflow.\n");
unlink(state_file);
} else {
rc = write(fd, buf, rc);
if(rc < 0) {
perror("write(babel-state)");
unlink(state_file);
}
fsync(fd);
}
close(fd);
}
if(pidfile)
unlink(pidfile);
debugf("Done.\n");
return 0;
usage:
fprintf(stderr,
"Syntax: %s "
"[-m multicast_address] [-p port] [-S state-file]\n"
" "
"[-h hello] [-H wired_hello] [-z kind[,factor]]\n"
" "
"[-k metric] [-A metric] [-s] [-l] [-w] [-u] [-g port]\n"
" "
"[-t table] [-T table] [-c file] [-C statement]\n"
" "
"[-d level] [-D] [-L logfile] [-I pidfile]\n"
" "
"[id] interface...\n",
argv[0]);
exit(1);
fail:
FOR_ALL_INTERFACES(ifp) {
if(!if_up(ifp))
continue;
interface_up(ifp, 0);
}
kernel_setup_socket(0);
kernel_setup(0);
fail_pid:
if(pidfile)
unlink(pidfile);
exit(1);
}
/* Schedule a neighbours check after roughly 3/2 times msecs have elapsed. */
void
schedule_neighbours_check(int msecs, int override)
{
struct timeval timeout;
timeval_add_msec(&timeout, &now, roughly(msecs * 3 / 2));
if(override)
check_neighbours_timeout = timeout;
else
timeval_min(&check_neighbours_timeout, &timeout);
}
int
resize_receive_buffer(int size)
{
if(size <= receive_buffer_size)
return 0;
if(receive_buffer == NULL) {
receive_buffer = malloc(size);
if(receive_buffer == NULL) {
perror("malloc(receive_buffer)");
return -1;
}
receive_buffer_size = size;
} else {
unsigned char *new;
new = realloc(receive_buffer, size);
if(new == NULL) {
perror("realloc(receive_buffer)");
return -1;
}
receive_buffer = new;
receive_buffer_size = size;
}
return 1;
}
static void
sigexit(int signo)
{
exiting = 1;
}
static void
sigdump(int signo)
{
dumping = 1;
}
static void
sigreopening(int signo)
{
reopening = 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);
sigemptyset(&ss);
sa.sa_handler = SIG_IGN;
sa.sa_mask = ss;
sa.sa_flags = 0;
sigaction(SIGPIPE, &sa, NULL);
sigemptyset(&ss);
sa.sa_handler = sigdump;
sa.sa_mask = ss;
sa.sa_flags = 0;
sigaction(SIGUSR1, &sa, NULL);
sigemptyset(&ss);
sa.sa_handler = sigreopening;
sa.sa_mask = ss;
sa.sa_flags = 0;
sigaction(SIGUSR2, &sa, NULL);
#ifdef SIGINFO
sigemptyset(&ss);
sa.sa_handler = sigdump;
sa.sa_mask = ss;
sa.sa_flags = 0;
sigaction(SIGINFO, &sa, NULL);
#endif
}
static void
dump_route_callback(struct babel_route *route, void *closure)
{
FILE *out = (FILE*)closure;
const unsigned char *nexthop =
memcmp(route->nexthop, route->neigh->address, 16) == 0 ?
NULL : route->nexthop;
char channels[100];
if(route->channels[0] == 0)
channels[0] = '\0';
else {
int k, j = 0;
snprintf(channels, 100, " chan (");
j = strlen(channels);
for(k = 0; k < DIVERSITY_HOPS; k++) {
if(route->channels[k] == 0)
break;
if(k > 0)
channels[j++] = ',';
snprintf(channels + j, 100 - j, "%d", route->channels[k]);
j = strlen(channels);
}
snprintf(channels + j, 100 - j, ")");
if(k == 0)
channels[0] = '\0';
}
fprintf(out, "%s metric %d refmetric %d id %s seqno %d%s age %d "
"via %s neigh %s%s%s%s\n",
format_prefix(route->src->prefix, route->src->plen),
route_metric(route), route->refmetric,
format_eui64(route->src->id),
(int)route->seqno,
channels,
(int)(now.tv_sec - route->time),
route->neigh->ifp->name,
format_address(route->neigh->address),
nexthop ? " nexthop " : "",
nexthop ? format_address(nexthop) : "",
route->installed ? " (installed)" :
route_feasible(route) ? " (feasible)" : "");
}
static void
dump_xroute_callback(struct xroute *xroute, void *closure)
{
FILE *out = (FILE*)closure;
fprintf(out, "%s metric %d (exported)\n",
format_prefix(xroute->prefix, xroute->plen),
xroute->metric);
}
static void
dump_tables(FILE *out)
{
struct neighbour *neigh;
fprintf(out, "\n");
fprintf(out, "My id %s seqno %d\n", format_eui64(myid), myseqno);
FOR_ALL_NEIGHBOURS(neigh) {
fprintf(out, "Neighbour %s dev %s reach %04x rxcost %d txcost %d chan %d%s.\n",
format_address(neigh->address),
neigh->ifp->name,
neigh->reach,
neighbour_rxcost(neigh),
neigh->txcost,
neigh->ifp->channel,
if_up(neigh->ifp) ? "" : " (down)");
}
for_all_xroutes(dump_xroute_callback, out);
for_all_routes(dump_route_callback, out);
fflush(out);
}
static int
reopen_logfile()
{
int lfd, rc;
if(logfile == NULL)
return 0;
lfd = open(logfile, O_CREAT | O_WRONLY | O_APPEND, 0644);
if(lfd < 0)
return -1;
fflush(stdout);
fflush(stderr);
rc = dup2(lfd, 1);
if(rc < 0)
return -1;
rc = dup2(lfd, 2);
if(rc < 0)
return -1;
if(lfd > 2)
close(lfd);
return 1;
}
static int
kernel_routes_callback(int changed, void *closure)
{
if (changed & CHANGE_LINK)
kernel_link_changed = 1;
if (changed & CHANGE_ADDR)
kernel_addr_changed = 1;
if (changed & CHANGE_ROUTE)
kernel_routes_changed = 1;
return 1;
}
/*
Copyright (c) 2007, 2008 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 INFINITY ((unsigned short)(~0))
#ifndef RTPROT_BABEL
#define RTPROT_BABEL 42
#endif
#define RTPROT_BABEL_LOCAL -2
#undef MAX
#undef MIN
#define MAX(x,y) ((x)<=(y)?(y):(x))
#define MIN(x,y) ((x)<=(y)?(x):(y))
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
/* nothing */
#elif defined(__GNUC__)
#define inline __inline
#if (__GNUC__ >= 3)
#define restrict __restrict
#else
#define restrict /**/
#endif
#else
#define inline /**/
#define restrict /**/
#endif
#if defined(__GNUC__) && (__GNUC__ >= 3)
#define ATTRIBUTE(x) __attribute__ (x)
#define LIKELY(_x) __builtin_expect(!!(_x), 1)
#define UNLIKELY(_x) __builtin_expect(!!(_x), 0)
#else
#define ATTRIBUTE(x) /**/
#define LIKELY(_x) !!(_x)
#define UNLIKELY(_x) !!(_x)
#endif
#if defined(__GNUC__) && (__GNUC__ >= 4) && (__GNUC_MINOR__ >= 3)
#define COLD __attribute__ ((cold))
#else
#define COLD /**/
#endif
#ifndef IF_NAMESIZE
#include <sys/socket.h>
#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
extern struct timeval now;
extern int debug;
extern time_t reboot_time;
extern int default_wireless_hello_interval, default_wired_hello_interval;
extern int resend_delay;
extern int link_detect;
extern int all_wireless;
extern int local_socket;
extern unsigned char myid[8];
extern const unsigned char zeroes[16], ones[16];
extern int protocol_port;
extern unsigned char protocol_group[16];
extern int protocol_socket;
extern int kernel_socket;
extern int max_request_hopcount;
void schedule_neighbours_check(int msecs, int override);
int resize_receive_buffer(int size);
.TH BABELD 8
.SH NAME
babeld \- ad-hoc network routing daemon
.SH SYNOPSIS
.B babeld
.IR option ...
[
.B \-\-
]
.IR interface ...
.SH DESCRIPTION
Babel is a loop-avoiding distance-vector routing protocol roughly
based on DSDV and AODV, but with provisions for link cost estimation
and redistribution of routes from other routing protocols.
While it is optimised for wireless mesh networks, Babel will also work
efficiently on wired networks.
.SH OPTIONS
.TP
.BI \-m " multicast-address"
Specify the link-local multicast address to be used by the protocol.
The default is
.BR ff02:0:0:0:0:0:1:6 .
.TP
.BI \-p " port"
Specify the UDP port number to be used by the protocol. The default is
.BR 6696 .
.TP
.BI \-S " state-file"
Set the name of the file used for preserving long-term information
between invocations of the
.B babeld
daemon. If this file is deleted, the daemon will run in passive mode
for 3 minutes when it is next started (see
.B -P
below), and other hosts might initially ignore it. The default is
.BR /var/lib/babel-state .
.TP
.BI \-h " hello-interval"
Specify the interval in seconds at which scheduled hello packets are
sent on wireless interfaces. The default is 4 seconds.
.TP
.BI \-H " wired-hello-interval"
Specify the interval in seconds at which scheduled hello packets are
sent on wired interfaces. The default is 4 seconds.
.TP
.BI \-z " kind" " \fR[\fB," factor "\fR]"
Enable diversity-sensitive routing. The value
.B kind
defines the diversity algorithm used, and can be one of
.B 0
(no diversity),
.B 1
(per-interface diversity with no memory),
.B 2
(per-channel diversity with no memory),
or
.B 3
(per-channel diversity with memory).
The value
.B factor
specifies by how much the cost of non-interfering routes is multiplied,
in units of 1/256; the default is 128 (i.e. division by 2).
.TP
.BI \-k " priority"
Specify the priority value used when installing routes into the kernel.
The default is 0.
.TP
.BI \-A " priority"
Allow duplicating external routes when their kernel priority is at least
.IR priority .
Do not use this option unless you know what you are doing, as it can cause
persistent route flapping.
.TP
.B \-l
Use IFF_RUNNING (carrier sense) when determining interface availability.
.TP
.B \-w
Don't optimise wired links, assume all interfaces are wireless unless
explicitly overridden in the configuration file.
.TP
.B \-s
Do not perform split-horizon processing on wired interfaces.
Split-horizon is not performed on wireless interfaces.
.TP
.B \-u
Do not flush unfeasible (useless) routes. This is useful in order to
announce more information to a front-end (see
.BR \-g ).
.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. A value of
3 additionally dumps all interactions with the OS kernel. The default
is 0.
.TP
.BI \-g " port"
Listen for connections from a front-end on port
.IR port .
.TP
.BI \-t " table"
Use the given kernel routing table for routes inserted by
.BR babeld .
.TP
.BI \-T " table"
Export routes from the given kernel routing table.
.TP
.BI \-c " filename"
Specify the name of the configuration file. The default is
.BR /etc/babeld.conf .
.TP
.BI \-C " statement"
Specify a configuration statement directly on the command line.
.TP
.B \-D
Daemonise at startup.
.TP
.BI \-L " logfile"
Specify a file to log random ``how do you do?'' messages to. This
defaults to standard error if not daemonising, and to
.B /var/log/babeld.log
otherwise.
.TP
.BI \-I " pidfile"
Specify a file to write our process id to. The default is
.BR /var/run/babeld.pid .
.TP
.IR interface ...
The list of interfaces on which the protocol should operate.
.SH CONFIGURATION FILE FORMAT
The configuration file is a sequence of lines each of which specifies
either an interface or a filtering rule. Blank lines are ignored. Comments
are introduced with an octothorp
.RB `` # ''
and terminate at the end of the line.
.SS Interface configuration
An interface is configured by a single line with the following format:
.IP
.B interface
.I name
.RI [ parameter ...]
.PP
.I Name
is the name of the interface (something like
.BR eth0 ).
Each
.I parameter
specifies a parameter of the given interface. It can be one of:
.TP
.BR wired " {" true | false | auto }
This specifies whether to enable optimisations specific to wired interfaces.
By default, this is determined automatically unless the
.B \-w
command-line flag was specified.
.TP
.BR link\-quality " {" true | false | auto }
This specifies whether link quality estimation should be performed on this
interface. The default is to perform link quality estimation on wireless
interfaces but not on wired interfaces.
.TP
.BR split\-horizon " {" true | false | auto }
This specifies whether to perform split-horizon processing on this
interface. The default is to never perform split-horizon processing on
wireless interfaces; on wired interfaces, the default depends on the
.B \-s
flag.
.TP
.BI rxcost " cost"
This defines the cost of receiving frames on the given interface under
ideal conditions (no packet loss); how this relates to the actual cost used
for computing metrics of routes going through this interface depends on
whether link quality estimation is being done. The default is 96 for wired
interfaces, and 256 for wireless ones.
.TP
.BI channel " channel"
Sets the channel for this interface. The value
.I channel
can be either an integer, or one of the strings
.B interfering
or
.BR noninterfering .
The default is to autodetect the channel number for wireless interfaces,
and
.B noninterfering
for wired interfaces.
.TP
.BR faraway " {" true | false }
This specifies whether the network is "far away", in the sense that
networks behind it don't interfere with networks in front of it. By
default, networks are not far away.
.TP
.BI hello\-interval " interval"
This defines the interval between hello packets sent on this interface.
The default is specified with the
.B \-h
and
.B \-H
command-line flags.
.TP
.BI update\-interval " interval"
This defines the interval between full routing table dumps sent on this
interface; since Babel uses triggered updates and doesn't count to
infinity, this can be set to a fairly large value, unless significant
packet loss is expected. The default is four times the hello interval.
.SS Filtering rules
A filtering rule is defined by a single line with the following format:
.IP
.I filter
.IR selector ...
.I action
.PP
.I Filter
specifies the filter to which this entry will be added, and can be one of
.BR in ,
.BR out ,
or
.BR redistribute .
Each
.I selector
specifies the conditions under which the given statement matches. It
can be one of
.TP
.BI ip " prefix"
This entry only applies to routes in the given prefix.
.TP
.BI eq " plen"
This entry only applies to routes with a prefix length equal to
.BR plen .
.TP
.BI le " plen"
This entry only applies to routes with a prefix length less or equal to
.BR plen .
.TP
.BI ge " plen"
This entry only applies to routes with a prefix length greater or equal to
.BR plen .
.TP
.BI neigh " address"
This entry only applies to routes learned from a neighbour with
link-local address
.IR address .
.TP
.BI id " id"
This entry only applies to routes originated by a router with router-id
.IR id .
.TP
.BI proto " p"
This entry only applies to kernel routes with kernel protocol number
.IR p .
If neither
.B proto
nor
.B local
is specified, this entry applies to all non-local kernel routes with
a protocol different from "boot".
.TP
.B local
This entry only applies to local addresses.
.TP
.BI if " interface"
For an input filter, this specifies the interface over which the route
is learned. For an output filter, this specifies the interface over
which this route is advertised. For a redistribute statement, this
specifies the interface over which the route forwards packets.
.PP
.I Action
specifies the action to be taken when this entry matches. It can have
one of the following values:
.TP
.B allow
Allow this route, without changing its metric (or setting its metric
to 0 in case of a redistribute filter).
.TP
.B deny
Ignore this route.
.TP
.BI metric " value"
For an input or output filter, allow this route after increasing its metric by
.IR value .
For a redistribute filter, redistribute this route with metric
.IR value .
.PP
If
.I action
is not specified, it defaults to
.BR allow .
By default,
.B babeld
redistributes all local addresses, and no other routes. In order to
make sure that only the routes you specify are redistributed, you
should include the line
.IP
redistribute local deny
.PP
as the last line in your configuration file.
.SH EXAMPLES
You can participate in a Babel network by simply running
.IP
# babeld wlan0
.PP
where
.B wlan0
is the name of your wireless interface.
In order to gateway between multiple interfaces, just list them all on
the command line:
.IP
# babeld wlan0 eth0 sit1
.PP
On an access point, you'll probably want to redistribute some external
routes into Babel:
.IP
# babeld \\
\-C 'redistribute metric 256' \\
wlan0
.PP
or, if you want to constrain the routes that you redistribute,
.IP
# babeld \\
\-C 'redistribute proto 11 ip ::/0 le 64 metric 256' \\
\-C 'redistribute proto 11 ip 0.0.0.0/0 le 24 metric 256' \\
wlan0
.SH FILES
.TP
.B /etc/babeld.conf
The default location of the configuration file.
.TP
.B /var/lib/babel\-state
The default location of the file storing long-term state.
.TP
.B /var/run/babeld.pid
The default location of the pid file.
.TP
.B /var/log/babeld.log
The default location of the log file.
.SH SIGNALS
.TP
.B SIGUSR1
Dump Babel's routing tables to standard output or to the log file.
.TP
.B SIGUSR2
Check interfaces and kernel routes right now, then reopen the log file.
.SH SECURITY
Babel is a completely insecure protocol: any attacker able to inject
IP packets with a link-local source address can disrupt the protocol's
operation. This is no different from unsecured neighbour discovery or ARP.
Since Babel uses link-local IPv6 packets only, there is no need to update
firewalls to allow forwarding of Babel protocol packets. If local
filtering is being done, UDP datagrams to the port used by the protocol
should be allowed. As Babel uses unicast packets in some cases, it is not
enough to just allow packets destined to Babel's multicast address.
.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-2010 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 <assert.h>
#ifdef __linux
/* Defining it rather than including <linux/rtnetlink.h> because this
* implies <asm/types.h> on Linux 2.4 */
#define RTPROT_BOOT 3 /* Route installed during boot */
#endif
#include "babeld.h"
#include "util.h"
#include "interface.h"
#include "configuration.h"
struct filter *input_filters = NULL;
struct filter *output_filters = NULL;
struct filter *redistribute_filters = NULL;
struct interface_conf *interface_confs = NULL;
/* This file implements a recursive descent parser with one character
lookahead. The looked-ahead character is returned from most
functions.
Throughout this file, -1 signals that the look-ahead is EOF,
while -2 signals an error. */
/* get_next_char callback */
typedef int (*gnc_t)(void*);
static int
skip_whitespace(int c, gnc_t gnc, void *closure)
{
while(c == ' ' || c == '\t')
c = gnc(closure);
return c;
}
static int
skip_to_eol(int c, gnc_t gnc, void *closure)
{
while(c != '\n' && c >= 0)
c = gnc(closure);
if(c == '\n')
c = gnc(closure);
return c;
}
static int
getword(int c, char **token_r, gnc_t gnc, void *closure)
{
char buf[256];
int i = 0;
c = skip_whitespace(c, gnc, closure);
if(c < 0 || c == '"' || c == '\n')
return -2;
do {
if(i >= 255) return -2;
buf[i++] = c;
c = gnc(closure);
} while(c != ' ' && c != '\t' && c != '\n' && c >= 0);
buf[i] = '\0';
*token_r = strdup(buf);
return c;
}
static int
getstring(int c, char **token_r, gnc_t gnc, void *closure)
{
char buf[256];
int i = 0;
c = skip_whitespace(c, gnc, closure);
if(c < 0)
return c;
if(c == '\n')
return -2;
/* Unquoted strings have the same syntax as words. */
if(c != '"')
return getword(c, token_r, gnc, closure);
c = gnc(closure);
while(1) {
if(i >= 255 || c == '\n') return -2;
if(c == '"') {
c = gnc(closure);
break;
}
if(c == '\\')
c = gnc(closure);
if(c < 0)
return -2;
buf[i++] = c;
c = gnc(closure);
}
buf[i] = '\0';
*token_r = strdup(buf);
return c;
}
static int
getint(int c, int *int_r, gnc_t gnc, void *closure)
{
char *t, *end;
int i;
c = getword(c, &t, gnc, closure);
if(c < -1)
return c;
i = strtol(t, &end, 0);
if(*end != '\0') {
free(t);
return -2;
}
free(t);
*int_r = i;
return c;
}
static int
getmsec(int c, int *int_r, gnc_t gnc, void *closure)
{
char *t;
int i;
c = getword(c, &t, gnc, closure);
if(c < -1)
return c;
i = parse_msec(t);
if(i < 0) {
free(t);
return -2;
}
free(t);
*int_r = i;
return c;
}
static int
getbool(int c, int *int_r, gnc_t gnc, void *closure)
{
char *t;
int i;
c = getword(c, &t, gnc, closure);
if(c < -1)
return c;
if(strcmp(t, "true") == 0 || strcmp(t, "yes") == 0)
i = CONFIG_YES;
else if(strcmp(t, "false") == 0 || strcmp(t, "no") == 0)
i = CONFIG_NO;
else if(strcmp(t, "default") == 0 || strcmp(t, "auto") == 0)
i = CONFIG_DEFAULT;
else {
free(t);
return -2;
}
free(t);
*int_r = i;
return c;
}
static int
getip(int c, unsigned char **ip_r, int *af_r, gnc_t gnc, void *closure)
{
char *t;
unsigned char *ip;
unsigned char addr[16];
int af, rc;
c = getword(c, &t, gnc, closure);
if(c < -1)
return c;
rc = parse_address(t, addr, &af);
if(rc < 0) {
free(t);
return -2;
}
free(t);
ip = malloc(16);
if(ip == NULL) {
return -2;
}
memcpy(ip, addr, 16);
*ip_r = ip;
if(af_r)
*af_r = af;
return c;
}
static int
getid(int c, unsigned char **id_r, gnc_t gnc, void *closure)
{
char *t;
unsigned char *idp;
unsigned char id[8];
int rc;
c = getword(c, &t, gnc, closure);
if(c < -1)
return c;
rc = parse_eui64(t, id);
if(rc < 0) {
free(t);
return -2;
}
free(t);
idp = malloc(8);
if(idp == NULL) {
return -2;
}
memcpy(idp, id, 8);
*id_r = idp;
return c;
}
static int
getnet(int c, unsigned char **p_r, unsigned char *plen_r, int *af_r,
gnc_t gnc, void *closure)
{
char *t;
unsigned char *ip;
unsigned char addr[16];
unsigned char plen;
int af, rc;
c = getword(c, &t, gnc, closure);
if(c < -1)
return c;
rc = parse_net(t, addr, &plen, &af);
if(rc < 0) {
free(t);
return -2;
}
free(t);
ip = malloc(16);
if(ip == NULL)
return -2;
memcpy(ip, addr, 16);
*p_r = ip;
*plen_r = plen;
if(af_r) *af_r = af;
return c;
}
static struct filter *
parse_filter(gnc_t gnc, void *closure)
{
int c;
char *token;
struct filter *filter;
filter = calloc(1, sizeof(struct filter));
if(filter == NULL)
goto error;
filter->plen_le = 128;
c = gnc(closure);
if(c < -1)
goto error;
while(c >= 0 && c != '\n') {
c = skip_whitespace(c, gnc, closure);
if(c == '\n' || c == '#') {
c = skip_to_eol(c, gnc, closure);
break;
}
c = getword(c, &token, gnc, closure);
if(c < -1)
goto error;
if(strcmp(token, "ip") == 0) {
c = getnet(c, &filter->prefix, &filter->plen, &filter->af,
gnc, closure);
if(c < -1)
goto error;
} else if(strcmp(token, "eq") == 0) {
int p;
c = getint(c, &p, gnc, closure);
if(c < -1)
goto error;
filter->plen_ge = MAX(filter->plen_ge, p);
filter->plen_le = MIN(filter->plen_le, p);
} else if(strcmp(token, "le") == 0) {
int p;
c = getint(c, &p, gnc, closure);
if(c < -1)
goto error;
filter->plen_le = MIN(filter->plen_le, p);
} else if(strcmp(token, "ge") == 0) {
int p;
c = getint(c, &p, gnc, closure);
if(c < -1)
goto error;
filter->plen_ge = MAX(filter->plen_ge, p);
} else if(strcmp(token, "neigh") == 0) {
unsigned char *neigh = NULL;
c = getip(c, &neigh, NULL, gnc, closure);
if(c < -1)
goto error;
filter->neigh = neigh;
} else if(strcmp(token, "id") == 0) {
unsigned char *id = NULL;
c = getid(c, &id, gnc, closure);
if(c < -1)
goto error;
filter->id = id;
} else if(strcmp(token, "proto") == 0) {
int proto;
c = getint(c, &proto, gnc, closure);
if(c < -1)
goto error;
filter->proto = proto;
} else if(strcmp(token, "local") == 0) {
filter->proto = RTPROT_BABEL_LOCAL;
} else if(strcmp(token, "if") == 0) {
char *interface;
c = getstring(c, &interface, gnc, closure);
if(c < -1)
goto error;
filter->ifname = interface;
filter->ifindex = if_nametoindex(interface);
} else if(strcmp(token, "allow") == 0) {
filter->result = 0;
} else if(strcmp(token, "deny") == 0) {
filter->result = INFINITY;
} else if(strcmp(token, "metric") == 0) {
int metric;
c = getint(c, &metric, gnc, closure);
if(c < -1) goto error;
if(metric <= 0 || metric > INFINITY)
goto error;
filter->result = metric;
} else {
goto error;
}
free(token);
}
if(filter->af == 0) {
if(filter->plen_le < 128 || filter->plen_ge > 0)
filter->af = AF_INET6;
} else if(filter->af == AF_INET) {
filter->plen_le += 96;
filter->plen_ge += 96;
}
return filter;
error:
free(filter);
return NULL;
}
static struct interface_conf *
parse_ifconf(gnc_t gnc, void *closure)
{
int c;
char *token;
struct interface_conf *if_conf;
if_conf = calloc(1, sizeof(struct interface_conf));
if(if_conf == NULL)
goto error;
c = gnc(closure);
if(c < -1)
goto error;
c = skip_whitespace(c, gnc, closure);
if(c < -1 || c == '\n' || c == '#')
goto error;
c = getstring(c, &token, gnc, closure);
if(c < -1 || token == NULL)
goto error;
if_conf->ifname = token;
while(c >= 0 && c != '\n') {
c = skip_whitespace(c, gnc, closure);
if(c == '\n' || c == '#') {
c = skip_to_eol(c, gnc, closure);
break;
}
c = getword(c, &token, gnc, closure);
if(c < -1)
goto error;
if(strcmp(token, "rxcost") == 0) {
int cost;
c = getint(c, &cost, gnc, closure);
if(c < -1 || cost <= 0 || cost > 0xFFFF)
goto error;
if_conf->cost = cost;
} else if(strcmp(token, "hello-interval") == 0) {
int interval;
c = getmsec(c, &interval, gnc, closure);
if(c < -1 || interval <= 0 || interval > 10 * 0xFFFF)
goto error;
if_conf->hello_interval = interval;
} else if(strcmp(token, "update-interval") == 0) {
int interval;
c = getmsec(c, &interval, gnc, closure);
if(c < -1 || interval <= 0 || interval > 10 * 0xFFFF)
goto error;
if_conf->update_interval = interval;
} else if(strcmp(token, "wired") == 0) {
int v;
c = getbool(c, &v, gnc, closure);
if(c < -1)
goto error;
if_conf->wired = v;
} else if(strcmp(token, "faraway") == 0) {
int v;
c = getbool(c, &v, gnc, closure);
if(c < -1)
goto error;
if_conf->faraway = v;
} else if(strcmp(token, "link-quality") == 0) {
int v;
c = getbool(c, &v, gnc, closure);
if(c < -1)
goto error;
if_conf->lq = v;
} else if(strcmp(token, "split-horizon") == 0) {
int v;
c = getbool(c, &v, gnc, closure);
if(c < -1)
goto error;
if_conf->split_horizon = v;
} else if(strcmp(token, "channel") == 0) {
char *t, *end;
c = getword(c, &t, gnc, closure);
if(c < -1)
goto error;
if(strcmp(t, "noninterfering") == 0)
if_conf->channel = IF_CHANNEL_NONINTERFERING;
else if(strcmp(t, "interfering") == 0)
if_conf->channel = IF_CHANNEL_INTERFERING;
else {
if_conf->channel = strtol(t, &end, 0);
if(*end != '\0')
goto error;
}
free(t);
if((if_conf->channel < 1 || if_conf->channel > 254) &&
if_conf->channel != IF_CHANNEL_NONINTERFERING)
goto error;
} else {
goto error;
}
free(token);
}
return if_conf;
error:
free(if_conf);
return NULL;
}
static void
add_filter(struct filter *filter, struct filter **filters)
{
if(*filters == NULL) {
filter->next = NULL;
*filters = filter;
} else {
struct filter *f;
f = *filters;
while(f->next)
f = f->next;
filter->next = NULL;
f->next = filter;
}
}
static void
merge_ifconf(struct interface_conf *dest, struct interface_conf *src)
{
assert(strcmp(dest->ifname, src->ifname) == 0);
#define MERGE(field) \
do { \
if(src->field) \
dest->field = src->field; \
} while(0)
MERGE(hello_interval);
MERGE(update_interval);
MERGE(cost);
MERGE(wired);
MERGE(split_horizon);
MERGE(lq);
#undef MERGE
}
static void
add_ifconf(struct interface_conf *if_conf, struct interface_conf **if_confs)
{
if(*if_confs == NULL) {
if_conf->next = NULL;
*if_confs = if_conf;
} else {
struct interface_conf *if_c;
if_c = *if_confs;
while(if_c->next) {
if(strcmp(if_c->ifname, if_conf->ifname) == 0) {
merge_ifconf(if_c, if_conf);
free(if_conf);
return;
}
if_c = if_c->next;
}
if_conf->next = NULL;
if_c->next = if_conf;
}
}
static int
parse_config(gnc_t gnc, void *closure)
{
int c;
char *token;
c = gnc(closure);
if(c < -1)
return -1;
while(c >= 0) {
c = skip_whitespace(c, gnc, closure);
if(c == '\n' || c == '#') {
c = skip_to_eol(c, gnc, closure);
continue;
}
if(c < 0)
break;
c = getword(c, &token, gnc, closure);
if(c < -1)
return -1;
if(strcmp(token, "in") == 0) {
struct filter *filter;
filter = parse_filter(gnc, closure);
if(filter == NULL)
return -1;
add_filter(filter, &input_filters);
} else if(strcmp(token, "out") == 0) {
struct filter *filter;
filter = parse_filter(gnc, closure);
if(filter == NULL)
return -1;
add_filter(filter, &output_filters);
} else if(strcmp(token, "redistribute") == 0) {
struct filter *filter;
filter = parse_filter(gnc, closure);
if(filter == NULL)
return -1;
add_filter(filter, &redistribute_filters);
} else if(strcmp(token, "interface") == 0) {
struct interface_conf *if_conf;
if_conf = parse_ifconf(gnc, closure);
if(if_conf == NULL)
return -1;
add_ifconf(if_conf, &interface_confs);
} else {
return -1;
}
free(token);
}
return 1;
}
int
parse_config_from_file(char *filename)
{
FILE *f;
int rc;
f = fopen(filename, "r");
if(f == NULL)
return -1;
rc = parse_config((gnc_t)fgetc, f);
fclose(f);
return rc;
}
struct string_state {
char *string;
int n;
};
static int
gnc_string(struct string_state *s)
{
if(s->string[s->n] == '\0')
return -1;
else
return s->string[s->n++];
}
int
parse_config_from_string(char *string)
{
struct string_state s = { string, 0 };
return parse_config((gnc_t)gnc_string, &s);
}
static void
renumber_filter(struct filter *filter)
{
while(filter) {
if(filter->ifname)
filter->ifindex = if_nametoindex(filter->ifname);
filter = filter->next;
}
}
void
renumber_filters()
{
renumber_filter(input_filters);
renumber_filter(output_filters);
renumber_filter(redistribute_filters);
}
static int
filter_match(struct filter *f, const unsigned char *id,
const unsigned char *prefix, unsigned short plen,
const unsigned char *neigh, unsigned int ifindex, int proto)
{
if(f->af) {
if(plen >= 96 && v4mapped(prefix)) {
if(f->af == AF_INET6) return 0;
} else {
if(f->af == AF_INET) return 0;
}
}
if(f->id) {
if(!id || memcmp(f->id, id, 8) != 0)
return 0;
}
if(f->prefix) {
if(!prefix || plen < f->plen || !in_prefix(prefix, f->prefix, f->plen))
return 0;
}
if(f->plen_ge > 0 || f->plen_le < 128) {
if(!prefix)
return 0;
if(plen > f->plen_le)
return 0;
if(plen < f->plen_ge)
return 0;
}
if(f->neigh) {
if(!neigh || memcmp(f->neigh, neigh, 16) != 0)
return 0;
}
if(f->ifname) {
if(!f->ifindex) /* no such interface */
return 0;
if(!ifindex || f->ifindex != ifindex)
return 0;
}
if(f->proto) {
if(!proto || f->proto != proto)
return 0;
} else if(proto == RTPROT_BABEL_LOCAL) {
return 0;
#ifdef __linux
} else if(proto == RTPROT_BOOT) {
return 0;
#endif
}
return 1;
}
static int
do_filter(struct filter *f, const unsigned char *id,
const unsigned char *prefix, unsigned short plen,
const unsigned char *neigh, unsigned int ifindex, int proto)
{
while(f) {
if(filter_match(f, id, prefix, plen, neigh, ifindex, proto))
return f->result;
f = f->next;
}
return -1;
}
int
input_filter(const unsigned char *id,
const unsigned char *prefix, unsigned short plen,
const unsigned char *neigh, unsigned int ifindex)
{
int res;
res = do_filter(input_filters, id, prefix, plen, neigh, ifindex, 0);
if(res < 0)
res = 0;
return res;
}
int
output_filter(const unsigned char *id, const unsigned char *prefix,
unsigned short plen, unsigned int ifindex)
{
int res;
res = do_filter(output_filters, id, prefix, plen, NULL, ifindex, 0);
if(res < 0)
res = 0;
return res;
}
int
redistribute_filter(const unsigned char *prefix, unsigned short plen,
unsigned int ifindex, int proto)
{
int res;
res = do_filter(redistribute_filters, NULL, prefix, plen, NULL,
ifindex, proto);
if(res < 0)
res = INFINITY;
return res;
}
int
finalise_config()
{
struct filter *filter = calloc(1, sizeof(struct filter));
if(filter == NULL)
return -1;
filter->proto = RTPROT_BABEL_LOCAL;
filter->plen_le = 128;
add_filter(filter, &redistribute_filters);
while(interface_confs) {
struct interface_conf *if_conf;
void *vrc;
if_conf = interface_confs;
interface_confs = interface_confs->next;
if_conf->next = NULL;
vrc = add_interface(if_conf->ifname, if_conf);
if(vrc == NULL) {
fprintf(stderr, "Couldn't add interface %s.\n", if_conf->ifname);
return -1;
}
}
return 1;
}
/*
Copyright (c) 2007, 2008 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 filter {
int af;
char *ifname;
unsigned int ifindex;
unsigned char *id;
unsigned char *prefix;
unsigned char plen;
unsigned char plen_ge, plen_le;
unsigned char *neigh;
int proto; /* May be negative */
unsigned int result;
struct filter *next;
};
int parse_config_from_file(char *filename);
int parse_config_from_string(char *string);
void renumber_filters(void);
int input_filter(const unsigned char *id,
const unsigned char *prefix, unsigned short plen,
const unsigned char *neigh, unsigned int ifindex);
int output_filter(const unsigned char *id, const unsigned char *prefix,
unsigned short plen, unsigned int ifindex);
int redistribute_filter(const unsigned char *prefix, unsigned short plen,
unsigned int ifindex, int proto);
int finalise_config(void);
/*
Copyright (c) 2007, 2008 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 <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <assert.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <net/if.h>
#include <arpa/inet.h>
#include "babeld.h"
#include "util.h"
#include "kernel.h"
#include "interface.h"
#include "neighbour.h"
#include "message.h"
#include "route.h"
#include "configuration.h"
struct interface *interfaces = NULL;
static struct interface *
last_interface(void)
{
struct interface *ifp = interfaces;
if(!ifp)
return NULL;
while(ifp->next)
ifp = ifp->next;
return ifp;
}
struct interface *
add_interface(char *ifname, struct interface_conf *if_conf)
{
struct interface *ifp;
assert(!if_conf || strcmp(ifname, if_conf->ifname) == 0);
FOR_ALL_INTERFACES(ifp) {
if(strcmp(ifp->name, ifname) == 0) {
assert(if_conf == NULL);
return ifp;
}
}
ifp = malloc(sizeof(struct interface));
if(ifp == NULL)
return NULL;
memset(ifp, 0, sizeof(struct interface));
strncpy(ifp->name, ifname, IF_NAMESIZE);
ifp->conf = if_conf;
ifp->bucket_time = now.tv_sec;
ifp->bucket = BUCKET_TOKENS_MAX;
ifp->hello_seqno = (random() & 0xFFFF);
if(interfaces == NULL)
interfaces = ifp;
else
last_interface()->next = ifp;
return ifp;
}
/* This should be no more than half the hello interval, so that hellos
aren't sent late. The result is in milliseconds. */
unsigned
jitter(struct interface *ifp, int urgent)
{
unsigned interval = ifp->hello_interval;
if(urgent)
interval = MIN(interval, 100);
else
interval = MIN(interval, 4000);
return roughly(interval) / 4;
}
unsigned
update_jitter(struct interface *ifp, int urgent)
{
unsigned interval = ifp->hello_interval;
if(urgent)
interval = MIN(interval, 100);
else
interval = MIN(interval, 4000);
return roughly(interval);
}
void
set_timeout(struct timeval *timeout, int msecs)
{
timeval_add_msec(timeout, &now, roughly(msecs));
}
static int
check_interface_ipv4(struct interface *ifp)
{
unsigned char ipv4[4];
int rc;
if(ifp->ifindex > 0)
rc = kernel_interface_ipv4(ifp->name, ifp->ifindex, ipv4);
else
rc = 0;
if(rc > 0) {
if(!ifp->ipv4 || memcmp(ipv4, ifp->ipv4, 4) != 0) {
debugf("Noticed IPv4 change for %s.\n", ifp->name);
flush_interface_routes(ifp, 0);
if(!ifp->ipv4)
ifp->ipv4 = malloc(4);
if(ifp->ipv4)
memcpy(ifp->ipv4, ipv4, 4);
return 1;
}
} else {
if(ifp->ipv4) {
debugf("Noticed IPv4 change for %s.\n", ifp->name);
flush_interface_routes(ifp, 0);
free(ifp->ipv4);
ifp->ipv4 = NULL;
return 1;
}
}
return 0;
}
static int
check_interface_channel(struct interface *ifp)
{
int channel = IF_CONF(ifp, channel);
if(channel == IF_CHANNEL_UNKNOWN) {
if((ifp->flags & IF_WIRED)) {
channel = IF_CHANNEL_NONINTERFERING;
} else {
channel = kernel_interface_channel(ifp->name, ifp->ifindex);
if(channel < 0)
fprintf(stderr,
"Couldn't determine channel of interface %s: %s.\n",
ifp->name, strerror(errno));
if(channel <= 0)
channel = IF_CHANNEL_INTERFERING;
}
}
if(ifp->channel != channel) {
ifp->channel = channel;
return 1;
}
return 0;
}
int
interface_up(struct interface *ifp, int up)
{
int mtu, rc, wired;
struct ipv6_mreq mreq;
if((!!up) == if_up(ifp))
return 0;
if(up)
ifp->flags |= IF_UP;
else
ifp->flags &= ~IF_UP;
if(up) {
struct kernel_route ll[32];
if(ifp->ifindex <= 0) {
fprintf(stderr,
"Upping unknown interface %s.\n", ifp->name);
return interface_up(ifp, 0);
}
rc = kernel_setup_interface(1, ifp->name, ifp->ifindex);
if(rc < 0) {
fprintf(stderr, "kernel_setup_interface(%s, %d) failed.\n",
ifp->name, ifp->ifindex);
return interface_up(ifp, 0);
}
mtu = kernel_interface_mtu(ifp->name, ifp->ifindex);
if(mtu < 0) {
fprintf(stderr, "Warning: couldn't get MTU of interface %s (%d).\n",
ifp->name, ifp->ifindex);
mtu = 1280;
}
/* We need to be able to fit at least two messages into a packet,
so MTUs below 116 require lower layer fragmentation. */
/* In IPv6, the minimum MTU is 1280, and every host must be able
to reassemble up to 1500 bytes, but I'd rather not rely on this. */
if(mtu < 128) {
fprintf(stderr, "Suspiciously low MTU %d on interface %s (%d).\n",
mtu, ifp->name, ifp->ifindex);
mtu = 128;
}
if(ifp->sendbuf)
free(ifp->sendbuf);
/* 40 for IPv6 header, 8 for UDP header, 12 for good luck. */
ifp->bufsize = mtu - sizeof(packet_header) - 60;
ifp->sendbuf = malloc(ifp->bufsize);
if(ifp->sendbuf == NULL) {
fprintf(stderr, "Couldn't allocate sendbuf.\n");
ifp->bufsize = 0;
return interface_up(ifp, 0);
}
rc = resize_receive_buffer(mtu);
if(rc < 0)
fprintf(stderr, "Warning: couldn't resize "
"receive buffer for interface %s (%d) (%d bytes).\n",
ifp->name, ifp->ifindex, mtu);
if(IF_CONF(ifp, wired) == CONFIG_NO) {
wired = 0;
} else if(IF_CONF(ifp, wired) == CONFIG_YES) {
wired = 1;
} else if(all_wireless) {
wired = 0;
} else {
rc = kernel_interface_wireless(ifp->name, ifp->ifindex);
if(rc < 0) {
fprintf(stderr,
"Warning: couldn't determine whether %s (%d) "
"is a wireless interface.\n",
ifp->name, ifp->ifindex);
wired = 0;
} else {
wired = !rc;
}
}
if(wired) {
ifp->flags |= IF_WIRED;
ifp->cost = IF_CONF(ifp, cost);
if(ifp->cost <= 0) ifp->cost = 96;
if(IF_CONF(ifp, split_horizon) == CONFIG_NO)
ifp->flags &= ~IF_SPLIT_HORIZON;
else if(IF_CONF(ifp, split_horizon) == CONFIG_YES)
ifp->flags |= IF_SPLIT_HORIZON;
else if(split_horizon)
ifp->flags |= IF_SPLIT_HORIZON;
else
ifp->flags &= ~IF_SPLIT_HORIZON;
if(IF_CONF(ifp, lq) == CONFIG_YES)
ifp->flags |= IF_LQ;
else
ifp->flags &= ~IF_LQ;
} else {
ifp->flags &= ~IF_WIRED;
ifp->cost = IF_CONF(ifp, cost);
if(ifp->cost <= 0) ifp->cost = 256;
if(IF_CONF(ifp, split_horizon) == CONFIG_YES)
ifp->flags |= IF_SPLIT_HORIZON;
else
ifp->flags &= ~IF_SPLIT_HORIZON;
if(IF_CONF(ifp, lq) == CONFIG_NO)
ifp->flags &= ~IF_LQ;
else
ifp->flags |= IF_LQ;
}
if(IF_CONF(ifp, faraway) == CONFIG_YES)
ifp->flags |= IF_FARAWAY;
if(IF_CONF(ifp, hello_interval) > 0)
ifp->hello_interval = IF_CONF(ifp, hello_interval);
else if((ifp->flags & IF_WIRED))
ifp->hello_interval = default_wired_hello_interval;
else
ifp->hello_interval = default_wireless_hello_interval;
ifp->update_interval =
IF_CONF(ifp, update_interval) > 0 ?
IF_CONF(ifp, update_interval) :
ifp->hello_interval * 4;
memset(&mreq, 0, sizeof(mreq));
memcpy(&mreq.ipv6mr_multiaddr, protocol_group, 16);
mreq.ipv6mr_interface = ifp->ifindex;
rc = setsockopt(protocol_socket, IPPROTO_IPV6, IPV6_JOIN_GROUP,
(char*)&mreq, sizeof(mreq));
if(rc < 0) {
perror("setsockopt(IPV6_JOIN_GROUP)");
/* This is probably due to a missing link-local address,
so down this interface, and wait until the main loop
tries to up it again. */
return interface_up(ifp, 0);
}
if(ifp->ll)
free(ifp->ll);
ifp->numll = 0;
ifp->ll = NULL;
rc = kernel_addresses(ifp->name, ifp->ifindex, 1, ll, 32);
if(rc < 0) {
perror("kernel_ll_addresses");
} else if(rc > 0) {
ifp->ll = malloc(16 * rc);
if(ifp->ll == NULL) {
perror("malloc(ll)");
} else {
int i;
for(i = 0; i < rc; i++)
memcpy(ifp->ll[i], ll[i].prefix, 16);
ifp->numll = rc;
}
}
check_interface_channel(ifp);
update_interface_metric(ifp);
rc = check_interface_ipv4(ifp);
debugf("Upped interface %s (%s, cost=%d, channel=%d%s).\n",
ifp->name,
(ifp->flags & IF_WIRED) ? "wired" : "wireless",
ifp->cost,
ifp->channel,
ifp->ipv4 ? ", IPv4" : "");
set_timeout(&ifp->hello_timeout, ifp->hello_interval);
set_timeout(&ifp->update_timeout, ifp->update_interval);
send_hello(ifp);
if(rc > 0)
send_update(ifp, 0, NULL, 0);
send_request(ifp, NULL, 0);
} else {
flush_interface_routes(ifp, 0);
ifp->buffered = 0;
ifp->bufsize = 0;
free(ifp->sendbuf);
ifp->num_buffered_updates = 0;
ifp->update_bufsize = 0;
if(ifp->buffered_updates)
free(ifp->buffered_updates);
ifp->buffered_updates = NULL;
ifp->sendbuf = NULL;
if(ifp->ifindex > 0) {
memset(&mreq, 0, sizeof(mreq));
memcpy(&mreq.ipv6mr_multiaddr, protocol_group, 16);
mreq.ipv6mr_interface = ifp->ifindex;
rc = setsockopt(protocol_socket, IPPROTO_IPV6, IPV6_LEAVE_GROUP,
(char*)&mreq, sizeof(mreq));
if(rc < 0)
perror("setsockopt(IPV6_LEAVE_GROUP)");
kernel_setup_interface(0, ifp->name, ifp->ifindex);
}
if(ifp->ll)
free(ifp->ll);
ifp->ll = NULL;
ifp->numll = 0;
}
return 1;
}
int
interface_ll_address(struct interface *ifp, const unsigned char *address)
{
int i;
if(!if_up(ifp))
return 0;
for(i = 0; i < ifp->numll; i++)
if(memcmp(ifp->ll[i], address, 16) == 0)
return 1;
return 0;
}
void
check_interfaces(void)
{
struct interface *ifp;
int rc, ifindex_changed = 0;
unsigned int ifindex;
FOR_ALL_INTERFACES(ifp) {
ifindex = if_nametoindex(ifp->name);
if(ifindex != ifp->ifindex) {
debugf("Noticed ifindex change for %s.\n", ifp->name);
ifp->ifindex = 0;
interface_up(ifp, 0);
ifp->ifindex = ifindex;
ifindex_changed = 1;
}
if(ifp->ifindex > 0)
rc = kernel_interface_operational(ifp->name, ifp->ifindex);
else
rc = 0;
if((rc > 0) != if_up(ifp)) {
debugf("Noticed status change for %s.\n", ifp->name);
interface_up(ifp, rc > 0);
}
if(if_up(ifp)) {
check_interface_channel(ifp);
rc = check_interface_ipv4(ifp);
if(rc > 0) {
send_request(ifp, NULL, 0);
send_update(ifp, 0, NULL, 0);
}
}
}
if(ifindex_changed)
renumber_filters();
}
/*
Copyright (c) 2007, 2008 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 buffered_update {
unsigned char id[8];
unsigned char prefix[16];
unsigned char plen;
unsigned char pad[3];
};
struct interface_conf {
char *ifname;
unsigned hello_interval;
unsigned update_interval;
unsigned short cost;
char wired;
char split_horizon;
char lq;
char faraway;
int channel;
struct interface_conf *next;
};
#define CONFIG_DEFAULT 0
#define CONFIG_NO 1
#define CONFIG_YES 2
#define IF_UP (1 << 0)
#define IF_WIRED (1<<1)
#define IF_SPLIT_HORIZON (1 << 2)
#define IF_LQ (1 << 3)
#define IF_FARAWAY (1 << 4)
/* Only INTERFERING can appear on the wire. */
#define IF_CHANNEL_UNKNOWN 0
#define IF_CHANNEL_INTERFERING 255
#define IF_CHANNEL_NONINTERFERING -2
struct interface {
struct interface *next;
struct interface_conf *conf;
unsigned int ifindex;
unsigned short flags;
unsigned short cost;
int channel;
struct timeval hello_timeout;
struct timeval update_timeout;
struct timeval flush_timeout;
struct timeval update_flush_timeout;
char name[IF_NAMESIZE];
unsigned char *ipv4;
int numll;
unsigned char (*ll)[16];
int buffered;
int bufsize;
char have_buffered_hello;
char have_buffered_id;
char have_buffered_nh;
char have_buffered_prefix;
unsigned char buffered_id[16];
unsigned char buffered_nh[4];
unsigned char buffered_prefix[16];
unsigned char *sendbuf;
struct buffered_update *buffered_updates;
int num_buffered_updates;
int update_bufsize;
time_t bucket_time;
unsigned int bucket;
time_t last_update_time;
unsigned short hello_seqno;
unsigned hello_interval;
unsigned update_interval;
};
#define IF_CONF(_ifp, _field) \
((_ifp)->conf ? (_ifp)->conf->_field : 0)
extern struct interface *interfaces;
#define FOR_ALL_INTERFACES(_ifp) for(_ifp = interfaces; _ifp; _ifp = _ifp->next)
static inline int
if_up(struct interface *ifp)
{
return !!(ifp->flags & IF_UP);
}
struct interface *add_interface(char *ifname, struct interface_conf *if_conf);
unsigned jitter(struct interface *ifp, int urgent);
unsigned update_jitter(struct interface *ifp, int urgent);
void set_timeout(struct timeval *timeout, int msecs);
int interface_up(struct interface *ifp, int up);
int interface_ll_address(struct interface *ifp, const unsigned char *address);
void check_interfaces(void);
/*
Copyright 2007, 2008 by Grégoire Henry, Julien Cristau and 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 <sys/time.h>
#include <sys/param.h>
#include <time.h>
#include "babeld.h"
#ifdef __linux
#include "kernel_netlink.c"
#else
#include "kernel_socket.c"
#endif
/* Like gettimeofday, but returns monotonic time. If POSIX clocks are not
available, falls back to gettimeofday but enforces monotonicity. */
int
gettime(struct timeval *tv)
{
int rc;
static time_t offset = 0, previous = 0;
#if defined(_POSIX_TIMERS) && _POSIX_TIMERS > 0 && defined(CLOCK_MONOTONIC)
static int have_posix_clocks = -1;
if(UNLIKELY(have_posix_clocks < 0)) {
struct timespec ts;
rc = clock_gettime(CLOCK_MONOTONIC, &ts);
if(rc < 0) {
have_posix_clocks = 0;
} else {
have_posix_clocks = 1;
}
}
if(have_posix_clocks) {
struct timespec ts;
int rc;
rc = clock_gettime(CLOCK_MONOTONIC, &ts);
if(rc < 0)
return rc;
tv->tv_sec = ts.tv_sec;
tv->tv_usec = ts.tv_nsec / 1000;
return rc;
}
#endif
rc = gettimeofday(tv, NULL);
if(rc < 0)
return rc;
tv->tv_sec += offset;
if(UNLIKELY(previous > tv->tv_sec)) {
offset += previous - tv->tv_sec;
tv->tv_sec = previous;
}
previous = tv->tv_sec;
return rc;
}
/* If /dev/urandom doesn't exist, this will fail with ENOENT, which the
caller will deal with gracefully. */
int
read_random_bytes(void *buf, size_t len)
{
int fd;
int rc;
fd = open("/dev/urandom", O_RDONLY);
if(fd < 0) {
rc = -1;
} else {
rc = read(fd, buf, len);
if(rc < 0 || (unsigned)rc < len)
rc = -1;
close(fd);
}
return rc;
}
/*
Copyright (c) 2007, 2008 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 <netinet/in.h>
#include "babeld.h"
#define KERNEL_INFINITY 0xFFFF
struct kernel_route {
unsigned char prefix[16];
int plen;
int metric;
unsigned int ifindex;
int proto;
unsigned char gw[16];
};
#define ROUTE_FLUSH 0
#define ROUTE_ADD 1
#define ROUTE_MODIFY 2
#define CHANGE_LINK (1 << 0)
#define CHANGE_ROUTE (1 << 1)
#define CHANGE_ADDR (1 << 2)
extern int export_table, import_table;
int kernel_setup(int setup);
int kernel_setup_socket(int setup);
int kernel_setup_interface(int setup, const char *ifname, int ifindex);
int kernel_interface_operational(const char *ifname, int ifindex);
int kernel_interface_ipv4(const char *ifname, int ifindex,
unsigned char *addr_r);
int kernel_interface_mtu(const char *ifname, int ifindex);
int kernel_interface_wireless(const char *ifname, int ifindex);
int kernel_interface_channel(const char *ifname, int ifindex);
int kernel_route(int operation, const unsigned char *dest, unsigned short plen,
const unsigned char *gate, int ifindex, unsigned int metric,
const unsigned char *newgate, int newifindex,
unsigned int newmetric);
int kernel_routes(struct kernel_route *routes, int maxroutes);
int kernel_callback(int (*fn)(int, void*), void *closure);
int kernel_addresses(char *ifname, int ifindex, int ll,
struct kernel_route *routes, int maxroutes);
int if_eui64(char *ifname, int ifindex, unsigned char *eui);
int gettime(struct timeval *tv);
int read_random_bytes(void *buf, size_t len);
/*
Copyright 2007-2010 by Grégoire Henry, Julien Cristau and 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 <time.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <net/route.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <asm/types.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <linux/if_bridge.h>
#include <netinet/ether.h>
#if (__GLIBC__ < 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ <= 5)
#define RTA_TABLE 15
#endif
#include "babeld.h"
#include "kernel.h"
#include "util.h"
#include "interface.h"
int export_table = -1, import_table = -1;
static int old_forwarding = -1;
static int old_ipv4_forwarding = -1;
static int old_accept_redirects = -1;
static int old_rp_filter = -1;
static int dgram_socket = -1;
#ifndef ARPHRD_ETHER
#define ARPHRD_ETHER 1
#define NO_ARPHRD
#endif
/* Determine an interface's hardware address, in modified EUI-64 format */
int
if_eui64(char *ifname, int ifindex, unsigned char *eui)
{
int s, rc;
struct ifreq ifr;
s = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
if(s < 0) return -1;
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
rc = ioctl(s, SIOCGIFHWADDR, &ifr);
if(rc < 0) {
int saved_errno = errno;
close(s);
errno = saved_errno;
return -1;
}
close(s);
switch(ifr.ifr_hwaddr.sa_family) {
case ARPHRD_ETHER:
#ifndef NO_ARPHRD
case ARPHRD_FDDI:
case ARPHRD_IEEE802_TR:
case ARPHRD_IEEE802:
#endif
{
unsigned char *mac;
mac = (unsigned char *)ifr.ifr_hwaddr.sa_data;
/* Check for null address and group and global bits */
if(memcmp(mac, zeroes, 6) == 0 ||
(mac[0] & 1) != 0 || (mac[0] & 2) != 0) {
errno = ENOENT;
return -1;
}
memcpy(eui, mac, 3);
eui[3] = 0xFF;
eui[4] = 0xFE;
memcpy(eui + 5, mac + 3, 3);
eui[0] ^= 2;
return 1;
}
#ifndef NO_ARPHRD
case ARPHRD_EUI64:
case ARPHRD_IEEE1394:
case ARPHRD_INFINIBAND: {
unsigned char *mac;
mac = (unsigned char *)ifr.ifr_hwaddr.sa_data;
if(memcmp(mac, zeroes, 8) == 0 ||
(mac[0] & 1) != 0 || (mac[0] & 2) != 0) {
errno = ENOENT;
return -1;
}
memcpy(eui, mac, 8);
eui[0] ^= 2;
return 1;
}
#endif
default:
errno = ENOENT;
return -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;
}
struct netlink {
unsigned short seqno;
int sock;
struct sockaddr_nl sockaddr;
socklen_t socklen;
};
static struct netlink nl_command = { 0, -1, {0}, 0 };
static struct netlink nl_listen = { 0, -1, {0}, 0 };
static int nl_setup = 0;
static int
netlink_socket(struct netlink *nl, uint32_t groups)
{
int rc;
nl->sock = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
if(nl->sock < 0)
return -1;
memset(&nl->sockaddr, 0, sizeof(nl->sockaddr));
nl->sockaddr.nl_family = AF_NETLINK;
nl->sockaddr.nl_groups = groups;
nl->socklen = sizeof(nl->sockaddr);
nl->seqno = time(NULL);
rc = fcntl(nl->sock, F_GETFL, 0);
if(rc < 0)
goto fail;
rc = fcntl(nl->sock, F_SETFL, (rc | O_NONBLOCK));
if(rc < 0)
goto fail;
rc = bind(nl->sock, (struct sockaddr *)&nl->sockaddr, nl->socklen);
if(rc < 0)
goto fail;
rc = getsockname(nl->sock, (struct sockaddr *)&nl->sockaddr, &nl->socklen);
if(rc < 0)
goto fail;
return 0;
fail:
{
int saved_errno = errno;
close(nl->sock);
nl->sock = -1;
errno = saved_errno;
return -1;
}
}
static int
netlink_read(struct netlink *nl, struct netlink *nl_ignore, int answer,
int (*fn)(struct nlmsghdr *nh, void *data), void *data)
{
/* 'answer' must be true when we just have send a request on 'nl_socket' */
/* 'nl_ignore' is used in kernel_callback to ignore message originating */
/* from 'nl_command' while reading 'nl_listen' */
/* Return code : */
/* -1 : error */
/* 0 : if(fn) found_interesting; else found_ack; */
/* 1 : only if(fn) nothing interesting has been found */
/* 2 : nothing found, retry */
int err;
struct msghdr msg;
struct sockaddr_nl nladdr;
struct iovec iov;
struct nlmsghdr *nh;
int len;
int interesting = 0;
int done = 0;
char buf[8192];
memset(&nladdr, 0, sizeof(nladdr));
nladdr.nl_family = AF_NETLINK;
memset(&msg, 0, sizeof(msg));
msg.msg_name = &nladdr;
msg.msg_namelen = sizeof(nladdr);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
iov.iov_base = &buf;
do {
iov.iov_len = sizeof(buf);
len = recvmsg(nl->sock, &msg, 0);
if(len < 0 && (errno == EAGAIN || errno == EINTR)) {
int rc;
rc = wait_for_fd(0, nl->sock, 100);
if(rc <= 0) {
if(rc == 0)
errno = EAGAIN;
} else {
len = recvmsg(nl->sock, &msg, 0);
}
}
if(len < 0) {
perror("netlink_read: recvmsg()");
return 2;
} else if(len == 0) {
fprintf(stderr, "netlink_read: EOF\n");
goto socket_error;
} else if(msg.msg_namelen != nl->socklen) {
fprintf(stderr,
"netlink_read: unexpected sender address length (%d)\n",
msg.msg_namelen);
goto socket_error;
} else if(nladdr.nl_pid != 0) {
kdebugf("netlink_read: message not sent by kernel.\n");
return 2;
}
kdebugf("Netlink message: ");
for(nh = (struct nlmsghdr *)buf;
NLMSG_OK(nh, len);
nh = NLMSG_NEXT(nh, len)) {
kdebugf("%s", (nh->nlmsg_flags & NLM_F_MULTI) ? "[multi] " : "");
if(!answer)
done = 1;
if(nl_ignore && nh->nlmsg_pid == nl_ignore->sockaddr.nl_pid) {
kdebugf("(ignore), ");
continue;
} else if(answer && (nh->nlmsg_pid != nl->sockaddr.nl_pid ||
nh->nlmsg_seq != nl->seqno)) {
kdebugf("(wrong seqno %d %d /pid %d %d), ",
nh->nlmsg_seq, nl->seqno,
nh->nlmsg_pid, nl->sockaddr.nl_pid);
continue;
} else if(nh->nlmsg_type == NLMSG_DONE) {
kdebugf("(done)\n");
done = 1;
break;
} else if(nh->nlmsg_type == NLMSG_ERROR) {
struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(nh);
if(err->error == 0) {
kdebugf("(ACK)\n");
return 0;
} else {
kdebugf("netlink_read: %s\n", strerror(-err->error));
errno = -err->error;
return -1;
}
} else if(fn) {
kdebugf("(msg -> \"");
err = fn(nh,data);
kdebugf("\" %d), ", err);
if(err < 0) return err;
interesting = interesting || err;
continue;
}
kdebugf(", ");
}
kdebugf("\n");
if(msg.msg_flags & MSG_TRUNC)
fprintf(stderr, "netlink_read: message truncated\n");
} while (!done);
return interesting;
socket_error:
close(nl->sock);
nl->sock = -1;
errno = EIO;
return -1;
}
static int
netlink_talk(struct nlmsghdr *nh)
{
int rc;
struct sockaddr_nl nladdr;
struct msghdr msg;
struct iovec iov;
memset(&nladdr, 0, sizeof(nladdr));
nladdr.nl_family = AF_NETLINK;
nladdr.nl_pid = 0;
memset(&msg, 0, sizeof(msg));
msg.msg_name = &nladdr;
msg.msg_namelen = sizeof(nladdr);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
iov.iov_base = nh;
iov.iov_len = nh->nlmsg_len;
nh->nlmsg_flags |= NLM_F_ACK;
nh->nlmsg_seq = ++nl_command.seqno;
rc = sendmsg(nl_command.sock, &msg, 0);
if(rc < 0 && (errno == EAGAIN || errno == EINTR)) {
rc = wait_for_fd(1, nl_command.sock, 100);
if(rc <= 0) {
if(rc == 0)
errno = EAGAIN;
} else {
rc = sendmsg(nl_command.sock, &msg, 0);
}
}
if(rc < nh->nlmsg_len) {
int saved_errno = errno;
perror("sendmsg");
errno = saved_errno;
return -1;
}
rc = netlink_read(&nl_command, NULL, 1, NULL, NULL); /* ACK */
return rc;
}
static int
netlink_send_dump(int type, void *data, int len) {
struct sockaddr_nl nladdr;
struct msghdr msg;
struct iovec iov[2];
union {
char raw[NLMSG_ALIGN(sizeof(struct nlmsghdr))];
struct nlmsghdr nh;
} buf;
int rc;
/* At least we should send an 'struct rtgenmsg' */
if(data == NULL || len == 0) {
errno = EIO;
return -1;
}
/* And more : using anything else that 'struct rtgenmsg' is currently */
/* ignored by the linux kernel (today: 2.6.21) because NLM_F_MATCH is */
/* not yet implemented */
memset(&nladdr, 0, sizeof(nladdr));
nladdr.nl_family = AF_NETLINK;
memset(&msg, 0, sizeof(msg));
msg.msg_name = &nladdr;
msg.msg_namelen = sizeof(nladdr);
msg.msg_iov = iov;
msg.msg_iovlen = 2;
iov[0].iov_base = buf.raw;
iov[0].iov_len = sizeof(buf.raw);
iov[1].iov_base = data;
iov[1].iov_len = len;
memset(buf.raw, 0, sizeof(buf.raw));
buf.nh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST;
buf.nh.nlmsg_type = type;
buf.nh.nlmsg_seq = ++nl_command.seqno;
buf.nh.nlmsg_len = NLMSG_LENGTH(len);
rc = sendmsg(nl_command.sock, &msg, 0);
if(rc < buf.nh.nlmsg_len) {
int saved_errno = errno;
perror("sendmsg");
errno = saved_errno;
return -1;
}
return 0;
}
int
kernel_setup(int setup)
{
int rc;
if(setup) {
if(export_table < 0)
export_table = RT_TABLE_MAIN;
if(import_table < 0)
import_table = RT_TABLE_MAIN;
dgram_socket = socket(PF_INET, SOCK_DGRAM, 0);
if(dgram_socket < 0)
return -1;
rc = netlink_socket(&nl_command, 0);
if(rc < 0) {
perror("netlink_socket(0)");
return -1;
}
nl_setup = 1;
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_ipv4_forwarding =
read_proc("/proc/sys/net/ipv4/conf/all/forwarding");
if(old_ipv4_forwarding < 0) {
perror("Couldn't read IPv4 forwarding knob.");
return -1;
}
rc = write_proc("/proc/sys/net/ipv4/conf/all/forwarding", 1);
if(rc < 0) {
perror("Couldn't write IPv4 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;
}
old_rp_filter =
read_proc("/proc/sys/net/ipv4/conf/all/rp_filter");
if(old_rp_filter < 0) {
perror("Couldn't read rp_filter knob.");
return -1;
}
rc = write_proc("/proc/sys/net/ipv4/conf/all/rp_filter", 0);
if(rc < 0) {
perror("Couldn't write rp_filter knob.");
return -1;
}
return 1;
} else {
close(dgram_socket);
dgram_socket = -1;
if(old_forwarding >= 0) {
rc = write_proc("/proc/sys/net/ipv6/conf/all/forwarding",
old_forwarding);
if(rc < 0) {
perror("Couldn't write forwarding knob.\n");
return -1;
}
}
if(old_ipv4_forwarding >= 0) {
rc = write_proc("/proc/sys/net/ipv4/conf/all/forwarding",
old_ipv4_forwarding);
if(rc < 0) {
perror("Couldn't write IPv4 forwarding 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;
}
}
if(old_rp_filter >= 0) {
rc = write_proc("/proc/sys/net/ipv4/conf/all/rp_filter",
old_accept_redirects);
if(rc < 0) {
perror("Couldn't write rp_filter knob.\n");
return -1;
}
}
close(nl_command.sock);
nl_command.sock = -1;
nl_setup = 0;
return 1;
}
}
int
kernel_setup_socket(int setup)
{
int rc;
if(setup) {
rc = netlink_socket(&nl_listen,
RTMGRP_IPV6_ROUTE | RTMGRP_IPV4_ROUTE
| RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR);
if(rc < 0) {
perror("netlink_socket(RTMGRP_ROUTE | RTMGRP_LINK | RTMGRP_IFADDR)");
kernel_socket = -1;
return -1;
}
kernel_socket = nl_listen.sock;
return 1;
} else {
close(nl_listen.sock);
nl_listen.sock = -1;
kernel_socket = -1;
return 1;
}
}
int
kernel_setup_interface(int setup, const char *ifname, int ifindex)
{
return 1;
}
int
kernel_interface_operational(const char *ifname, int ifindex)
{
struct ifreq req;
int rc;
int flags = link_detect ? (IFF_UP | IFF_RUNNING) : IFF_UP;
memset(&req, 0, sizeof(req));
strncpy(req.ifr_name, ifname, sizeof(req.ifr_name));
rc = ioctl(dgram_socket, SIOCGIFFLAGS, &req);
if(rc < 0)
return -1;
return ((req.ifr_flags & flags) == flags);
}
int
kernel_interface_ipv4(const char *ifname, int ifindex, unsigned char *addr_r)
{
struct ifreq req;
int rc;
memset(&req, 0, sizeof(req));
strncpy(req.ifr_name, ifname, sizeof(req.ifr_name));
req.ifr_addr.sa_family = AF_INET;
rc = ioctl(dgram_socket, SIOCGIFADDR, &req);
if(rc < 0)
return -1;
memcpy(addr_r, &((struct sockaddr_in*)&req.ifr_addr)->sin_addr, 4);
return 1;
}
int
kernel_interface_mtu(const char *ifname, int ifindex)
{
struct ifreq req;
int rc;
memset(&req, 0, sizeof(req));
strncpy(req.ifr_name, ifname, sizeof(req.ifr_name));
rc = ioctl(dgram_socket, SIOCGIFMTU, &req);
if(rc < 0)
return -1;
return req.ifr_mtu;
}
static int
isbridge(const char *ifname, int ifindex)
{
char buf[256];
int rc, i;
unsigned long args[3];
int indices[256];
rc = snprintf(buf, 256, "/sys/class/net/%s", ifname);
if(rc < 0 || rc >= 256)
goto fallback;
if(access(buf, R_OK) < 0)
goto fallback;
rc = snprintf(buf, 256, "/sys/class/net/%s/bridge", ifname);
if(rc < 0 || rc >= 256)
goto fallback;
if(access(buf, F_OK) >= 0)
return 1;
else if(errno == ENOENT)
return 0;
fallback:
args[0] = BRCTL_GET_BRIDGES;
args[1] = (unsigned long)indices;
args[2] = 256;
rc = ioctl(dgram_socket, SIOCGIFBR, args);
if(rc < 0) {
if(errno == ENOPKG)
return 0;
else
return -1;
}
for(i = 0; i < rc; i++) {
if(indices[i] == ifindex)
return 1;
}
return 0;
}
int
kernel_interface_wireless(const char *ifname, int ifindex)
{
#ifndef SIOCGIWNAME
#define SIOCGIWNAME 0x8B01
#endif
struct ifreq req;
int rc;
if(isbridge(ifname, ifindex) != 0) {
/* Give up. */
return -1;
}
memset(&req, 0, sizeof(req));
strncpy(req.ifr_name, ifname, sizeof(req.ifr_name));
rc = ioctl(dgram_socket, SIOCGIWNAME, &req);
if(rc < 0) {
if(errno == EOPNOTSUPP || errno == EINVAL)
rc = 0;
else {
perror("ioctl(SIOCGIWNAME)");
rc = -1;
}
} else {
rc = 1;
}
return rc;
}
/* Sorry for that, but I haven't managed to get <linux/wireless.h>
to include cleanly. */
#define SIOCGIWFREQ 0x8B05
struct iw_freq {
int m;
short e;
unsigned char i;
unsigned char flags;
};
struct iwreq_subset {
union {
char ifrn_name[IFNAMSIZ];
} ifr_ifrn;
union {
struct iw_freq freq;
} u;
};
static int
freq_to_chan(struct iw_freq *freq)
{
int m = freq->m, e = freq->e;
/* If exponent is 0, assume the channel is encoded directly in m. */
if(e == 0 && m > 0 && m < 254)
return m;
if(e <= 6) {
int mega, step, c, i;
/* This encodes 1 MHz */
mega = 1000000;
for(i = 0; i < e; i++)
mega /= 10;
/* Channels 1 through 13 are 5 MHz apart, with channel 1 at 2412. */
step = 5 * mega;
c = 1 + (m - 2412 * mega + step / 2) / step;
if(c >= 1 && c <= 13)
return c;
/* Channel 14 is at 2484 MHz */
if(c >= 14 && m < 2484 * mega + step / 2)
return 14;
/* 802.11a channel 36 is at 5180 MHz */
c = 36 + (m - 5180 * mega + step / 2) / step;
if(c >= 34 && c <= 165)
return c;
}
errno = ENOENT;
return -1;
}
int
kernel_interface_channel(const char *ifname, int ifindex)
{
struct iwreq_subset iwreq;
int rc;
memset(&iwreq, 0, sizeof(iwreq));
strncpy(iwreq.ifr_ifrn.ifrn_name, ifname, IFNAMSIZ);
rc = ioctl(dgram_socket, SIOCGIWFREQ, &iwreq);
if(rc >= 0)
return freq_to_chan(&iwreq.u.freq);
else
return -1;
}
int
kernel_route(int operation, const unsigned char *dest, unsigned short plen,
const unsigned char *gate, int ifindex, unsigned int metric,
const unsigned char *newgate, int newifindex,
unsigned int newmetric)
{
union { char raw[1024]; struct nlmsghdr nh; } buf;
struct rtmsg *rtm;
struct rtattr *rta;
int len = sizeof(buf.raw);
int rc, ipv4;
if(!nl_setup) {
fprintf(stderr,"kernel_route: netlink not initialized.\n");
errno = EIO;
return -1;
}
/* if the socket has been closed after an IO error, */
/* we try to re-open it. */
if(nl_command.sock < 0) {
rc = netlink_socket(&nl_command, 0);
if(rc < 0) {
int olderrno = errno;
perror("kernel_route: netlink_socket()");
errno = olderrno;
return -1;
}
}
/* Check that the protocol family is consistent. */
if(plen >= 96 && v4mapped(dest)) {
if(!v4mapped(gate)) {
errno = EINVAL;
return -1;
}
} else {
if(v4mapped(gate)) {
errno = EINVAL;
return -1;
}
}
ipv4 = v4mapped(gate);
if(operation == ROUTE_MODIFY) {
if(newmetric == metric && memcmp(newgate, gate, 16) == 0 &&
newifindex == ifindex)
return 0;
/* It would be better to add the new route before removing the
old one, to avoid losing packets. However, this causes
problems with non-multipath kernels, which sometimes
silently fail the request, causing "stuck" routes. Let's
stick with the naive approach, and hope that the window is
small enough to be negligible. */
kernel_route(ROUTE_FLUSH, dest, plen,
gate, ifindex, metric,
NULL, 0, 0);
rc = kernel_route(ROUTE_ADD, dest, plen,
newgate, newifindex, newmetric,
NULL, 0, 0);
if(rc < 0) {
if(errno == EEXIST)
rc = 1;
/* Should we try to re-install the flushed route on failure?
Error handling is hard. */
}
return rc;
}
kdebugf("kernel_route: %s %s/%d metric %d dev %d nexthop %s\n",
operation == ROUTE_ADD ? "add" :
operation == ROUTE_FLUSH ? "flush" : "???",
format_address(dest), plen, metric, ifindex,
format_address(gate));
/* Unreachable default routes cause all sort of weird interactions;
ignore them. */
if(metric >= KERNEL_INFINITY && (plen == 0 || (ipv4 && plen == 96)))
return 0;
memset(buf.raw, 0, sizeof(buf.raw));
if(operation == ROUTE_ADD) {
buf.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL;
buf.nh.nlmsg_type = RTM_NEWROUTE;
} else {
buf.nh.nlmsg_flags = NLM_F_REQUEST;
buf.nh.nlmsg_type = RTM_DELROUTE;
}
rtm = NLMSG_DATA(&buf.nh);
rtm->rtm_family = ipv4 ? AF_INET : AF_INET6;
rtm->rtm_dst_len = ipv4 ? plen - 96 : plen;
rtm->rtm_table = export_table;
rtm->rtm_scope = RT_SCOPE_UNIVERSE;
if(metric < KERNEL_INFINITY)
rtm->rtm_type = RTN_UNICAST;
else
rtm->rtm_type = RTN_UNREACHABLE;
rtm->rtm_protocol = RTPROT_BABEL;
rtm->rtm_flags |= RTNH_F_ONLINK;
rta = RTM_RTA(rtm);
if(ipv4) {
rta = RTA_NEXT(rta, len);
rta->rta_len = RTA_LENGTH(sizeof(struct in_addr));
rta->rta_type = RTA_DST;
memcpy(RTA_DATA(rta), dest + 12, sizeof(struct in_addr));
} else {
rta = RTA_NEXT(rta, len);
rta->rta_len = RTA_LENGTH(sizeof(struct in6_addr));
rta->rta_type = RTA_DST;
memcpy(RTA_DATA(rta), dest, sizeof(struct in6_addr));
}
rta = RTA_NEXT(rta, len);
rta->rta_len = RTA_LENGTH(sizeof(int));
rta->rta_type = RTA_PRIORITY;
if(metric < KERNEL_INFINITY) {
*(int*)RTA_DATA(rta) = metric;
rta = RTA_NEXT(rta, len);
rta->rta_len = RTA_LENGTH(sizeof(int));
rta->rta_type = RTA_OIF;
*(int*)RTA_DATA(rta) = ifindex;
if(ipv4) {
rta = RTA_NEXT(rta, len);
rta->rta_len = RTA_LENGTH(sizeof(struct in_addr));
rta->rta_type = RTA_GATEWAY;
memcpy(RTA_DATA(rta), gate + 12, sizeof(struct in_addr));
} else {
rta = RTA_NEXT(rta, len);
rta->rta_len = RTA_LENGTH(sizeof(struct in6_addr));
rta->rta_type = RTA_GATEWAY;
memcpy(RTA_DATA(rta), gate, sizeof(struct in6_addr));
}
} else {
*(int*)RTA_DATA(rta) = -1;
}
buf.nh.nlmsg_len = (char*)rta + rta->rta_len - buf.raw;
return netlink_talk(&buf.nh);
}
static int
parse_kernel_route_rta(struct rtmsg *rtm, int len, struct kernel_route *route)
{
int table = rtm->rtm_table;
struct rtattr *rta= RTM_RTA(rtm);;
len -= NLMSG_ALIGN(sizeof(*rtm));
memset(&route->prefix, 0, sizeof(struct in6_addr));
memset(&route->gw, 0, sizeof(struct in6_addr));
route->plen = rtm->rtm_dst_len;
if(rtm->rtm_family == AF_INET) {
const unsigned char zeroes[4] = {0, 0, 0, 0};
v4tov6(route->prefix, zeroes);
route->plen += 96;
}
route->metric = 0;
route->ifindex = 0;
route->proto = rtm->rtm_protocol;
#define COPY_ADDR(d, s) \
do { \
if(rtm->rtm_family == AF_INET6) \
memcpy(d, s, 16); \
else if(rtm->rtm_family == AF_INET) \
v4tov6(d, s); \
else \
return -1; \
} while(0)
while(RTA_OK(rta, len)) {
switch (rta->rta_type) {
case RTA_DST:
COPY_ADDR(route->prefix, RTA_DATA(rta));
break;
case RTA_GATEWAY:
COPY_ADDR(route->gw, RTA_DATA(rta));
break;
case RTA_OIF:
route->ifindex = *(int*)RTA_DATA(rta);
break;
case RTA_PRIORITY:
route->metric = *(int*)RTA_DATA(rta);
if(route->metric < 0 || route->metric > KERNEL_INFINITY)
route->metric = KERNEL_INFINITY;
break;
case RTA_TABLE:
table = *(int*)RTA_DATA(rta);
break;
default:
break;
}
rta = RTA_NEXT(rta, len);
}
#undef COPY_ADDR
if(table != import_table)
return -1;
return 0;
}
static void
print_kernel_route(int add, int protocol, int type,
struct kernel_route *route)
{
char ifname[IFNAMSIZ];
char addr_prefix[INET6_ADDRSTRLEN];
char addr_gw[INET6_ADDRSTRLEN];
if(!inet_ntop(AF_INET6, route->prefix,
addr_prefix, sizeof(addr_prefix)) ||
!inet_ntop(AF_INET6,route->gw, addr_gw, sizeof(addr_gw)) ||
!if_indextoname(route->ifindex, ifname)) {
kdebugf("Couldn't format kernel route for printing.");
return;
}
kdebugf("%s kernel route: dest: %s/%d gw: %s metric: %d if: %s "
"(proto: %d, type: %d)",
add == RTM_NEWROUTE ? "Add" : "Delete",
addr_prefix, route->plen, addr_gw, route->metric, ifname,
protocol, type);
}
static int
filter_kernel_routes(struct nlmsghdr *nh, void *data)
{
int rc;
struct kernel_route *current_route;
struct kernel_route route;
int maxroutes = 0;
struct kernel_route *routes = NULL;
int *found = NULL;
int len;
struct rtmsg *rtm;
if(data) {
void **args = (void**)data;
maxroutes = *(int*)args[0];
routes = (struct kernel_route *)args[1];
found = (int*)args[2];
}
len = nh->nlmsg_len;
if(data && *found >= maxroutes)
return 0;
if(nh->nlmsg_type != RTM_NEWROUTE &&
(data || nh->nlmsg_type != RTM_DELROUTE))
return 0;
rtm = (struct rtmsg*)NLMSG_DATA(nh);
len -= NLMSG_LENGTH(0);
if(rtm->rtm_protocol == RTPROT_BABEL)
return 0;
if(rtm->rtm_src_len != 0)
return 0;
if(data)
current_route = &routes[*found];
else
current_route = &route;
rc = parse_kernel_route_rta(rtm, len, current_route);
if(rc < 0)
return 0;
if(martian_prefix(current_route->prefix, current_route->plen))
return 0;
/* Ignore default unreachable routes; no idea where they come from. */
if(current_route->plen == 0 && current_route->metric >= KERNEL_INFINITY)
return 0;
if(debug >= 2) {
if(rc >= 0) {
print_kernel_route(nh->nlmsg_type, rtm->rtm_protocol,
rtm->rtm_type, current_route);
}
}
if (data) *found = (*found)+1;
return 1;
}
/* This function should not return routes installed by us. */
int
kernel_routes(struct kernel_route *routes, int maxroutes)
{
int i, rc;
int maxr = maxroutes;
int found = 0;
void *data[3] = { &maxr, routes, &found };
int families[2] = { AF_INET6, AF_INET };
struct rtgenmsg g;
if(!nl_setup) {
fprintf(stderr,"kernel_routes: netlink not initialized.\n");
errno = EIO;
return -1;
}
if(nl_command.sock < 0) {
rc = netlink_socket(&nl_command, 0);
if(rc < 0) {
perror("kernel_routes: netlink_socket()");
return -1;
}
}
for(i = 0; i < 2; i++) {
memset(&g, 0, sizeof(g));
g.rtgen_family = families[i];
rc = netlink_send_dump(RTM_GETROUTE, &g, sizeof(g));
if(rc < 0)
return -1;
rc = netlink_read(&nl_command, NULL, 1,
filter_kernel_routes, (void *)data);
if(rc < 0)
return -1;
}
return found;
}
static char *
parse_ifname_rta(struct ifinfomsg *info, int len)
{
struct rtattr *rta = IFLA_RTA(info);
char *ifname = NULL;
len -= NLMSG_ALIGN(sizeof(*info));
while(RTA_OK(rta, len)) {
switch (rta->rta_type) {
case IFLA_IFNAME:
ifname = RTA_DATA(rta);
break;
default:
break;
}
rta = RTA_NEXT(rta, len);
}
return ifname;
}
static int
parse_addr_rta(struct ifaddrmsg *addr, int len, struct in6_addr *res)
{
struct rtattr *rta;
len -= NLMSG_ALIGN(sizeof(*addr));
rta = IFA_RTA(addr);
while (RTA_OK(rta, len)) {
switch(rta->rta_type) {
case IFA_LOCAL:
case IFA_ADDRESS:
switch (addr->ifa_family) {
case AF_INET:
if (res)
v4tov6(res->s6_addr, RTA_DATA(rta));
break;
case AF_INET6:
if (res)
memcpy(res->s6_addr, RTA_DATA(rta), 16);
break;
default:
kdebugf("ifaddr: unexpected address family %d\n", addr->ifa_family);
return -1;
break;
}
break;
default:
break;
}
rta = RTA_NEXT(rta, len);
}
return 0;
}
static int
filter_link(struct nlmsghdr *nh, void *data)
{
struct ifinfomsg *info;
int len;
int ifindex;
char *ifname;
unsigned int ifflags;
struct interface *ifp;
len = nh->nlmsg_len;
if(nh->nlmsg_type != RTM_NEWLINK && nh->nlmsg_type != RTM_DELLINK)
return 0;
info = (struct ifinfomsg*)NLMSG_DATA(nh);
len -= NLMSG_LENGTH(0);
ifindex = info->ifi_index;
ifflags = info->ifi_flags;
ifname = parse_ifname_rta(info, len);
if(ifname == NULL)
return 0;
kdebugf("filter_interfaces: link change on if %s(%d): 0x%x\n",
ifname, ifindex, (unsigned)ifflags);
FOR_ALL_INTERFACES(ifp) {
if (strcmp(ifp->name, ifname) == 0)
return 1;
}
return 0;
}
static int
filter_addresses(struct nlmsghdr *nh, void *data)
{
int rc;
int maxroutes = 0;
struct kernel_route *routes = NULL;
struct in6_addr addr;
int *found = NULL;
int len;
struct ifaddrmsg *ifa;
char ifname[IFNAMSIZ];
int ifindex = 0;
int ll = 0;
if (data) {
void **args = (void **)data;
maxroutes = *(int *)args[0];
routes = (struct kernel_route*)args[1];
found = (int *)args[2];
ifindex = args[3] ? *(int*)args[3] : 0;
ll = args[4] ? !!*(int*)args[4] : 0;
}
len = nh->nlmsg_len;
if (data && *found >= maxroutes)
return 0;
if (nh->nlmsg_type != RTM_NEWADDR &&
(data || nh->nlmsg_type != RTM_DELADDR))
return 0;
ifa = (struct ifaddrmsg *)NLMSG_DATA(nh);
len -= NLMSG_LENGTH(0);
rc = parse_addr_rta(ifa, len, &addr);
if (rc < 0)
return 0;
if (ll == !IN6_IS_ADDR_LINKLOCAL(&addr))
return 0;
if (ifindex && ifa->ifa_index != ifindex)
return 0;
kdebugf("found address on interface %s(%d): %s\n",
if_indextoname(ifa->ifa_index, ifname), ifa->ifa_index,
format_address(addr.s6_addr));
if (data) {
struct kernel_route *route = &routes[*found];
memcpy(route->prefix, addr.s6_addr, 16);
route->plen = 128;
route->metric = 0;
route->ifindex = ifa->ifa_index;
route->proto = RTPROT_BABEL_LOCAL;
memset(route->gw, 0, 16);
*found = (*found)+1;
}
return 1;
}
static int
filter_netlink(struct nlmsghdr *nh, void *data)
{
int rc;
int *changed = data;
switch (nh->nlmsg_type) {
case RTM_NEWROUTE:
case RTM_DELROUTE:
rc = filter_kernel_routes(nh, NULL);
if (changed && rc > 0)
*changed |= CHANGE_ROUTE;
return rc;
case RTM_NEWLINK:
case RTM_DELLINK:
rc = filter_link(nh, NULL);
if (changed && rc > 0)
*changed |= CHANGE_LINK;
return rc;
case RTM_NEWADDR:
case RTM_DELADDR:
rc = filter_addresses(nh, NULL);
if (changed && rc > 0)
*changed |= CHANGE_ADDR;
return rc;
default:
kdebugf("filter_netlink: unexpected message type %d\n",
nh->nlmsg_type);
break;
}
return 0;
}
int
kernel_addresses(char *ifname, int ifindex, int ll,
struct kernel_route *routes, int maxroutes)
{
int maxr = maxroutes;
int found = 0;
void *data[] = { &maxr, routes, &found, &ifindex, &ll, NULL };
struct rtgenmsg g;
int rc;
if (!nl_setup) {
fprintf(stderr, "kernel_addresses: netlink not initialized.\n");
errno = ENOSYS;
return -1;
}
if (nl_command.sock < 0) {
rc = netlink_socket(&nl_command, 0);
if (rc < 0) {
int save = errno;
perror("kernel_addresses: netlink_socket()");
errno = save;
return -1;
}
}
memset(&g, 0, sizeof(g));
g.rtgen_family = AF_UNSPEC;
rc = netlink_send_dump(RTM_GETADDR, &g, sizeof(g));
if (rc < 0)
return -1;
rc = netlink_read(&nl_command, NULL, 1, filter_addresses, (void*)data);
if (rc < 0)
return -1;
return found;
}
int
kernel_callback(int (*fn)(int, void*), void *closure)
{
int rc;
int changed = 0;
kdebugf("\nReceived changes in kernel tables.\n");
if(nl_listen.sock < 0) {
rc = kernel_setup_socket(1);
if(rc < 0) {
perror("kernel_callback: kernel_setup_socket(1)");
return -1;
}
}
rc = netlink_read(&nl_listen, &nl_command, 0, filter_netlink, &changed);
if(rc < 0 && nl_listen.sock < 0)
kernel_setup_socket(1);
/* if netlink return 0 (found something interesting) */
/* or -1 (i.e. IO error), we call... back ! */
if(rc)
return fn(changed, closure);
return 0;
}
/*
Copyright (c) 2007 by Grégoire Henry
Copyright (c) 2008, 2009 by Juliusz Chroboczek
Copyright (c) 2010 by Vincent Gross
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 <time.h>
#include <strings.h>
#include <netinet/in.h>
#include <netinet/icmp6.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/sysctl.h>
#include <sys/socket.h>
#include <ifaddrs.h>
#include <net/if.h>
#include <net/if_dl.h>
#include <net/if_media.h>
#include <net/route.h>
#include "babeld.h"
#include "neighbour.h"
#include "kernel.h"
#include "util.h"
static int get_sdl(struct sockaddr_dl *sdl, char *ifname);
static const unsigned char v4prefix[16] =
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0, 0, 0, 0 };
int export_table = -1, import_table = -1;
int
if_eui64(char *ifname, int ifindex, unsigned char *eui)
{
struct sockaddr_dl sdl;
char *tmp = NULL;
if (get_sdl(&sdl, ifname) < 0) {
return -1;
}
tmp = sdl.sdl_data + sdl.sdl_nlen;
if (sdl.sdl_alen == 8) {
memcpy(eui, tmp, 8);
eui[0] ^= 2;
} else if (sdl.sdl_alen == 6) {
memcpy(eui, tmp, 3);
eui[3] = 0xFF;
eui[4] = 0xFE;
memcpy(eui+5, tmp+3, 3);
} else {
return -1;
}
return 0;
}
/* fill sdl with the structure corresponding to ifname.
Warning: make a syscall (and get all interfaces).
return -1 if an error occurs, 0 otherwise. */
static int
get_sdl(struct sockaddr_dl *sdl, char *ifname)
{
int mib[6];
size_t buf_len = 0;
int offset = 0;
char *buffer = NULL;
struct if_msghdr *ifm = NULL;
struct sockaddr_dl *tmp_sdl = NULL;
int rc;
mib[0] = CTL_NET;
mib[1] = PF_ROUTE;
mib[2] = 0;
mib[3] = AF_LINK;
mib[4] = NET_RT_IFLIST;
mib[5] = 0;
rc = sysctl(mib, 6, NULL, &buf_len, NULL, 0);
if(rc < 0)
return -1;
buffer = (char *)malloc(buf_len);
if(buffer == NULL)
return -1;
rc = sysctl(mib, 6, buffer, &buf_len, NULL, 0);
if(rc < 0)
goto fail;
offset = 0;
while (offset < (int) buf_len) {
ifm = (struct if_msghdr *) &buffer[offset];
switch (ifm->ifm_type) {
case RTM_IFINFO:
tmp_sdl = (struct sockaddr_dl *) (ifm + 1);
if (strncmp(ifname, tmp_sdl->sdl_data, tmp_sdl->sdl_nlen) == 0
&& strlen(ifname) == tmp_sdl->sdl_nlen) {
memcpy(sdl, tmp_sdl, sizeof(struct sockaddr_dl));
return 0;
}
default:
break;
}
offset += ifm->ifm_msglen;
}
fail:
free(buffer);
return -1;
}
/* KAME said : "Following two macros are highly depending on KAME Release" */
#define IN6_LINKLOCAL_IFINDEX(a) ((a).s6_addr[2] << 8 | (a).s6_addr[3])
#define SET_IN6_LINKLOCAL_IFINDEX(a, i) \
do { \
(a).s6_addr[2] = ((i) >> 8) & 0xff; \
(a).s6_addr[3] = (i) & 0xff; \
} while (0)
#if defined(__APPLE__)
#define ROUNDUP(a) \
((a) > 0 ? (1 + (((a) - 1) | (sizeof(uint32_t) - 1))) : sizeof(uint32_t))
#else
#define ROUNDUP(a) \
((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
#endif
static int old_forwarding = -1;
static int old_accept_redirects = -1;
static int ifindex_lo = -1;
static int seq;
static int
mask2len(const unsigned char *p, const int size)
{
int i = 0, j;
for(j = 0; j < size; j++, p++) {
if(*p != 0xff)
break;
i += 8;
}
if(j < size) {
switch(*p) {
#define MASKLEN(m, l) case m: do { i += l; break; } while (0)
MASKLEN(0xfe, 7); break;
MASKLEN(0xfc, 6); break;
MASKLEN(0xf8, 5); break;
MASKLEN(0xf0, 4); break;
MASKLEN(0xe0, 3); break;
MASKLEN(0xc0, 2); break;
MASKLEN(0x80, 1); break;
#undef MASKLEN
}
}
return i;
}
static void
plen2mask(int n, struct in6_addr *dest)
{
unsigned char *p;
int i;
static const int pl2m[9] = {
0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff
};
memset(dest, 0, sizeof(struct in6_addr));
p = (u_char *)dest;
for (i = 0; i < 16; i++, p++, n -= 8) {
if (n >= 8) {
*p = 0xff;
continue;
}
*p = pl2m[n];
break;
}
return;
}
int
kernel_setup(int setup)
{
int rc = 0;
int forwarding = 1;
int accept_redirects = 0;
int mib[4];
size_t datasize;
mib[0] = CTL_NET;
mib[1] = AF_INET6;
seq = time(NULL);
mib[2] = IPPROTO_IPV6;
mib[3] = IPV6CTL_FORWARDING;
datasize = sizeof(old_forwarding);
if (setup)
rc = sysctl(mib, 4, &old_forwarding, &datasize,
&forwarding, datasize);
else if (old_forwarding >= 0)
rc = sysctl(mib, 4, NULL, NULL,
&old_forwarding, datasize);
if (rc == -1) {
perror("Couldn't tweak forwarding knob.");
return -1;
}
rc = 0;
mib[2] = IPPROTO_ICMPV6;
#if defined(IPV6CTL_SENDREDIRECTS)
mib[3] = IPV6CTL_SENDREDIRECTS;
#else
mib[3] = ICMPV6CTL_REDIRACCEPT;
#endif
datasize = sizeof(old_accept_redirects);
if (setup)
rc = sysctl(mib, 4, &old_accept_redirects, &datasize,
&accept_redirects, datasize);
else if (old_accept_redirects >= 0)
rc = sysctl(mib, 4, NULL, NULL,
&old_accept_redirects, datasize);
if (rc == -1) {
perror("Couldn't tweak accept_redirects knob.");
return -1;
}
return 1;
}
int
kernel_setup_socket(int setup)
{
int rc;
int zero = 0;
if(setup) {
if(kernel_socket < 0) {
kernel_socket = socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC);
if(kernel_socket < 0)
return -1;
}
rc = setsockopt(kernel_socket, SOL_SOCKET, SO_USELOOPBACK,
&zero, sizeof(zero));
if(rc < 0)
goto error;
return 1;
} else {
close(kernel_socket);
kernel_socket = -1;
return 1;
}
error: {
int savederrno = errno;
perror("setsockopt(kernel_socket)");
close(kernel_socket);
errno = savederrno;
kernel_socket = -1;
return -1;
}
}
int
kernel_setup_interface(int setup, const char *ifname, int ifindex)
{
return 1;
}
int
kernel_interface_operational(const char *ifname, int ifindex)
{
struct ifreq req;
int s, rc;
int flags = link_detect ? (IFF_UP | IFF_RUNNING) : IFF_UP;
s = socket(PF_INET, SOCK_DGRAM, 0);
if(s < 0)
return -1;
memset(&req, 0, sizeof(req));
memset(&req, 0, sizeof(req));
strncpy(req.ifr_name, ifname, sizeof(req.ifr_name));
rc = ioctl(s, SIOCGIFFLAGS, &req);
close(s);
if(rc < 0)
return -1;
return ((req.ifr_flags & flags) == flags);
}
int
kernel_interface_ipv4(const char *ifname, int ifindex, unsigned char *addr_r)
{
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));
req.ifr_addr.sa_family = AF_INET;
rc = ioctl(s, SIOCGIFADDR, &req);
close(s);
if(rc < 0) {
return -1;
}
memcpy(addr_r, &((struct sockaddr_in*)&req.ifr_addr)->sin_addr, 4);
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)
{
struct ifmediareq ifmr;
int s, rc;
s = socket(PF_INET6, SOCK_DGRAM, 0);
memset(&ifmr, 0, sizeof(ifmr));
strncpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name));
rc = ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr);
close(s);
if (rc < 0)
return rc;
if ((ifmr.ifm_active & IFM_NMASK) == IFM_IEEE80211)
return 1;
else
return 0;
}
int
kernel_interface_channel(const char *ifname, int ifindex)
{
errno = ENOSYS;
return -1;
}
int
kernel_route(int operation, const unsigned char *dest, unsigned short plen,
const unsigned char *gate, int ifindex, unsigned int metric,
const unsigned char *newgate, int newifindex,
unsigned int newmetric)
{
struct {
struct rt_msghdr m_rtm;
char m_space[512];
} msg;
char *data = msg.m_space;
int rc, ipv4;
char local6[1][1][16] = IN6ADDR_LOOPBACK_INIT;
char local4[1][1][16] =
{{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x01 }}};
/* Check that the protocol family is consistent. */
if(plen >= 96 && v4mapped(dest)) {
if(!v4mapped(gate)) {
errno = EINVAL;
return -1;
}
ipv4 = 1;
} else {
if(v4mapped(gate)) {
errno = EINVAL;
return -1;
}
ipv4 = 0;
}
if(operation == ROUTE_MODIFY && newmetric == metric &&
memcmp(newgate, gate, 16) == 0 && newifindex == ifindex)
return 0;
if(operation == ROUTE_MODIFY) {
/* Do not use ROUTE_MODIFY when changing to a neighbour.
It is the only way to remove the "gateway" flag. */
if(ipv4 && plen == 128 && memcmp(dest, newgate, 16) == 0) {
kernel_route(ROUTE_FLUSH, dest, plen,
gate, ifindex, metric,
NULL, 0, 0);
return kernel_route(ROUTE_ADD, dest, plen,
newgate, newifindex, newmetric,
NULL, 0, 0);
} else {
metric = newmetric;
gate = newgate;
ifindex = newifindex;
}
}
kdebugf("kernel_route: %s %s/%d metric %d dev %d nexthop %s\n",
operation == ROUTE_ADD ? "add" :
operation == ROUTE_FLUSH ? "flush" : "change",
format_address(dest), plen, metric, ifindex,
format_address(gate));
if(kernel_socket < 0) kernel_setup_socket(1);
memset(&msg, 0, sizeof(msg));
msg.m_rtm.rtm_version = RTM_VERSION;
switch(operation) {
case ROUTE_FLUSH:
msg.m_rtm.rtm_type = RTM_DELETE; break;
case ROUTE_ADD:
msg.m_rtm.rtm_type = RTM_ADD; break;
case ROUTE_MODIFY:
msg.m_rtm.rtm_type = RTM_CHANGE; break;
default:
return -1;
};
msg.m_rtm.rtm_index = ifindex;
msg.m_rtm.rtm_flags = RTF_UP;
if(plen == 128) msg.m_rtm.rtm_flags |= RTF_HOST;
if(metric == KERNEL_INFINITY) {
msg.m_rtm.rtm_flags |= RTF_BLACKHOLE;
if(ifindex_lo < 0) {
ifindex_lo = if_nametoindex("lo0");
if(ifindex_lo <= 0)
return -1;
}
msg.m_rtm.rtm_index = ifindex_lo;
}
msg.m_rtm.rtm_seq = ++seq;
msg.m_rtm.rtm_addrs = RTA_DST | RTA_GATEWAY;
if(plen != 128) msg.m_rtm.rtm_addrs |= RTA_NETMASK;
#define PUSHEUI(ifindex) \
do { char ifname[IFNAMSIZ]; \
struct sockaddr_dl *sdl = (struct sockaddr_dl*) data; \
if(!if_indextoname((ifindex), ifname)) \
return -1; \
if(get_sdl(sdl, ifname) < 0) \
return -1; \
data = data + ROUNDUP(sdl->sdl_len); \
} while (0)
#define PUSHADDR(src) \
do { struct sockaddr_in *sin = (struct sockaddr_in*) data; \
sin->sin_len = sizeof(struct sockaddr_in); \
sin->sin_family = AF_INET; \
memcpy(&sin->sin_addr, (src) + 12, 4); \
data = data + ROUNDUP(sin->sin_len); \
} while (0)
#define PUSHADDR6(src) \
do { struct sockaddr_in6 *sin6 = (struct sockaddr_in6*) data; \
sin6->sin6_len = sizeof(struct sockaddr_in6); \
sin6->sin6_family = AF_INET6; \
memcpy(&sin6->sin6_addr, (src), 16); \
if(IN6_IS_ADDR_LINKLOCAL (&sin6->sin6_addr)) \
SET_IN6_LINKLOCAL_IFINDEX (sin6->sin6_addr, ifindex); \
data = data + ROUNDUP(sin6->sin6_len); \
} while (0)
/* KAME ipv6 stack does not support IPv4 mapped IPv6, so we have to
* duplicate the codepath */
if(ipv4) {
PUSHADDR(dest);
if (metric == KERNEL_INFINITY) {
PUSHADDR(**local4);
} else if(plen == 128 && memcmp(dest+12, gate+12, 4) == 0) {
#if defined(RTF_CLONING)
msg.m_rtm.rtm_flags |= RTF_CLONING;
#endif
PUSHEUI(ifindex);
} else {
msg.m_rtm.rtm_flags |= RTF_GATEWAY;
PUSHADDR(gate);
}
if((msg.m_rtm.rtm_addrs & RTA_NETMASK) != 0) {
struct in6_addr tmp_sin6_addr;
plen2mask(plen, &tmp_sin6_addr);
PUSHADDR((char *)&tmp_sin6_addr);
}
} else {
PUSHADDR6(dest);
if (metric == KERNEL_INFINITY) {
PUSHADDR6(**local6);
} else {
msg.m_rtm.rtm_flags |= RTF_GATEWAY;
PUSHADDR6(gate);
}
if((msg.m_rtm.rtm_addrs & RTA_NETMASK) != 0) {
struct in6_addr tmp_sin6_addr;
plen2mask(plen, &tmp_sin6_addr);
PUSHADDR6((char*)&tmp_sin6_addr);
}
}
#undef PUSHEUI
#undef PUSHADDR
#undef PUSHADDR6
msg.m_rtm.rtm_msglen = data - (char *)&msg;
rc = write(kernel_socket, (char*)&msg, msg.m_rtm.rtm_msglen);
if (rc < msg.m_rtm.rtm_msglen)
return -1;
return 1;
}
static void
print_kernel_route(int add, struct kernel_route *route)
{
char ifname[IFNAMSIZ];
if(!if_indextoname(route->ifindex, ifname))
memcpy(ifname,"unk",4);
fprintf(stderr,
"%s kernel route: dest: %s gw: %s metric: %d if: %s(%d) \n",
add == RTM_ADD ? "Add" :
add == RTM_DELETE ? "Delete" : "Change",
format_prefix(route->prefix, route->plen),
format_address(route->gw),
route->metric,
ifname, route->ifindex
);
}
static int
parse_kernel_route(const struct rt_msghdr *rtm, struct kernel_route *route)
{
struct sockaddr *sa;
void *rta = (void*)rtm + sizeof(struct rt_msghdr);
uint32_t excluded_flags = 0;
if(ifindex_lo < 0) {
ifindex_lo = if_nametoindex("lo0");
if(ifindex_lo <= 0)
return -1;
}
memset(route, 0, sizeof(*route));
route->metric = 0;
route->ifindex = rtm->rtm_index;
#if defined(RTF_IFSCOPE)
/* Filter out kernel route on OS X */
excluded_flags |= RTF_IFSCOPE;
#endif
#if defined(RTF_MULTICAST)
/* Filter out multicast route on others BSD */
excluded_flags |= RTF_MULTICAST;
#endif
/* Prefix */
if(!(rtm->rtm_addrs & RTA_DST))
return -1;
sa = (struct sockaddr *)rta;
rta += ROUNDUP(sa->sa_len);
if(sa->sa_family == AF_INET6) {
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
memcpy(route->prefix, &sin6->sin6_addr, 16);
if(IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)
|| IN6_IS_ADDR_MC_LINKLOCAL(&sin6->sin6_addr))
return -1;
} else if(sa->sa_family == AF_INET) {
struct sockaddr_in *sin = (struct sockaddr_in *)sa;
#if defined(IN_LINKLOCAL)
if(IN_LINKLOCAL(ntohl(sin->sin_addr.s_addr)))
return -1;
#endif
if(IN_MULTICAST(ntohl(sin->sin_addr.s_addr)))
return -1;
v4tov6(route->prefix, (unsigned char *)&sin->sin_addr);
} else {
return -1;
}
/* Gateway */
if(!(rtm->rtm_addrs & RTA_GATEWAY))
return -1;
sa = (struct sockaddr *)rta;
rta += ROUNDUP(sa->sa_len);
if(sa->sa_family == AF_INET6) {
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
memcpy(route->gw, &sin6->sin6_addr, 16);
if(IN6_IS_ADDR_LINKLOCAL (&sin6->sin6_addr)) {
route->ifindex = IN6_LINKLOCAL_IFINDEX(sin6->sin6_addr);
SET_IN6_LINKLOCAL_IFINDEX(sin6->sin6_addr, 0);
}
} else if(sa->sa_family == AF_INET) {
struct sockaddr_in *sin = (struct sockaddr_in *)sa;
v4tov6(route->gw, (unsigned char *)&sin->sin_addr);
}
if(route->ifindex == ifindex_lo)
return -1;
/* Netmask */
if((rtm->rtm_addrs & RTA_NETMASK) != 0) {
sa = (struct sockaddr *)rta;
rta += ROUNDUP(sa->sa_len);
if(!v4mapped(route->prefix)) {
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
route->plen = mask2len((unsigned char*)&sin6->sin6_addr, 16);
} else {
struct sockaddr_in *sin = (struct sockaddr_in *)sa;
route->plen = mask2len((unsigned char*)&sin->sin_addr, 4);
}
}
if(v4mapped(route->prefix)) route->plen += 96;
if(rtm->rtm_flags & RTF_HOST) route->plen = 128;
return 0;
}
int
kernel_routes(struct kernel_route *routes, int maxroutes)
{
int mib[6];
char *buf, *p;
size_t len;
struct rt_msghdr *rtm;
int rc, i;
mib[0] = CTL_NET;
mib[1] = PF_ROUTE;
mib[2] = 0;
mib[3] = AF_UNSPEC; /* Address family */
mib[4] = NET_RT_DUMP; /* Dump the kernel routing table */
mib[5] = 0; /* No flags */
rc = sysctl(mib, 6, NULL, &len, NULL, 0);
if (rc < 0) {
perror("kernel_routes(len)");
return -1;
}
buf = malloc(len);
if(!buf) {
perror("kernel_routes(malloc)");
return -1;
}
rc = sysctl(mib, 6, buf, &len, NULL, 0);
if (rc < 0) {
perror("kernel_routes(dump)");
goto fail;
}
i = 0;
p = buf;
while(p < buf + len && i < maxroutes) {
rtm = (struct rt_msghdr*)p;
rc = parse_kernel_route(rtm, &routes[i]);
if(rc)
goto cont;
if(debug > 2)
print_kernel_route(1,&routes[i]);
i++;
cont:
p += rtm->rtm_msglen;
}
free(buf);
return i;
fail:
free(buf);
return -1;
}
static int
socket_read(int sock)
{
int rc;
struct {
struct rt_msghdr rtm;
struct sockaddr_storage addr[RTAX_MAX];
} buf;
rc = read(sock, &buf, sizeof(buf));
if(rc <= 0) {
perror("kernel_callback(read)");
return 0;
}
if(buf.rtm.rtm_msglen != rc) {
kdebugf("kernel_callback(length)\n");
return -1;
}
if(buf.rtm.rtm_type == RTM_ADD ||
buf.rtm.rtm_type == RTM_DELETE ||
buf.rtm.rtm_type == RTM_CHANGE) {
struct kernel_route route;
if(buf.rtm.rtm_errno)
return 0;
rc = parse_kernel_route(&buf.rtm, &route);
if(rc < 0)
return 0;
if(debug > 2)
print_kernel_route(1,&route);
return 1;
}
return 0;
}
int
kernel_addresses(char *ifname, int ifindex, int ll,
struct kernel_route *routes, int maxroutes)
{
struct ifaddrs *ifa, *ifap;
int rc, i;
rc = getifaddrs(&ifa);
if(rc < 0)
return -1;
ifap = ifa;
i = 0;
while(ifap && i < maxroutes) {
if((ifname != NULL && strcmp(ifap->ifa_name, ifname) != 0))
goto next;
if(ifap->ifa_addr->sa_family == AF_INET6) {
struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)ifap->ifa_addr;
if(!!ll != !!IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr))
goto next;
memcpy(routes[i].prefix, &sin6->sin6_addr, 16);
if(ll)
/* This a perfect example of counter-productive optimisation :
KAME encodes interface index onto bytes 2 and 3, so we have to
reset those bytes to 0 before passing them to babeld. */
memset(routes[i].prefix + 2, 0, 2);
routes[i].plen = 128;
routes[i].metric = 0;
routes[i].ifindex = ifindex;
routes[i].proto = RTPROT_BABEL_LOCAL;
memset(routes[i].gw, 0, 16);
i++;
} else if(ifap->ifa_addr->sa_family == AF_INET) {
struct sockaddr_in *sin = (struct sockaddr_in*)ifap->ifa_addr;
if(ll)
goto next;
#if defined(IN_LINKLOCAL)
if(IN_LINKLOCAL(htonl(sin->sin_addr.s_addr)))
goto next;
#endif
memcpy(routes[i].prefix, v4prefix, 12);
memcpy(routes[i].prefix + 12, &sin->sin_addr, 4);
routes[i].plen = 128;
routes[i].metric = 0;
routes[i].ifindex = ifindex;
routes[i].proto = RTPROT_BABEL_LOCAL;
memset(routes[i].gw, 0, 16);
i++;
}
next:
ifap = ifap->ifa_next;
}
freeifaddrs(ifa);
return i;
}
int
kernel_callback(int (*fn)(int, void*), void *closure)
{
int rc;
if(kernel_socket < 0) kernel_setup_socket(1);
kdebugf("Reading kernel table modification.");
rc = socket_read(kernel_socket);
if(rc)
return fn(~0, closure);
return 0;
}
/* Local Variables: */
/* c-basic-offset: 4 */
/* indent-tabs-mode: nil */
/* End: */
/*
Copyright (c) 2008 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include "babeld.h"
#include "interface.h"
#include "source.h"
#include "neighbour.h"
#include "xroute.h"
#include "route.h"
#include "util.h"
#include "local.h"
#ifdef NO_LOCAL_INTERFACE
int dummy;
#else
int
local_read(int s)
{
int rc;
char buf[500];
/* Ignore anything that comes in, except for EOF */
rc = read(s, buf, 500);
if(rc <= 0)
return rc;
return 1;
}
static int
write_timeout(int fd, const void *buf, int len)
{
int n = 0, rc = 0;
const char *b = buf;
while(n < len) {
rc = write(fd, b + n, len - n);
if(rc < 0) {
if(errno == EAGAIN || errno == EINTR) {
rc = wait_for_fd(1, fd, 100);
if(rc > 0) {
rc = write(fd, b + n, len - n);
}
}
}
if(rc > 0)
n += rc;
else
break;
}
if(n >= len)
return 1;
else {
if(rc >= 0)
errno = EAGAIN;
return -1;
}
}
void
local_notify_self()
{
char buf[512];
int rc;
if(local_socket < 0)
return;
rc = snprintf(buf, 512, "add self alamakota id %s\n",
format_eui64(myid));
if(rc < 0 || rc >= 512)
goto fail;
rc = write_timeout(local_socket, buf, rc);
if(rc < 0)
goto fail;
return;
fail:
shutdown(local_socket, 1);
return;
}
static const char *
local_kind(int kind)
{
switch(kind) {
case LOCAL_FLUSH: return "flush";
case LOCAL_CHANGE: return "change";
case LOCAL_ADD: return "add";
default: return "???";
}
}
void
local_notify_neighbour(struct neighbour *neigh, int kind)
{
char buf[512];
int rc;
if(local_socket < 0)
return;
rc = snprintf(buf, 512,
"%s neighbour %lx address %s "
"if %s reach %04x rxcost %d txcost %d cost %d\n",
local_kind(kind),
/* Neighbours never move around in memory , so we can use the
address as a unique identifier. */
(unsigned long int)neigh,
format_address(neigh->address),
neigh->ifp->name,
neigh->reach,
neighbour_rxcost(neigh),
neighbour_txcost(neigh),
neighbour_cost(neigh));
if(rc < 0 || rc >= 512)
goto fail;
rc = write_timeout(local_socket, buf, rc);
if(rc < 0)
goto fail;
return;
fail:
shutdown(local_socket, 1);
return;
}
void
local_notify_xroute(struct xroute *xroute, int kind)
{
char buf[512];
int rc;
if(local_socket < 0)
return;
rc = snprintf(buf, 512, "%s xroute %s prefix %s metric %d\n",
local_kind(kind),
format_prefix(xroute->prefix, xroute->plen),
format_prefix(xroute->prefix, xroute->plen),
xroute->metric);
if(rc < 0 || rc >= 512)
goto fail;
rc = write_timeout(local_socket, buf, rc);
if(rc < 0)
goto fail;
return;
fail:
shutdown(local_socket, 1);
return;
}
void
local_notify_route(struct babel_route *route, int kind)
{
char buf[512];
int rc;
if(local_socket < 0)
return;
rc = snprintf(buf, 512,
"%s route %s-%lx prefix %s installed %s "
"id %s metric %d refmetric %d via %s if %s\n",
local_kind(kind),
format_prefix(route->src->prefix, route->src->plen),
(unsigned long)route->neigh,
format_prefix(route->src->prefix, route->src->plen),
route->installed ? "yes" : "no",
format_eui64(route->src->id),
route_metric(route), route->refmetric,
format_address(route->neigh->address),
route->neigh->ifp->name);
if(rc < 0 || rc >= 512)
goto fail;
rc = write_timeout(local_socket, buf, rc);
if(rc < 0)
goto fail;
return;
fail:
shutdown(local_socket, 1);
return;
}
static void
local_notify_xroute_callback(struct xroute *xroute, void *closure)
{
local_notify_xroute(xroute, LOCAL_ADD);
}
static void
local_notify_route_callback(struct babel_route *route, void *closure)
{
local_notify_route(route, LOCAL_ADD);
}
void
local_notify_all()
{
int rc;
struct neighbour *neigh;
const char *header = "BABEL 0.0\n";
if(local_socket < 0)
return;
rc = write_timeout(local_socket, header, strlen(header));
if(rc < 0)
goto fail;
local_notify_self();
FOR_ALL_NEIGHBOURS(neigh) {
local_notify_neighbour(neigh, LOCAL_ADD);
}
for_all_xroutes(local_notify_xroute_callback, NULL);
for_all_routes(local_notify_route_callback, NULL);
return;
fail:
shutdown(local_socket, 1);
return;
}
#endif
/*
Copyright (c) 2008 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;
struct babel_route;
struct xroute;
#define LOCAL_FLUSH 0
#define LOCAL_ADD 1
#define LOCAL_CHANGE 2
#ifndef NO_LOCAL_INTERFACE
int local_read(int s);
void local_notify_self(void);
void local_notify_neighbour(struct neighbour *neigh, int kind);
void local_notify_xroute(struct xroute *xroute, int kind);
void local_notify_route(struct babel_route *route, int kind);
void local_notify_all(void);
#else
#define local_notify_self() do {} while(0)
#define local_notify_neighbour(n, k) do {} while(0)
#define local_notify_xroute(x, k) do {} while(0)
#define local_notify_route(r, k) do {} while(0)
#define local_dump() do {} while 0
#endif
/*
Copyright (c) 2007, 2008 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 <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "babeld.h"
#include "util.h"
#include "net.h"
#include "interface.h"
#include "source.h"
#include "neighbour.h"
#include "route.h"
#include "xroute.h"
#include "resend.h"
#include "message.h"
#include "configuration.h"
#include "kernel.h"
unsigned char packet_header[4] = {42, 2};
int split_horizon = 1;
unsigned short myseqno = 0;
struct timeval seqno_time = {0, 0};
#define UNICAST_BUFSIZE 1024
int unicast_buffered = 0;
unsigned char *unicast_buffer = NULL;
struct neighbour *unicast_neighbour = NULL;
struct timeval unicast_flush_timeout = {0, 0};
static const unsigned char v4prefix[16] =
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0, 0, 0, 0 };
/* Parse a network prefix, encoded in the somewhat baroque compressed
representation used by Babel. Return the number of bytes parsed. */
static int
network_prefix(int ae, int plen, unsigned int omitted,
const unsigned char *p, const unsigned char *dp,
unsigned int len, unsigned char *p_r)
{
unsigned pb;
unsigned char prefix[16];
int ret = -1;
if(plen >= 0)
pb = (plen + 7) / 8;
else if(ae == 1)
pb = 4;
else
pb = 16;
if(pb > 16)
return -1;
memset(prefix, 0, 16);
switch(ae) {
case 0:
ret = 0;
break;
case 1:
if(omitted > 4 || pb > 4 || (pb > omitted && len < pb - omitted))
return -1;
memcpy(prefix, v4prefix, 12);
if(omitted) {
if (dp == NULL || !v4mapped(dp)) return -1;
memcpy(prefix, dp, 12 + omitted);
}
if(pb > omitted) memcpy(prefix + 12 + omitted, p, pb - omitted);
ret = pb - omitted;
break;
case 2:
if(omitted > 16 || (pb > omitted && len < pb - omitted)) return -1;
if(omitted) {
if (dp == NULL || v4mapped(dp)) return -1;
memcpy(prefix, dp, omitted);
}
if(pb > omitted) memcpy(prefix + omitted, p, pb - omitted);
ret = pb - omitted;
break;
case 3:
if(pb > 8 && len < pb - 8) return -1;
prefix[0] = 0xfe;
prefix[1] = 0x80;
if(pb > 8) memcpy(prefix + 8, p, pb - 8);
ret = pb - 8;
break;
default:
return -1;
}
mask_prefix(p_r, prefix, plen < 0 ? 128 : ae == 1 ? plen + 96 : plen);
return ret;
}
static void
parse_route_attributes(const unsigned char *a, int alen,
unsigned char *channels)
{
int type, len, i = 0;
while(i < alen) {
type = a[i];
if(type == 0) {
i++;
continue;
}
if(i + 1 > alen) {
fprintf(stderr, "Received truncated attributes.\n");
return;
}
len = a[i + 1];
if(i + len > alen) {
fprintf(stderr, "Received truncated attributes.\n");
return;
}
if(type == 1) {
/* Nothing. */
} else if(type == 2) {
if(len > DIVERSITY_HOPS) {
fprintf(stderr,
"Received overlong channel information (%d > %d).\n",
len, DIVERSITY_HOPS);
len = DIVERSITY_HOPS;
}
if(memchr(a + i + 2, 0, len) != NULL) {
/* 0 is reserved. */
fprintf(stderr, "Channel information contains 0!");
return;
}
memset(channels, 0, DIVERSITY_HOPS);
memcpy(channels, a + i + 2, len);
} else {
fprintf(stderr, "Received unknown route attribute %d.\n", type);
}
i += len + 2;
}
}
static int
network_address(int ae, const unsigned char *a, unsigned int len,
unsigned char *a_r)
{
return network_prefix(ae, -1, 0, a, NULL, len, a_r);
}
static int
channels_len(unsigned char *channels)
{
unsigned char *p = memchr(channels, 0, DIVERSITY_HOPS);
return p ? (p - channels) : DIVERSITY_HOPS;
}
void
parse_packet(const unsigned char *from, struct interface *ifp,
const unsigned char *packet, int packetlen)
{
int i;
const unsigned char *message;
unsigned char type, len;
int bodylen;
struct neighbour *neigh;
int have_router_id = 0, have_v4_prefix = 0, have_v6_prefix = 0,
have_v4_nh = 0, have_v6_nh = 0;
unsigned char router_id[8], v4_prefix[16], v6_prefix[16],
v4_nh[16], v6_nh[16];
if(!linklocal(from)) {
fprintf(stderr, "Received packet from non-local address %s.\n",
format_address(from));
return;
}
if(packet[0] != 42) {
fprintf(stderr, "Received malformed packet on %s from %s.\n",
ifp->name, format_address(from));
return;
}
if(packet[1] != 2) {
fprintf(stderr,
"Received packet with unknown version %d on %s from %s.\n",
packet[1], ifp->name, format_address(from));
return;
}
neigh = find_neighbour(from, ifp);
if(neigh == NULL) {
fprintf(stderr, "Couldn't allocate neighbour.\n");
return;
}
DO_NTOHS(bodylen, packet + 2);
if(bodylen + 4 > packetlen) {
fprintf(stderr, "Received truncated packet (%d + 4 > %d).\n",
bodylen, packetlen);
bodylen = packetlen - 4;
}
i = 0;
while(i < bodylen) {
message = packet + 4 + i;
type = message[0];
if(type == MESSAGE_PAD1) {
debugf("Received pad1 from %s on %s.\n",
format_address(from), ifp->name);
i++;
continue;
}
if(i + 1 > bodylen) {
fprintf(stderr, "Received truncated message.\n");
break;
}
len = message[1];
if(i + len > bodylen) {
fprintf(stderr, "Received truncated message.\n");
break;
}
if(type == MESSAGE_PADN) {
debugf("Received pad%d from %s on %s.\n",
len, format_address(from), ifp->name);
} else if(type == MESSAGE_ACK_REQ) {
unsigned short nonce, interval;
if(len < 6) goto fail;
DO_NTOHS(nonce, message + 4);
DO_NTOHS(interval, message + 6);
debugf("Received ack-req (%04X %d) from %s on %s.\n",
nonce, interval, format_address(from), ifp->name);
send_ack(neigh, nonce, interval);
} else if(type == MESSAGE_ACK) {
debugf("Received ack from %s on %s.\n",
format_address(from), ifp->name);
/* Nothing right now */
} else if(type == MESSAGE_HELLO) {
unsigned short seqno, interval;
int changed;
if(len < 6) goto fail;
DO_NTOHS(seqno, message + 4);
DO_NTOHS(interval, message + 6);
debugf("Received hello %d (%d) from %s on %s.\n",
seqno, interval,
format_address(from), ifp->name);
changed = update_neighbour(neigh, seqno, interval);
update_neighbour_metric(neigh, changed);
if(interval > 0)
schedule_neighbours_check(interval * 10, 0);
} else if(type == MESSAGE_IHU) {
unsigned short txcost, interval;
unsigned char address[16];
int rc;
if(len < 6) goto fail;
DO_NTOHS(txcost, message + 4);
DO_NTOHS(interval, message + 6);
rc = network_address(message[2], message + 8, len - 6, address);
if(rc < 0) goto fail;
debugf("Received ihu %d (%d) from %s on %s for %s.\n",
txcost, interval,
format_address(from), ifp->name,
format_address(address));
if(message[2] == 0 || interface_ll_address(ifp, address)) {
int changed = txcost != neigh->txcost;
neigh->txcost = txcost;
neigh->ihu_time = now;
neigh->ihu_interval = interval;
update_neighbour_metric(neigh, changed);
if(interval > 0)
schedule_neighbours_check(interval * 10 * 3, 0);
}
} else if(type == MESSAGE_ROUTER_ID) {
if(len < 10) {
have_router_id = 0;
goto fail;
}
memcpy(router_id, message + 4, 8);
have_router_id = 1;
debugf("Received router-id %s from %s on %s.\n",
format_eui64(router_id), format_address(from), ifp->name);
} else if(type == MESSAGE_NH) {
unsigned char nh[16];
int rc;
if(len < 2) {
have_v4_nh = 0;
have_v6_nh = 0;
goto fail;
}
rc = network_address(message[2], message + 4, len - 2,
nh);
if(rc < 0) {
have_v4_nh = 0;
have_v6_nh = 0;
goto fail;
}
debugf("Received nh %s (%d) from %s on %s.\n",
format_address(nh), message[2],
format_address(from), ifp->name);
if(message[2] == 1) {
memcpy(v4_nh, nh, 16);
have_v4_nh = 1;
} else {
memcpy(v6_nh, nh, 16);
have_v6_nh = 1;
}
} else if(type == MESSAGE_UPDATE) {
unsigned char prefix[16], *nh;
unsigned char plen;
unsigned char channels[DIVERSITY_HOPS];
unsigned short interval, seqno, metric;
int rc, parsed_len;
if(len < 10) {
if(len < 2 || message[3] & 0x80)
have_v4_prefix = have_v6_prefix = 0;
goto fail;
}
DO_NTOHS(interval, message + 6);
DO_NTOHS(seqno, message + 8);
DO_NTOHS(metric, message + 10);
if(message[5] == 0 ||
(message[3] == 1 ? have_v4_prefix : have_v6_prefix))
rc = network_prefix(message[2], message[4], message[5],
message + 12,
message[2] == 1 ? v4_prefix : v6_prefix,
len - 10, prefix);
else
rc = -1;
if(rc < 0) {
if(message[3] & 0x80)
have_v4_prefix = have_v6_prefix = 0;
goto fail;
}
parsed_len = 10 + rc;
plen = message[4] + (message[2] == 1 ? 96 : 0);
if(message[3] & 0x80) {
if(message[2] == 1) {
memcpy(v4_prefix, prefix, 16);
have_v4_prefix = 1;
} else {
memcpy(v6_prefix, prefix, 16);
have_v6_prefix = 1;
}
}
if(message[3] & 0x40) {
if(message[2] == 1) {
memset(router_id, 0, 4);
memcpy(router_id + 4, prefix + 12, 4);
} else {
memcpy(router_id, prefix + 8, 8);
}
have_router_id = 1;
}
if(!have_router_id && message[2] != 0) {
fprintf(stderr, "Received prefix with no router id.\n");
goto fail;
}
debugf("Received update%s%s for %s from %s on %s.\n",
(message[3] & 0x80) ? "/prefix" : "",
(message[3] & 0x40) ? "/id" : "",
format_prefix(prefix, plen),
format_address(from), ifp->name);
if(message[2] == 0) {
if(metric < 0xFFFF) {
fprintf(stderr,
"Received wildcard update with finite metric.\n");
goto done;
}
retract_neighbour_routes(neigh);
goto done;
} else if(message[2] == 1) {
if(!have_v4_nh)
goto fail;
nh = v4_nh;
} else if(have_v6_nh) {
nh = v6_nh;
} else {
nh = neigh->address;
}
if(message[2] == 1) {
if(!ifp->ipv4)
goto done;
}
if((ifp->flags & IF_FARAWAY)) {
channels[0] = 0;
} else {
/* This will be overwritten by parse_route_attributes below. */
if(metric < 256) {
/* Assume non-interfering (wired) link. */
channels[0] = 0;
} else {
/* Assume interfering. */
channels[0] = IF_CHANNEL_INTERFERING;
channels[1] = 0;
}
if(parsed_len < len)
parse_route_attributes(message + 2 + parsed_len,
len - parsed_len, channels);
}
update_route(router_id, prefix, plen, seqno, metric, interval,
neigh, nh,
channels, channels_len(channels));
} else if(type == MESSAGE_REQUEST) {
unsigned char prefix[16], plen;
int rc;
if(len < 2) goto fail;
rc = network_prefix(message[2], message[3], 0,
message + 4, NULL, len - 2, prefix);
if(rc < 0) goto fail;
plen = message[3] + (message[2] == 1 ? 96 : 0);
debugf("Received request for %s from %s on %s.\n",
message[2] == 0 ? "any" : format_prefix(prefix, plen),
format_address(from), ifp->name);
if(message[2] == 0) {
/* If a neighbour is requesting a full route dump from us,
we might as well send it an IHU. */
send_ihu(neigh, NULL);
/* Since nodes send wildcard requests on boot, booting
a large number of nodes at the same time may cause an
update storm. Ignore a wildcard request that happens
shortly after we sent a full update. */
if(neigh->ifp->last_update_time <
now.tv_sec - MAX(neigh->ifp->hello_interval / 100, 1))
send_update(neigh->ifp, 0, NULL, 0);
} else {
send_update(neigh->ifp, 0, prefix, plen);
}
} else if(type == MESSAGE_MH_REQUEST) {
unsigned char prefix[16], plen;
unsigned short seqno;
int rc;
if(len < 14) goto fail;
DO_NTOHS(seqno, message + 4);
rc = network_prefix(message[2], message[3], 0,
message + 16, NULL, len - 14, prefix);
if(rc < 0) goto fail;
plen = message[3] + (message[2] == 1 ? 96 : 0);
debugf("Received request (%d) for %s from %s on %s (%s, %d).\n",
message[6],
format_prefix(prefix, plen),
format_address(from), ifp->name,
format_eui64(message + 8), seqno);
handle_request(neigh, prefix, plen, message[6],
seqno, message + 8);
} else {
debugf("Received unknown packet type %d from %s on %s.\n",
type, format_address(from), ifp->name);
}
done:
i += len + 2;
continue;
fail:
fprintf(stderr, "Couldn't parse packet (%d, %d) from %s on %s.\n",
message[0], message[1], format_address(from), ifp->name);
goto done;
}
return;
}
/* Under normal circumstances, there are enough moderation mechanisms
elsewhere in the protocol to make sure that this last-ditch check
should never trigger. But I'm superstitious. */
static int
check_bucket(struct interface *ifp)
{
if(ifp->bucket <= 0) {
int seconds = now.tv_sec - ifp->bucket_time;
if(seconds > 0) {
ifp->bucket = MIN(BUCKET_TOKENS_MAX,
seconds * BUCKET_TOKENS_PER_SEC);
}
/* Reset bucket time unconditionally, in case clock is stepped. */
ifp->bucket_time = now.tv_sec;
}
if(ifp->bucket > 0) {
ifp->bucket--;
return 1;
} else {
return 0;
}
}
void
flushbuf(struct interface *ifp)
{
int rc;
struct sockaddr_in6 sin6;
assert(ifp->buffered <= ifp->bufsize);
flushupdates(ifp);
if(ifp->buffered > 0) {
debugf(" (flushing %d buffered bytes on %s)\n",
ifp->buffered, ifp->name);
if(check_bucket(ifp)) {
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 = ifp->ifindex;
DO_HTONS(packet_header + 2, ifp->buffered);
rc = babel_send(protocol_socket,
packet_header, sizeof(packet_header),
ifp->sendbuf, ifp->buffered,
(struct sockaddr*)&sin6, sizeof(sin6));
if(rc < 0)
perror("send");
} else {
fprintf(stderr, "Warning: bucket full, dropping packet to %s.\n",
ifp->name);
}
}
VALGRIND_MAKE_MEM_UNDEFINED(ifp->sendbuf, ifp->bufsize);
ifp->buffered = 0;
ifp->have_buffered_hello = 0;
ifp->have_buffered_id = 0;
ifp->have_buffered_nh = 0;
ifp->have_buffered_prefix = 0;
ifp->flush_timeout.tv_sec = 0;
ifp->flush_timeout.tv_usec = 0;
}
static void
schedule_flush(struct interface *ifp)
{
unsigned msecs = jitter(ifp, 0);
if(ifp->flush_timeout.tv_sec != 0 &&
timeval_minus_msec(&ifp->flush_timeout, &now) < msecs)
return;
set_timeout(&ifp->flush_timeout, msecs);
}
static void
schedule_flush_now(struct interface *ifp)
{
/* Almost now */
unsigned msecs = roughly(10);
if(ifp->flush_timeout.tv_sec != 0 &&
timeval_minus_msec(&ifp->flush_timeout, &now) < msecs)
return;
set_timeout(&ifp->flush_timeout, msecs);
}
static void
schedule_unicast_flush(unsigned msecs)
{
if(!unicast_neighbour)
return;
if(unicast_flush_timeout.tv_sec != 0 &&
timeval_minus_msec(&unicast_flush_timeout, &now) < msecs)
return;
unicast_flush_timeout.tv_usec = (now.tv_usec + msecs * 1000) % 1000000;
unicast_flush_timeout.tv_sec =
now.tv_sec + (now.tv_usec / 1000 + msecs) / 1000;
}
static void
ensure_space(struct interface *ifp, int space)
{
if(ifp->bufsize - ifp->buffered < space)
flushbuf(ifp);
}
static void
start_message(struct interface *ifp, int type, int len)
{
if(ifp->bufsize - ifp->buffered < len + 2)
flushbuf(ifp);
ifp->sendbuf[ifp->buffered++] = type;
ifp->sendbuf[ifp->buffered++] = len;
}
static void
end_message(struct interface *ifp, int type, int bytes)
{
assert(ifp->buffered >= bytes + 2 &&
ifp->sendbuf[ifp->buffered - bytes - 2] == type &&
ifp->sendbuf[ifp->buffered - bytes - 1] == bytes);
schedule_flush(ifp);
}
static void
accumulate_byte(struct interface *ifp, unsigned char value)
{
ifp->sendbuf[ifp->buffered++] = value;
}
static void
accumulate_short(struct interface *ifp, unsigned short value)
{
DO_HTONS(ifp->sendbuf + ifp->buffered, value);
ifp->buffered += 2;
}
static void
accumulate_bytes(struct interface *ifp,
const unsigned char *value, unsigned len)
{
memcpy(ifp->sendbuf + ifp->buffered, value, len);
ifp->buffered += len;
}
static int
start_unicast_message(struct neighbour *neigh, int type, int len)
{
if(unicast_neighbour) {
if(neigh != unicast_neighbour ||
unicast_buffered + len + 2 >=
MIN(UNICAST_BUFSIZE, neigh->ifp->bufsize))
flush_unicast(0);
}
if(!unicast_buffer)
unicast_buffer = malloc(UNICAST_BUFSIZE);
if(!unicast_buffer) {
perror("malloc(unicast_buffer)");
return -1;
}
unicast_neighbour = neigh;
unicast_buffer[unicast_buffered++] = type;
unicast_buffer[unicast_buffered++] = len;
return 1;
}
static void
end_unicast_message(struct neighbour *neigh, int type, int bytes)
{
assert(unicast_neighbour == neigh && unicast_buffered >= bytes + 2 &&
unicast_buffer[unicast_buffered - bytes - 2] == type &&
unicast_buffer[unicast_buffered - bytes - 1] == bytes);
schedule_unicast_flush(jitter(neigh->ifp, 0));
}
static void
accumulate_unicast_byte(struct neighbour *neigh, unsigned char value)
{
unicast_buffer[unicast_buffered++] = value;
}
static void
accumulate_unicast_short(struct neighbour *neigh, unsigned short value)
{
DO_HTONS(unicast_buffer + unicast_buffered, value);
unicast_buffered += 2;
}
static void
accumulate_unicast_bytes(struct neighbour *neigh,
const unsigned char *value, unsigned len)
{
memcpy(unicast_buffer + unicast_buffered, value, len);
unicast_buffered += len;
}
void
send_ack(struct neighbour *neigh, unsigned short nonce, unsigned short interval)
{
int rc;
debugf("Sending ack (%04x) to %s on %s.\n",
nonce, format_address(neigh->address), neigh->ifp->name);
rc = start_unicast_message(neigh, MESSAGE_ACK, 2); if(rc < 0) return;
accumulate_unicast_short(neigh, nonce);
end_unicast_message(neigh, MESSAGE_ACK, 2);
/* Roughly yields a value no larger than 3/2, so this meets the deadline */
schedule_unicast_flush(roughly(interval * 6));
}
void
send_hello_noupdate(struct interface *ifp, unsigned interval)
{
/* This avoids sending multiple hellos in a single packet, which breaks
link quality estimation. */
if(ifp->have_buffered_hello)
flushbuf(ifp);
ifp->hello_seqno = seqno_plus(ifp->hello_seqno, 1);
set_timeout(&ifp->hello_timeout, ifp->hello_interval);
if(!if_up(ifp))
return;
debugf("Sending hello %d (%d) to %s.\n",
ifp->hello_seqno, interval, ifp->name);
start_message(ifp, MESSAGE_HELLO, 6);
accumulate_short(ifp, 0);
accumulate_short(ifp, ifp->hello_seqno);
accumulate_short(ifp, interval > 0xFFFF ? 0xFFFF : interval);
end_message(ifp, MESSAGE_HELLO, 6);
ifp->have_buffered_hello = 1;
}
void
send_hello(struct interface *ifp)
{
send_hello_noupdate(ifp, (ifp->hello_interval + 9) / 10);
/* Send full IHU every 3 hellos, and marginal IHU each time */
if(ifp->hello_seqno % 3 == 0)
send_ihu(NULL, ifp);
else
send_marginal_ihu(ifp);
}
void
flush_unicast(int dofree)
{
struct sockaddr_in6 sin6;
int rc;
if(unicast_buffered == 0)
goto done;
if(!if_up(unicast_neighbour->ifp))
goto done;
/* Preserve ordering of messages */
flushbuf(unicast_neighbour->ifp);
if(check_bucket(unicast_neighbour->ifp)) {
memset(&sin6, 0, sizeof(sin6));
sin6.sin6_family = AF_INET6;
memcpy(&sin6.sin6_addr, unicast_neighbour->address, 16);
sin6.sin6_port = htons(protocol_port);
sin6.sin6_scope_id = unicast_neighbour->ifp->ifindex;
DO_HTONS(packet_header + 2, unicast_buffered);
rc = babel_send(protocol_socket,
packet_header, sizeof(packet_header),
unicast_buffer, unicast_buffered,
(struct sockaddr*)&sin6, sizeof(sin6));
if(rc < 0)
perror("send(unicast)");
} else {
fprintf(stderr,
"Warning: bucket full, dropping unicast packet"
"to %s if %s.\n",
format_address(unicast_neighbour->address),
unicast_neighbour->ifp->name);
}
done:
VALGRIND_MAKE_MEM_UNDEFINED(unicast_buffer, UNICAST_BUFSIZE);
unicast_buffered = 0;
if(dofree && unicast_buffer) {
free(unicast_buffer);
unicast_buffer = NULL;
}
unicast_neighbour = NULL;
unicast_flush_timeout.tv_sec = 0;
unicast_flush_timeout.tv_usec = 0;
}
static void
really_send_update(struct interface *ifp,
const unsigned char *id,
const unsigned char *prefix, unsigned char plen,
unsigned short seqno, unsigned short metric,
unsigned char *channels, int channels_len)
{
int add_metric, v4, real_plen, omit = 0;
const unsigned char *real_prefix;
unsigned short flags = 0;
int channels_size;
if(diversity_kind != DIVERSITY_CHANNEL)
channels_len = -1;
channels_size = channels_len >= 0 ? channels_len + 2 : 0;
if(!if_up(ifp))
return;
add_metric = output_filter(id, prefix, plen, ifp->ifindex);
if(add_metric >= INFINITY)
return;
metric = MIN(metric + add_metric, INFINITY);
/* Worst case */
ensure_space(ifp, 20 + 12 + 28);
v4 = plen >= 96 && v4mapped(prefix);
if(v4) {
if(!ifp->ipv4)
return;
if(!ifp->have_buffered_nh ||
memcmp(ifp->buffered_nh, ifp->ipv4, 4) != 0) {
start_message(ifp, MESSAGE_NH, 6);
accumulate_byte(ifp, 1);
accumulate_byte(ifp, 0);
accumulate_bytes(ifp, ifp->ipv4, 4);
end_message(ifp, MESSAGE_NH, 6);
memcpy(ifp->buffered_nh, ifp->ipv4, 4);
ifp->have_buffered_nh = 1;
}
real_prefix = prefix + 12;
real_plen = plen - 96;
} else {
if(ifp->have_buffered_prefix) {
while(omit < plen / 8 &&
ifp->buffered_prefix[omit] == prefix[omit])
omit++;
}
if(!ifp->have_buffered_prefix || plen >= 48)
flags |= 0x80;
real_prefix = prefix;
real_plen = plen;
}
if(!ifp->have_buffered_id || memcmp(id, ifp->buffered_id, 8) != 0) {
if(real_plen == 128 && memcmp(real_prefix + 8, id, 8) == 0) {
flags |= 0x40;
} else {
start_message(ifp, MESSAGE_ROUTER_ID, 10);
accumulate_short(ifp, 0);
accumulate_bytes(ifp, id, 8);
end_message(ifp, MESSAGE_ROUTER_ID, 10);
}
memcpy(ifp->buffered_id, id, 16);
ifp->have_buffered_id = 1;
}
start_message(ifp, MESSAGE_UPDATE, 10 + (real_plen + 7) / 8 - omit +
channels_size);
accumulate_byte(ifp, v4 ? 1 : 2);
accumulate_byte(ifp, flags);
accumulate_byte(ifp, real_plen);
accumulate_byte(ifp, omit);
accumulate_short(ifp, (ifp->update_interval + 5) / 10);
accumulate_short(ifp, seqno);
accumulate_short(ifp, metric);
accumulate_bytes(ifp, real_prefix + omit, (real_plen + 7) / 8 - omit);
/* Note that an empty channels TLV is different from no such TLV. */
if(channels_len >= 0) {
accumulate_byte(ifp, 2);
accumulate_byte(ifp, channels_len);
accumulate_bytes(ifp, channels, channels_len);
}
end_message(ifp, MESSAGE_UPDATE, 10 + (real_plen + 7) / 8 - omit +
channels_size);
if(flags & 0x80) {
memcpy(ifp->buffered_prefix, prefix, 16);
ifp->have_buffered_prefix = 1;
}
}
static int
compare_buffered_updates(const void *av, const void *bv)
{
const struct buffered_update *a = av, *b = bv;
int rc, v4a, v4b, ma, mb;
rc = memcmp(a->id, b->id, 8);
if(rc != 0)
return rc;
v4a = (a->plen >= 96 && v4mapped(a->prefix));
v4b = (b->plen >= 96 && v4mapped(b->prefix));
if(v4a > v4b)
return 1;
else if(v4a < v4b)
return -1;
ma = (!v4a && a->plen == 128 && memcmp(a->prefix + 8, a->id, 8) == 0);
mb = (!v4b && b->plen == 128 && memcmp(b->prefix + 8, b->id, 8) == 0);
if(ma > mb)
return -1;
else if(mb > ma)
return 1;
if(a->plen < b->plen)
return 1;
else if(a->plen > b->plen)
return -1;
return memcmp(a->prefix, b->prefix, 16);
}
void
flushupdates(struct interface *ifp)
{
struct xroute *xroute;
struct babel_route *route;
const unsigned char *last_prefix = NULL;
unsigned char last_plen = 0xFF;
int i;
if(ifp == NULL) {
struct interface *ifp_aux;
FOR_ALL_INTERFACES(ifp_aux)
flushupdates(ifp_aux);
return;
}
if(ifp->num_buffered_updates > 0) {
struct buffered_update *b = ifp->buffered_updates;
int n = ifp->num_buffered_updates;
ifp->buffered_updates = NULL;
ifp->update_bufsize = 0;
ifp->num_buffered_updates = 0;
if(!if_up(ifp))
goto done;
debugf(" (flushing %d buffered updates on %s (%d))\n",
n, ifp->name, ifp->ifindex);
/* In order to send fewer update messages, we want to send updates
with the same router-id together, with IPv6 going out before IPv4. */
for(i = 0; i < n; i++) {
route = find_installed_route(b[i].prefix, b[i].plen);
if(route)
memcpy(b[i].id, route->src->id, 8);
else
memcpy(b[i].id, myid, 8);
}
qsort(b, n, sizeof(struct buffered_update), compare_buffered_updates);
for(i = 0; i < n; i++) {
/* The same update may be scheduled multiple times before it is
sent out. Since our buffer is now sorted, it is enough to
compare with the previous update. */
if(last_prefix) {
if(b[i].plen == last_plen &&
memcmp(b[i].prefix, last_prefix, 16) == 0)
continue;
}
xroute = find_xroute(b[i].prefix, b[i].plen);
route = find_installed_route(b[i].prefix, b[i].plen);
if(xroute && (!route || xroute->metric <= kernel_metric)) {
really_send_update(ifp, myid,
xroute->prefix, xroute->plen,
myseqno, xroute->metric,
NULL, 0);
last_prefix = xroute->prefix;
last_plen = xroute->plen;
} else if(route) {
unsigned char channels[DIVERSITY_HOPS];
int chlen;
struct interface *route_ifp = route->neigh->ifp;
unsigned short metric;
unsigned short seqno;
seqno = route->seqno;
metric =
route_interferes(route, ifp) ?
route_metric(route) :
route_metric_noninterfering(route);
if(metric < INFINITY)
satisfy_request(route->src->prefix, route->src->plen,
seqno, route->src->id, ifp);
if((ifp->flags & IF_SPLIT_HORIZON) &&
route->neigh->ifp == ifp)
continue;
if(route_ifp->channel == IF_CHANNEL_NONINTERFERING) {
memcpy(channels, route->channels, DIVERSITY_HOPS);
} else {
if(route_ifp->channel == IF_CHANNEL_UNKNOWN)
channels[0] = IF_CHANNEL_INTERFERING;
else {
assert(route_ifp->channel > 0 &&
route_ifp->channel <= 255);
channels[0] = route_ifp->channel;
}
memcpy(channels + 1, route->channels, DIVERSITY_HOPS - 1);
}
chlen = channels_len(channels);
really_send_update(ifp, route->src->id,
route->src->prefix,
route->src->plen,
seqno, metric,
channels, chlen);
update_source(route->src, seqno, metric);
last_prefix = route->src->prefix;
last_plen = route->src->plen;
} else {
/* There's no route for this prefix. This can happen shortly
after an xroute has been retracted, so send a retraction. */
really_send_update(ifp, myid, b[i].prefix, b[i].plen,
myseqno, INFINITY, NULL, -1);
}
}
schedule_flush_now(ifp);
done:
free(b);
}
ifp->update_flush_timeout.tv_sec = 0;
ifp->update_flush_timeout.tv_usec = 0;
}
static void
schedule_update_flush(struct interface *ifp, int urgent)
{
unsigned msecs;
msecs = update_jitter(ifp, urgent);
if(ifp->update_flush_timeout.tv_sec != 0 &&
timeval_minus_msec(&ifp->update_flush_timeout, &now) < msecs)
return;
set_timeout(&ifp->update_flush_timeout, msecs);
}
static void
buffer_update(struct interface *ifp,
const unsigned char *prefix, unsigned char plen)
{
if(ifp->num_buffered_updates > 0 &&
ifp->num_buffered_updates >= ifp->update_bufsize)
flushupdates(ifp);
if(ifp->update_bufsize == 0) {
int n;
assert(ifp->buffered_updates == NULL);
/* Allocate enough space to hold a full update. Since the
number of installed routes will grow over time, make sure we
have enough space to send a full-ish frame. */
n = installed_routes_estimate() + xroutes_estimate() + 4;
n = MAX(n, ifp->bufsize / 16);
again:
ifp->buffered_updates = malloc(n * sizeof(struct buffered_update));
if(ifp->buffered_updates == NULL) {
perror("malloc(buffered_updates)");
if(n > 4) {
/* Try again with a tiny buffer. */
n = 4;
goto again;
}
return;
}
ifp->update_bufsize = n;
ifp->num_buffered_updates = 0;
}
memcpy(ifp->buffered_updates[ifp->num_buffered_updates].prefix,
prefix, 16);
ifp->buffered_updates[ifp->num_buffered_updates].plen = plen;
ifp->num_buffered_updates++;
}
static void
buffer_update_callback(struct babel_route *route, void *closure)
{
buffer_update((struct interface*)closure,
route->src->prefix, route->src->plen);
}
void
send_update(struct interface *ifp, int urgent,
const unsigned char *prefix, unsigned char plen)
{
if(ifp == NULL) {
struct interface *ifp_aux;
struct babel_route *route;
FOR_ALL_INTERFACES(ifp_aux)
send_update(ifp_aux, urgent, prefix, plen);
if(prefix) {
/* Since flushupdates only deals with non-wildcard interfaces, we
need to do this now. */
route = find_installed_route(prefix, plen);
if(route && route_metric(route) < INFINITY)
satisfy_request(prefix, plen, route->src->seqno, route->src->id,
NULL);
}
return;
}
if(!if_up(ifp))
return;
if(prefix) {
debugf("Sending update to %s for %s.\n",
ifp->name, format_prefix(prefix, plen));
buffer_update(ifp, prefix, plen);
} else {
send_self_update(ifp);
debugf("Sending update to %s for any.\n", ifp->name);
for_all_installed_routes(buffer_update_callback, ifp);
set_timeout(&ifp->update_timeout, ifp->update_interval);
ifp->last_update_time = now.tv_sec;
}
schedule_update_flush(ifp, urgent);
}
void
send_update_resend(struct interface *ifp,
const unsigned char *prefix, unsigned char plen)
{
assert(prefix != NULL);
send_update(ifp, 1, prefix, plen);
record_resend(RESEND_UPDATE, prefix, plen, 0, 0, NULL, resend_delay);
}
void
send_wildcard_retraction(struct interface *ifp)
{
if(ifp == NULL) {
struct interface *ifp_aux;
FOR_ALL_INTERFACES(ifp_aux)
send_wildcard_retraction(ifp_aux);
return;
}
if(!if_up(ifp))
return;
start_message(ifp, MESSAGE_UPDATE, 10);
accumulate_byte(ifp, 0);
accumulate_byte(ifp, 0x40);
accumulate_byte(ifp, 0);
accumulate_byte(ifp, 0);
accumulate_short(ifp, 0xFFFF);
accumulate_short(ifp, myseqno);
accumulate_short(ifp, 0xFFFF);
end_message(ifp, MESSAGE_UPDATE, 10);
ifp->have_buffered_id = 0;
}
void
update_myseqno()
{
myseqno = seqno_plus(myseqno, 1);
seqno_time = now;
}
static void
send_xroute_update_callback(struct xroute *xroute, void *closure)
{
struct interface *ifp = (struct interface*)closure;
send_update(ifp, 0, xroute->prefix, xroute->plen);
}
void
send_self_update(struct interface *ifp)
{
if(ifp == NULL) {
struct interface *ifp_aux;
FOR_ALL_INTERFACES(ifp_aux) {
if(!if_up(ifp_aux))
continue;
send_self_update(ifp_aux);
}
return;
}
debugf("Sending self update to %s.\n", ifp->name);
for_all_xroutes(send_xroute_update_callback, ifp);
}
void
send_ihu(struct neighbour *neigh, struct interface *ifp)
{
int rxcost, interval;
int ll;
if(neigh == NULL && ifp == NULL) {
struct interface *ifp_aux;
FOR_ALL_INTERFACES(ifp_aux) {
if(if_up(ifp_aux))
continue;
send_ihu(NULL, ifp_aux);
}
return;
}
if(neigh == NULL) {
struct neighbour *ngh;
FOR_ALL_NEIGHBOURS(ngh) {
if(ngh->ifp == ifp)
send_ihu(ngh, ifp);
}
return;
}
if(ifp && neigh->ifp != ifp)
return;
ifp = neigh->ifp;
if(!if_up(ifp))
return;
rxcost = neighbour_rxcost(neigh);
interval = (ifp->hello_interval * 3 + 9) / 10;
/* Conceptually, an IHU is a unicast message. We usually send them as
multicast, since this allows aggregation into a single packet and
avoids an ARP exchange. If we already have a unicast message queued
for this neighbour, however, we might as well piggyback the IHU. */
debugf("Sending %sihu %d on %s to %s.\n",
unicast_neighbour == neigh ? "unicast " : "",
rxcost,
neigh->ifp->name,
format_address(neigh->address));
ll = linklocal(neigh->address);
if(unicast_neighbour != neigh) {
start_message(ifp, MESSAGE_IHU, ll ? 14 : 22);
accumulate_byte(ifp, ll ? 3 : 2);
accumulate_byte(ifp, 0);
accumulate_short(ifp, rxcost);
accumulate_short(ifp, interval);
if(ll)
accumulate_bytes(ifp, neigh->address + 8, 8);
else
accumulate_bytes(ifp, neigh->address, 16);
end_message(ifp, MESSAGE_IHU, ll ? 14 : 22);
} else {
int rc;
rc = start_unicast_message(neigh, MESSAGE_IHU, ll ? 14 : 22);
if(rc < 0) return;
accumulate_unicast_byte(neigh, ll ? 3 : 2);
accumulate_unicast_byte(neigh, 0);
accumulate_unicast_short(neigh, rxcost);
accumulate_unicast_short(neigh, interval);
if(ll)
accumulate_unicast_bytes(neigh, neigh->address + 8, 8);
else
accumulate_unicast_bytes(neigh, neigh->address, 16);
end_unicast_message(neigh, MESSAGE_IHU, ll ? 14 : 22);
}
}
/* Send IHUs to all marginal neighbours */
void
send_marginal_ihu(struct interface *ifp)
{
struct neighbour *neigh;
FOR_ALL_NEIGHBOURS(neigh) {
if(ifp && neigh->ifp != ifp)
continue;
if(neigh->txcost >= 384 || (neigh->reach & 0xF000) != 0xF000)
send_ihu(neigh, ifp);
}
}
void
send_request(struct interface *ifp,
const unsigned char *prefix, unsigned char plen)
{
int v4, len;
if(ifp == NULL) {
struct interface *ifp_auxn;
FOR_ALL_INTERFACES(ifp_auxn) {
if(if_up(ifp_auxn))
continue;
send_request(ifp_auxn, prefix, plen);
}
return;
}
/* make sure any buffered updates go out before this request. */
flushupdates(ifp);
if(!if_up(ifp))
return;
debugf("sending request to %s for %s.\n",
ifp->name, prefix ? format_prefix(prefix, plen) : "any");
v4 = plen >= 96 && v4mapped(prefix);
len = !prefix ? 2 : v4 ? 6 : 18;
start_message(ifp, MESSAGE_REQUEST, len);
accumulate_byte(ifp, !prefix ? 0 : v4 ? 1 : 2);
accumulate_byte(ifp, !prefix ? 0 : v4 ? plen - 96 : plen);
if(prefix) {
if(v4)
accumulate_bytes(ifp, prefix + 12, 4);
else
accumulate_bytes(ifp, prefix, 16);
}
end_message(ifp, MESSAGE_REQUEST, len);
}
void
send_unicast_request(struct neighbour *neigh,
const unsigned char *prefix, unsigned char plen)
{
int rc, v4, len;
/* make sure any buffered updates go out before this request. */
flushupdates(neigh->ifp);
debugf("sending unicast request to %s for %s.\n",
format_address(neigh->address),
prefix ? format_prefix(prefix, plen) : "any");
v4 = plen >= 96 && v4mapped(prefix);
len = !prefix ? 2 : v4 ? 6 : 18;
rc = start_unicast_message(neigh, MESSAGE_REQUEST, len);
if(rc < 0) return;
accumulate_unicast_byte(neigh, !prefix ? 0 : v4 ? 1 : 2);
accumulate_unicast_byte(neigh, !prefix ? 0 : v4 ? plen - 96 : plen);
if(prefix) {
if(v4)
accumulate_unicast_bytes(neigh, prefix + 12, 4);
else
accumulate_unicast_bytes(neigh, prefix, 16);
}
end_unicast_message(neigh, MESSAGE_REQUEST, len);
}
void
send_multihop_request(struct interface *ifp,
const unsigned char *prefix, unsigned char plen,
unsigned short seqno, const unsigned char *id,
unsigned short hop_count)
{
int v4, pb, len;
/* Make sure any buffered updates go out before this request. */
flushupdates(ifp);
if(ifp == NULL) {
struct interface *ifp_aux;
FOR_ALL_INTERFACES(ifp_aux) {
if(!if_up(ifp_aux))
continue;
send_multihop_request(ifp_aux, prefix, plen, seqno, id, hop_count);
}
return;
}
if(!if_up(ifp))
return;
debugf("Sending request (%d) on %s for %s.\n",
hop_count, ifp->name, format_prefix(prefix, plen));
v4 = plen >= 96 && v4mapped(prefix);
pb = v4 ? ((plen - 96) + 7) / 8 : (plen + 7) / 8;
len = 6 + 8 + pb;
start_message(ifp, MESSAGE_MH_REQUEST, len);
accumulate_byte(ifp, v4 ? 1 : 2);
accumulate_byte(ifp, v4 ? plen - 96 : plen);
accumulate_short(ifp, seqno);
accumulate_byte(ifp, hop_count);
accumulate_byte(ifp, 0);
accumulate_bytes(ifp, id, 8);
if(prefix) {
if(v4)
accumulate_bytes(ifp, prefix + 12, pb);
else
accumulate_bytes(ifp, prefix, pb);
}
end_message(ifp, MESSAGE_MH_REQUEST, len);
}
void
send_unicast_multihop_request(struct neighbour *neigh,
const unsigned char *prefix, unsigned char plen,
unsigned short seqno, const unsigned char *id,
unsigned short hop_count)
{
int rc, v4, pb, len;
/* Make sure any buffered updates go out before this request. */
flushupdates(neigh->ifp);
debugf("Sending multi-hop request to %s for %s (%d hops).\n",
format_address(neigh->address),
format_prefix(prefix, plen), hop_count);
v4 = plen >= 96 && v4mapped(prefix);
pb = v4 ? ((plen - 96) + 7) / 8 : (plen + 7) / 8;
len = 6 + 8 + pb;
rc = start_unicast_message(neigh, MESSAGE_MH_REQUEST, len);
if(rc < 0) return;
accumulate_unicast_byte(neigh, v4 ? 1 : 2);
accumulate_unicast_byte(neigh, v4 ? plen - 96 : plen);
accumulate_unicast_short(neigh, seqno);
accumulate_unicast_byte(neigh, hop_count);
accumulate_unicast_byte(neigh, 0);
accumulate_unicast_bytes(neigh, id, 8);
if(prefix) {
if(v4)
accumulate_unicast_bytes(neigh, prefix + 12, pb);
else
accumulate_unicast_bytes(neigh, prefix, pb);
}
end_unicast_message(neigh, MESSAGE_MH_REQUEST, len);
}
void
send_request_resend(struct neighbour *neigh,
const unsigned char *prefix, unsigned char plen,
unsigned short seqno, unsigned char *id)
{
if(neigh)
send_unicast_multihop_request(neigh, prefix, plen, seqno, id, 127);
else
send_multihop_request(NULL, prefix, plen, seqno, id, 127);
record_resend(RESEND_REQUEST, prefix, plen, seqno, id,
neigh ? neigh->ifp : NULL, resend_delay);
}
void
handle_request(struct neighbour *neigh, const unsigned char *prefix,
unsigned char plen, unsigned char hop_count,
unsigned short seqno, const unsigned char *id)
{
struct xroute *xroute;
struct babel_route *route;
struct neighbour *successor = NULL;
xroute = find_xroute(prefix, plen);
route = find_installed_route(prefix, plen);
if(xroute && (!route || xroute->metric <= kernel_metric)) {
if(hop_count > 0 && memcmp(id, myid, 8) == 0) {
if(seqno_compare(seqno, myseqno) > 0) {
if(seqno_minus(seqno, myseqno) > 100) {
/* Hopelessly out-of-date request */
return;
}
update_myseqno();
}
}
send_update(neigh->ifp, 1, prefix, plen);
return;
}
if(route &&
(memcmp(id, route->src->id, 8) != 0 ||
seqno_compare(seqno, route->seqno) <= 0)) {
send_update(neigh->ifp, 1, prefix, plen);
return;
}
if(hop_count <= 1)
return;
if(route && memcmp(id, route->src->id, 8) == 0 &&
seqno_minus(seqno, route->seqno) > 100) {
/* Hopelessly out-of-date */
return;
}
if(request_redundant(neigh->ifp, prefix, plen, seqno, id))
return;
/* Let's try to forward this request. */
if(route && route_metric(route) < INFINITY)
successor = route->neigh;
if(!successor || successor == neigh) {
/* We were about to forward a request to its requestor. Try to
find a different neighbour to forward the request to. */
struct babel_route *other_route;
other_route = find_best_route(prefix, plen, 0, neigh);
if(other_route && route_metric(other_route) < INFINITY)
successor = other_route->neigh;
}
if(!successor || successor == neigh)
/* Give up */
return;
send_unicast_multihop_request(successor, prefix, plen, seqno, id,
hop_count - 1);
record_resend(RESEND_REQUEST, prefix, plen, seqno, id,
neigh->ifp, 0);
}
/*
Copyright (c) 2007, 2008 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 MAX_BUFFERED_UPDATES 200
#define BUCKET_TOKENS_MAX 200
#define BUCKET_TOKENS_PER_SEC 40
#define MESSAGE_PAD1 0
#define MESSAGE_PADN 1
#define MESSAGE_ACK_REQ 2
#define MESSAGE_ACK 3
#define MESSAGE_HELLO 4
#define MESSAGE_IHU 5
#define MESSAGE_ROUTER_ID 6
#define MESSAGE_NH 7
#define MESSAGE_UPDATE 8
#define MESSAGE_REQUEST 9
#define MESSAGE_MH_REQUEST 10
extern unsigned short myseqno;
extern struct timeval seqno_time;
extern int broadcast_ihu;
extern int split_horizon;
extern unsigned char packet_header[4];
extern struct neighbour *unicast_neighbour;
extern struct timeval unicast_flush_timeout;
void parse_packet(const unsigned char *from, struct interface *ifp,
const unsigned char *packet, int packetlen);
void flushbuf(struct interface *ifp);
void flushupdates(struct interface *ifp);
void send_ack(struct neighbour *neigh, unsigned short nonce,
unsigned short interval);
void send_hello_noupdate(struct interface *ifp, unsigned interval);
void send_hello(struct interface *ifp);
void flush_unicast(int dofree);
void send_update(struct interface *ifp, int urgent,
const unsigned char *prefix, unsigned char plen);
void send_update_resend(struct interface *ifp,
const unsigned char *prefix, unsigned char plen);
void send_wildcard_retraction(struct interface *ifp);
void update_myseqno(void);
void send_self_update(struct interface *ifp);
void send_ihu(struct neighbour *neigh, struct interface *ifp);
void send_marginal_ihu(struct interface *ifp);
void send_request(struct interface *ifp,
const unsigned char *prefix, unsigned char plen);
void send_unicast_request(struct neighbour *neigh,
const unsigned char *prefix, unsigned char plen);
void send_multihop_request(struct interface *ifp,
const unsigned char *prefix, unsigned char plen,
unsigned short seqno, const unsigned char *id,
unsigned short hop_count);
void
send_unicast_multihop_request(struct neighbour *neigh,
const unsigned char *prefix, unsigned char plen,
unsigned short seqno, const unsigned char *id,
unsigned short hop_count);
void send_request_resend(struct neighbour *neigh,
const unsigned char *prefix, unsigned char plen,
unsigned short seqno, unsigned char *id);
void handle_request(struct neighbour *neigh, const unsigned char *prefix,
unsigned char plen, unsigned char hop_count,
unsigned short seqno, const unsigned char *id);
/*
Copyright (c) 2007, 2008 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 "babeld.h"
#include "util.h"
#include "interface.h"
#include "neighbour.h"
#include "source.h"
#include "route.h"
#include "message.h"
#include "resend.h"
#include "local.h"
struct neighbour *neighs = NULL;
static struct neighbour *
find_neighbour_nocreate(const unsigned char *address, struct interface *ifp)
{
struct neighbour *neigh;
FOR_ALL_NEIGHBOURS(neigh) {
if(memcmp(address, neigh->address, 16) == 0 &&
neigh->ifp == ifp)
return neigh;
}
return NULL;
}
void
flush_neighbour(struct neighbour *neigh)
{
flush_neighbour_routes(neigh);
if(unicast_neighbour == neigh)
flush_unicast(1);
flush_resends(neigh);
if(neighs == neigh) {
neighs = neigh->next;
} else {
struct neighbour *previous = neighs;
while(previous->next != neigh)
previous = previous->next;
previous->next = neigh->next;
}
local_notify_neighbour(neigh, LOCAL_FLUSH);
free(neigh);
}
struct neighbour *
find_neighbour(const unsigned char *address, struct interface *ifp)
{
struct neighbour *neigh;
const struct timeval zero = {0, 0};
neigh = find_neighbour_nocreate(address, ifp);
if(neigh)
return neigh;
debugf("Creating neighbour %s on %s.\n",
format_address(address), ifp->name);
neigh = malloc(sizeof(struct neighbour));
if(neigh == NULL) {
perror("malloc(neighbour)");
return NULL;
}
neigh->hello_seqno = -1;
memcpy(neigh->address, address, 16);
neigh->reach = 0;
neigh->txcost = INFINITY;
neigh->ihu_time = now;
neigh->hello_time = zero;
neigh->hello_interval = 0;
neigh->ihu_interval = 0;
neigh->ifp = ifp;
neigh->next = neighs;
neighs = neigh;
local_notify_neighbour(neigh, LOCAL_ADD);
send_hello(ifp);
return neigh;
}
/* Recompute a neighbour's rxcost. Return true if anything changed.
This does not call local_notify_neighbour, see update_neighbour_metric. */
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 rc;
missed_hellos =
((int)timeval_minus_msec(&now, &neigh->hello_time) -
neigh->hello_interval * 7) /
(neigh->hello_interval * 10);
if(missed_hellos <= 0)
return rc;
timeval_add_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;
if(missed_hellos < -8) {
/* Probably a neighbour that rebooted and lost its seqno.
Reboot the universe. */
neigh->reach = 0;
missed_hellos = 0;
rc = 1;
} else if(missed_hellos < 0) {
if(hello_interval > neigh->hello_interval) {
/* This neighbour has increased its hello interval,
and we didn't notice. */
neigh->reach <<= -missed_hellos;
missed_hellos = 0;
} else {
/* Late hello. Probably due to the link layer buffering
packets during a link outage. Ignore it, but reset
the expected seqno. */
neigh->hello_seqno = hello;
hello = -1;
missed_hellos = 0;
}
rc = 1;
}
} else {
missed_hellos = 0;
}
neigh->hello_time = now;
neigh->hello_interval = hello_interval;
}
if(missed_hellos > 0) {
neigh->reach >>= missed_hellos;
neigh->hello_seqno = seqno_plus(neigh->hello_seqno, 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)
rc = 1;
}
/* Make sure to give neighbours some feedback early after association */
if((neigh->reach & 0xBF00) == 0x8000) {
/* A new neighbour */
send_hello(neigh->ifp);
} 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_self_update(neigh->ifp);
}
}
if((neigh->reach & 0xFC00) == 0xC000) {
/* This is a newish neighbour, let's request a full route dump.
We ought to avoid this when the network is dense */
send_unicast_request(neigh, NULL, 0);
send_ihu(neigh, NULL);
}
return rc;
}
static int
reset_txcost(struct neighbour *neigh)
{
unsigned delay;
delay = timeval_minus_msec(&now, &neigh->ihu_time);
if(neigh->ihu_interval > 0 && delay < neigh->ihu_interval * 10 * 3)
return 0;
/* If we're losing a lot of packets, we probably lost an IHU too */
if(delay >= 180000 || (neigh->reach & 0xFFF0) == 0 ||
(neigh->ihu_interval > 0 &&
delay >= neigh->ihu_interval * 10 * 10)) {
neigh->txcost = INFINITY;
neigh->ihu_time = now;
return 1;
}
return 0;
}
unsigned
neighbour_txcost(struct neighbour *neigh)
{
return neigh->txcost;
}
unsigned
check_neighbours()
{
struct neighbour *neigh;
int changed, rc;
unsigned msecs = 50000;
debugf("Checking neighbours.\n");
neigh = neighs;
while(neigh) {
changed = update_neighbour(neigh, -1, 0);
if(neigh->reach == 0 ||
neigh->hello_time.tv_sec > now.tv_sec || /* clock stepped */
timeval_minus_msec(&now, &neigh->hello_time) > 300000) {
struct neighbour *old = neigh;
neigh = neigh->next;
flush_neighbour(old);
continue;
}
rc = reset_txcost(neigh);
changed = changed || rc;
update_neighbour_metric(neigh, changed);
if(neigh->hello_interval > 0)
msecs = MIN(msecs, neigh->hello_interval * 10);
if(neigh->ihu_interval > 0)
msecs = MIN(msecs, neigh->ihu_interval * 10);
neigh = neigh->next;
}
return msecs;
}
unsigned
neighbour_rxcost(struct neighbour *neigh)
{
unsigned delay;
unsigned short reach = neigh->reach;
delay = timeval_minus_msec(&now, &neigh->hello_time);
if((reach & 0xFFF0) == 0 || delay >= 180000) {
return INFINITY;
} else if((neigh->ifp->flags & IF_LQ)) {
int sreach =
((reach & 0x8000) >> 2) +
((reach & 0x4000) >> 1) +
(reach & 0x3FFF);
/* 0 <= sreach <= 0x7FFF */
int cost = (0x8000 * neigh->ifp->cost) / (sreach + 1);
/* cost >= interface->cost */
if(delay >= 40000)
cost = (cost * (delay - 20000) + 10000) / 20000;
return MIN(cost, INFINITY);
} else {
/* To lose one hello is a misfortune, to lose two is carelessness. */
if((reach & 0xC000) == 0xC000)
return neigh->ifp->cost;
else if((reach & 0xC000) == 0)
return INFINITY;
else if((reach & 0x2000))
return neigh->ifp->cost;
else
return INFINITY;
}
}
unsigned
neighbour_cost(struct neighbour *neigh)
{
unsigned a, b;
if(!if_up(neigh->ifp))
return INFINITY;
a = neighbour_txcost(neigh);
if(a >= INFINITY)
return INFINITY;
b = neighbour_rxcost(neigh);
if(b >= INFINITY)
return INFINITY;
if(!(neigh->ifp->flags & IF_LQ) || (a < 256 && b < 256)) {
return a;
} else {
/* a = 256/alpha, b = 256/beta, where alpha and beta are the expected
probabilities of a packet getting through in the direct and reverse
directions. */
a = MAX(a, 256);
b = MAX(b, 256);
/* 1/(alpha * beta), which is just plain ETX. */
/* Since a and b are capped to 16 bits, overflow is impossible. */
return (a * b + 128) >> 8;
}
}
/*
Copyright (c) 2007, 2008 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 {
struct neighbour *next;
/* This is -1 when unknown, so don't make it unsigned */
int hello_seqno;
unsigned char address[16];
unsigned short reach;
unsigned short txcost;
struct timeval hello_time;
struct timeval ihu_time;
unsigned short hello_interval; /* in centiseconds */
unsigned short ihu_interval; /* in centiseconds */
struct interface *ifp;
};
extern struct neighbour *neighs;
#define FOR_ALL_NEIGHBOURS(_neigh) \
for(_neigh = neighs; _neigh; _neigh = _neigh->next)
int neighbour_valid(struct neighbour *neigh);
void flush_neighbour(struct neighbour *neigh);
struct neighbour *find_neighbour(const unsigned char *address,
struct interface *ifp);
int update_neighbour(struct neighbour *neigh, int hello, int hello_interval);
unsigned check_neighbours(void);
unsigned neighbour_txcost(struct neighbour *neigh);
unsigned neighbour_rxcost(struct neighbour *neigh);
unsigned neighbour_cost(struct neighbour *neigh);
/*
Copyright (c) 2007, 2008 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 <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <errno.h>
#include "babeld.h"
#include "util.h"
#include "net.h"
int
babel_socket(int port)
{
struct sockaddr_in6 sin6;
int s, rc;
int saved_errno;
int one = 1, zero = 0;
const int ds = 0xc0; /* CS6 - Network Control */
s = socket(PF_INET6, SOCK_DGRAM, 0);
if(s < 0)
return -1;
rc = setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one));
if(rc < 0)
goto fail;
rc = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
if(rc < 0)
goto fail;
rc = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
&zero, sizeof(zero));
if(rc < 0)
goto fail;
rc = setsockopt(s, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
&one, sizeof(one));
if(rc < 0)
goto fail;
rc = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
&one, sizeof(one));
if(rc < 0)
goto fail;
#ifdef IPV6_TCLASS
rc = setsockopt(s, IPPROTO_IPV6, IPV6_TCLASS, &ds, sizeof(ds));
#else
rc = -1;
errno = ENOSYS;
#endif
if(rc < 0)
perror("Couldn't set traffic class");
rc = fcntl(s, F_GETFL, 0);
if(rc < 0)
goto fail;
rc = fcntl(s, F_SETFL, (rc | O_NONBLOCK));
if(rc < 0)
goto fail;
rc = fcntl(s, F_GETFD, 0);
if(rc < 0)
goto fail;
rc = fcntl(s, F_SETFD, rc | FD_CLOEXEC);
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;
return s;
fail:
saved_errno = errno;
close(s);
errno = saved_errno;
return -1;
}
int
babel_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
babel_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;
again:
rc = sendmsg(s, &msg, 0);
if(rc < 0) {
if(errno == EINTR)
goto again;
else if(errno == EAGAIN) {
int rc2;
rc2 = wait_for_fd(1, s, 5);
if(rc2 > 0)
goto again;
errno = EAGAIN;
}
}
return rc;
}
int
tcp_server_socket(int port, int local)
{
struct sockaddr_in6 sin6;
int s, rc, saved_errno;
int one = 1;
s = socket(PF_INET6, SOCK_STREAM, 0);
if(s < 0)
return -1;
rc = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
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;
rc = fcntl(s, F_GETFD, 0);
if(rc < 0)
goto fail;
rc = fcntl(s, F_SETFD, rc | FD_CLOEXEC);
if(rc < 0)
goto fail;
memset(&sin6, 0, sizeof(sin6));
sin6.sin6_family = AF_INET6;
sin6.sin6_port = htons(port);
if(local) {
rc = inet_pton(AF_INET6, "::1", &sin6.sin6_addr);
if(rc < 0)
goto fail;
}
rc = bind(s, (struct sockaddr*)&sin6, sizeof(sin6));
if(rc < 0)
goto fail;
rc = listen(s, 2);
if(rc < 0)
goto fail;
return s;
fail:
saved_errno = errno;
close(s);
errno = saved_errno;
return -1;
}
/*
Copyright (c) 2007, 2008 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 babel_socket(int port);
int babel_recv(int s, void *buf, int buflen, struct sockaddr *sin, int slen);
int babel_send(int s,
const void *buf1, int buflen1, const void *buf2, int buflen2,
const struct sockaddr *sin, int slen);
int tcp_server_socket(int port, int local);
/*
Copyright (c) 2007, 2008 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 <sys/time.h>
#include <time.h>
#include <string.h>
#include <stdlib.h>
#include "babeld.h"
#include "util.h"
#include "neighbour.h"
#include "resend.h"
#include "message.h"
#include "interface.h"
#include "configuration.h"
struct timeval resend_time = {0, 0};
struct resend *to_resend = NULL;
static int
resend_match(struct resend *resend,
int kind, const unsigned char *prefix, unsigned char plen)
{
return (resend->kind == kind &&
resend->plen == plen && memcmp(resend->prefix, prefix, 16) == 0);
}
/* This is called by neigh.c when a neighbour is flushed */
void
flush_resends(struct neighbour *neigh)
{
/* Nothing for now */
}
static struct resend *
find_resend(int kind, const unsigned char *prefix, unsigned char plen,
struct resend **previous_return)
{
struct resend *current, *previous;
previous = NULL;
current = to_resend;
while(current) {
if(resend_match(current, kind, prefix, plen)) {
if(previous_return)
*previous_return = previous;
return current;
}
previous = current;
current = current->next;
}
return NULL;
}
struct resend *
find_request(const unsigned char *prefix, unsigned char plen,
struct resend **previous_return)
{
return find_resend(RESEND_REQUEST, prefix, plen, previous_return);
}
int
record_resend(int kind, const unsigned char *prefix, unsigned char plen,
unsigned short seqno, const unsigned char *id,
struct interface *ifp, int delay)
{
struct resend *resend;
unsigned int ifindex = ifp ? ifp->ifindex : 0;
if((kind == RESEND_REQUEST &&
input_filter(NULL, prefix, plen, NULL, ifindex) >= INFINITY) ||
(kind == RESEND_UPDATE &&
output_filter(NULL, prefix, plen, ifindex) >= INFINITY))
return 0;
if(delay >= 0xFFFF)
delay = 0xFFFF;
resend = find_resend(kind, prefix, plen, NULL);
if(resend) {
if(resend->delay && delay)
resend->delay = MIN(resend->delay, delay);
else if(delay)
resend->delay = delay;
resend->time = now;
resend->max = RESEND_MAX;
if(id && memcmp(resend->id, id, 8) == 0 &&
seqno_compare(resend->seqno, seqno) > 0) {
return 0;
}
if(id)
memcpy(resend->id, id, 8);
else
memset(resend->id, 0, 8);
resend->seqno = seqno;
if(resend->ifp != ifp)
resend->ifp = NULL;
} else {
resend = malloc(sizeof(struct resend));
if(resend == NULL)
return -1;
resend->kind = kind;
resend->max = RESEND_MAX;
resend->delay = delay;
memcpy(resend->prefix, prefix, 16);
resend->plen = plen;
resend->seqno = seqno;
if(id)
memcpy(resend->id, id, 8);
else
memset(resend->id, 0, 8);
resend->ifp = ifp;
resend->time = now;
resend->next = to_resend;
to_resend = resend;
}
if(resend->delay) {
struct timeval timeout;
timeval_add_msec(&timeout, &resend->time, resend->delay);
timeval_min(&resend_time, &timeout);
}
return 1;
}
static int
resend_expired(struct resend *resend)
{
switch(resend->kind) {
case RESEND_REQUEST:
return timeval_minus_msec(&now, &resend->time) >= REQUEST_TIMEOUT;
default:
return resend->max <= 0;
}
}
int
unsatisfied_request(const unsigned char *prefix, unsigned char plen,
unsigned short seqno, const unsigned char *id)
{
struct resend *request;
request = find_request(prefix, plen, NULL);
if(request == NULL || resend_expired(request))
return 0;
if(memcmp(request->id, id, 8) != 0 ||
seqno_compare(request->seqno, seqno) <= 0)
return 1;
return 0;
}
/* Determine whether a given request should be forwarded. */
int
request_redundant(struct interface *ifp,
const unsigned char *prefix, unsigned char plen,
unsigned short seqno, const unsigned char *id)
{
struct resend *request;
request = find_request(prefix, plen, NULL);
if(request == NULL || resend_expired(request))
return 0;
if(memcmp(request->id, id, 8) == 0 &&
seqno_compare(request->seqno, seqno) > 0)
return 0;
if(request->ifp != NULL && request->ifp != ifp)
return 0;
if(request->max > 0)
/* Will be resent. */
return 1;
if(timeval_minus_msec(&now, &request->time) <
(ifp ? MIN(ifp->hello_interval, 1000) : 1000))
/* Fairly recent. */
return 1;
return 0;
}
int
satisfy_request(const unsigned char *prefix, unsigned char plen,
unsigned short seqno, const unsigned char *id,
struct interface *ifp)
{
struct resend *request, *previous;
request = find_request(prefix, plen, &previous);
if(request == NULL)
return 0;
if(ifp != NULL && request->ifp != ifp)
return 0;
if(memcmp(request->id, id, 8) != 0 ||
seqno_compare(request->seqno, seqno) <= 0) {
/* We cannot remove the request, as we may be walking the list right
now. Mark it as expired, so that expire_resend will remove it. */
request->max = 0;
request->time.tv_sec = 0;
recompute_resend_time();
return 1;
}
return 0;
}
void
expire_resend()
{
struct resend *current, *previous;
int recompute = 0;
previous = NULL;
current = to_resend;
while(current) {
if(resend_expired(current)) {
if(previous == NULL) {
to_resend = current->next;
free(current);
current = to_resend;
} else {
previous->next = current->next;
free(current);
current = previous->next;
}
recompute = 1;
} else {
previous = current;
current = current->next;
}
}
if(recompute)
recompute_resend_time();
}
void
recompute_resend_time()
{
struct resend *request;
struct timeval resend = {0, 0};
request = to_resend;
while(request) {
if(!resend_expired(request) && request->delay > 0 && request->max > 0) {
struct timeval timeout;
timeval_add_msec(&timeout, &request->time, request->delay);
timeval_min(&resend, &timeout);
}
request = request->next;
}
resend_time = resend;
}
void
do_resend()
{
struct resend *resend;
resend = to_resend;
while(resend) {
if(!resend_expired(resend) && resend->delay > 0 && resend->max > 0) {
struct timeval timeout;
timeval_add_msec(&timeout, &resend->time, resend->delay);
if(timeval_compare(&now, &timeout) >= 0) {
switch(resend->kind) {
case RESEND_REQUEST:
send_multihop_request(resend->ifp,
resend->prefix, resend->plen,
resend->seqno, resend->id, 127);
break;
case RESEND_UPDATE:
send_update(resend->ifp, 1,
resend->prefix, resend->plen);
break;
default: abort();
}
resend->delay = MIN(0xFFFF, resend->delay * 2);
resend->max--;
}
}
resend = resend->next;
}
recompute_resend_time();
}
/*
Copyright (c) 2007, 2008 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 REQUEST_TIMEOUT 65000
#define RESEND_MAX 3
#define RESEND_REQUEST 1
#define RESEND_UPDATE 2
struct resend {
unsigned char kind;
unsigned char max;
unsigned short delay;
struct timeval time;
unsigned char prefix[16];
unsigned char plen;
unsigned short seqno;
unsigned char id[8];
struct interface *ifp;
struct resend *next;
};
extern struct timeval resend_time;
struct resend *find_request(const unsigned char *prefix, unsigned char plen,
struct resend **previous_return);
void flush_resends(struct neighbour *neigh);
int record_resend(int kind, const unsigned char *prefix, unsigned char plen,
unsigned short seqno, const unsigned char *id,
struct interface *ifp, int delay);
int unsatisfied_request(const unsigned char *prefix, unsigned char plen,
unsigned short seqno, const unsigned char *id);
int request_redundant(struct interface *ifp,
const unsigned char *prefix, unsigned char plen,
unsigned short seqno, const unsigned char *id);
int satisfy_request(const unsigned char *prefix, unsigned char plen,
unsigned short seqno, const unsigned char *id,
struct interface *ifp);
void expire_resend(void);
void recompute_resend_time(void);
void do_resend(void);
/*
Copyright (c) 2007-2011 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 <sys/time.h>
#include "babeld.h"
#include "util.h"
#include "kernel.h"
#include "interface.h"
#include "source.h"
#include "neighbour.h"
#include "route.h"
#include "xroute.h"
#include "message.h"
#include "resend.h"
#include "configuration.h"
#include "local.h"
struct babel_route **routes = NULL;
static int route_slots = 0, max_route_slots = 0;
int kernel_metric = 0;
int allow_duplicates = -1;
int diversity_kind = DIVERSITY_NONE;
int diversity_factor = 256; /* in units of 1/256 */
int keep_unfeasible = 0;
/* We maintain a list of "slots", ordered by prefix. Every slot
contains a linked list of the routes to this prefix, with the
installed route, if any, at the head of the list. */
static int
route_compare(const unsigned char *prefix, unsigned char plen,
struct babel_route *route)
{
int i = memcmp(prefix, route->src->prefix, 16);
if(i != 0)
return i;
if(plen < route->src->plen)
return -1;
else if(plen > route->src->plen)
return 1;
else
return 0;
}
/* Performs binary search, returns -1 in case of failure. In the latter
case, new_return is the place where to insert the new element. */
static int
find_route_slot(const unsigned char *prefix, unsigned char plen,
int *new_return)
{
int p, m, g, c;
if(route_slots < 1) {
if(new_return)
*new_return = 0;
return -1;
}
p = 0; g = route_slots - 1;
do {
m = (p + g) / 2;
c = route_compare(prefix, plen, routes[m]);
if(c == 0)
return m;
else if(c < 0)
g = m - 1;
else
p = m + 1;
} while(p <= g);
if(new_return)
*new_return = p;
return -1;
}
struct babel_route *
find_route(const unsigned char *prefix, unsigned char plen,
struct neighbour *neigh, const unsigned char *nexthop)
{
struct babel_route *route;
int i = find_route_slot(prefix, plen, NULL);
if(i < 0)
return NULL;
route = routes[i];
while(route) {
if(route->neigh == neigh && memcmp(route->nexthop, nexthop, 16) == 0)
return route;
route = route->next;
}
return NULL;
}
struct babel_route *
find_installed_route(const unsigned char *prefix, unsigned char plen)
{
int i = find_route_slot(prefix, plen, NULL);
if(i >= 0 && routes[i]->installed)
return routes[i];
return NULL;
}
/* Returns an overestimate of the number of installed routes. */
int
installed_routes_estimate(void)
{
return route_slots;
}
static int
resize_route_table(int new_slots)
{
struct babel_route **new_routes;
assert(new_slots >= route_slots);
if(new_slots == 0) {
new_routes = NULL;
free(routes);
} else {
new_routes = realloc(routes, new_slots * sizeof(struct babel_route*));
if(new_routes == NULL)
return -1;
}
max_route_slots = new_slots;
routes = new_routes;
return 1;
}
/* Insert a route into the table. If successful, retains the route.
On failure, caller must free the route. */
static struct babel_route *
insert_route(struct babel_route *route)
{
int i, n;
assert(!route->installed);
i = find_route_slot(route->src->prefix, route->src->plen, &n);
if(i < 0) {
if(route_slots >= max_route_slots)
resize_route_table(max_route_slots < 1 ? 8 : 2 * max_route_slots);
if(route_slots >= max_route_slots)
return NULL;
route->next = NULL;
if(n < route_slots)
memmove(routes + n + 1, routes + n,
(route_slots - n) * sizeof(struct babel_route*));
route_slots++;
routes[n] = route;
} else {
struct babel_route *r;
r = routes[i];
while(r->next)
r = r->next;
r->next = route;
route->next = NULL;
}
return route;
}
void
flush_route(struct babel_route *route)
{
int i;
struct source *src;
unsigned oldmetric;
int lost = 0;
oldmetric = route_metric(route);
src = route->src;
if(route->installed) {
uninstall_route(route);
lost = 1;
}
i = find_route_slot(route->src->prefix, route->src->plen, NULL);
assert(i >= 0 && i < route_slots);
local_notify_route(route, LOCAL_FLUSH);
if(route == routes[i]) {
routes[i] = route->next;
route->next = NULL;
free(route);
if(routes[i] == NULL) {
if(i < route_slots - 1)
memmove(routes + i, routes + i + 1,
(route_slots - i - 1) * sizeof(struct babel_route*));
routes[route_slots - 1] = NULL;
route_slots--;
}
if(route_slots == 0)
resize_route_table(0);
else if(max_route_slots > 8 && route_slots < max_route_slots / 4)
resize_route_table(max_route_slots / 2);
} else {
struct babel_route *r = routes[i];
while(r->next != route)
r = r->next;
r->next = route->next;
route->next = NULL;
free(route);
}
if(lost)
route_lost(src, oldmetric);
release_source(src);
}
void
flush_all_routes()
{
int i;
/* Start from the end, to avoid shifting the table. */
i = route_slots - 1;
while(i >= 0) {
while(i < route_slots) {
/* Uninstall first, to avoid calling route_lost. */
if(routes[i]->installed)
uninstall_route(routes[0]);
flush_route(routes[i]);
}
i--;
}
check_sources_released();
}
void
flush_neighbour_routes(struct neighbour *neigh)
{
int i;
i = 0;
while(i < route_slots) {
struct babel_route *r;
r = routes[i];
while(r) {
if(r->neigh == neigh) {
flush_route(r);
goto again;
}
r = r->next;
}
i++;
again:
;
}
}
void
flush_interface_routes(struct interface *ifp, int v4only)
{
int i;
i = 0;
while(i < route_slots) {
struct babel_route *r;
r = routes[i];
while(r) {
if(r->neigh->ifp == ifp &&
(!v4only || v4mapped(r->nexthop))) {
flush_route(r);
goto again;
}
r = r->next;
}
i++;
again:
;
}
}
/* Iterate a function over all routes. */
void
for_all_routes(void (*f)(struct babel_route*, void*), void *closure)
{
int i;
for(i = 0; i < route_slots; i++) {
struct babel_route *r = routes[i];
while(r) {
(*f)(r, closure);
r = r->next;
}
}
}
void
for_all_installed_routes(void (*f)(struct babel_route*, void*), void *closure)
{
int i;
for(i = 0; i < route_slots; i++) {
if(routes[i]->installed)
(*f)(routes[i], closure);
}
}
static int
metric_to_kernel(int metric)
{
return metric < INFINITY ? kernel_metric : KERNEL_INFINITY;
}
/* This is used to maintain the invariant that the installed route is at
the head of the list. */
static void
move_installed_route(struct babel_route *route, int i)
{
assert(i >= 0 && i < route_slots);
assert(route->installed);
if(route != routes[i]) {
struct babel_route *r = routes[i];
while(r->next != route)
r = r->next;
r->next = route->next;
route->next = routes[i];
routes[i] = route;
}
}
void
install_route(struct babel_route *route)
{
int i, rc;
if(route->installed)
return;
if(!route_feasible(route))
fprintf(stderr, "WARNING: installing unfeasible route "
"(this shouldn't happen).");
i = find_route_slot(route->src->prefix, route->src->plen, NULL);
assert(i >= 0 && i < route_slots);
if(routes[i] != route && routes[i]->installed) {
fprintf(stderr, "WARNING: attempting to install duplicate route "
"(this shouldn't happen).");
return;
}
rc = kernel_route(ROUTE_ADD, route->src->prefix, route->src->plen,
route->nexthop,
route->neigh->ifp->ifindex,
metric_to_kernel(route_metric(route)), NULL, 0, 0);
if(rc < 0) {
int save = errno;
perror("kernel_route(ADD)");
if(save != EEXIST)
return;
}
route->installed = 1;
move_installed_route(route, i);
local_notify_route(route, LOCAL_CHANGE);
}
void
uninstall_route(struct babel_route *route)
{
int rc;
if(!route->installed)
return;
rc = kernel_route(ROUTE_FLUSH, route->src->prefix, route->src->plen,
route->nexthop,
route->neigh->ifp->ifindex,
metric_to_kernel(route_metric(route)), NULL, 0, 0);
if(rc < 0)
perror("kernel_route(FLUSH)");
route->installed = 0;
local_notify_route(route, LOCAL_CHANGE);
}
/* This is equivalent to uninstall_route followed with install_route,
but without the race condition. The destination of both routes
must be the same. */
static void
switch_routes(struct babel_route *old, struct babel_route *new)
{
int rc;
if(!old) {
install_route(new);
return;
}
if(!old->installed)
return;
if(!route_feasible(new))
fprintf(stderr, "WARNING: switching to unfeasible route "
"(this shouldn't happen).");
rc = kernel_route(ROUTE_MODIFY, old->src->prefix, old->src->plen,
old->nexthop, old->neigh->ifp->ifindex,
metric_to_kernel(route_metric(old)),
new->nexthop, new->neigh->ifp->ifindex,
metric_to_kernel(route_metric(new)));
if(rc < 0) {
perror("kernel_route(MODIFY)");
return;
}
old->installed = 0;
new->installed = 1;
move_installed_route(new, find_route_slot(new->src->prefix, new->src->plen,
NULL));
local_notify_route(old, LOCAL_CHANGE);
local_notify_route(new, LOCAL_CHANGE);
}
static void
change_route_metric(struct babel_route *route,
unsigned refmetric, unsigned cost, unsigned add)
{
int old, new;
int newmetric = MIN(refmetric + cost + add, INFINITY);
old = metric_to_kernel(route_metric(route));
new = metric_to_kernel(newmetric);
if(route->installed && old != new) {
int rc;
rc = kernel_route(ROUTE_MODIFY, route->src->prefix, route->src->plen,
route->nexthop, route->neigh->ifp->ifindex,
old,
route->nexthop, route->neigh->ifp->ifindex,
new);
if(rc < 0) {
perror("kernel_route(MODIFY metric)");
return;
}
}
route->refmetric = refmetric;
route->cost = cost;
route->add_metric = add;
local_notify_route(route, LOCAL_CHANGE);
}
static void
retract_route(struct babel_route *route)
{
change_route_metric(route, INFINITY, INFINITY, 0);
}
int
route_feasible(struct babel_route *route)
{
return update_feasible(route->src, route->seqno, route->refmetric);
}
int
route_old(struct babel_route *route)
{
return route->time < now.tv_sec - route->hold_time * 7 / 8;
}
int
route_expired(struct babel_route *route)
{
return route->time < now.tv_sec - route->hold_time;
}
static int
channels_interfere(int ch1, int ch2)
{
if(ch1 == IF_CHANNEL_NONINTERFERING || ch2 == IF_CHANNEL_NONINTERFERING)
return 0;
if(ch1 == IF_CHANNEL_INTERFERING || ch2 == IF_CHANNEL_INTERFERING)
return 1;
return ch1 == ch2;
}
int
route_interferes(struct babel_route *route, struct interface *ifp)
{
switch(diversity_kind) {
case DIVERSITY_NONE:
return 1;
case DIVERSITY_INTERFACE_1:
return route->neigh->ifp == ifp;
case DIVERSITY_CHANNEL_1:
case DIVERSITY_CHANNEL:
if(route->neigh->ifp == ifp)
return 1;
if(channels_interfere(ifp->channel, route->neigh->ifp->channel))
return 1;
if(diversity_kind == DIVERSITY_CHANNEL) {
int i;
for(i = 0; i < DIVERSITY_HOPS; i++) {
if(route->channels[i] == 0)
break;
if(channels_interfere(ifp->channel, route->channels[i]))
return 1;
}
}
return 0;
default:
fprintf(stderr, "Unknown kind of diversity.\n");
return 1;
}
}
int
update_feasible(struct source *src,
unsigned short seqno, unsigned short refmetric)
{
if(src == NULL)
return 1;
if(src->time < now.tv_sec - SOURCE_GC_TIME)
/* Never mind what is probably stale data */
return 1;
if(refmetric >= INFINITY)
/* Retractions are always feasible */
return 1;
return (seqno_compare(seqno, src->seqno) > 0 ||
(src->seqno == seqno && refmetric < src->metric));
}
static int
route_acceptable(struct babel_route *route, int feasible,
struct neighbour *exclude)
{
if(route_expired(route))
return 0;
if(feasible && !route_feasible(route))
return 0;
if(exclude && route->neigh == exclude)
return 0;
return 1;
}
struct babel_route *
find_best_route(const unsigned char *prefix, unsigned char plen, int feasible,
struct neighbour *exclude)
{
struct babel_route *route, *r;
int i = find_route_slot(prefix, plen, NULL);
if(i < 0)
return NULL;
route = routes[i];
while(route && !route_acceptable(route, feasible, exclude))
route = route->next;
if(!route)
return NULL;
r = route->next;
while(r) {
if(route_acceptable(r, feasible, exclude) &&
(route_metric(r) < route_metric(route)))
route = r;
r = r->next;
}
return route;
}
void
update_route_metric(struct babel_route *route)
{
int oldmetric = route_metric(route);
if(route_expired(route)) {
if(route->refmetric < INFINITY) {
route->seqno = seqno_plus(route->src->seqno, 1);
retract_route(route);
if(oldmetric < INFINITY)
route_changed(route, route->src, oldmetric);
}
} else {
struct neighbour *neigh = route->neigh;
int add_metric = input_filter(route->src->id,
route->src->prefix, route->src->plen,
neigh->address,
neigh->ifp->ifindex);
change_route_metric(route, route->refmetric,
neighbour_cost(route->neigh), add_metric);
if(route_metric(route) != oldmetric)
route_changed(route, route->src, oldmetric);
}
}
/* Called whenever a neighbour's cost changes, to update the metric of
all routes through that neighbour. Calls local_notify_neighbour. */
void
update_neighbour_metric(struct neighbour *neigh, int changed)
{
if(changed) {
int i;
for(i = 0; i < route_slots; i++) {
struct babel_route *r = routes[i];
while(r) {
if(r->neigh == neigh)
update_route_metric(r);
r = r->next;
}
}
}
local_notify_neighbour(neigh, LOCAL_CHANGE);
}
void
update_interface_metric(struct interface *ifp)
{
int i;
for(i = 0; i < route_slots; i++) {
struct babel_route *r = routes[i];
while(r) {
if(r->neigh->ifp == ifp)
update_route_metric(r);
r = r->next;
}
}
}
/* This is called whenever we receive an update. */
struct babel_route *
update_route(const unsigned char *id,
const unsigned char *prefix, unsigned char plen,
unsigned short seqno, unsigned short refmetric,
unsigned short interval,
struct neighbour *neigh, const unsigned char *nexthop,
const unsigned char *channels, int channels_len)
{
struct babel_route *route;
struct source *src;
int metric, feasible;
int add_metric;
int hold_time = MAX((4 * interval) / 100 + interval / 50, 15);
if(memcmp(id, myid, 8) == 0)
return NULL;
if(martian_prefix(prefix, plen)) {
fprintf(stderr, "Rejecting martian route to %s through %s.\n",
format_prefix(prefix, plen), format_address(id));
return NULL;
}
add_metric = input_filter(id, prefix, plen,
neigh->address, neigh->ifp->ifindex);
if(add_metric >= INFINITY)
return NULL;
route = find_route(prefix, plen, neigh, nexthop);
if(route && memcmp(route->src->id, id, 8) == 0)
/* Avoid scanning the source table. */
src = route->src;
else
src = find_source(id, prefix, plen, 1, seqno);
if(src == NULL)
return NULL;
feasible = update_feasible(src, seqno, refmetric);
metric = MIN((int)refmetric + neighbour_cost(neigh) + add_metric, INFINITY);
if(route) {
struct source *oldsrc;
unsigned short oldmetric;
int lost = 0;
oldsrc = route->src;
oldmetric = route_metric(route);
/* If a successor switches sources, we must accept his update even
if it makes a route unfeasible in order to break any routing loops
in a timely manner. If the source remains the same, we ignore
the update. */
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->id),
route->seqno, route->refmetric,
format_address(src->id), seqno, refmetric);
if(src != route->src) {
uninstall_route(route);
lost = 1;
}
}
route->src = retain_source(src);
if((feasible || keep_unfeasible) && refmetric < INFINITY)
route->time = now.tv_sec;
route->seqno = seqno;
change_route_metric(route,
refmetric, neighbour_cost(neigh), add_metric);
route->hold_time = hold_time;
route_changed(route, oldsrc, oldmetric);
if(lost)
route_lost(oldsrc, oldmetric);
if(!feasible)
send_unfeasible_request(neigh, route->installed && route_old(route),
seqno, metric, src);
release_source(oldsrc);
} else {
struct babel_route *new_route;
if(refmetric >= INFINITY)
/* Somebody's retracting a route we never saw. */
return NULL;
if(!feasible) {
send_unfeasible_request(neigh, 0, seqno, metric, src);
if(!keep_unfeasible)
return NULL;
}
route = malloc(sizeof(struct babel_route));
if(route == NULL) {
perror("malloc(route)");
return NULL;
}
route->src = retain_source(src);
route->refmetric = refmetric;
route->cost = neighbour_cost(neigh);
route->add_metric = add_metric;
route->seqno = seqno;
route->neigh = neigh;
memcpy(route->nexthop, nexthop, 16);
route->time = now.tv_sec;
route->hold_time = hold_time;
route->installed = 0;
memset(&route->channels, 0, sizeof(route->channels));
if(channels_len > 0)
memcpy(&route->channels, channels,
MIN(channels_len, DIVERSITY_HOPS));
route->next = NULL;
new_route = insert_route(route);
if(new_route == NULL) {
fprintf(stderr, "Couldn't insert route.\n");
free(route);
return NULL;
}
local_notify_route(route, LOCAL_ADD);
consider_route(route);
}
return route;
}
/* We just received an unfeasible update. If it's any good, send
a request for a new seqno. */
void
send_unfeasible_request(struct neighbour *neigh, int force,
unsigned short seqno, unsigned short metric,
struct source *src)
{
struct babel_route *route = find_installed_route(src->prefix, src->plen);
if(seqno_minus(src->seqno, seqno) > 100) {
/* Probably a source that lost its seqno. Let it time-out. */
return;
}
if(force || !route || route_metric(route) >= metric + 512) {
send_unicast_multihop_request(neigh, src->prefix, src->plen,
src->metric >= INFINITY ?
src->seqno :
seqno_plus(src->seqno, 1),
src->id, 127);
}
}
/* This takes a feasible route and decides whether to install it. */
void
consider_route(struct babel_route *route)
{
struct babel_route *installed;
struct xroute *xroute;
if(route->installed)
return;
if(!route_feasible(route))
return;
xroute = find_xroute(route->src->prefix, route->src->plen);
if(xroute && (allow_duplicates < 0 || xroute->metric >= allow_duplicates))
return;
installed = find_installed_route(route->src->prefix, route->src->plen);
if(installed == NULL)
goto install;
if(route_metric(route) >= INFINITY)
return;
if(route_metric(installed) >= INFINITY)
goto install;
if(route_metric(installed) >= route_metric(route) + 64)
goto install;
return;
install:
switch_routes(installed, route);
if(installed && route->installed)
send_triggered_update(route, installed->src, route_metric(installed));
else
send_update(NULL, 1, route->src->prefix, route->src->plen);
return;
}
void
retract_neighbour_routes(struct neighbour *neigh)
{
int i;
for(i = 0; i < route_slots; i++) {
struct babel_route *r = routes[i];
while(r) {
if(r->neigh == neigh) {
if(r->refmetric != INFINITY) {
unsigned short oldmetric = route_metric(r);
retract_route(r);
if(oldmetric != INFINITY)
route_changed(r, r->src, oldmetric);
}
}
r = r->next;
}
i++;
}
}
void
send_triggered_update(struct babel_route *route, struct source *oldsrc,
unsigned oldmetric)
{
unsigned newmetric, diff;
/* 1 means send speedily, 2 means resend */
int urgent;
if(!route->installed)
return;
newmetric = route_metric(route);
diff =
newmetric >= oldmetric ? newmetric - oldmetric : oldmetric - newmetric;
if(route->src != oldsrc || (oldmetric < INFINITY && newmetric >= INFINITY))
/* Switching sources can cause transient routing loops.
Retractions can cause blackholes. */
urgent = 2;
else if(newmetric > oldmetric && oldmetric < 6 * 256 && diff >= 512)
/* Route getting significantly worse */
urgent = 1;
else if(unsatisfied_request(route->src->prefix, route->src->plen,
route->seqno, route->src->id))
/* Make sure that requests are satisfied speedily */
urgent = 1;
else if(oldmetric >= INFINITY && newmetric < INFINITY)
/* New route */
urgent = 0;
else if(newmetric < oldmetric && diff < 1024)
/* Route getting better. This may be a transient fluctuation, so
don't advertise it to avoid making routes unfeasible later on. */
return;
else if(diff < 384)
/* Don't fret about trivialities */
return;
else
urgent = 0;
if(urgent >= 2)
send_update_resend(NULL, route->src->prefix, route->src->plen);
else
send_update(NULL, urgent, route->src->prefix, route->src->plen);
if(oldmetric < INFINITY) {
if(newmetric >= oldmetric + 512) {
send_request_resend(NULL, route->src->prefix, route->src->plen,
route->src->metric >= INFINITY ?
route->src->seqno :
seqno_plus(route->src->seqno, 1),
route->src->id);
} else if(newmetric >= oldmetric + 288) {
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
route_changed(struct babel_route *route,
struct source *oldsrc, unsigned short oldmetric)
{
if(route->installed) {
struct babel_route *better_route;
/* Do this unconditionally -- microoptimisation is not worth it. */
better_route =
find_best_route(route->src->prefix, route->src->plen, 1, NULL);
if(better_route && route_metric(better_route) < route_metric(route))
consider_route(better_route);
if(route->installed)
/* We didn't switch to the better route. */
send_triggered_update(route, oldsrc, oldmetric);
} else {
/* Reconsider routes even when their metric didn't decrease,
they may not have been feasible before. */
consider_route(route);
}
}
/* We just lost the installed route to a given destination. */
void
route_lost(struct source *src, unsigned oldmetric)
{
struct babel_route *new_route;
new_route = find_best_route(src->prefix, src->plen, 1, NULL);
if(new_route) {
consider_route(new_route);
} else if(oldmetric < INFINITY) {
/* Avoid creating a blackhole. */
send_update_resend(NULL, src->prefix, src->plen);
/* If the route was usable enough, try to get an alternate one.
If it was not, we could be dealing with oscillations around
the value of INFINITY. */
if(oldmetric <= INFINITY / 2)
send_request_resend(NULL, src->prefix, src->plen,
src->metric >= INFINITY ?
src->seqno : seqno_plus(src->seqno, 1),
src->id);
}
}
/* This is called periodically to flush old routes. It will also send
requests for routes that are about to expire. */
void
expire_routes(void)
{
struct babel_route *r;
int i;
debugf("Expiring old routes.\n");
i = 0;
while(i < route_slots) {
r = routes[i];
while(r) {
/* Protect against clock being stepped. */
if(r->time > now.tv_sec || route_old(r)) {
flush_route(r);
goto again;
}
update_route_metric(r);
if(r->installed && r->refmetric < INFINITY) {
if(route_old(r))
/* Route about to expire, send a request. */
send_unicast_request(r->neigh,
r->src->prefix, r->src->plen);
}
r = r->next;
}
i++;
again:
;
}
}
/*
Copyright (c) 2007-2011 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 DIVERSITY_NONE 0
#define DIVERSITY_INTERFACE_1 1
#define DIVERSITY_CHANNEL_1 2
#define DIVERSITY_CHANNEL 3
#define DIVERSITY_HOPS 8
struct babel_route {
struct source *src;
unsigned short refmetric;
unsigned short cost;
unsigned short add_metric;
unsigned short seqno;
struct neighbour *neigh;
unsigned char nexthop[16];
time_t time;
unsigned short hold_time; /* in seconds */
short installed;
unsigned char channels[DIVERSITY_HOPS];
struct babel_route *next;
};
extern struct babel_route **routes;
extern int kernel_metric, allow_duplicates;
extern int diversity_kind, diversity_factor;
extern int keep_unfeasible;
static inline int
route_metric(const struct babel_route *route)
{
int m = (int)route->refmetric + route->cost + route->add_metric;
return MIN(m, INFINITY);
}
static inline int
route_metric_noninterfering(const struct babel_route *route)
{
int m =
(int)route->refmetric +
(diversity_factor * route->cost + 128) / 256 +
route->add_metric;
m = MAX(m, route->refmetric + 1);
return MIN(m, INFINITY);
}
struct babel_route *find_route(const unsigned char *prefix, unsigned char plen,
struct neighbour *neigh, const unsigned char *nexthop);
struct babel_route *find_installed_route(const unsigned char *prefix,
unsigned char plen);
int installed_routes_estimate(void);
void flush_route(struct babel_route *route);
void flush_all_routes(void);
void flush_neighbour_routes(struct neighbour *neigh);
void flush_interface_routes(struct interface *ifp, int v4only);
void for_all_routes(void (*f)(struct babel_route*, void*), void *closure);
void for_all_installed_routes(void (*f)(struct babel_route*, void*), void *closure);
void install_route(struct babel_route *route);
void uninstall_route(struct babel_route *route);
void switch_route(struct babel_route *old, struct babel_route *new);
int route_feasible(struct babel_route *route);
int route_old(struct babel_route *route);
int route_expired(struct babel_route *route);
int route_interferes(struct babel_route *route, struct interface *ifp);
int update_feasible(struct source *src,
unsigned short seqno, unsigned short refmetric);
struct babel_route *find_best_route(const unsigned char *prefix, unsigned char plen,
int feasible, struct neighbour *exclude);
struct babel_route *install_best_route(const unsigned char prefix[16],
unsigned char plen);
void update_neighbour_metric(struct neighbour *neigh, int changed);
void update_interface_metric(struct interface *ifp);
void update_route_metric(struct babel_route *route);
struct babel_route *update_route(const unsigned char *id,
const unsigned char *prefix, unsigned char plen,
unsigned short seqno, unsigned short refmetric,
unsigned short interval, struct neighbour *neigh,
const unsigned char *nexthop,
const unsigned char *channels, int channels_len);
void retract_neighbour_routes(struct neighbour *neigh);
void send_unfeasible_request(struct neighbour *neigh, int force,
unsigned short seqno, unsigned short metric,
struct source *src);
void consider_route(struct babel_route *route);
void send_triggered_update(struct babel_route *route,
struct source *oldsrc, unsigned oldmetric);
void route_changed(struct babel_route *route,
struct source *oldsrc, unsigned short oldmetric);
void route_lost(struct source *src, unsigned oldmetric);
void expire_routes(void);
/*
Copyright (c) 2007, 2008 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 <sys/time.h>
#include <assert.h>
#include "babeld.h"
#include "util.h"
#include "source.h"
#include "interface.h"
#include "route.h"
struct source *srcs = NULL;
struct source*
find_source(const unsigned char *id, const unsigned char *p, unsigned char plen,
int create, unsigned short seqno)
{
struct source *src;
for(src = srcs; src; src = src->next) {
/* This should really be a hash table. For now, check the
last byte first. */
if(src->id[7] != id[7])
continue;
if(memcmp(src->id, id, 8) != 0)
continue;
if(src->plen != plen)
continue;
if(memcmp(src->prefix, p, 16) == 0)
return src;
}
if(!create)
return NULL;
src = malloc(sizeof(struct source));
if(src == NULL) {
perror("malloc(source)");
return NULL;
}
memcpy(src->id, id, 8);
memcpy(src->prefix, p, 16);
src->plen = plen;
src->seqno = seqno;
src->metric = INFINITY;
src->time = now.tv_sec;
src->route_count = 0;
src->next = srcs;
srcs = src;
return src;
}
struct source *
retain_source(struct source *src)
{
assert(src->route_count < 0xffff);
src->route_count++;
return src;
}
void
release_source(struct source *src)
{
assert(src->route_count > 0);
src->route_count--;
}
int
flush_source(struct source *src)
{
if(src->route_count > 0)
/* The source is in use by a route. */
return 0;
if(srcs == src) {
srcs = src->next;
} else {
struct source *previous = srcs;
while(previous->next != src)
previous = previous->next;
previous->next = src->next;
}
free(src);
return 1;
}
void
update_source(struct source *src,
unsigned short seqno, unsigned short metric)
{
if(metric >= INFINITY)
return;
/* If a source is expired, pretend that it doesn't exist and update
it unconditionally. This makes ensures that old data will
eventually be overridden, and prevents us from getting stuck if
a router loses its sequence number. */
if(src->time < now.tv_sec - SOURCE_GC_TIME ||
seqno_compare(src->seqno, seqno) < 0 ||
(src->seqno == seqno && src->metric > metric)) {
src->seqno = seqno;
src->metric = metric;
}
src->time = now.tv_sec;
}
void
expire_sources()
{
struct source *src;
src = srcs;
while(src) {
if(src->time > now.tv_sec)
/* clock stepped */
src->time = now.tv_sec;
if(src->time < now.tv_sec - SOURCE_GC_TIME) {
struct source *old = src;
src = src->next;
flush_source(old);
continue;
}
src = src->next;
}
}
void
check_sources_released(void)
{
struct source *src;
for(src = srcs; src; src = src->next) {
if(src->route_count != 0)
fprintf(stderr, "Warning: source %s %s has refcount %d.\n",
format_eui64(src->id),
format_prefix(src->prefix, src->plen),
(int)src->route_count);
}
}
/*
Copyright (c) 2007, 2008 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 SOURCE_GC_TIME 200
struct source {
struct source *next;
unsigned char id[8];
unsigned char prefix[16];
unsigned char plen;
unsigned short seqno;
unsigned short metric;
unsigned short route_count;
time_t time;
};
struct source *find_source(const unsigned char *id,
const unsigned char *p,
unsigned char plen,
int create, unsigned short seqno);
struct source *retain_source(struct source *src);
void release_source(struct source *src);
int flush_source(struct source *src);
void update_source(struct source *src,
unsigned short seqno, unsigned short metric);
void expire_sources(void);
void check_sources_released(void);
/*
Copyright (c) 2007, 2008 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 <unistd.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "babeld.h"
#include "util.h"
int
roughly(int value)
{
if(value < 0)
return -roughly(-value);
else if(value <= 1)
return value;
else
return value * 3 / 4 + random() % (value / 2);
}
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;
}
}
unsigned
timeval_minus_msec(const struct timeval *s1, const struct timeval *s2)
{
if(s1->tv_sec < s2->tv_sec)
return 0;
/* Avoid overflow. */
if(s1->tv_sec - s2->tv_sec > 2000000)
return 2000000000;
if(s1->tv_sec > s2->tv_sec)
return
(unsigned)((unsigned)(s1->tv_sec - s2->tv_sec) * 1000 +
((int)s1->tv_usec - s2->tv_usec) / 1000);
if(s1->tv_usec <= s2->tv_usec)
return 0;
return (unsigned)(s1->tv_usec - s2->tv_usec) / 1000u;
}
void
timeval_add_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)
{
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, time_t secs)
{
if(d->tv_sec == 0 || d->tv_sec > secs) {
d->tv_sec = secs;
d->tv_usec = random() % 1000000;
}
}
/* There's no good name for a positive int in C, call it nat. */
int
parse_nat(const char *string)
{
long l;
char *end;
l = strtol(string, &end, 0);
while(*end == ' ' || *end == '\t')
end++;
if(*end != '\0')
return -1;
if(l < 0 || l > INT_MAX)
return -1;
return (int)l;
}
int
parse_msec(const char *string)
{
unsigned int in, fl;
int i, j;
in = fl = 0;
i = 0;
while(string[i] == ' ' || string[i] == '\t')
i++;
while(string[i] >= '0' && string[i] <= '9') {
in = in * 10 + string[i] - '0';
i++;
}
if(string[i] == '.') {
i++;
j = 0;
while(string[i] >= '0' && string[i] <= '9') {
fl = fl * 10 + string[i] - '0';
i++;
j++;
}
while(j > 3) {
fl /= 10;
j--;
}
while(j < 3) {
fl *= 10;
j++;
}
}
while(string[i] == ' ' || string[i] == '\t')
i++;
if(string[i] == '\0')
return in * 1000 + fl;
return -1;
}
void
do_debugf(int level, const char *format, ...)
{
va_list args;
va_start(args, format);
if(debug >= level) {
vfprintf(stderr, format, args);
fflush(stderr);
}
va_end(args);
}
int
in_prefix(const unsigned char *restrict address,
const unsigned char *restrict prefix, unsigned char plen)
{
unsigned char m;
if(plen > 128)
plen = 128;
if(memcmp(address, prefix, plen / 8) != 0)
return 0;
if(plen % 8 == 0)
return 1;
m = 0xFF << (8 - (plen % 8));
return ((address[plen / 8] & m) == (prefix[plen / 8] & m));
}
unsigned char *
mask_prefix(unsigned char *restrict ret,
const unsigned char *restrict prefix, unsigned char plen)
{
if(plen >= 128) {
memcpy(ret, prefix, 16);
return ret;
}
memset(ret, 0, 16);
memcpy(ret, prefix, plen / 8);
if(plen % 8 != 0)
ret[plen / 8] =
(prefix[plen / 8] & ((0xFF << (8 - (plen % 8))) & 0xFF));
return ret;
}
static const unsigned char v4prefix[16] =
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0, 0, 0, 0 };
static const unsigned char llprefix[16] =
{0xFE, 0x80};
const char *
format_address(const unsigned char *address)
{
static char buf[4][INET6_ADDRSTRLEN];
static int i = 0;
i = (i + 1) % 4;
if(v4mapped(address))
inet_ntop(AF_INET, address + 12, buf[i], INET6_ADDRSTRLEN);
else
inet_ntop(AF_INET6, address, buf[i], INET6_ADDRSTRLEN);
return buf[i];
}
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;
if(plen >= 96 && v4mapped(prefix)) {
inet_ntop(AF_INET, prefix + 12, buf[i], INET6_ADDRSTRLEN);
n = strlen(buf[i]);
snprintf(buf[i] + n, INET6_ADDRSTRLEN + 4 - n, "/%d", plen - 96);
} else {
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];
}
const char *
format_eui64(const unsigned char *eui)
{
static char buf[4][28];
static int i = 0;
i = (i + 1) % 4;
snprintf(buf[i], 28, "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
eui[0], eui[1], eui[2], eui[3],
eui[4], eui[5], eui[6], eui[7]);
return buf[i];
}
int
parse_address(const char *address, unsigned char *addr_r, int *af_r)
{
struct in_addr ina;
struct in6_addr ina6;
int rc;
rc = inet_pton(AF_INET, address, &ina);
if(rc > 0) {
memcpy(addr_r, v4prefix, 12);
memcpy(addr_r + 12, &ina, 4);
if(af_r) *af_r = AF_INET;
return 0;
}
rc = inet_pton(AF_INET6, address, &ina6);
if(rc > 0) {
memcpy(addr_r, &ina6, 16);
if(af_r) *af_r = AF_INET6;
return 0;
}
return -1;
}
int
parse_net(const char *net, unsigned char *prefix_r, unsigned char *plen_r,
int *af_r)
{
char buf[INET6_ADDRSTRLEN];
char *slash, *end;
unsigned char prefix[16];
long plen;
int af;
struct in_addr ina;
struct in6_addr ina6;
int rc;
if(strcmp(net, "default") == 0) {
memset(prefix, 0, 16);
plen = 0;
af = AF_INET6;
} else {
slash = strchr(net, '/');
if(slash == NULL) {
rc = parse_address(net, prefix, &af);
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 = inet_pton(AF_INET, buf, &ina);
if(rc > 0) {
memcpy(prefix, v4prefix, 12);
memcpy(prefix + 12, &ina, 4);
plen = strtol(slash + 1, &end, 0);
if(*end != '\0' || plen < 0 || plen > 32)
return -1;
plen += 96;
af = AF_INET;
} else {
rc = inet_pton(AF_INET6, buf, &ina6);
if(rc > 0) {
memcpy(prefix, &ina6, 16);
plen = strtol(slash + 1, &end, 0);
if(*end != '\0' || plen < 0 || plen > 128)
return -1;
af = AF_INET6;
} else {
return -1;
}
}
}
}
mask_prefix(prefix_r, prefix, plen);
*plen_r = plen;
if(af_r) *af_r = af;
return 0;
}
int
parse_eui64(const char *eui, unsigned char *eui_r)
{
int n;
n = sscanf(eui, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
&eui_r[0], &eui_r[1], &eui_r[2], &eui_r[3],
&eui_r[4], &eui_r[5], &eui_r[6], &eui_r[7]);
if(n == 8)
return 0;
n = sscanf(eui, "%02hhx-%02hhx-%02hhx-%02hhx-%02hhx-%02hhx-%02hhx-%02hhx",
&eui_r[0], &eui_r[1], &eui_r[2], &eui_r[3],
&eui_r[4], &eui_r[5], &eui_r[6], &eui_r[7]);
if(n == 8)
return 0;
n = sscanf(eui, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
&eui_r[0], &eui_r[1], &eui_r[2],
&eui_r[5], &eui_r[6], &eui_r[7]);
if(n == 6) {
eui_r[3] = 0xFF;
eui_r[4] = 0xFE;
return 0;
}
return -1;
}
int
wait_for_fd(int direction, int fd, int msecs)
{
fd_set fds;
int rc;
struct timeval tv;
tv.tv_sec = msecs / 1000;
tv.tv_usec = (msecs % 1000) * 1000;
FD_ZERO(&fds);
FD_SET(fd, &fds);
if(direction)
rc = select(fd + 1, NULL, &fds, NULL, &tv);
else
rc = select(fd + 1, &fds, NULL, NULL, &tv);
return rc;
}
int
martian_prefix(const unsigned char *prefix, int plen)
{
return
(plen >= 8 && prefix[0] == 0xFF) ||
(plen >= 10 && prefix[0] == 0xFE && (prefix[1] & 0xC0) == 0x80) ||
(plen >= 128 && memcmp(prefix, zeroes, 15) == 0 &&
(prefix[15] == 0 || prefix[15] == 1)) ||
(plen >= 96 && v4mapped(prefix) &&
((plen >= 104 && (prefix[12] == 127 || prefix[12] == 0)) ||
(plen >= 100 && (prefix[12] & 0xE0) == 0xE0)));
}
int
linklocal(const unsigned char *address)
{
return memcmp(address, llprefix, 8) == 0;
}
int
v4mapped(const unsigned char *address)
{
return memcmp(address, v4prefix, 12) == 0;
}
void
v4tov6(unsigned char *dst, const unsigned char *src)
{
memcpy(dst, v4prefix, 12);
memcpy(dst + 12, src, 4);
}
int
daemonise()
{
int rc;
fflush(stdout);
fflush(stderr);
rc = fork();
if(rc < 0)
return -1;
if(rc > 0)
exit(0);
rc = setsid();
if(rc < 0)
return -1;
return 1;
}
/*
Copyright (c) 2007, 2008 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.
*/
#if defined(i386) || defined(__mc68020__) || defined(__x86_64__)
#define DO_NTOHS(_d, _s) do{ _d = ntohs(*(const unsigned short*)(_s)); }while(0)
#define DO_NTOHL(_d, _s) do{ _d = ntohl(*(const unsigned*)(_s)); } while(0)
#define DO_HTONS(_d, _s) do{ *(unsigned short*)(_d) = htons(_s); } while(0)
#define DO_HTONL(_d, _s) do{ *(unsigned*)(_d) = htonl(_s); } while(0)
/* Some versions of gcc seem to be buggy, and ignore the packed attribute.
Disable this code until the issue is clarified. */
/* #elif defined __GNUC__*/
#elif 0
struct __us { unsigned short x __attribute__((packed)); };
#define DO_NTOHS(_d, _s) \
do { _d = ntohs(((const struct __us*)(_s))->x); } while(0)
#define DO_HTONS(_d, _s) \
do { ((struct __us*)(_d))->x = htons(_s); } while(0)
#else
#define DO_NTOHS(_d, _s) \
do { short _dd; \
memcpy(&(_dd), (_s), 2); \
_d = ntohs(_dd); } while(0)
#define DO_HTONS(_d, _s) \
do { unsigned short _dd; \
_dd = htons(_s); \
memcpy((_d), &(_dd), 2); } while(0)
#endif
static inline int
seqno_compare(unsigned short s1, unsigned short s2)
{
if(s1 == s2)
return 0;
else
return ((s2 - s1) & 0x8000) ? 1 : -1;
}
static inline short
seqno_minus(unsigned short s1, unsigned short s2)
{
return (short)((s1 - s2) & 0xFFFF);
}
static inline unsigned short
seqno_plus(unsigned short s, int plus)
{
return ((s + plus) & 0xFFFF);
}
int roughly(int value);
void timeval_minus(struct timeval *d,
const struct timeval *s1, const struct timeval *s2);
unsigned timeval_minus_msec(const struct timeval *s1, const struct timeval *s2)
ATTRIBUTE ((pure));
void timeval_add_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, time_t secs);
int parse_nat(const char *string) ATTRIBUTE ((pure));
int parse_msec(const char *string) ATTRIBUTE ((pure));
void do_debugf(int level, const char *format, ...)
ATTRIBUTE ((format (printf, 2, 3))) COLD;
int in_prefix(const unsigned char *restrict address,
const unsigned char *restrict prefix, unsigned char plen)
ATTRIBUTE ((pure));
unsigned char *mask_prefix(unsigned char *restrict ret,
const unsigned char *restrict prefix,
unsigned char plen);
const char *format_address(const unsigned char *address);
const char *format_prefix(const unsigned char *address, unsigned char prefix);
const char *format_eui64(const unsigned char *eui);
int parse_address(const char *address, unsigned char *addr_r, int *af_r);
int parse_net(const char *net, unsigned char *prefix_r, unsigned char *plen_r,
int *af_r);
int parse_eui64(const char *eui, unsigned char *eui_r);
int wait_for_fd(int direction, int fd, int msecs);
int martian_prefix(const unsigned char *prefix, int plen) ATTRIBUTE ((pure));
int linklocal(const unsigned char *address) ATTRIBUTE ((pure));
int v4mapped(const unsigned char *address) ATTRIBUTE ((pure));
void v4tov6(unsigned char *dst, const unsigned char *src);
int daemonise(void);
/* 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 NO_DEBUG
#if defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L
#define debugf(...) do {} while(0)
#define kdebugf(...) do {} while(0)
#elif defined __GNUC__
#define debugf(_args...) do {} while(0)
#define kdebugf(_args...) do {} while(0)
#else
static inline void debugf(const char *format, ...) { return; }
static inline void kdebugf(const char *format, ...) { return; }
#endif
#else
#if defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L
#define debugf(...) \
do { \
if(UNLIKELY(debug >= 2)) do_debugf(2, __VA_ARGS__); \
} while(0)
#define kdebugf(...) \
do { \
if(UNLIKELY(debug >= 3)) do_debugf(3, __VA_ARGS__); \
} while(0)
#elif defined __GNUC__
#define debugf(_args...) \
do { \
if(UNLIKELY(debug >= 2)) do_debugf(2, _args); \
} while(0)
#define kdebugf(_args...) \
do { \
if(UNLIKELY(debug >= 3)) do_debugf(3, _args); \
} while(0)
#else
static inline void debugf(const char *format, ...) { return; }
static inline void kdebugf(const char *format, ...) { return; }
#endif
#endif
/*
Copyright (c) 2007, 2008 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 <sys/time.h>
#include <netinet/in.h>
#include "babeld.h"
#include "kernel.h"
#include "neighbour.h"
#include "message.h"
#include "route.h"
#include "xroute.h"
#include "util.h"
#include "configuration.h"
#include "interface.h"
#include "local.h"
static struct xroute *xroutes;
static int numxroutes = 0, maxxroutes = 0;
struct xroute *
find_xroute(const unsigned char *prefix, unsigned char 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;
}
void
flush_xroute(struct xroute *xroute)
{
int i;
i = xroute - xroutes;
assert(i >= 0 && i < numxroutes);
local_notify_xroute(xroute, LOCAL_FLUSH);
if(i != numxroutes - 1)
memcpy(xroutes + i, xroutes + numxroutes - 1, sizeof(struct xroute));
numxroutes--;
VALGRIND_MAKE_MEM_UNDEFINED(xroutes + numxroutes, sizeof(struct xroute));
if(numxroutes == 0) {
free(xroutes);
xroutes = NULL;
maxxroutes = 0;
} else if(maxxroutes > 8 && numxroutes < maxxroutes / 4) {
struct xroute *new_xroutes;
int n = maxxroutes / 2;
new_xroutes = realloc(xroutes, n * sizeof(struct xroute));
if(new_xroutes == NULL)
return;
xroutes = new_xroutes;
maxxroutes = n;
}
}
int
add_xroute(unsigned char prefix[16], unsigned char plen,
unsigned short metric, unsigned int ifindex, int proto)
{
struct xroute *xroute = find_xroute(prefix, plen);
if(xroute) {
if(xroute->metric <= metric)
return 0;
xroute->metric = metric;
local_notify_xroute(xroute, LOCAL_CHANGE);
return 1;
}
if(numxroutes >= maxxroutes) {
struct xroute *new_xroutes;
int n = maxxroutes < 1 ? 8 : 2 * maxxroutes;
new_xroutes = xroutes == NULL ?
malloc(n * sizeof(struct xroute)) :
realloc(xroutes, n * sizeof(struct xroute));
if(new_xroutes == NULL)
return -1;
maxxroutes = n;
xroutes = new_xroutes;
}
memcpy(xroutes[numxroutes].prefix, prefix, 16);
xroutes[numxroutes].plen = plen;
xroutes[numxroutes].metric = metric;
xroutes[numxroutes].ifindex = ifindex;
xroutes[numxroutes].proto = proto;
numxroutes++;
local_notify_xroute(&xroutes[numxroutes - 1], LOCAL_ADD);
return 1;
}
/* Returns an overestimate of the number of xroutes. */
int
xroutes_estimate()
{
return numxroutes;
}
void
for_all_xroutes(void (*f)(struct xroute*, void*), void *closure)
{
int i;
for(i = 0; i < numxroutes; i++)
(*f)(&xroutes[i], closure);
}
int
check_xroutes(int send_updates)
{
int i, j, metric, export, change = 0, rc;
struct kernel_route *routes;
int numroutes;
static int maxroutes = 8;
const int maxmaxroutes = 16 * 1024;
debugf("\nChecking kernel routes.\n");
again:
routes = malloc(maxroutes * sizeof(struct kernel_route));
if(routes == NULL)
return -1;
rc = kernel_addresses(NULL, 0, 0, routes, maxroutes);
if(rc < 0) {
perror("kernel_addresses");
numroutes = 0;
} else {
numroutes = rc;
}
if(numroutes >= maxroutes)
goto resize;
rc = kernel_routes(routes + numroutes, maxroutes - numroutes);
if(rc < 0)
fprintf(stderr, "Couldn't get kernel routes.\n");
else
numroutes += rc;
if(numroutes >= maxroutes)
goto resize;
/* Check for any routes that need to be flushed */
i = 0;
while(i < numxroutes) {
export = 0;
metric = redistribute_filter(xroutes[i].prefix, xroutes[i].plen,
xroutes[i].ifindex, xroutes[i].proto);
if(metric < INFINITY && metric == xroutes[i].metric) {
for(j = 0; j < numroutes; j++) {
if(xroutes[i].plen == routes[j].plen &&
memcmp(xroutes[i].prefix, routes[j].prefix, 16) == 0 &&
xroutes[i].ifindex == routes[j].ifindex &&
xroutes[i].proto == routes[j].proto) {
export = 1;
break;
}
}
}
if(!export) {
unsigned char prefix[16], plen;
struct babel_route *route;
memcpy(prefix, xroutes[i].prefix, 16);
plen = xroutes[i].plen;
flush_xroute(&xroutes[i]);
route = find_best_route(prefix, plen, 1, NULL);
if(route)
install_route(route);
/* send_update_resend only records the prefix, so the update
will only be sent after we perform all of the changes. */
if(send_updates)
send_update_resend(NULL, prefix, plen);
change = 1;
} else {
i++;
}
}
/* Add any new routes */
for(i = 0; i < numroutes; i++) {
if(martian_prefix(routes[i].prefix, routes[i].plen))
continue;
metric = redistribute_filter(routes[i].prefix, routes[i].plen,
routes[i].ifindex, routes[i].proto);
if(metric < INFINITY) {
rc = add_xroute(routes[i].prefix, routes[i].plen,
metric, routes[i].ifindex, routes[i].proto);
if(rc > 0) {
struct babel_route *route;
route = find_installed_route(routes[i].prefix, routes[i].plen);
if(route) {
if(allow_duplicates < 0 ||
routes[i].metric < allow_duplicates)
uninstall_route(route);
}
change = 1;
if(send_updates)
send_update(NULL, 0, routes[i].prefix, routes[i].plen);
}
}
}
free(routes);
/* Set up maxroutes for the next call. */
maxroutes = MIN(numroutes + 8, maxmaxroutes);
return change;
resize:
free(routes);
if(maxroutes >= maxmaxroutes)
return -1;
maxroutes = MIN(maxmaxroutes, 2 * maxroutes);
goto again;
}
/*
Copyright (c) 2007, 2008 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 {
unsigned char prefix[16];
unsigned char plen;
unsigned short metric;
unsigned int ifindex;
int proto;
};
struct xroute *find_xroute(const unsigned char *prefix, unsigned char plen);
void flush_xroute(struct xroute *xroute);
int add_xroute(unsigned char prefix[16], unsigned char plen,
unsigned short metric, unsigned int ifindex, int proto);
int xroutes_estimate(void);
void for_all_xroutes(void (*f)(struct xroute*, void*), void *closure);
int check_xroutes(int send_updates);
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