pax_global_header 0000666 0000000 0000000 00000000064 12603261366 0014517 g ustar 00root root 0000000 0000000 52 comment=273bcc70824af88574c3724b3c6a69a9722d4cc4
babeld-babeld-1.6.3-273bcc70824af88574c3724b3c6a69a9722d4cc4/ 0000775 0000000 0000000 00000000000 12603261366 0021453 5 ustar 00root root 0000000 0000000 babeld-babeld-1.6.3-273bcc70824af88574c3724b3c6a69a9722d4cc4/CHANGES 0000664 0000000 0000000 00000043472 12603261366 0022460 0 ustar 00root root 0000000 0000000 1 October 2015: babeld-1.6.3
* Changed the handling of kernel configuration and added the
skip-kernel-setup option. Thanks to Toke Høiland-Jørgensen.
* Added the option "router-id" and removed the flag "-R". This is an
incompatible change.
31 July 2015: babeld-1.6.2
* Added the ability to specify a router-id explicitly (-R).
* Changed router-id computation to use all interfaces, which increases
the chances of a stable id.
* Changed the format of babel-state to only contain the seqno -- the
validation of router-id was useless, and actually harmful when the
router-id changed multiple times.
* Fixed a bug with native source-specific routing. Thanks to Matthieu
Boutier.
16 June 2015: babeld-1.6.1
* Fixed a buffer overflow in zone_equal. This is probably not
exploitable, but might cause incorrect routing tables in the presence
of source-specific routing.
* Added support for defaulting ipv6-subtrees automatically based on the
kernel version.
* Fixed compilation under musl.
14 April 2015: babeld-1.6.0
* Added support for source-specific routing. Thanks to Matthieu Boutier.
* Added support for reflecting metrics as kernel priorities. Thanks to
Pierre Pfister.
* Worked around a Linux kernel bug with an infinite EAGAIN loop. Thanks
to Dave Taht.
* Changed wildcard updates to not set flag 0x40. Reported by Markus
Stenberg.
* Made ipv6-subtrees a runtime option. Thanks to Matthieu Boutier.
4 July 2014: babeld-1.5.1
* Added support for reading multiple configuration files by specifying
the -c command-line flag multiple times.
* Be less noisy about unknown TLV types. Thanks to Baptiste Jonglez.
22 May 2014: babeld-1.5.0
* Added support for an RTT-based metric -- see the description of
"enable-timestamps" in the manual page. This work was done by
Baptiste Jonglez with help from Matthieu Boutier.
15 November 2013: babeld-1.4.3
* Added random-id option to config file (equivalent to -r).
* Fixed parsing of compressed IPv4 updates. Thanks to Matthieu Boutier.
* Fixed formatting of seqno requests with short prefixes. Thanks to
Matthieu Boutier.
* Fixed possible DoS on the local interface. Thanks to Baptiste Jonglez.
* Fixed advertising costs higher than INFINITY on the local interface.
Thanks to Baptiste Jonglez.
* Fixed an assertion failure when an interface configuration is
split into multiple config file directives.
* Disable atomic route changes on BSD, which are buggy at least under
Mac OS X. Thanks to Grégoire Henry.
19 June 2013: babeld-1.4.2
* Extensive changes to the configuration parser. It is now possible
to set all command-line options from the configuration file, and
to specify default values for interface parameters.
* Allow redistributing routes from multiple kernel tables. Thanks
to Toke Høiland-Jørgensen.
* Fix some whitespace issues in the configuration parser.
* Fix a bug in the configuration parser that could give wrong values
to some exotic interface parameters (channel and faraway).
* Fix a bug that could cause some extra traffic at shutdown. Thanks
to Matthieu Boutier.
* Under Linux, set rp_filter explicitly for all interfaces. This
avoids mysterious routing failures on distributions that set
rp_filter by default. Reported by Baptiste Jonglez.
19 June 2013: babeld-1.3.8
* Fix a bug in the configuration parser that could give wrong values
to some exotic interface parameters (channel and faraway).
* Fix a bug that could cause some extra traffic at shutdown. Thanks
to Matthieu Boutier.
* Under Linux, set rp_filter explicitly for all interfaces. This
avoids mysterious routing failures on distributions that set
rp_filter by default. Reported by Baptiste Jonglez.
26 May 2013: babeld-1.4.1
* Fix a bug that would cause the channel list to remain stuck at its
initial value when running with -z3.
26 May 2013: babeld-1.3.7
* Fix a bug that would cause the channel list to remain stuck at its
initial value when running with -z3.
3 May 2013: babeld-1.4.0
* Change the route selection algorithm to used a smoothed metric in
addition to the "real" metric. This reduces the amount of route
flapping without any special case hacks.
* New flag -r, use a random router-id. This avoids temporarily
unreachable nodes after a reboot when persistent storage is not
available.
* INCOMPATIBLE CHANGE: the local interface now announces the local
hostname, and marks the end of the initial route dump. (Thanks to
Gabriel Kerneis.)
* The local interface is now able to accept multiple simultaneous
connections.
* Detect BATMAN interfaces, don't assume they are wired.
2 May 2013: babeld-1.3.6
* Work around recent Linux kernels advertising the IPv6 route cache
as if it were part of the routing table. Thanks to Baptiste Jonglez.
12 April 2013: babeld-1.3.5
* Fix parsing of "channel interfering". Reported by Gioacchino
Mazzurco.
* Correctly reset rp_filter to its old value at shutdown. Thanks to
Matthias Schiffer.
* Work around a race condition that could cause us to fail to notice
an interface's link-local address, and hence mark all neighbours
as unreachable. Reported by Gabriel Kerneis.
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.
babeld-babeld-1.6.3-273bcc70824af88574c3724b3c6a69a9722d4cc4/LICENCE 0000664 0000000 0000000 00000002060 12603261366 0022436 0 ustar 00root root 0000000 0000000 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.
babeld-babeld-1.6.3-273bcc70824af88574c3724b3c6a69a9722d4cc4/Makefile 0000664 0000000 0000000 00000002544 12603261366 0023120 0 ustar 00root root 0000000 0000000 PREFIX = /usr/local
MANDIR = $(PREFIX)/share/man
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 \
disambiguation.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 \
disambiguation.o
babeld: $(OBJS)
$(CC) $(CFLAGS) $(LDFLAGS) -o babeld $(OBJS) $(LDLIBS)
.SUFFIXES: .man .html
.man.html:
rman -f html $< | \
sed -e "s|\\(.*(8)\\)|\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)$(MANDIR)/man8
cp -f babeld.man $(TARGET)$(MANDIR)/man8/babeld.8
uninstall:
-rm -f $(TARGET)$(PREFIX)/bin/babeld
-rm -f $(TARGET)$(MANDIR)/man8/babeld.8
clean:
-rm -f babeld babeld.html *.o *~ core TAGS gmon.out
kernel.o: kernel_netlink.c kernel_socket.c
babeld-babeld-1.6.3-273bcc70824af88574c3724b3c6a69a9722d4cc4/README 0000664 0000000 0000000 00000006451 12603261366 0022341 0 ustar 00root root 0000000 0000000 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.univ-paris-diderot.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.univ-paris-diderot.fr/~jch/software/ahcp/
-- Juliusz Chroboczek
babeld-babeld-1.6.3-273bcc70824af88574c3724b3c6a69a9722d4cc4/babeld.c 0000664 0000000 0000000 00000074677 12603261366 0023055 0 ustar 00root root 0000000 0000000 /*
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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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 have_id = 0;
int debug = 0;
int link_detect = 0;
int all_wireless = 0;
int has_ipv6_subtrees = 0;
int default_wireless_hello_interval = -1;
int default_wired_hello_interval = -1;
int resend_delay = -1;
int random_id = 0;
int do_daemonise = 0;
int skip_kernel_setup = 0;
const char *logfile = NULL,
*pidfile = "/var/run/babeld.pid",
*state_file = "/var/lib/babel-state";
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};
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_rules_changed = 0;
static int kernel_link_changed = 0;
static int kernel_addr_changed = 0;
struct timeval check_neighbours_timeout, check_interfaces_timeout;
static volatile sig_atomic_t exiting = 0, dumping = 0, reopening = 0;
static int accept_local_connections(fd_set *readfds);
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;
const char **config_files = NULL;
int num_config_files = 0;
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;
change_smoothing_half_life(4);
has_ipv6_subtrees = kernel_has_ipv6_subtrees();
while(1) {
opt = getopt(argc, argv,
"m:p:h:H:i:k:A:sruS:d:g:lwz:M: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_thousands(optarg);
if(default_wireless_hello_interval <= 0 ||
default_wireless_hello_interval > 0xFFFF * 10)
goto usage;
break;
case 'H':
default_wired_hello_interval = parse_thousands(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 'r':
random_id = 1;
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 'M': {
int l = parse_nat(optarg);
if(l < 0 || l > 3600)
goto usage;
change_smoothing_half_life(l);
break;
}
case 't':
export_table = parse_nat(optarg);
if(export_table < 0 || export_table > 0xFFFF)
goto usage;
break;
case 'T':
if(add_import_table(parse_nat(optarg)))
goto usage;
break;
case 'c':
config_files = realloc(config_files,
(num_config_files + 1) * sizeof(char*));
if(config_files == NULL) {
fprintf(stderr, "Couldn't allocate config file.\n");
exit(1);
}
config_files[num_config_files++] = 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(num_config_files == 0) {
if(access("/etc/babeld.conf", F_OK) >= 0) {
config_files = malloc(sizeof(char*));
if(config_files == NULL) {
fprintf(stderr, "Couldn't allocate config file.\n");
exit(1);
}
config_files[num_config_files++] = "/etc/babeld.conf";
}
}
for(i = 0; i < num_config_files; i++) {
int line;
rc = parse_config_from_file(config_files[i], &line);
if(rc < 0) {
fprintf(stderr,
"Couldn't parse configuration from file %s "
"(error at line %d).\n",
config_files[i], line);
exit(1);
}
}
free(config_files);
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;
}
if(!have_id && !random_id) {
/* We use all available interfaces here, since this increases the
chances of getting a stable router-id in case the set of Babel
interfaces changes. */
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);
have_id = 1;
break;
}
}
if(!have_id) {
if(!random_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;
}
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];
int s;
rc = read(fd, buf, 99);
if(rc < 0) {
perror("read(babel-state)");
} else {
buf[rc] = '\0';
rc = sscanf(buf, "%d\n", &s);
if(rc == 1 && s >= 0 && s <= 0xFFFF) {
myseqno = seqno_plus(s, 1);
} 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;
if(receive_buffer == NULL)
goto fail;
check_interfaces();
rc = check_xroutes(0);
if(rc < 0)
fprintf(stderr, "Warning: couldn't check exported routes.\n");
kernel_routes_changed = 0;
kernel_rules_changed = 0;
kernel_link_changed = 0;
kernel_addr_changed = 0;
kernel_dump_time = now.tv_sec + roughly(30);
schedule_neighbours_check(5000, 1);
schedule_interfaces_check(30000, 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, 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(&tv, &check_interfaces_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_server_socket >= 0 &&
num_local_sockets < MAX_LOCAL_SOCKETS) {
FD_SET(local_server_socket, &readfds);
maxfd = MAX(maxfd, local_server_socket);
}
for(i = 0; i < num_local_sockets; i++) {
FD_SET(local_sockets[i], &readfds);
maxfd = MAX(maxfd, local_sockets[i]);
}
#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
accept_local_connections(&readfds);
i = 0;
while(i < num_local_sockets) {
if(FD_ISSET(local_sockets[i], &readfds)) {
rc = local_read(local_sockets[i]);
if(rc <= 0) {
if(rc < 0) {
if(errno == EINTR)
continue;
perror("read(local_socket)");
}
close(local_sockets[i]);
local_sockets[i] = local_sockets[--num_local_sockets];
continue;
}
}
i++;
}
#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 ||
kernel_rules_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_rules_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();
/* Multiply by 3/2 to allow neighbours to expire. */
msecs = MAX(3 * msecs / 2, 10);
schedule_neighbours_check(msecs, 1);
}
if(timeval_compare(&check_interfaces_timeout, &now) < 0) {
check_interfaces();
schedule_interfaces_check(30000, 1);
}
if(now.tv_sec >= expiry_time) {
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, 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 {
char buf[10];
rc = snprintf(buf, 10, "%d\n", (int)myseqno);
if(rc < 0 || rc >= 10) {
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] [-r] [-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);
}
static int
accept_local_connections(fd_set *readfds)
{
int rc, s;
if(local_server_socket < 0 || !FD_ISSET(local_server_socket, readfds))
return 0;
s = accept(local_server_socket, NULL, NULL);
if(s < 0) {
if(errno != EINTR && errno != EAGAIN) {
perror("accept(local_server_socket)");
return -1;
}
return 0;
}
if(num_local_sockets >= MAX_LOCAL_SOCKETS) {
/* This should never happen, since we don't select for
the server socket in this case. But I'm paranoid. */
fprintf(stderr, "Internal error: too many local sockets.\n");
close(s);
return -1;
}
rc = fcntl(s, F_GETFL, 0);
if(rc < 0) {
fprintf(stderr, "Unable to get flags of local socket.\n");
close(s);
return -1;
}
rc = fcntl(s, F_SETFL, (rc | O_NONBLOCK));
if(rc < 0) {
fprintf(stderr, "Unable to set flags of local socket.\n");
close(s);
return -1;
}
local_sockets[num_local_sockets++] = s;
local_notify_all_1(s);
return 1;
}
void
schedule_neighbours_check(int msecs, int override)
{
struct timeval timeout;
timeval_add_msec(&timeout, &now, roughly(msecs));
if(override)
check_neighbours_timeout = timeout;
else
timeval_min(&check_neighbours_timeout, &timeout);
}
void
schedule_interfaces_check(int msecs, int override)
{
struct timeval timeout;
timeval_add_msec(&timeout, &now, roughly(msecs));
if(override)
check_interfaces_timeout = timeout;
else
timeval_min(&check_interfaces_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(FILE *out, struct babel_route *route)
{
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, "%u", (unsigned)route->channels[k]);
j = strlen(channels);
}
snprintf(channels + j, 100 - j, ")");
if(k == 0)
channels[0] = '\0';
}
fprintf(out, "%s from %s metric %d (%d) refmetric %d id %s "
"seqno %d%s age %d via %s neigh %s%s%s%s\n",
format_prefix(route->src->prefix, route->src->plen),
format_prefix(route->src->src_prefix, route->src->src_plen),
route_metric(route), route_smoothed_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(FILE *out, struct xroute *xroute)
{
fprintf(out, "%s from %s metric %d (exported)\n",
format_prefix(xroute->prefix, xroute->plen),
format_prefix(xroute->src_prefix, xroute->src_plen),
xroute->metric);
}
static void
dump_tables(FILE *out)
{
struct neighbour *neigh;
struct xroute_stream *xroutes;
struct route_stream *routes;
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 "
"rtt %s rttcost %d chan %d%s.\n",
format_address(neigh->address),
neigh->ifp->name,
neigh->reach,
neighbour_rxcost(neigh),
neigh->txcost,
format_thousands(neigh->rtt),
neighbour_rttcost(neigh),
neigh->ifp->channel,
if_up(neigh->ifp) ? "" : " (down)");
}
xroutes = xroute_stream();
if(xroutes) {
while(1) {
struct xroute *xroute = xroute_stream_next(xroutes);
if(xroute == NULL) break;
dump_xroute(out, xroute);
}
xroute_stream_done(xroutes);
}
routes = route_stream(0);
if(routes) {
while(1) {
struct babel_route *route = route_stream_next(routes);
if(route == NULL) break;
dump_route(out, route);
}
route_stream_done(routes);
}
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;
if(changed & CHANGE_RULE)
kernel_rules_changed = 1;
return 1;
}
babeld-babeld-1.6.3-273bcc70824af88574c3724b3c6a69a9722d4cc4/babeld.h 0000664 0000000 0000000 00000006052 12603261366 0023040 0 ustar 00root root 0000000 0000000 /*
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
#include
#endif
#ifdef HAVE_VALGRIND
#include
#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 random_id;
extern int skip_kernel_setup;
extern int do_daemonise;
extern const char *logfile, *pidfile, *state_file;
extern int link_detect;
extern int all_wireless;
extern int has_ipv6_subtrees;
extern unsigned char myid[8];
extern int have_id;
extern const unsigned char zeroes[16], ones[16];
extern int protocol_port, local_server_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);
void schedule_interfaces_check(int msecs, int override);
int resize_receive_buffer(int size);
babeld-babeld-1.6.3-273bcc70824af88574c3724b3c6a69a9722d4cc4/babeld.man 0000664 0000000 0000000 00000041707 12603261366 0023372 0 ustar 00root root 0000000 0000000 .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 \-M " half-time"
Specify the half-time in seconds of the exponential decay used for
smoothing metrics for performing route selection; the value 0 disables
smoothing. The default is 4s.
.TP
.BI \-k " priority"
Specify the priority value used when installing routes into the kernel.
The default is 0.
.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 \-r
Use a random router-id. The default is to use persistent router-ids
derived from the MAC address of the first interface, which is easier
to debug and more reliably prevents routing loops but may sometimes
cause a node to be unreachable for 120 seconds just after boot.
.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. This can be
specified multiple times in order to export routes from more than one
table.
.TP
.BI \-c " filename"
Specify the name of the configuration file. This flag can be repeated
multiple times. 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
a global option, an interface specification or a filtering
rule. Comments are introduced by an octothorp
.RB `` # ''
and terminate at the end of the line.
.SS Global options
.TP
.BI protocol-group " group"
This specifies the link-local multicast address to be used by the
protocol, and is equivalent to the command-line option
.BR \-m .
.TP
.BI protocol-port " port"
This specifies the UDP port number to be used by the protocol, and is equivalent to the command-line option
.BR \-p .
.TP
.BI kernel-priority " priority"
This specifies the priority value used when installing routes into the
kernel, and is equivalent to the command-line option
.BR \-k .
.TP
.BR reflect-kernel-metric " {" true | false }
Reflect route metrics as kernel priorities. The priority effectively used
is
.B kernel-priority
+
.BR metric .
.TP
.BI allow-duplicates " priority"
This allows 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
.BR keep-unfeasible " {" true | false }
This specifies whether to keep unfeasible (useless) routes, and is
equivalent to the command-line option
.BR \-u .
.TP
.BR random-id " {" true | false }
This specifies whether to use a random router-id, and is
equivalent to the command-line option
.BR \-r .
.TP
.BR ipv6-subtrees " {" true | false }
This specifies whether to use native source-specific IPv6 forwarding
rather than multiple routing tables. The default is chosen automatically
depending on the kernel version.
.TP
.BI debug " level"
This specifies the debugging level, and is equivalent to the command-line
option
.BR \-d .
.TP
.BI local-port " port"
This specifies the TCP port on which
.B babeld
will listen for connections from a front-end, and is equivalent to the
command-line option
.BR \-g .
.TP
.BI export-table " table"
This specifies the kernel routing table to use for routes inserted by
.BR babeld ,
and is equivalent to the command-line option
.BR \-t .
.TP
.BI import-table " table"
This specifies a kernel routing table from which routes are
redistributed by
.BR babeld ,
and can be specified multiple times with a cumulative effect. This is
equivalent to the command-line option
.BR \-T .
.TP
.BR link-detect " {" true | false }
This specifies whether to use carrier sense for determining interface
availability, and is equivalent to the command-line option
.BR \-l .
.TP
.BR diversity " {" true | false | "\fIkind\fB" }
This specifies the diversity algorithm to use;
.B true
is equivalent to
.I kind
3. The default is
.B false
(do not use any diversity algorithm).
.TP
.BI diversity-factor " factor"
This specifies by how much the cost of non-interfering routes should
be multiplied, in units of 1/256. The default is 128 (division by 2).
.TP
.BI smoothing-half-life " seconds"
This specifies the half-life in seconds of the exponential decay used
for smoothing metrics for performing route selection, and is
equivalent to the command-line option
.BR \-M .
.TP
.BR deamonise " {" true | false }
This specifies whether to daemonize at startup, and is equivalent to
the command-line option
.BR \-D .
.TP
.BR skip-kernel-setup " {" true | false }
If this flag is set, no kernel (sysctl) setup is performed on startup. This can
be useful when running in environments where system permissions prevent setting
kernel parameters, for instance inside a Linux container.
.TP
.BI router-id " id"
Specify the router-id explicitly, as a modified EUI-64 or a MAC-48
address. If two nodes have the same router-id, bad things will happen.
Don't use this option unless you know what you are doing.
.TP
.BI state-file " filename"
This specifies the name of the file used for preserving long-term
information between invocations of the
.B babeld
daemon, and is equivalent to the command-line option
.BR \-S .
.TP
.BI log-file " filename"
This specifies the name of the file used to log random messages to,
and is equivalent to the command-line option
.BR \-L .
.TP
.BI pid-file " filename"
This specifies the name of the file to which
.B babeld
writes out its process id, and is equivalent to the command-line option
.BR \-I .
.TP
.BI first-table-number " table"
This specifies the index of the first routing table to use for
source-specific routes. The default is 10.
.TP
.BI first-rule-priority " priority"
This specifies smallest (highest) rule priority used with source-specific
routes. The default is 100.
.SS Interface configuration
An interface is configured by a line with the following format:
.IP
.B interface
.I name
.RI [ parameter ...]
.PP
where
.I name
is the name of the interface (something like
.BR eth0 ).
The default value of an interface parameter can be specified changed
by a line of the form
.IP
.B default
.RI [ parameter ...]
.PP
Each
.I parameter
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.
.TP
.BR enable\-timestamps " {" true | false }
Enable sending timestamps with each Hello and IHU message in order to
compute RTT values. The default is
.B true
if
.B max\-rtt\-penalty
is non-zero (see below), and
.B false
otherwise.
.TP
.BI rtt\-decay " decay"
This specifies the decay factor for the exponential moving average of
RTT samples, in units of 1/256. Must be between 1 and 256, inclusive.
Higher values discard old samples faster. The default is
.BR 42 .
.TP
.BI rtt\-min " rtt"
This specifies the minimum RTT, in milliseconds, starting from which
we increase the cost to a neighbour. The additional cost is linear in
(rtt -
.BR rtt\-min ).
The default is
.B 10
ms.
.TP
.BI rtt\-max " rtt"
This specifies the maximum RTT, in milliseconds, above which we don't
increase the cost to a neighbour. The default is
.B 120
ms.
.TP
.BI max\-rtt\-penalty " cost"
This specifies the maximum cost added to a neighbour because of RTT,
i.e. when the RTT is higher or equal than
.BR rtt\-max .
The default is
.BR 0 ,
which effectively disables the use of a RTT-based cost.
.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 src-ip " prefix"
This entry only applies to routes with a source prefix in the given prefix.
.TP
.BI src-eq " plen"
This entry only applies to routes with a source prefix length equal to
.BR plen .
.TP
.BI src-le " plen"
This entry only applies to routes with a source prefix length less or
equal to
.BR plen .
.TP
.BI src-ge " plen"
This entry only applies to routes with a source 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 .
.TP
.BI src-prefix " prefix"
For a redistribute filter, set the source prefix of this route to
.IR prefix .
.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
.SS Source-sensitive routing
.PP
If your want to redistribute kernel routes as source-specific to the network,
with the 2001:DB8:0:1::/64 prefix:
.IP
redistribute src-prefix 2001:DB8:0:1::/64
.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 SEE ALSO
.BR routed (8),
.BR route6d (8),
.BR zebra (8),
.BR ahcpd (8).
.SH AUTHOR
Juliusz Chroboczek.
babeld-babeld-1.6.3-273bcc70824af88574c3724b3c6a69a9722d4cc4/configuration.c 0000664 0000000 0000000 00000074116 12603261366 0024477 0 ustar 00root root 0000000 0000000 /*
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
#include
#include
#include
#include
#ifdef __linux
/* Defining it rather than including because this
* implies on Linux 2.4 */
#define RTPROT_BOOT 3 /* Route installed during boot */
#endif
#include "babeld.h"
#include "util.h"
#include "interface.h"
#include "route.h"
#include "kernel.h"
#include "configuration.h"
struct filter *input_filters = NULL;
struct filter *output_filters = NULL;
struct filter *redistribute_filters = NULL;
struct interface_conf *default_interface_conf = 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' || c == '#' || c < 0)
return -2;
do {
if(i >= 255) return -2;
buf[i++] = c;
c = gnc(closure);
} while(c != ' ' && c != '\t' && c != '\n' && c != '#' && 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 || c == '\n' || c == '#')
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
getthousands(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_thousands(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 int
parse_filter(int c, gnc_t gnc, void *closure, struct filter **filter_return)
{
char *token;
struct filter *filter;
filter = calloc(1, sizeof(struct filter));
if(filter == NULL)
goto error;
filter->plen_le = 128;
filter->src_plen_le = 128;
while(1) {
c = skip_whitespace(c, gnc, closure);
if(c < 0 || 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) {
int af;
c = getnet(c, &filter->prefix, &filter->plen, &af,
gnc, closure);
if(c < -1)
goto error;
if(filter->af == AF_UNSPEC)
filter->af = af;
else if(filter->af != af)
goto error;
} else if(strcmp(token, "src-ip") == 0) {
int af;
c = getnet(c, &filter->src_prefix, &filter->src_plen, &af,
gnc, closure);
if(c < -1)
goto error;
if(filter->af == AF_UNSPEC)
filter->af = af;
else if(filter->af != af)
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, "src-eq") == 0) {
int p;
c = getint(c, &p, gnc, closure);
if(c < -1)
goto error;
filter->src_plen_ge = MAX(filter->src_plen_ge, p);
filter->src_plen_le = MIN(filter->src_plen_le, p);
} else if(strcmp(token, "src-le") == 0) {
int p;
c = getint(c, &p, gnc, closure);
if(c < -1)
goto error;
filter->src_plen_le = MIN(filter->src_plen_le, p);
} else if(strcmp(token, "src-ge") == 0) {
int p;
c = getint(c, &p, gnc, closure);
if(c < -1)
goto error;
filter->src_plen_ge = MAX(filter->src_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->action.add_metric = 0;
} else if(strcmp(token, "deny") == 0) {
filter->action.add_metric = 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->action.add_metric = metric;
} else if(strcmp(token, "src-prefix") == 0) {
int af;
c = getnet(c, &filter->action.src_prefix, &filter->action.src_plen,
&af, gnc, closure);
if(c < -1)
goto error;
if(filter->af == AF_UNSPEC)
filter->af = af;
else if(filter->af != af)
goto error;
if(af == AF_INET && filter->action.src_plen == 96)
memset(&filter->action.src_prefix, 0, 16);
} else {
goto error;
}
free(token);
}
if(filter->af == 0) {
if(filter->plen_le < 128 || filter->plen_ge > 0 ||
filter->src_plen_le < 128 || filter->src_plen_ge > 0)
filter->af = AF_INET6;
} else if(filter->af == AF_INET) {
filter->plen_le += 96;
filter->plen_ge += 96;
}
*filter_return = filter;
return c;
error:
free(filter);
return -2;
}
static int
parse_anonymous_ifconf(int c, gnc_t gnc, void *closure,
struct interface_conf *if_conf,
struct interface_conf **if_conf_return)
{
char *token;
if(if_conf == NULL) {
if_conf = calloc(1, sizeof(struct interface_conf));
if(if_conf == NULL)
goto error;
}
while(1) {
c = skip_whitespace(c, gnc, closure);
if(c < 0 || 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 = getthousands(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 = getthousands(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 > 255) &&
if_conf->channel != IF_CHANNEL_NONINTERFERING)
goto error;
} else if(strcmp(token, "enable-timestamps") == 0) {
int v;
c = getbool(c, &v, gnc, closure);
if(c < -1)
goto error;
if_conf->enable_timestamps = v;
} else if(strcmp(token, "rtt-decay") == 0) {
int decay;
c = getint(c, &decay, gnc, closure);
if(c < -1 || decay <= 0 || decay > 256)
goto error;
if_conf->rtt_decay = decay;
} else if(strcmp(token, "rtt-min") == 0) {
int rtt;
c = getthousands(c, &rtt, gnc, closure);
if(c < -1 || rtt <= 0)
goto error;
if_conf->rtt_min = rtt;
} else if(strcmp(token, "rtt-max") == 0) {
int rtt;
c = getthousands(c, &rtt, gnc, closure);
if(c < -1 || rtt <= 0)
goto error;
if_conf->rtt_max = rtt;
} else if(strcmp(token, "max-rtt-penalty") == 0) {
int cost;
c = getint(c, &cost, gnc, closure);
if(c < -1 || cost <= 0 || cost > 0xFFFF)
goto error;
if_conf->max_rtt_penalty = cost;
} else {
goto error;
}
free(token);
}
*if_conf_return = if_conf;
return c;
error:
free(if_conf);
return -2;
}
static int
parse_ifconf(int c, gnc_t gnc, void *closure,
struct interface_conf **if_conf_return)
{
char *token;
struct interface_conf *if_conf;
if_conf = calloc(1, sizeof(struct interface_conf));
if(if_conf == NULL)
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;
return parse_anonymous_ifconf(c, gnc, closure, if_conf, if_conf_return);
error:
free(if_conf);
return -2;
}
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,
const struct interface_conf *src1,
const struct interface_conf *src2)
{
#define MERGE(field) \
do { \
if(src1->field) \
dest->field = src1->field; \
else \
dest->field = src2->field; \
} while(0)
MERGE(hello_interval);
MERGE(update_interval);
MERGE(cost);
MERGE(wired);
MERGE(split_horizon);
MERGE(lq);
MERGE(faraway);
MERGE(channel);
MERGE(enable_timestamps);
MERGE(rtt_decay);
MERGE(rtt_min);
MERGE(rtt_max);
MERGE(max_rtt_penalty);
#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 *prev, *next;
next = *if_confs;
prev = NULL;
while(next) {
if(strcmp(next->ifname, if_conf->ifname) == 0) {
merge_ifconf(next, if_conf, next);
free(if_conf);
return;
}
prev = next;
next = next->next;
}
if_conf->next = NULL;
prev->next = if_conf;
}
}
static int
parse_option(int c, gnc_t gnc, void *closure, char *token)
{
if(strcmp(token, "protocol-port") == 0 ||
strcmp(token, "kernel-priority") == 0 ||
strcmp(token, "allow-duplicates") == 0 ||
#ifndef NO_LOCAL_INTERFACE
strcmp(token, "local-port") == 0 ||
#endif
strcmp(token, "export-table") == 0 ||
strcmp(token, "import-table") == 0) {
int v;
c = getint(c, &v, gnc, closure);
if(c < -1 || v <= 0 || v >= 0xFFFF)
goto error;
if(strcmp(token, "protocol-port") == 0)
protocol_port = v;
else if(strcmp(token, "kernel-priority") == 0)
kernel_metric = v;
else if(strcmp(token, "allow_duplicates") == 0)
allow_duplicates = v;
#ifndef NO_LOCAL_INTERFACE
else if(strcmp(token, "local-port") == 0)
local_server_port = v;
#endif
else if(strcmp(token, "export-table") == 0)
export_table = v;
else if(strcmp(token, "import-table") == 0)
add_import_table(v);
else
abort();
} else if(strcmp(token, "keep-unfeasible") == 0 ||
strcmp(token, "link-detect") == 0 ||
strcmp(token, "random-id") == 0 ||
strcmp(token, "daemonise") == 0 ||
strcmp(token, "skip-kernel-setup") == 0 ||
strcmp(token, "ipv6-subtrees") == 0 ||
strcmp(token, "reflect-kernel-metric") == 0) {
int b;
c = getbool(c, &b, gnc, closure);
if(c < -1)
goto error;
b = (b == CONFIG_YES);
if(strcmp(token, "keep-unfeasible") == 0)
keep_unfeasible = b;
else if(strcmp(token, "link-detect") == 0)
link_detect = b;
else if(strcmp(token, "random-id") == 0)
random_id = b;
else if(strcmp(token, "daemonise") == 0)
do_daemonise = b;
else if(strcmp(token, "skip-kernel-setup") == 0)
skip_kernel_setup = b;
else if(strcmp(token, "ipv6-subtrees") == 0)
has_ipv6_subtrees = b;
else if(strcmp(token, "reflect-kernel-metric") == 0)
reflect_kernel_metric = b;
else
abort();
} else if(strcmp(token, "protocol-group") == 0) {
unsigned char *group = NULL;
c = getip(c, &group, NULL, gnc, closure);
if(c < -1)
goto error;
memcpy(protocol_group, group, 16);
free(group);
} else if(strcmp(token, "state-file") == 0 ||
strcmp(token, "log-file") == 0 ||
strcmp(token, "pid-file") == 0) {
char *file;
c = getstring(c, &file, gnc, closure);
if(c < -1)
goto error;
if(strcmp(token, "state-file") == 0)
state_file = file;
else if(strcmp(token, "log-file") == 0)
logfile = file;
else if(strcmp(token, "pid-file") == 0)
pidfile = file;
else
abort();
} else if(strcmp(token, "debug") == 0) {
int d;
c = getint(c, &d, gnc, closure);
if(c < -1 || d < 0)
goto error;
debug = d;
} else if(strcmp(token, "diversity") == 0) {
int d;
c = skip_whitespace(c, gnc, closure);
if(c >= '0' && c <= '9') {
c = getint(c, &d, gnc, closure);
if(c < -1)
goto error;
} else {
int b;
c = getbool(c, &b, gnc, closure);
if(c < -1)
goto error;
d = (b == CONFIG_YES) ? 3 : 0;
}
diversity_kind = d;
} else if(strcmp(token, "diversity-factor") == 0) {
int f;
c = getint(c, &f, gnc, closure);
if(c < -1 || f < 0 || f > 256)
goto error;
diversity_factor = f;
} else if(strcmp(token, "smoothing-half-life") == 0) {
int h;
c = getint(c, &h, gnc, closure);
if(c < -1 || h < 0)
goto error;
change_smoothing_half_life(h);
} else if(strcmp(token, "first-table-number") == 0) {
int n;
c = getint(c, &n, gnc, closure);
if(c < -1 || n <= 0 || n + SRC_TABLE_NUM >= 254)
goto error;
src_table_idx = n;
} else if(strcmp(token, "first-rule-priority") == 0) {
int n;
c = getint(c, &n, gnc, closure);
if(c < -1 || n <= 0 || n + SRC_TABLE_NUM >= 32765)
goto error;
src_table_prio = n;
} else if(strcmp(token, "router-id") == 0) {
unsigned char *id = NULL;
c = getid(c, &id, gnc, closure);
if(c < -1 || id == NULL)
goto error;
memcpy(myid, id, 8);
free(id);
have_id = 1;
} else {
goto error;
}
c = skip_whitespace(c, gnc, closure);
if(c < 0 || c == '\n' || c == '#') {
c = skip_to_eol(c, gnc, closure);
return c;
}
/* Fall through */
error:
return -2;
}
static int
parse_config(gnc_t gnc, void *closure)
{
int c;
char *token;
c = gnc(closure);
if(c < -1)
return -1;
while(1) {
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;
c = parse_filter(c, gnc, closure, &filter);
if(c < -1)
return -1;
add_filter(filter, &input_filters);
} else if(strcmp(token, "out") == 0) {
struct filter *filter;
c = parse_filter(c, gnc, closure, &filter);
if(c < -1)
return -1;
add_filter(filter, &output_filters);
} else if(strcmp(token, "redistribute") == 0) {
struct filter *filter;
c = parse_filter(c, gnc, closure, &filter);
if(c < -1)
return -1;
add_filter(filter, &redistribute_filters);
} else if(strcmp(token, "interface") == 0) {
struct interface_conf *if_conf;
c = parse_ifconf(c, gnc, closure, &if_conf);
if(c < -1)
return -1;
add_ifconf(if_conf, &interface_confs);
} else if(strcmp(token, "default") == 0) {
struct interface_conf *if_conf;
c = parse_anonymous_ifconf(c, gnc, closure, NULL, &if_conf);
if(c < -1)
return -1;
if(default_interface_conf == NULL)
default_interface_conf = if_conf;
else {
merge_ifconf(default_interface_conf,
if_conf, default_interface_conf);
free(if_conf);
}
} else {
c = parse_option(c, gnc, closure, token);
if(c < -1)
return -1;
}
free(token);
}
return 1;
}
struct file_state {
FILE *f;
int line;
};
static int
gnc_file(struct file_state *s)
{
int c;
c = fgetc(s->f);
if(c == '\n')
s->line++;
return c;
}
int
parse_config_from_file(const char *filename, int *line_return)
{
struct file_state s = { NULL, 1 };
int rc;
s.f = fopen(filename, "r");
if(s.f == NULL) {
*line_return = 0;
return -1;
}
rc = parse_config((gnc_t)gnc_file, &s);
fclose(s.f);
*line_return = s.line;
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 *src_prefix, unsigned short src_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->src_prefix) {
if(!src_prefix || src_plen < f->src_plen ||
!in_prefix(src_prefix, f->src_prefix, f->src_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->src_plen_ge > 0 || f->src_plen_le < 128) {
if(!src_prefix)
return 0;
if(src_plen > f->src_plen_le)
return 0;
if(src_plen < f->src_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 *src_prefix, unsigned short src_plen,
const unsigned char *neigh, unsigned int ifindex, int proto,
struct filter_result *result)
{
if(result)
memset(result, 0, sizeof(struct filter_result));
while(f) {
if(filter_match(f, id, prefix, plen, src_prefix, src_plen,
neigh, ifindex, proto)) {
if(result)
memcpy(result, &f->action, sizeof(struct filter_result));
return f->action.add_metric;
}
f = f->next;
}
return -1;
}
int
input_filter(const unsigned char *id,
const unsigned char *prefix, unsigned short plen,
const unsigned char *src_prefix, unsigned short src_plen,
const unsigned char *neigh, unsigned int ifindex)
{
int res;
res = do_filter(input_filters, id, prefix, plen,
src_prefix, src_plen, neigh, ifindex, 0, NULL);
if(res < 0)
res = 0;
return res;
}
int
output_filter(const unsigned char *id,
const unsigned char *prefix, unsigned short plen,
const unsigned char *src_prefix, unsigned short src_plen,
unsigned int ifindex)
{
int res;
res = do_filter(output_filters, id, prefix, plen,
src_prefix, src_plen, NULL, ifindex, 0, NULL);
if(res < 0)
res = 0;
return res;
}
int
redistribute_filter(const unsigned char *prefix, unsigned short plen,
const unsigned char *src_prefix, unsigned short src_plen,
unsigned int ifindex, int proto,
struct filter_result *result)
{
int res;
res = do_filter(redistribute_filters, NULL, prefix, plen,
src_prefix, src_plen, NULL, ifindex, proto, result);
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;
filter->src_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;
if(default_interface_conf)
merge_ifconf(if_conf, if_conf, default_interface_conf);
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;
}
babeld-babeld-1.6.3-273bcc70824af88574c3724b3c6a69a9722d4cc4/configuration.h 0000664 0000000 0000000 00000005104 12603261366 0024473 0 ustar 00root root 0000000 0000000 /*
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_result {
unsigned int add_metric; /* allow = 0, deny = INF, metric = <0..INF> */
unsigned char *src_prefix;
unsigned char src_plen;
};
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 *src_prefix;
unsigned char src_plen;
unsigned char src_plen_ge, src_plen_le;
unsigned char *neigh;
int proto; /* May be negative */
struct filter_result action;
struct filter *next;
};
extern struct interface_conf *default_interface_conf;
int parse_config_from_file(const char *filename, int *line_return);
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 *src_prefix, unsigned short src_plen,
const unsigned char *neigh, unsigned int ifindex);
int output_filter(const unsigned char *id,
const unsigned char *prefix, unsigned short plen,
const unsigned char *src_prefix, unsigned short src_plen,
unsigned int ifindex);
int redistribute_filter(const unsigned char *prefix, unsigned short plen,
const unsigned char *src_prefix, unsigned short src_plen,
unsigned int ifindex, int proto,
struct filter_result *result);
int finalise_config(void);
babeld-babeld-1.6.3-273bcc70824af88574c3724b3c6a69a9722d4cc4/disambiguation.c 0000664 0000000 0000000 00000034267 12603261366 0024632 0 ustar 00root root 0000000 0000000 /*
Copyright (c) 2014 by Matthieu Boutier 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
#include
#include
#include
#include
#include
#include "babeld.h"
#include "util.h"
#include "interface.h"
#include "kernel.h"
#include "route.h"
#include "source.h"
#include "neighbour.h"
struct zone {
const unsigned char *dst_prefix;
unsigned char dst_plen;
const unsigned char *src_prefix;
unsigned char src_plen;
};
/* This function assumes rt1 and rt2 non disjoint. */
static int
rt_cmp(const struct babel_route *rt1, const struct babel_route *rt2)
{
enum prefix_status dst_st, src_st;
const struct source *r1 = rt1->src, *r2 = rt2->src;
dst_st = prefix_cmp(r1->prefix, r1->plen, r2->prefix, r2->plen);
if(dst_st == PST_MORE_SPECIFIC)
return -1;
else if(dst_st == PST_LESS_SPECIFIC)
return 1;
src_st = prefix_cmp(r1->src_prefix, r1->src_plen,
r2->src_prefix, r2->src_plen);
if(src_st == PST_MORE_SPECIFIC)
return -1;
else if(src_st == PST_LESS_SPECIFIC)
return 1;
return 0;
}
static const struct babel_route *
min_route(const struct babel_route *r1, const struct babel_route *r2)
{
int rc;
if (!r1) return r2;
if (!r2) return r1;
rc = rt_cmp(r1, r2);
return rc <= 0 ? r1 : r2;
}
static int
conflicts(const struct babel_route *rt, const struct babel_route *rt1)
{
enum prefix_status dst_st, src_st;
const struct source *r = rt->src, *r1 = rt1->src;
dst_st = prefix_cmp(r->prefix, r->plen, r1->prefix, r1->plen);
if(dst_st == PST_DISJOINT || dst_st == PST_EQUALS)
return 0;
src_st = prefix_cmp(r->src_prefix, r->src_plen,
r1->src_prefix, r1->src_plen);
return ((dst_st == PST_LESS_SPECIFIC && src_st == PST_MORE_SPECIFIC) ||
(dst_st == PST_MORE_SPECIFIC && src_st == PST_LESS_SPECIFIC));
}
static const struct zone*
to_zone(const struct babel_route *rt, struct zone *zone)
{
zone->dst_prefix = rt->src->prefix;
zone->dst_plen = rt->src->plen;
zone->src_prefix = rt->src->src_prefix;
zone->src_plen = rt->src->src_plen;
return zone;
}
/* fill zone with rt cap rt1, and returns a pointer to zone, or NULL if the
intersection is empty. */
static const struct zone*
inter(const struct babel_route *rt, const struct babel_route *rt1,
struct zone *zone)
{
enum prefix_status dst_st, src_st;
const struct source *r = rt->src, *r1 = rt1->src;
dst_st = prefix_cmp(r->prefix, r->plen, r1->prefix, r1->plen);
if(dst_st == PST_DISJOINT)
return NULL;
src_st = prefix_cmp(r->src_prefix, r->src_plen,
r1->src_prefix, r1->src_plen);
if(src_st == PST_DISJOINT)
return NULL;
if (dst_st == PST_MORE_SPECIFIC || dst_st == PST_EQUALS) {
zone->dst_prefix = r->prefix;
zone->dst_plen = r->plen;
} else {
zone->dst_prefix = r1->prefix;
zone->dst_plen = r1->plen;
}
if (src_st == PST_MORE_SPECIFIC || src_st == PST_EQUALS) {
zone->src_prefix = r->src_prefix;
zone->src_plen = r->src_plen;
} else {
zone->src_prefix = r1->src_prefix;
zone->src_plen = r1->src_plen;
}
return zone;
}
static int
zone_equal(const struct zone *z1, const struct zone *z2)
{
return z1 && z2 && z1->dst_plen == z2->dst_plen &&
memcmp(z1->dst_prefix, z2->dst_prefix, 16) == 0 &&
z1->src_plen == z2->src_plen &&
memcmp(z1->src_prefix, z2->src_prefix, 16) == 0;
}
static const struct babel_route *
min_conflict(const struct zone *zone, const struct babel_route *rt)
{
struct babel_route *rt1 = NULL;
const struct babel_route *min = NULL;
struct route_stream *stream = NULL;
struct zone curr_zone;
stream = route_stream(1);
if(!stream) {
fprintf(stderr, "Couldn't allocate route stream.\n");
return NULL;
}
while(1) {
rt1 = route_stream_next(stream);
if(rt1 == NULL) break;
if(!(conflicts(rt, rt1) &&
zone_equal(inter(rt, rt1, &curr_zone), zone)))
continue;
min = min_route(rt1, min);
}
route_stream_done(stream);
return min;
}
static const struct babel_route *
conflict_solution(const struct babel_route *rt)
{
const struct babel_route *rt1 = NULL, *rt2 = NULL;
struct route_stream *stream1 = NULL;
struct route_stream *stream2 = NULL;
const struct babel_route *min = NULL; /* == solution */
struct zone zone;
struct zone tmp;
stream1 = route_stream(1);
if(!stream1) {
return NULL;
}
while(1) {
rt1 = route_stream_next(stream1);
if(rt1 == NULL) break;
stream2 = route_stream(1);
if(!stream2) {
route_stream_done(stream1);
fprintf(stderr, "Couldn't allocate route stream.\n");
return NULL;
}
while(1) {
rt2 = route_stream_next(stream2);
if (rt2 == NULL) break;
if(!(conflicts(rt1, rt2) &&
zone_equal(inter(rt1, rt2, &tmp), to_zone(rt, &zone)) &&
rt_cmp(rt1, rt2) < 0))
continue;
min = min_route(rt1, min);
}
route_stream_done(stream2);
}
route_stream_done(stream1);
return min;
}
static int
is_installed(struct zone *zone)
{
return zone != NULL &&
find_installed_route(zone->dst_prefix, zone->dst_plen,
zone->src_prefix, zone->src_plen) != NULL;
}
static int
add_route(const struct zone *zone, const struct babel_route *route)
{
return kernel_route(ROUTE_ADD, zone->dst_prefix, zone->dst_plen,
zone->src_prefix, zone->src_plen,
route->nexthop,
route->neigh->ifp->ifindex,
metric_to_kernel(route_metric(route)), NULL, 0, 0);
}
static int
del_route(const struct zone *zone, const struct babel_route *route)
{
return kernel_route(ROUTE_FLUSH, zone->dst_prefix, zone->dst_plen,
zone->src_prefix, zone->src_plen,
route->nexthop,
route->neigh->ifp->ifindex,
metric_to_kernel(route_metric(route)), NULL, 0, 0);
}
static int
chg_route(const struct zone *zone, const struct babel_route *old,
const struct babel_route *new)
{
return kernel_route(ROUTE_MODIFY, zone->dst_prefix, zone->dst_plen,
zone->src_prefix, zone->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)));
}
static int
chg_route_metric(const struct zone *zone, const struct babel_route *route,
int old_metric, int new_metric)
{
return kernel_route(ROUTE_MODIFY, zone->dst_prefix, zone->dst_plen,
zone->src_prefix, zone->src_plen,
route->nexthop, route->neigh->ifp->ifindex,
old_metric,
route->nexthop, route->neigh->ifp->ifindex,
new_metric);
}
int
kinstall_route(const struct babel_route *route)
{
int rc;
struct zone zone;
const struct babel_route *rt1 = NULL;
const struct babel_route *rt2 = NULL;
struct route_stream *stream = NULL;
int v4 = v4mapped(route->nexthop);
debugf("install_route(%s from %s)\n",
format_prefix(route->src->prefix, route->src->plen),
format_prefix(route->src->src_prefix, route->src->src_plen));
/* Install source-specific conflicting routes */
if(kernel_disambiguate(v4)) {
to_zone(route, &zone);
rc = add_route(&zone, route);
goto end;
}
stream = route_stream(1);
if(!stream) {
fprintf(stderr, "Couldn't allocate route stream.\n");
return -1;
}
/* Install source-specific conflicting routes */
while(1) {
rt1 = route_stream_next(stream);
if(rt1 == NULL) break;
inter(route, rt1, &zone);
if(!(conflicts(route, rt1) &&
!is_installed(&zone) &&
rt_cmp(rt1, min_conflict(&zone, route)) == 0))
continue;
rt2 = min_conflict(&zone, rt1);
if(rt2 == NULL)
add_route(&zone, min_route(route, rt1));
else if(rt_cmp(route, rt2) < 0 && rt_cmp(route, rt1) < 0)
chg_route(&zone, rt2, route);
}
route_stream_done(stream);
/* Non conflicting case */
to_zone(route, &zone);
rt1 = conflict_solution(route);
if(rt1 == NULL)
rc = add_route(&zone, route);
else
rc = chg_route(&zone, rt1, route);
end:
if(rc < 0) {
int save = errno;
perror("kernel_route(ADD)");
if(save != EEXIST)
return -1;
}
return 0;
}
int
kuninstall_route(const struct babel_route *route)
{
int rc;
struct zone zone;
const struct babel_route *rt1 = NULL, *rt2 = NULL;
struct route_stream *stream = NULL;
int v4 = v4mapped(route->nexthop);
debugf("uninstall_route(%s from %s)\n",
format_prefix(route->src->prefix, route->src->plen),
format_prefix(route->src->src_prefix, route->src->src_plen));
to_zone(route, &zone);
if(kernel_disambiguate(v4)) {
rc = del_route(&zone, route);
if(rc < 0)
perror("kernel_route(FLUSH)");
return rc;
}
/* Remove the route, or change if the route was solving a conflict. */
rt1 = conflict_solution(route);
if(rt1 == NULL)
rc = del_route(&zone, route);
else
rc = chg_route(&zone, route, rt1);
if(rc < 0)
perror("kernel_route(FLUSH)");
/* Remove source-specific conflicting routes */
stream = route_stream(1);
if(!stream) {
fprintf(stderr, "Couldn't allocate route stream.\n");
return -1;
}
while(1) {
rt1 = route_stream_next(stream);
if(rt1 == NULL) break;
inter(route, rt1, &zone);
if(!(conflicts(route, rt1) &&
!is_installed(&zone) &&
rt_cmp(rt1, min_conflict(&zone, route)) == 0))
continue;
rt2 = min_conflict(&zone, rt1);
if(rt2 == NULL)
del_route(&zone, min_route(route, rt1));
else if(rt_cmp(route, rt2) < 0 && rt_cmp(route, rt1) < 0)
chg_route(&zone, route, rt2);
}
route_stream_done(stream);
return rc;
}
int
kswitch_routes(const struct babel_route *old, const struct babel_route *new)
{
int rc;
struct zone zone;
struct babel_route *rt1 = NULL;
struct route_stream *stream = NULL;
debugf("switch_routes(%s from %s)\n",
format_prefix(old->src->prefix, old->src->plen),
format_prefix(old->src->src_prefix, old->src->src_plen));
to_zone(old, &zone);
rc = chg_route(&zone, old, new);
if(rc < 0) {
perror("kernel_route(MODIFY)");
return -1;
}
/* Remove source-specific conflicting routes */
if(!kernel_disambiguate(v4mapped(old->nexthop))) {
stream = route_stream(1);
if(!stream) {
fprintf(stderr, "Couldn't allocate route stream.\n");
return -1;
}
while(1) {
rt1 = route_stream_next(stream);
if(rt1 == NULL) break;
inter(old, rt1, &zone);
if(!(conflicts(old, rt1) &&
!is_installed(&zone) &&
rt_cmp(rt1, min_conflict(&zone, old)) == 0 &&
rt_cmp(old, rt1) < 0 &&
rt_cmp(old, min_conflict(&zone, rt1)) == 0))
continue;
chg_route(&zone, old, new);
}
route_stream_done(stream);
}
return rc;
}
int
kchange_route_metric(const struct babel_route *route,
unsigned refmetric, unsigned cost, unsigned add)
{
int old_metric = metric_to_kernel(route_metric(route));
int new_metric = metric_to_kernel(MIN(refmetric + cost + add, INFINITY));
int rc;
struct babel_route *rt1 = NULL;
struct route_stream *stream = NULL;
struct zone zone;
debugf("change_route_metric(%s from %s, %d -> %d)\n",
format_prefix(route->src->prefix, route->src->plen),
format_prefix(route->src->src_prefix, route->src->src_plen),
old_metric, new_metric);
to_zone(route, &zone);
rc = chg_route_metric(&zone, route, old_metric, new_metric);
if(rc < 0) {
perror("kernel_route(MODIFY metric)");
return -1;
}
if(!kernel_disambiguate(v4mapped(route->nexthop))) {
stream = route_stream(1);
if(!stream) {
fprintf(stderr, "Couldn't allocate route stream.\n");
return -1;
}
while(1) {
rt1 = route_stream_next(stream);
if(rt1 == NULL) break;
inter(route, rt1, &zone);
if(!(conflicts(route, rt1) &&
!is_installed(&zone) &&
rt_cmp(rt1, min_conflict(&zone, route)) == 0 &&
rt_cmp(route, rt1) < 0 &&
rt_cmp(route, min_conflict(&zone, rt1)) == 0))
continue;
chg_route_metric(&zone, route, old_metric, new_metric);
}
route_stream_done(stream);
}
return rc;
}
babeld-babeld-1.6.3-273bcc70824af88574c3724b3c6a69a9722d4cc4/disambiguation.h 0000664 0000000 0000000 00000002554 12603261366 0024631 0 ustar 00root root 0000000 0000000 /*
Copyright (c) 2014 by Matthieu Boutier 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.
*/
int kinstall_route(struct babel_route *route);
int kuninstall_route(struct babel_route *route);
int kswitch_routes(struct babel_route *old, struct babel_route *new);
int kchange_route_metric(struct babel_route *route,
unsigned refmetric, unsigned cost, unsigned add);
babeld-babeld-1.6.3-273bcc70824af88574c3724b3c6a69a9722d4cc4/interface.c 0000664 0000000 0000000 00000034112 12603261366 0023560 0 ustar 00root root 0000000 0000000 /*
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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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;
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 ? if_conf : default_interface_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;
}
static int
check_link_local_addresses(struct interface *ifp)
{
struct kernel_route ll[32];
int rc, i;
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_addresses(link local)");
return -1;
} else if(rc == 0) {
fprintf(stderr, "Interface %s has no link-local address.\n",
ifp->name);
/* Most probably DAD hasn't finished yet. Reschedule us
real soon. */
schedule_interfaces_check(2000, 0);
return -1;
} else {
ifp->ll = malloc(16 * rc);
if(ifp->ll == NULL) {
perror("malloc(ll)");
} else {
for(i = 0; i < rc; i++)
memcpy(ifp->ll[i], ll[i].prefix, 16);
ifp->numll = rc;
}
}
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) {
if(ifp->ifindex <= 0) {
fprintf(stderr,
"Upping unknown interface %s.\n", ifp->name);
goto fail;
}
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);
goto fail;
}
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;
goto fail;
}
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;
ifp->rtt_decay =
IF_CONF(ifp, rtt_decay) > 0 ?
IF_CONF(ifp, rtt_decay) : 42;
ifp->rtt_min =
IF_CONF(ifp, rtt_min) > 0 ?
IF_CONF(ifp, rtt_min) : 10000;
ifp->rtt_max =
IF_CONF(ifp, rtt_max) > 0 ?
IF_CONF(ifp, rtt_max) : 120000;
if(ifp->rtt_max <= ifp->rtt_min) {
fprintf(stderr,
"Uh, rtt-max is less than or equal to rtt-min (%d <= %d). "
"Setting it to %d.\n", ifp->rtt_max, ifp->rtt_min,
ifp->rtt_min + 10000);
ifp->rtt_max = ifp->rtt_min + 10000;
}
ifp->max_rtt_penalty = IF_CONF(ifp, max_rtt_penalty);
if(IF_CONF(ifp, enable_timestamps) == CONFIG_YES ||
(IF_CONF(ifp, enable_timestamps) == CONFIG_DEFAULT &&
ifp->max_rtt_penalty > 0))
ifp->flags |= IF_TIMESTAMPS;
rc = check_link_local_addresses(ifp);
if(rc < 0) {
goto fail;
}
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)");
goto fail;
}
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, NULL, 0);
send_request(ifp, NULL, 0, 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;
fail:
assert(up);
interface_up(ifp, 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)) {
/* Bother, said Pooh. We should probably check for a change
in IPv4 addresses at this point. */
check_link_local_addresses(ifp);
check_interface_channel(ifp);
rc = check_interface_ipv4(ifp);
if(rc > 0) {
send_request(ifp, NULL, 0, NULL, 0);
send_update(ifp, 0, NULL, 0, NULL, 0);
}
}
}
if(ifindex_changed)
renumber_filters();
}
babeld-babeld-1.6.3-273bcc70824af88574c3724b3c6a69a9722d4cc4/interface.h 0000664 0000000 0000000 00000010073 12603261366 0023565 0 ustar 00root root 0000000 0000000 /*
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 src_prefix[16];
unsigned char plen;
unsigned char src_plen; /* 0 <=> no src prefix */
unsigned char pad[2];
};
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;
int enable_timestamps;
unsigned int rtt_decay;
unsigned int rtt_min;
unsigned int rtt_max;
unsigned int max_rtt_penalty;
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)
#define IF_TIMESTAMPS (1 << 5)
/* 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;
/* Relative position of the Hello message in the send buffer, or
(-1) if there is none. */
int 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;
time_t last_specific_update_time;
unsigned short hello_seqno;
unsigned hello_interval;
unsigned update_interval;
/* A higher value means we forget old RTT samples faster. Must be
between 1 and 256, inclusive. */
unsigned int rtt_decay;
/* Parameters for computing the cost associated to RTT. */
unsigned int rtt_min;
unsigned int rtt_max;
unsigned int max_rtt_penalty;
};
#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);
babeld-babeld-1.6.3-273bcc70824af88574c3724b3c6a69a9722d4cc4/kernel.c 0000664 0000000 0000000 00000006636 12603261366 0023112 0 ustar 00root root 0000000 0000000 /*
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
#include
#include
#include
#include "babeld.h"
#ifdef __linux
#include "kernel_netlink.c"
#else
#include "kernel_socket.c"
#endif
int src_table_idx = 10;
int src_table_prio = 100;
/* 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, int len)
{
int fd, rc;
fd = open("/dev/urandom", O_RDONLY);
if(fd < 0) {
errno = ENOSYS;
return -1;
}
rc = read(fd, buf, len);
if(rc < len)
rc = -1;
close(fd);
return rc;
}
int
add_import_table(int table)
{
if(table < 0 || table > 0xFFFF) return -1;
if(import_table_count > MAX_IMPORT_TABLES - 1) return -2;
import_tables[import_table_count++] = table;
return 0;
}
int
kernel_older_than(const char *sysname, int version, int sub_version)
{
struct utsname un;
int rc;
int v = 0;
int sub_v = 0;
rc = uname(&un);
if (rc < 0)
return -1;
if (strcmp(sysname, un.sysname) != 0)
return -1;
rc = sscanf(un.release, "%d.%d", &v, &sub_v);
if (rc < 2)
return -1;
return (v < version || (v == version && sub_v < sub_version));
}
babeld-babeld-1.6.3-273bcc70824af88574c3724b3c6a69a9722d4cc4/kernel.h 0000664 0000000 0000000 00000006265 12603261366 0023115 0 ustar 00root root 0000000 0000000 /*
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
#include "babeld.h"
#define KERNEL_INFINITY 0xFFFF
struct kernel_route {
unsigned char prefix[16];
int plen;
unsigned char src_prefix[16];
int src_plen; /* no source prefix <=> src_plen == 0 */
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)
#define CHANGE_RULE (1 << 3)
#ifndef MAX_IMPORT_TABLES
#define MAX_IMPORT_TABLES 10
#endif
extern int export_table, import_tables[MAX_IMPORT_TABLES], import_table_count;
int add_import_table(int table);
#define SRC_TABLE_NUM 10
extern int src_table_idx; /* number of the first table */
extern int src_table_prio; /* first prio range */
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_disambiguate(int v4);
int kernel_route(int operation, const unsigned char *dest, unsigned short plen,
const unsigned char *src, unsigned short src_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, int len);
int kernel_older_than(const char *sysname, int version, int sub_version);
int kernel_has_ipv6_subtrees(void);
babeld-babeld-1.6.3-273bcc70824af88574c3724b3c6a69a9722d4cc4/kernel_netlink.c 0000664 0000000 0000000 00000151477 12603261366 0024642 0 ustar 00root root 0000000 0000000 /*
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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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"
#ifndef MAX_INTERFACES
#define MAX_INTERFACES 20
#endif
int export_table = -1, import_tables[MAX_IMPORT_TABLES], import_table_count = 0;
struct sysctl_setting {
char *name;
int want;
int was;
};
#define NUM_SYSCTLS 4
static struct sysctl_setting sysctl_settings[NUM_SYSCTLS] = {
{"/proc/sys/net/ipv6/conf/all/forwarding", 1, -1},
{"/proc/sys/net/ipv4/conf/all/forwarding", 1, -1},
{"/proc/sys/net/ipv6/conf/all/accept_redirects", 0, -1},
{"/proc/sys/net/ipv4/conf/all/rp_filter", 0, -1},
};
struct old_if {
char *ifname;
int rp_filter;
};
struct old_if old_if[MAX_INTERFACES];
int num_old_if = 0;
static int dgram_socket = -1;
#ifndef ARPHRD_ETHER
#warning ARPHRD_ETHER not defined, we might not support exotic link layers
#define ARPHRD_ETHER 1
#define NO_ARPHRD
#endif
static int find_table(const unsigned char *src, unsigned short src_plen);
static void release_tables(void);
static int filter_kernel_rules(struct nlmsghdr *nh, void *data);
static void install_missing_rules(char rule_exists[SRC_TABLE_NUM], int v4);
/* 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)
{
struct sysctl_setting *s;
int i, rc;
if(setup) {
if(export_table < 0)
export_table = RT_TABLE_MAIN;
if(import_table_count < 1)
import_tables[import_table_count++] = 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;
if(skip_kernel_setup) return 1;
for(i=0; iwas = read_proc(s->name);
if(s->was < 0) {
perror("Couldn't read sysctl");
return -1;
}
if(s->was != s->want) {
rc = write_proc(s->name, s->want);
if(rc < 0) {
perror("Couldn't write sysctl");
return -1;
}
}
}
return 1;
} else {
release_tables();
close(dgram_socket);
dgram_socket = -1;
close(nl_command.sock);
nl_command.sock = -1;
nl_setup = 0;
if(skip_kernel_setup) return 1;
for(i=0; iwas >= 0 && s->was != s->want) {
rc = write_proc(s->name,s->was);
if(rc < 0) {
perror("Couldn't write sysctl");
return -1;
}
}
}
return 1;
}
}
static inline unsigned int
rtnlgrp_to_mask(unsigned int grp)
{
return grp ? 1 << (grp - 1) : 0;
}
int
kernel_setup_socket(int setup)
{
int rc;
if(setup) {
rc = netlink_socket(&nl_listen,
rtnlgrp_to_mask(RTNLGRP_IPV6_ROUTE)
| rtnlgrp_to_mask(RTNLGRP_IPV4_ROUTE)
| rtnlgrp_to_mask(RTNLGRP_LINK)
| rtnlgrp_to_mask(RTNLGRP_IPV4_IFADDR)
| rtnlgrp_to_mask(RTNLGRP_IPV6_IFADDR)
/* We monitor rules, because it can be change by third parties. For example
a /etc/init.d/network restart on OpenWRT flush the rules. */
| rtnlgrp_to_mask(RTNLGRP_IPV4_RULE)
| rtnlgrp_to_mask(RTNLGRP_IPV6_RULE));
if(rc < 0) {
perror("netlink_socket(_ROUTE | _LINK | _IFADDR | _RULE)");
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;
}
}
static int
get_old_if(const char *ifname)
{
int i;
for(i = 0; i < num_old_if; i++)
if(strcmp(old_if[i].ifname, ifname) == 0)
return i;
if(num_old_if >= MAX_INTERFACES)
return -1;
old_if[num_old_if].ifname = strdup(ifname);
if(old_if[num_old_if].ifname == NULL)
return -1;
old_if[num_old_if].rp_filter = -1;
return num_old_if++;
}
int
kernel_setup_interface(int setup, const char *ifname, int ifindex)
{
char buf[100];
int i, rc;
/* rp_filter has weird semantics: both all/rp_filter and ifname/rp_filter
must be set to 0 for the rp_filter to be disabled. Deal with it. */
rc = snprintf(buf, 100, "/proc/sys/net/ipv4/conf/%s/rp_filter", ifname);
if(rc < 0 || rc >= 100)
return -1;
i = get_old_if(ifname);
if(setup) {
if(i >= 0)
old_if[i].rp_filter = read_proc(buf);
if(i < 0 || old_if[i].rp_filter < 0)
fprintf(stderr,
"Warning: cannot save old configuration for %s.\n",
ifname);
rc = write_proc(buf, 0);
if(rc < 0)
return -1;
} else {
if(i >= 0 && old_if[i].rp_filter >= 0)
rc = write_proc(buf, old_if[i].rp_filter);
else
rc = -1;
if(rc < 0)
fprintf(stderr,
"Warning: cannot restore old configuration for %s.\n",
ifname);
}
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;
}
static int
isbatman(const char *ifname, int ifindex)
{
char buf[256];
int rc;
rc = snprintf(buf, 256, "/sys/devices/virtual/net/%s/mesh", ifname);
if(rc < 0 || rc >= 256)
return -1;
if(access(buf, F_OK) >= 0)
return 1;
if(errno != ENOENT)
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 || isbatman(ifname, ifindex) != 0)
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
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;
}
/* Return true if we cannot handle disambiguation ourselves. */
int
kernel_disambiguate(int v4)
{
return !v4 && has_ipv6_subtrees;
}
int
kernel_has_ipv6_subtrees(void)
{
return (kernel_older_than("Linux", 3, 11) == 0);
}
int
kernel_route(int operation, const unsigned char *dest, unsigned short plen,
const unsigned char *src, unsigned short src_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, table, use_src = 0;
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) ||
(src_plen > 0 && (!v4mapped(src) || src_plen < 96))) {
errno = EINVAL;
return -1;
}
} else {
if(v4mapped(gate)|| (src_plen > 0 && v4mapped(src))) {
errno = EINVAL;
return -1;
}
}
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,
src, src_plen,
gate, ifindex, metric,
NULL, 0, 0);
rc = kernel_route(ROUTE_ADD, dest, plen,
src, src_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;
}
ipv4 = v4mapped(gate);
if(src_plen == 0) {
table = export_table;
} else if(kernel_disambiguate(ipv4)) {
table = export_table;
use_src = 1;
} else {
table = find_table(src, src_plen);
if(table < 0)
return -1;
}
kdebugf("kernel_route: %s %s from %s "
"table %d metric %d dev %d nexthop %s\n",
operation == ROUTE_ADD ? "add" :
operation == ROUTE_FLUSH ? "flush" : "???",
format_prefix(dest, plen), format_prefix(src, src_plen),
table, 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;
if(use_src)
rtm->rtm_src_len = src_plen;
rtm->rtm_table = 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));
if(use_src) {
rta = RTA_NEXT(rta, len);
rta->rta_len = RTA_LENGTH(sizeof(struct in6_addr));
rta->rta_type = RTA_SRC;
memcpy(RTA_DATA(rta), src, 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);;
int i;
len -= NLMSG_ALIGN(sizeof(*rtm));
memset(route, 0, sizeof(struct kernel_route));
if(rtm->rtm_family == AF_INET) {
/* if RTA_DST is not a TLV, that's a default destination */
const unsigned char zeroes[4] = {0, 0, 0, 0};
v4tov6(route->prefix, zeroes);
route->plen = 96;
}
route->proto = rtm->rtm_protocol;
#define GET_PLEN(p) (rtm->rtm_family == AF_INET) ? p + 96 : p
#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:
route->plen = GET_PLEN(rtm->rtm_dst_len);
COPY_ADDR(route->prefix, RTA_DATA(rta));
break;
case RTA_SRC:
route->src_plen = GET_PLEN(rtm->rtm_src_len);
COPY_ADDR(route->src_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
#undef GET_PLEN
for(i = 0; i < import_table_count; i++)
if(table == import_tables[i])
return 0;
return -1;
}
static void
print_kernel_route(int add, int protocol, int type,
struct kernel_route *route)
{
char ifname[IFNAMSIZ];
char addr_prefix[INET6_ADDRSTRLEN];
char src_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;
}
if(route->src_plen >= 0) {
if(!inet_ntop(AF_INET6, route->src_prefix,
src_addr_prefix, sizeof(src_addr_prefix))) {
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, from: %s/%d)",
add == RTM_NEWROUTE ? "Add" : "Delete",
addr_prefix, route->plen, addr_gw, route->metric, ifname,
protocol, type, src_addr_prefix, route->src_plen);
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;
/* Ignore cached routes, advertised by some kernels (linux 3.x). */
if(rtm->rtm_flags & RTM_F_CLONED)
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) ||
martian_prefix(current_route->src_prefix, current_route->src_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 };
char rule_exists[SRC_TABLE_NUM] = {0};
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;
rc = netlink_send_dump(RTM_GETRULE, &g, sizeof(g));
if(rc < 0)
return -1;
rc = netlink_read(&nl_command, NULL, 1,
filter_kernel_rules, rule_exists);
if(rc < 0)
return -1;
install_missing_rules(rule_exists, families[i] == AF_INET);
}
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;
}
/* If data is null, takes all addresses. If data is not null, takes
either link-local or global addresses depending of the value of
data[4]. */
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(data && 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;
case RTM_NEWRULE:
case RTM_DELRULE:
rc = filter_kernel_rules(nh, NULL);
if(changed && rc > 0)
*changed |= CHANGE_RULE;
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;
}
/* Routing table's rules */
static int
add_rule(int prio, const unsigned char *src_prefix, int src_plen, int table)
{
char buffer[64] = {0}; /* 56 needed */
struct nlmsghdr *message_header = (void*)buffer;
struct rtmsg *message = NULL;
struct rtattr *current_attribute = NULL;
int is_v4 = v4mapped(src_prefix);
int addr_size = is_v4 ? sizeof(struct in_addr) : sizeof(struct in6_addr);
kdebugf("Add rule v%c prio %d from %s\n", is_v4 ? '4' : '6', prio,
format_prefix(src_prefix, src_plen));
if(is_v4) {
src_prefix += 12;
src_plen -= 96;
if(src_plen < 0) {
errno = EINVAL;
return -1;
}
}
#if RTA_ALIGNTO != NLMSG_ALIGNTO
#error "RTA_ALIGNTO != NLMSG_ALIGNTO"
#endif
/* Set the header */
message_header->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL;
message_header->nlmsg_type = RTM_NEWRULE;
message_header->nlmsg_len = NLMSG_ALIGN(sizeof(struct nlmsghdr));
/* Append the message */
message = NLMSG_DATA(message_header);
message->rtm_family = is_v4 ? AF_INET : AF_INET6;
message->rtm_dst_len = 0;
message->rtm_src_len = src_plen;
message->rtm_tos = 0;
message->rtm_table = table;
message->rtm_protocol = RTPROT_BABEL;
message->rtm_scope = RT_SCOPE_UNIVERSE;
message->rtm_type = RTN_UNICAST;
message->rtm_flags = 0;
message_header->nlmsg_len += NLMSG_ALIGN(sizeof(struct rtmsg));
/* Append each attribute */
current_attribute = RTM_RTA(message);
/* prio */
current_attribute->rta_len = RTA_LENGTH(sizeof(int));
current_attribute->rta_type = FRA_PRIORITY;
*(int*)RTA_DATA(current_attribute) = prio;
message_header->nlmsg_len += current_attribute->rta_len;
current_attribute = (void*)
((char*)current_attribute) + current_attribute->rta_len;
/* src */
current_attribute->rta_len = RTA_LENGTH(addr_size);
current_attribute->rta_type = FRA_SRC;
memcpy(RTA_DATA(current_attribute), src_prefix, addr_size);
message_header->nlmsg_len += current_attribute->rta_len;
current_attribute = (void*)
((char*)current_attribute) + current_attribute->rta_len;
/* send message */
if(message_header->nlmsg_len > 64) {
errno = EINVAL;
return -1;
}
return netlink_talk(message_header);
}
static int
flush_rule(int prio, int family)
{
char buffer[64] = {0}; /* 36 needed */
struct nlmsghdr *message_header = (void*)buffer;
struct rtmsg *message = NULL;
struct rtattr *current_attribute = NULL;
memset(buffer, 0, sizeof(buffer));
kdebugf("Flush rule v%c prio %d\n", family == AF_INET ? '4' : '6', prio);
#if RTA_ALIGNTO != NLMSG_ALIGNTO
#error "RTA_ALIGNTO != NLMSG_ALIGNTO"
#endif
/* Set the header */
message_header->nlmsg_flags = NLM_F_REQUEST;
message_header->nlmsg_type = RTM_DELRULE;
message_header->nlmsg_len = NLMSG_ALIGN(sizeof(struct nlmsghdr));
/* Append the message */
message = NLMSG_DATA(message_header);
message->rtm_family = family;
message->rtm_dst_len = 0;
message->rtm_src_len = 0;
message->rtm_tos = 0;
message->rtm_table = 0;
message->rtm_protocol = RTPROT_BABEL;
message->rtm_scope = RT_SCOPE_UNIVERSE;
message->rtm_type = RTN_UNSPEC;
message->rtm_flags = 0;
message_header->nlmsg_len += NLMSG_ALIGN(sizeof(struct rtmsg));
/* Append each attribute */
current_attribute = RTM_RTA(message);
/* prio */
current_attribute->rta_len = RTA_LENGTH(sizeof(int));
current_attribute->rta_type = FRA_PRIORITY;
*(int*)RTA_DATA(current_attribute) = prio;
message_header->nlmsg_len += current_attribute->rta_len;
current_attribute = (void*)
((char*)current_attribute) + current_attribute->rta_len;
/* send message */
if(message_header->nlmsg_len > 64) {
errno = EINVAL;
return -1;
}
return netlink_talk(message_header);
}
/* Source specific functions and data structures */
/* The table used for non-specific routes is "export_table", therefore, we can
take the convention of plen == 0 <=> empty table. */
struct kernel_table {
unsigned char src[16];
unsigned char plen;
unsigned char table;
};
/* kernel_tables contains informations about the rules we installed. It is an
array indexed by: - src_table_prio.
(First entries are the most specific, since they have priority.) */
static struct kernel_table kernel_tables[SRC_TABLE_NUM];
/* used tables is indexed by: - src_table_idx
used_tables[i] == 1 <=> the table number (i + src_table_idx) is used */
static char used_tables[SRC_TABLE_NUM] = {0};
static unsigned char src_table_used = 0; /* number of tables used */
static inline int
change_table_priority(const unsigned char *src, int plen, int table,
int old_prio, int new_prio)
{
int rc;
kdebugf("/Swap: ");
rc = add_rule(new_prio, src, plen, table);
if(rc < 0)
return rc;
kdebugf("\\Swap: ");
return flush_rule(old_prio, v4mapped(src) ? AF_INET : AF_INET6);
}
/* Return a new table at index [idx] of kernel_tables. If cell at that index is
not free, we need to shift cells (and rules). If it's full, return NULL. */
static struct kernel_table *
insert_table(const unsigned char *src, unsigned short src_plen, int idx)
{
int table;
int rc;
int i;
if(idx < 0 || idx >= SRC_TABLE_NUM) {
fprintf(stderr, "Incorrect table number %d\n", idx);
return NULL;
}
if(src_table_used >= SRC_TABLE_NUM) {
kdebugf("All allowed routing tables are used!\n");
return NULL;
}
/* find a free table number */
for(table = 0; table < SRC_TABLE_NUM; table++)
if(!used_tables[table])
break;
table += src_table_idx;
/* Create the table's rule at the right place. Shift rules if necessary. */
if(kernel_tables[idx].plen != 0) {
/* shift right */
i = src_table_used;
while(i > idx) {
i--;
rc = change_table_priority(kernel_tables[i].src,
kernel_tables[i].plen,
kernel_tables[i].table,
i + src_table_prio,
i + 1 + src_table_prio);
if(rc < 0) {
perror("change_table_priority");
return NULL;
}
kernel_tables[i+1] = kernel_tables[i];
kernel_tables[i].plen = 0;
}
}
rc = add_rule(idx + src_table_prio, src, src_plen, table);
if(rc < 0) {
perror("add rule");
return NULL;
}
used_tables[table - src_table_idx] = 1;
memcpy(kernel_tables[idx].src, src, 16);
kernel_tables[idx].plen = src_plen;
kernel_tables[idx].table = table;
src_table_used++;
return &kernel_tables[idx];
}
/* Sorting kernel_tables in a well ordered fashion will increase code complexity
and decrease performances, because more rule shifs will be required, so more
system calls invoked. */
static int
find_table_slot(const unsigned char *src, unsigned short src_plen,
int *new_return)
{
struct kernel_table *kt = NULL;
int i;
*new_return = -1;
for(i = 0; i < SRC_TABLE_NUM; i++) {
kt = &kernel_tables[i];
if(kt->plen == 0)
goto new_table_here; /* empty table here */
switch(prefix_cmp(src, src_plen, kt->src, kt->plen)) {
case PST_LESS_SPECIFIC:
case PST_DISJOINT:
continue; /* try to find a comparable route entry. */
case PST_MORE_SPECIFIC:
goto new_table_here;
case PST_EQUALS:
return i;
}
}
return -1;
new_table_here:
*new_return = i;
return -1;
}
static int
find_table(const unsigned char *src, unsigned short src_plen)
{
struct kernel_table *kt = NULL;
int i, new_i;
if(src_plen == 0 ||
(kernel_disambiguate(src_plen >= 96 && v4mapped(src)))) {
fprintf(stderr, "Find_table called for route handled by kernel "
"(this shouldn't happen).");
return -1;
}
i = find_table_slot(src, src_plen, &new_i);
if(i < 0) {
if(new_i < 0)
return -1;
kt = insert_table(src, src_plen, new_i);
} else {
kt = &kernel_tables[i];
}
return kt == NULL ? -1 : kt->table;
}
static void
release_tables(void)
{
int i;
for(i = 0; i < SRC_TABLE_NUM; i++) {
if(kernel_tables[i].plen != 0) {
flush_rule(i + src_table_prio,
v4mapped(kernel_tables[i].src) ? AF_INET : AF_INET6);
kernel_tables[i].plen = 0;
}
used_tables[i] = 0;
}
src_table_used = 0;
}
static int
filter_kernel_rules(struct nlmsghdr *nh, void *data)
{
int i, len, has_priority = 0;
unsigned int rta_len;
struct rtmsg *rtm = NULL;
struct rtattr *rta = NULL;
int is_v4 = 0;
char *rule_exists = data;
unsigned char src[16];
unsigned char src_plen;
unsigned int table, priority = 0xFFFFFFFF;
len = nh->nlmsg_len;
rtm = (struct rtmsg*)NLMSG_DATA(nh);
len -= NLMSG_LENGTH(0);
src_plen = rtm->rtm_src_len;
table = rtm->rtm_table;
if(rtm->rtm_family != AF_INET && rtm->rtm_family != AF_INET6) {
kdebugf("filter_rules: Unknown family: %d\n", rtm->rtm_family);
return -1;
}
is_v4 = rtm->rtm_family == AF_INET;
rta = RTM_RTA(rtm);
len -= NLMSG_ALIGN(sizeof(*rtm));
#define GET_PLEN(p) (rtm->rtm_family == AF_INET) ? p + 96 : p
#define COPY_ADDR(d, s) \
do { \
if(!is_v4) { \
if(UNLIKELY(rta_len < 16)) { \
fprintf(stderr, "filter_rules: truncated message."); \
return -1; \
} \
memcpy(d, s, 16); \
}else { \
if(UNLIKELY(rta_len < 4)) { \
fprintf(stderr, "filter_rules: truncated message."); \
return -1; \
} \
v4tov6(d, s); \
} \
} while(0)
for(; RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) {
rta_len = RTA_PAYLOAD(rta);
switch(rta->rta_type) {
case FRA_UNSPEC: break;
case FRA_SRC:
src_plen = GET_PLEN(rtm->rtm_src_len);
COPY_ADDR(src, RTA_DATA(rta));
break;
case FRA_PRIORITY:
priority = *(unsigned int*)RTA_DATA(rta);
has_priority = 1;
break;
case FRA_TABLE:
table = *(int*)RTA_DATA(rta);
break;
default:
kdebugf("filter_rules: Unknown rule attribute: %d.\n",
rta->rta_type);
break;
}
}
#undef COPY_ADDR
#undef GET_PLEN
kdebugf("filter_rules: from %s prio %d table %d\n",
format_prefix(src, src_plen), priority, table);
if(martian_prefix(src, src_plen) || !has_priority)
return 0;
i = priority - src_table_prio;
if(i < 0 || SRC_TABLE_NUM <= i)
return 0;
/* There is an unexpected change on one of our rules. */
if(!rule_exists)
return 1;
if(prefix_cmp(src, src_plen,
kernel_tables[i].src, kernel_tables[i].plen) == PST_EQUALS &&
table == kernel_tables[i].table &&
!rule_exists[i]) {
rule_exists[i] = 1;
} else {
int rc;
do {
rc = flush_rule(i + src_table_prio, is_v4 ? AF_INET : AF_INET6);
} while(rc >= 0);
/* flush unexpected rules, but keep information in kernel_tables[i]. It
will be used afterwards to reinstall the rule. */
if(errno != ENOENT && errno != EEXIST)
fprintf(stderr,
"filter_rules: cannot remove from %s prio %d table %d"
"(%s)\n",
format_prefix(src, src_plen), priority, table,
strerror(errno));
rule_exists[i] = 0;
}
return 1;
}
/* This functions should be executed wrt the code just bellow: [rule_exists]
contains is a boolean array telling whether the rules we should have
installed in the kernel are installed or not. If they aren't, then reinstall
them (this can append when rules are modified by third parties). */
static void
install_missing_rules(char rule_exists[SRC_TABLE_NUM], int v4)
{
int i, rc;
for(i = 0; i < SRC_TABLE_NUM; i++)
if(v4mapped(kernel_tables[i].src) == v4 &&
!rule_exists[i] && kernel_tables[i].plen != 0) {
rc = add_rule(i + src_table_prio, kernel_tables[i].src,
kernel_tables[i].plen, kernel_tables[i].table);
if(rc < 0)
fprintf(stderr,
"install_missing_rules: "
"Cannot install rule: table %d prio %d from %s\n",
kernel_tables[i].table, i + src_table_prio,
format_prefix(kernel_tables[i].src,
kernel_tables[i].plen));
}
}
babeld-babeld-1.6.3-273bcc70824af88574c3724b3c6a69a9722d4cc4/kernel_socket.c 0000664 0000000 0000000 00000055001 12603261366 0024450 0 ustar 00root root 0000000 0000000 /*
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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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_count = 0, import_tables[MAX_IMPORT_TABLES];
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;
if(skip_kernel_setup) return 1;
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, NULL, 0);
if(rc == 0 && old_forwarding != forwarding) {
rc = sysctl(mib, 4, &old_forwarding, &datasize,
&forwarding, datasize);
}
}
else if(old_forwarding >= 0 && old_forwarding != forwarding)
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, NULL, 0);
if(rc == 0 && old_accept_redirects != accept_redirects) {
rc = sysctl(mib, 4, &old_accept_redirects, &datasize,
&accept_redirects, datasize);
}
} else if(old_accept_redirects >= 0 && old_accept_redirects != accept_redirects)
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_disambiguate(int v4)
{
return 0;
}
int
kernel_has_ipv6_subtrees(void)
{
return 0;
}
int
kernel_route(int operation, const unsigned char *dest, unsigned short plen,
const unsigned char *src, unsigned short src_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 }}};
/* Source-specific routes are not implemented yet for BSD. */
if(src_plen > 0) {
errno = ENOSYS;
return -1;
}
/* 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) {
/* Avoid atomic route changes that is buggy on OS X. */
kernel_route(ROUTE_FLUSH, dest, plen,
src, src_plen,
gate, ifindex, metric,
NULL, 0, 0);
return kernel_route(ROUTE_ADD, dest, plen,
src, src_plen,
newgate, newifindex, newmetric,
NULL, 0, 0);
}
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 | RTF_PROTO2;
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;
char *rta = (char*)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
/* Filter out our own route */
excluded_flags |= RTF_PROTO2;
if((rtm->rtm_flags & excluded_flags) != 0)
return -1;
/* 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((int)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: */
babeld-babeld-1.6.3-273bcc70824af88574c3724b3c6a69a9722d4cc4/local.c 0000664 0000000 0000000 00000017000 12603261366 0022707 0 ustar 00root root 0000000 0000000 /*
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
#include
#include
#include
#include
#include
#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_server_socket = -1, local_sockets[MAX_LOCAL_SOCKETS];
int num_local_sockets = 0;
int local_server_port = -1;
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;
}
}
static void
local_notify_self_1(int s)
{
char buf[512];
char host[64];
int rc;
rc = gethostname(host, 64);
if(rc < 0)
strncpy(host, "alamakota", 64);
rc = snprintf(buf, 512, "add self %.64s id %s\n",
host, format_eui64(myid));
if(rc < 0 || rc >= 512)
goto fail;
rc = write_timeout(s, buf, rc);
if(rc < 0)
goto fail;
return;
fail:
shutdown(s, 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 "???";
}
}
static void
local_notify_neighbour_1(int s, struct neighbour *neigh, int kind)
{
char buf[512], rttbuf[64];
int rc;
rttbuf[0] = '\0';
if(valid_rtt(neigh)) {
rc = snprintf(rttbuf, 64, " rtt %s rttcost %d",
format_thousands(neigh->rtt), neighbour_rttcost(neigh));
if(rc < 0 || rc >= 64)
rttbuf[0] = '\0';
}
rc = snprintf(buf, 512,
"%s neighbour %lx address %s "
"if %s reach %04x rxcost %d txcost %d%s 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),
rttbuf,
neighbour_cost(neigh));
if(rc < 0 || rc >= 512)
goto fail;
rc = write_timeout(s, buf, rc);
if(rc < 0)
goto fail;
return;
fail:
shutdown(s, 1);
return;
}
void
local_notify_neighbour(struct neighbour *neigh, int kind)
{
int i;
for(i = 0; i < num_local_sockets; i++)
local_notify_neighbour_1(local_sockets[i], neigh, kind);
}
static void
local_notify_xroute_1(int s, struct xroute *xroute, int kind)
{
char buf[512];
int rc;
const char *dst_prefix = format_prefix(xroute->prefix,
xroute->plen);
const char *src_prefix = format_prefix(xroute->src_prefix,
xroute->src_plen);
rc = snprintf(buf, 512, "%s xroute %s-%s prefix %s from %s metric %d\n",
local_kind(kind), dst_prefix, src_prefix,
dst_prefix, src_prefix, xroute->metric);
if(rc < 0 || rc >= 512)
goto fail;
rc = write_timeout(s, buf, rc);
if(rc < 0)
goto fail;
return;
fail:
shutdown(s, 1);
return;
}
void
local_notify_xroute(struct xroute *xroute, int kind)
{
int i;
for(i = 0; i < num_local_sockets; i++)
local_notify_xroute_1(local_sockets[i], xroute, kind);
}
static void
local_notify_route_1(int s, struct babel_route *route, int kind)
{
char buf[512];
int rc;
const char *dst_prefix = format_prefix(route->src->prefix,
route->src->plen);
const char *src_prefix = format_prefix(route->src->src_prefix,
route->src->src_plen);
rc = snprintf(buf, 512,
"%s route %s-%lx-%s prefix %s from %s installed %s "
"id %s metric %d refmetric %d via %s if %s\n",
local_kind(kind),
dst_prefix, (unsigned long)route->neigh, src_prefix,
dst_prefix, src_prefix,
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(s, buf, rc);
if(rc < 0)
goto fail;
return;
fail:
shutdown(s, 1);
return;
}
void
local_notify_route(struct babel_route *route, int kind)
{
int i;
for(i = 0; i < num_local_sockets; i++)
local_notify_route_1(local_sockets[i], route, kind);
}
void
local_notify_all_1(int s)
{
int rc;
struct neighbour *neigh;
const char *header = "BABEL 0.0\n";
struct xroute_stream *xroutes;
struct route_stream *routes;
rc = write_timeout(s, header, strlen(header));
if(rc < 0)
goto fail;
local_notify_self_1(s);
FOR_ALL_NEIGHBOURS(neigh) {
local_notify_neighbour_1(s, neigh, LOCAL_ADD);
}
xroutes = xroute_stream();
if(xroutes) {
while(1) {
struct xroute *xroute = xroute_stream_next(xroutes);
if(xroute == NULL)
break;
local_notify_xroute_1(s, xroute, LOCAL_ADD);
}
xroute_stream_done(xroutes);
}
routes = route_stream(0);
if(routes) {
while(1) {
struct babel_route *route = route_stream_next(routes);
if(route == NULL)
break;
local_notify_route_1(s, route, LOCAL_ADD);
}
route_stream_done(routes);
}
rc = write_timeout(s, "done\n", 5);
if(rc < 0)
goto fail;
return;
fail:
shutdown(s, 1);
return;
}
#endif
babeld-babeld-1.6.3-273bcc70824af88574c3724b3c6a69a9722d4cc4/local.h 0000664 0000000 0000000 00000003471 12603261366 0022723 0 ustar 00root root 0000000 0000000 /*
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
#ifndef MAX_LOCAL_SOCKETS
#define MAX_LOCAL_SOCKETS 4
#endif
extern int local_server_socket, local_sockets[MAX_LOCAL_SOCKETS];
extern int num_local_sockets;
extern int local_server_port;
int local_read(int s);
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_1(int s);
#else
#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
babeld-babeld-1.6.3-273bcc70824af88574c3724b3c6a69a9722d4cc4/message.c 0000664 0000000 0000000 00000213113 12603261366 0023244 0 ustar 00root root 0000000 0000000 /*
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
#include
#include
#include
#include
#include
#include
#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_update_subtlv(const unsigned char *a, int alen,
unsigned char *channels)
{
int type, len, i = 0;
while(i < alen) {
type = a[i];
if(type == SUBTLV_PAD1) {
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 == SUBTLV_PADN) {
/* Nothing. */
} else if(type == SUBTLV_DIVERSITY) {
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 {
debugf("Received unknown update sub-TLV %d.\n", type);
}
i += len + 2;
}
}
static int
parse_hello_subtlv(const unsigned char *a, int alen,
unsigned int *hello_send_us)
{
int type, len, i = 0, ret = 0;
while(i < alen) {
type = a[0];
if(type == SUBTLV_PAD1) {
i++;
continue;
}
if(i + 1 > alen) {
fprintf(stderr, "Received truncated sub-TLV on Hello message.\n");
return -1;
}
len = a[i + 1];
if(i + len > alen) {
fprintf(stderr, "Received truncated sub-TLV on Hello message.\n");
return -1;
}
if(type == SUBTLV_PADN) {
/* Nothing to do. */
} else if(type == SUBTLV_TIMESTAMP) {
if(len >= 4) {
DO_NTOHL(*hello_send_us, a + i + 2);
ret = 1;
} else {
fprintf(stderr,
"Received incorrect RTT sub-TLV on Hello message.\n");
}
} else {
debugf("Received unknown Hello sub-TLV type %d.\n", type);
}
i += len + 2;
}
return ret;
}
static int
parse_ihu_subtlv(const unsigned char *a, int alen,
unsigned int *hello_send_us,
unsigned int *hello_rtt_receive_time)
{
int type, len, i = 0, ret = 0;
while(i < alen) {
type = a[0];
if(type == SUBTLV_PAD1) {
i++;
continue;
}
if(i + 1 > alen) {
fprintf(stderr, "Received truncated sub-TLV on IHU message.\n");
return -1;
}
len = a[i + 1];
if(i + len > alen) {
fprintf(stderr, "Received truncated sub-TLV on IHU message.\n");
return -1;
}
if(type == SUBTLV_PADN) {
/* Nothing to do. */
} else if(type == SUBTLV_TIMESTAMP) {
if(len >= 8) {
DO_NTOHL(*hello_send_us, a + i + 2);
DO_NTOHL(*hello_rtt_receive_time, a + i + 6);
ret = 1;
}
else {
fprintf(stderr,
"Received incorrect RTT sub-TLV on IHU message.\n");
}
} else {
debugf("Received unknown IHU sub-TLV type %d.\n", type);
}
i += len + 2;
}
return ret;
}
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];
int have_hello_rtt = 0;
/* Content of the RTT sub-TLV on IHU messages. */
unsigned int hello_send_us = 0, hello_rtt_receive_time = 0;
if(ifp->flags & IF_TIMESTAMPS) {
/* We want to track exactly when we received this packet. */
gettime(&now);
}
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;
unsigned int timestamp;
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)
/* Multiply by 3/2 to allow hellos to expire. */
schedule_neighbours_check(interval * 15, 0);
/* Sub-TLV handling. */
if(len > 8) {
if(parse_hello_subtlv(message + 8, len - 6, ×tamp) > 0) {
neigh->hello_send_us = timestamp;
neigh->hello_rtt_receive_time = now;
have_hello_rtt = 1;
}
}
} 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)
/* Multiply by 3/2 to allow neighbours to expire. */
schedule_neighbours_check(interval * 45, 0);
/* RTT sub-TLV. */
if(len > 10 + rc)
parse_ihu_subtlv(message + 8 + rc, len - 6 - rc,
&hello_send_us, &hello_rtt_receive_time);
}
} 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[2] == 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_update_subtlv 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_update_subtlv(message + 2 + parsed_len,
len - parsed_len, channels);
}
update_route(router_id, prefix, plen, zeroes, 0, 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, zeroes, 0);
} else {
send_update(neigh->ifp, 0, prefix, plen, zeroes, 0);
}
} 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, zeroes, 0, message[6],
seqno, message + 8);
} else if(type == MESSAGE_UPDATE_SRC_SPECIFIC) {
unsigned char prefix[16], src_prefix[16], *nh;
unsigned char ae, plen, src_plen, omitted;
unsigned char channels[DIVERSITY_HOPS];
unsigned short interval, seqno, metric;
const unsigned char *src_prefix_beginning = NULL;
int rc, parsed_len = 0;
if(len < 10)
goto fail;
ae = message[2];
src_plen = message[3];
plen = message[4];
omitted = message[5];
DO_NTOHS(interval, message + 6);
DO_NTOHS(seqno, message + 8);
DO_NTOHS(metric, message + 10);
if(omitted == 0 || (ae == 1 ? have_v4_prefix : have_v6_prefix))
rc = network_prefix(ae, plen, omitted, message + 12,
ae == 1 ? v4_prefix : v6_prefix,
len - 10, prefix);
else
rc = -1;
if(rc < 0)
goto fail;
parsed_len = 10 + rc;
src_prefix_beginning = message + 2 + parsed_len;
rc = network_prefix(ae, src_plen, 0, src_prefix_beginning, NULL,
len - parsed_len, src_prefix);
if(rc < 0)
goto fail;
parsed_len += rc;
if(ae == 1) {
plen += 96;
src_plen += 96;
}
if(!have_router_id) {
fprintf(stderr, "Received prefix with no router id.\n");
goto fail;
}
debugf("Received ss-update for (%s from %s) from %s on %s.\n",
format_prefix(prefix, plen),
format_prefix(src_prefix, src_plen),
format_address(from), ifp->name);
if(ae == 0) {
debugf("Received invalid Source-Specific wildcard update.\n");
retract_neighbour_routes(neigh);
goto done;
} else if(ae == 1) {
if(!have_v4_nh)
goto fail;
nh = v4_nh;
} else if(have_v6_nh) {
nh = v6_nh;
} else {
nh = neigh->address;
}
if(ae == 1) {
if(!ifp->ipv4)
goto done;
}
if((ifp->flags & IF_FARAWAY)) {
channels[0] = 0;
} else {
/* This will be overwritten by parse_update_subtlv 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_update_subtlv(message + 2 + parsed_len,
len - parsed_len, channels);
}
update_route(router_id, prefix, plen, src_prefix, src_plen,
seqno, metric, interval, neigh, nh,
channels, channels_len(channels));
} else if(type == MESSAGE_REQUEST_SRC_SPECIFIC) {
unsigned char prefix[16], plen, ae, src_prefix[16], src_plen;
int rc, parsed = 5;
if(len < 3) goto fail;
ae = message[2];
plen = message[3];
src_plen = message[4];
rc = network_prefix(ae, plen, 0, message + parsed,
NULL, len + 2 - parsed, prefix);
if(rc < 0) goto fail;
if(ae == 1)
plen += 96;
parsed += rc;
rc = network_prefix(ae, src_plen, 0, message + parsed,
NULL, len + 2 - parsed, src_prefix);
if(rc < 0) goto fail;
if(ae == 1)
src_plen += 96;
parsed += rc;
if(ae == 0) {
debugf("Received request for any source-specific "
"from %s on %s.\n",
format_address(from), ifp->name);
/* See comments for std requests. */
send_ihu(neigh, NULL);
if(neigh->ifp->last_specific_update_time <
now.tv_sec - MAX(neigh->ifp->hello_interval / 100, 1))
send_update(neigh->ifp, 0, zeroes, 0, NULL, 0);
} else {
debugf("Received request for (%s from %s) from %s on %s.\n",
format_prefix(prefix, plen),
format_prefix(src_prefix, src_plen),
format_address(from), ifp->name);
send_update(neigh->ifp, 0, prefix, plen, src_prefix, src_plen);
}
} else if(type == MESSAGE_MH_REQUEST_SRC_SPECIFIC) {
unsigned char prefix[16], plen, ae, src_prefix[16], src_plen, hopc;
const unsigned char *router_id;
unsigned short seqno;
int rc, parsed = 16;
if(len < 14) goto fail;
ae = message[2];
plen = message[3];
DO_NTOHS(seqno, message + 4);
hopc = message[6];
src_plen = message[7];
router_id = message + 8;
rc = network_prefix(ae, plen, 0, message + parsed,
NULL, len + 2 - parsed, prefix);
if(rc < 0) goto fail;
if(ae == 1)
plen += 96;
parsed += rc;
rc = network_prefix(ae, src_plen, 0, message + parsed,
NULL, len + 2 - parsed, src_prefix);
if(rc < 0) goto fail;
if(ae == 1)
src_plen += 96;
debugf("Received request (%d) for (%s, %s)"
" from %s on %s (%s, %d).\n",
message[6],
format_prefix(prefix, plen),
format_prefix(src_prefix, src_plen),
format_address(from), ifp->name,
format_eui64(router_id), seqno);
handle_request(neigh, prefix, plen, src_prefix, src_plen,
hopc, seqno, router_id);
} 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;
}
/* We can calculate the RTT to this neighbour. */
if(have_hello_rtt && hello_send_us && hello_rtt_receive_time) {
int remote_waiting_us, local_waiting_us;
unsigned int rtt, smoothed_rtt;
unsigned int old_rttcost;
int changed = 0;
remote_waiting_us = neigh->hello_send_us - hello_rtt_receive_time;
local_waiting_us = time_us(neigh->hello_rtt_receive_time) -
hello_send_us;
/* Sanity checks (validity window of 10 minutes). */
if(remote_waiting_us < 0 || local_waiting_us < 0 ||
remote_waiting_us > 600000000 || local_waiting_us > 600000000)
return;
rtt = MAX(0, local_waiting_us - remote_waiting_us);
debugf("RTT to %s on %s sample result: %d us.\n",
format_address(from), ifp->name, rtt);
old_rttcost = neighbour_rttcost(neigh);
if(valid_rtt(neigh)) {
/* Running exponential average. */
smoothed_rtt = (ifp->rtt_decay * rtt +
(256 - ifp->rtt_decay) * neigh->rtt);
/* Rounding (up or down) to get closer to the sample. */
neigh->rtt = (neigh->rtt >= rtt) ? smoothed_rtt / 256 :
(smoothed_rtt + 255) / 256;
} else {
/* We prefer to be conservative with new neighbours
(higher RTT) */
assert(rtt <= 0x7FFFFFFF);
neigh->rtt = 2*rtt;
}
changed = (neighbour_rttcost(neigh) == old_rttcost ? 0 : 1);
update_neighbour_metric(neigh, changed);
neigh->rtt_time = now;
}
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;
}
}
static int
fill_rtt_message(struct interface *ifp)
{
if((ifp->flags & IF_TIMESTAMPS) && (ifp->buffered_hello >= 0)) {
if(ifp->sendbuf[ifp->buffered_hello + 8] == SUBTLV_PADN &&
ifp->sendbuf[ifp->buffered_hello + 9] == 4) {
unsigned int time;
/* Change the type of sub-TLV. */
ifp->sendbuf[ifp->buffered_hello + 8] = SUBTLV_TIMESTAMP;
gettime(&now);
time = time_us(now);
DO_HTONL(ifp->sendbuf + ifp->buffered_hello + 10, time);
return 1;
} else {
fprintf(stderr,
"No space left for timestamp sub-TLV "
"(this shouldn't happen)\n");
return -1;
}
}
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);
fill_rtt_message(ifp);
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->buffered_hello = -1;
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_int(struct interface *ifp, unsigned int value)
{
DO_HTONL(ifp->sendbuf + ifp->buffered, value);
ifp->buffered += 4;
}
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_int(struct neighbour *neigh, unsigned int value)
{
DO_HTONL(unicast_buffer + unicast_buffered, value);
unicast_buffered += 4;
}
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->buffered_hello >= 0)
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, (ifp->flags & IF_TIMESTAMPS) ? 12 : 6);
ifp->buffered_hello = ifp->buffered - 2;
accumulate_short(ifp, 0);
accumulate_short(ifp, ifp->hello_seqno);
accumulate_short(ifp, interval > 0xFFFF ? 0xFFFF : interval);
if(ifp->flags & IF_TIMESTAMPS) {
/* Sub-TLV containing the local time of emission. We use a
Pad4 sub-TLV, which we'll fill just before sending. */
accumulate_byte(ifp, SUBTLV_PADN);
accumulate_byte(ifp, 4);
accumulate_int(ifp, 0);
}
end_message(ifp, MESSAGE_HELLO, (ifp->flags & IF_TIMESTAMPS) ? 12 : 6);
}
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);
fill_rtt_message(unicast_neighbour->ifp);
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,
const unsigned char *src_prefix, unsigned char src_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;
const unsigned char *real_src_prefix = NULL;
int real_src_plen = 0;
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, src_prefix,
src_plen, ifp->ifindex);
if(add_metric >= INFINITY)
return;
metric = MIN(metric + add_metric, INFINITY);
/* Worst case */
ensure_space(ifp, 20 + 12 + 28 + 18);
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;
if(src_plen != 0 /* it should never be 96 */) {
real_src_prefix = src_prefix + 12;
real_src_plen = src_plen - 96;
}
} else {
if(ifp->have_buffered_prefix) {
while(omit < plen / 8 &&
ifp->buffered_prefix[omit] == prefix[omit])
omit++;
}
if(src_plen == 0 && (!ifp->have_buffered_prefix || plen >= 48))
flags |= 0x80;
real_prefix = prefix;
real_plen = plen;
real_src_prefix = src_prefix;
real_src_plen = src_plen;
}
if(!ifp->have_buffered_id || memcmp(id, ifp->buffered_id, 8) != 0) {
if(src_plen == 0 && 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;
}
if(src_plen == 0)
start_message(ifp, MESSAGE_UPDATE, 10 + (real_plen + 7) / 8 - omit +
channels_size);
else
start_message(ifp, MESSAGE_UPDATE_SRC_SPECIFIC,
10 + (real_plen + 7) / 8 - omit +
(real_src_plen + 7) / 8 + channels_size);
accumulate_byte(ifp, v4 ? 1 : 2);
if(src_plen != 0)
accumulate_byte(ifp, real_src_plen);
else
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);
if(src_plen != 0)
accumulate_bytes(ifp, real_src_prefix, (real_src_plen + 7) / 8);
/* 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);
}
if(src_plen == 0)
end_message(ifp, MESSAGE_UPDATE, 10 + (real_plen + 7) / 8 - omit +
channels_size);
else
end_message(ifp, MESSAGE_UPDATE_SRC_SPECIFIC,
10 + (real_plen + 7) / 8 - omit +
(real_src_plen + 7) / 8 + 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;
rc = memcmp(a->prefix, b->prefix, 16);
if(rc != 0)
return rc;
if(a->src_plen < b->src_plen)
return -1;
else if(a->src_plen > b->src_plen)
return 1;
return memcmp(a->src_prefix, b->src_prefix, 16);
}
void
flushupdates(struct interface *ifp)
{
struct xroute *xroute;
struct babel_route *route;
const unsigned char *last_prefix = NULL;
const unsigned char *last_src_prefix = NULL;
unsigned char last_plen = 0xFF;
unsigned char last_src_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,
b[i].src_prefix, b[i].src_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 &&
b[i].plen == last_plen &&
b[i].src_plen == last_src_plen &&
memcmp(b[i].prefix, last_prefix, 16) == 0 &&
memcmp(b[i].src_prefix, last_src_prefix, 16) == 0)
continue;
xroute = find_xroute(b[i].prefix, b[i].plen,
b[i].src_prefix, b[i].src_plen);
route = find_installed_route(b[i].prefix, b[i].plen,
b[i].src_prefix, b[i].src_plen);
if(xroute && (!route || xroute->metric <= kernel_metric)) {
really_send_update(ifp, myid,
xroute->prefix, xroute->plen,
xroute->src_prefix, xroute->src_plen,
myseqno, xroute->metric,
NULL, 0);
last_prefix = xroute->prefix;
last_plen = xroute->plen;
last_src_prefix = xroute->src_prefix;
last_src_plen = xroute->src_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,
route->src->src_prefix,
route->src->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,
route->src->src_prefix, route->src->src_plen,
seqno, metric,
channels, chlen);
update_source(route->src, seqno, metric);
last_prefix = route->src->prefix;
last_plen = route->src->plen;
last_src_prefix = route->src->src_prefix;
last_src_plen = route->src->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,
b[i].src_prefix, b[i].src_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,
const unsigned char *src_prefix, unsigned char src_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;
memcpy(ifp->buffered_updates[ifp->num_buffered_updates].src_prefix,
src_prefix, 16);
ifp->buffered_updates[ifp->num_buffered_updates].src_plen = src_plen;
ifp->num_buffered_updates++;
}
/* Full wildcard update with prefix == src_prefix == NULL,
Standard wildcard update with prefix == NULL && src_prefix != NULL,
Specific wildcard update with prefix != NULL && src_prefix == NULL. */
void
send_update(struct interface *ifp, int urgent,
const unsigned char *prefix, unsigned char plen,
const unsigned char *src_prefix, unsigned char src_plen)
{
if(ifp == NULL) {
struct interface *ifp_aux;
struct babel_route *route;
FOR_ALL_INTERFACES(ifp_aux)
send_update(ifp_aux, urgent, prefix, plen, src_prefix, src_plen);
if(prefix) {
/* Since flushupdates only deals with non-wildcard interfaces, we
need to do this now. */
route = find_installed_route(prefix, plen, src_prefix, src_plen);
if(route && route_metric(route) < INFINITY)
satisfy_request(prefix, plen, src_prefix, src_plen,
route->src->seqno, route->src->id, NULL);
}
return;
}
if(!if_up(ifp))
return;
if(prefix && src_prefix) {
debugf("Sending update to %s for %s from %s.\n",
ifp->name, format_prefix(prefix, plen),
format_prefix(src_prefix, src_plen));
buffer_update(ifp, prefix, plen, src_prefix, src_plen);
} else if(prefix || src_prefix) {
struct route_stream *routes;
send_self_update(ifp);
debugf("Sending update to %s for any.\n", ifp->name);
routes = route_stream(1);
if(routes) {
while(1) {
struct babel_route *route = route_stream_next(routes);
if(route == NULL)
break;
if((src_prefix && route->src->src_plen != 0) ||
(prefix && route->src->src_plen == 0))
continue;
buffer_update(ifp, route->src->prefix, route->src->plen,
route->src->src_prefix, route->src->src_plen);
}
route_stream_done(routes);
} else {
fprintf(stderr, "Couldn't allocate route stream.\n");
}
set_timeout(&ifp->update_timeout, ifp->update_interval);
if(!prefix)
ifp->last_update_time = now.tv_sec;
else
ifp->last_specific_update_time = now.tv_sec;
} else {
send_update(ifp, urgent, NULL, 0, zeroes, 0);
send_update(ifp, urgent, zeroes, 0, NULL, 0);
}
schedule_update_flush(ifp, urgent);
}
void
send_update_resend(struct interface *ifp,
const unsigned char *prefix, unsigned char plen,
const unsigned char *src_prefix, unsigned char src_plen)
{
assert(prefix != NULL);
send_update(ifp, 1, prefix, plen, src_prefix, src_plen);
record_resend(RESEND_UPDATE, prefix, plen, src_prefix, src_plen,
0, NULL, 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, 0);
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;
}
void
send_self_update(struct interface *ifp)
{
struct xroute_stream *xroutes;
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);
xroutes = xroute_stream();
if(xroutes) {
while(1) {
struct xroute *xroute = xroute_stream_next(xroutes);
if(xroute == NULL) break;
send_update(ifp, 0, xroute->prefix, xroute->plen,
xroute->src_prefix, xroute->src_plen);
}
xroute_stream_done(xroutes);
} else {
fprintf(stderr, "Couldn't allocate xroute stream.\n");
}
}
void
send_ihu(struct neighbour *neigh, struct interface *ifp)
{
int rxcost, interval;
int ll;
int send_rtt_data;
int msglen;
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((ifp->flags & IF_TIMESTAMPS) && neigh->hello_send_us
/* Checks whether the RTT data is not too old to be sent. */
&& timeval_minus_msec(&now, &neigh->hello_rtt_receive_time) < 1000000) {
send_rtt_data = 1;
} else {
neigh->hello_send_us = 0;
send_rtt_data = 0;
}
/* The length depends on the format of the address, and then an
optional 10-bytes sub-TLV for timestamps (used to compute a RTT). */
msglen = (ll ? 14 : 22) + (send_rtt_data ? 10 : 0);
if(unicast_neighbour != neigh) {
start_message(ifp, MESSAGE_IHU, msglen);
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);
if(send_rtt_data) {
accumulate_byte(ifp, SUBTLV_TIMESTAMP);
accumulate_byte(ifp, 8);
accumulate_int(ifp, neigh->hello_send_us);
accumulate_int(ifp, time_us(neigh->hello_rtt_receive_time));
}
end_message(ifp, MESSAGE_IHU, msglen);
} else {
int rc;
rc = start_unicast_message(neigh, MESSAGE_IHU, msglen);
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);
if(send_rtt_data) {
accumulate_unicast_byte(neigh, SUBTLV_TIMESTAMP);
accumulate_unicast_byte(neigh, 8);
accumulate_unicast_int(neigh, neigh->hello_send_us);
accumulate_unicast_int(neigh,
time_us(neigh->hello_rtt_receive_time));
}
end_unicast_message(neigh, MESSAGE_IHU, msglen);
}
}
/* 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);
}
}
/* Standard wildcard request with prefix == NULL && src_prefix == zeroes,
Specific wildcard request with prefix == zeroes && src_prefix == NULL. */
void
send_request(struct interface *ifp,
const unsigned char *prefix, unsigned char plen,
const unsigned char *src_prefix, unsigned char src_plen)
{
int v4, pb, spb, 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, src_prefix, src_plen);
}
return;
}
/* make sure any buffered updates go out before this request. */
flushupdates(ifp);
if(!if_up(ifp))
return;
if(prefix && src_prefix) {
debugf("sending request to %s for %s from %s.\n", ifp->name,
format_prefix(prefix, plen),
format_prefix(src_prefix, src_plen));
} else if (prefix) {
debugf("sending request to %s for any specific.\n", ifp->name);
start_message(ifp, MESSAGE_REQUEST_SRC_SPECIFIC, 3);
accumulate_byte(ifp, 0);
accumulate_byte(ifp, 0);
accumulate_byte(ifp, 0);
end_message(ifp, MESSAGE_REQUEST_SRC_SPECIFIC, 3);
return;
} else if (src_prefix) {
debugf("sending request to %s for any.\n", ifp->name);
start_message(ifp, MESSAGE_REQUEST, 2);
accumulate_byte(ifp, 0);
accumulate_byte(ifp, 0);
end_message(ifp, MESSAGE_REQUEST, 2);
return;
} else {
send_request(ifp, NULL, 0, zeroes, 0);
send_request(ifp, zeroes, 0, NULL, 0);
return;
}
v4 = plen >= 96 && v4mapped(prefix);
pb = v4 ? ((plen - 96) + 7) / 8 : (plen + 7) / 8;
len = 2 + pb;
if (src_plen != 0) {
spb = v4 ? ((src_plen - 96) + 7) / 8 : (src_plen + 7) / 8;
len += spb + 1;
start_message(ifp, MESSAGE_REQUEST_SRC_SPECIFIC, len);
} else {
start_message(ifp, MESSAGE_REQUEST, len);
}
accumulate_byte(ifp, v4 ? 1 : 2);
accumulate_byte(ifp, v4 ? plen - 96 : plen);
if(src_plen != 0)
accumulate_byte(ifp, v4 ? src_plen - 96 : src_plen);
if(v4)
accumulate_bytes(ifp, prefix + 12, pb);
else
accumulate_bytes(ifp, prefix, pb);
if(src_plen != 0) {
if(v4)
accumulate_bytes(ifp, src_prefix + 12, spb);
else
accumulate_bytes(ifp, src_prefix, spb);
end_message(ifp, MESSAGE_REQUEST_SRC_SPECIFIC, len);
return;
}
end_message(ifp, MESSAGE_REQUEST, len);
}
void
send_unicast_request(struct neighbour *neigh,
const unsigned char *prefix, unsigned char plen,
const unsigned char *src_prefix, unsigned char src_plen)
{
int rc, v4, pb, spb, len;
/* make sure any buffered updates go out before this request. */
flushupdates(neigh->ifp);
if(prefix && src_prefix) {
debugf("sending unicast request to %s for %s from %s.\n",
format_address(neigh->address),
format_prefix(prefix, plen),
format_prefix(src_prefix, src_plen));
} else if (prefix) {
debugf("sending unicast request to %s for any specific.\n",
format_address(neigh->address));
rc = start_unicast_message(neigh, MESSAGE_REQUEST_SRC_SPECIFIC, 3);
if(rc < 0) return;
accumulate_unicast_byte(neigh, 0);
accumulate_unicast_byte(neigh, 0);
accumulate_unicast_byte(neigh, 0);
end_unicast_message(neigh, MESSAGE_REQUEST_SRC_SPECIFIC, 3);
return;
} else if (src_prefix) {
debugf("sending unicast request to %s for any.\n",
format_address(neigh->address));
rc = start_unicast_message(neigh, MESSAGE_REQUEST, 2);
if(rc < 0) return;
accumulate_unicast_byte(neigh, 0);
accumulate_unicast_byte(neigh, 0);
end_unicast_message(neigh, MESSAGE_REQUEST, 2);
return;
} else {
send_unicast_request(neigh, NULL, 0, zeroes, 0);
send_unicast_request(neigh, zeroes, 0, NULL, 0);
return;
}
v4 = plen >= 96 && v4mapped(prefix);
pb = v4 ? ((plen - 96) + 7) / 8 : (plen + 7) / 8;
len = 2 + pb;
if(src_plen != 0) {
spb = v4 ? ((src_plen - 96) + 7) / 8 : (src_plen + 7) / 8;
len += spb + 1;
rc = start_unicast_message(neigh, MESSAGE_REQUEST_SRC_SPECIFIC, len);
} else {
rc = start_unicast_message(neigh, MESSAGE_REQUEST, len);
}
if(rc < 0) return;
accumulate_unicast_byte(neigh, v4 ? 1 : 2);
accumulate_unicast_byte(neigh, v4 ? plen - 96 : plen);
if(src_plen != 0)
accumulate_unicast_byte(neigh, v4 ? src_plen - 96 : src_plen);
if(v4)
accumulate_unicast_bytes(neigh, prefix + 12, pb);
else
accumulate_unicast_bytes(neigh, prefix, pb);
if(src_plen != 0) {
if(v4)
accumulate_unicast_bytes(neigh, src_prefix + 12, spb);
else
accumulate_unicast_bytes(neigh, src_prefix, spb);
end_unicast_message(neigh, MESSAGE_REQUEST_SRC_SPECIFIC, len);
return;
}
end_unicast_message(neigh, MESSAGE_REQUEST, len);
}
void
send_multihop_request(struct interface *ifp,
const unsigned char *prefix, unsigned char plen,
const unsigned char *src_prefix, unsigned char src_plen,
unsigned short seqno, const unsigned char *id,
unsigned short hop_count)
{
int v4, pb, spb, 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, src_prefix, src_plen,
seqno, id, hop_count);
}
return;
}
if(!if_up(ifp))
return;
debugf("Sending request (%d) on %s for %s from %s.\n",
hop_count, ifp->name, format_prefix(prefix, plen),
format_prefix(src_prefix, src_plen));
v4 = plen >= 96 && v4mapped(prefix);
pb = v4 ? ((plen - 96) + 7) / 8 : (plen + 7) / 8;
len = 6 + 8 + pb;
if(src_plen != 0) {
spb = v4 ? ((src_plen - 96) + 7) / 8 : (src_plen + 7) / 8;
len += spb;
start_message(ifp, MESSAGE_MH_REQUEST_SRC_SPECIFIC, len);
} else {
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, v4 ? src_plen - 96 : src_plen);
accumulate_bytes(ifp, id, 8);
if(prefix) {
if(v4)
accumulate_bytes(ifp, prefix + 12, pb);
else
accumulate_bytes(ifp, prefix, pb);
}
if(src_plen != 0) {
if(v4)
accumulate_bytes(ifp, src_prefix + 12, spb);
else
accumulate_bytes(ifp, src_prefix, spb);
end_message(ifp, MESSAGE_MH_REQUEST_SRC_SPECIFIC, len);
return;
}
end_message(ifp, MESSAGE_MH_REQUEST, len);
}
void
send_unicast_multihop_request(struct neighbour *neigh,
const unsigned char *prefix, unsigned char plen,
const unsigned char *src_prefix,
unsigned char src_plen,
unsigned short seqno, const unsigned char *id,
unsigned short hop_count)
{
int rc, v4, pb, spb, len;
/* Make sure any buffered updates go out before this request. */
flushupdates(neigh->ifp);
debugf("Sending multi-hop request to %s for %s from %s (%d hops).\n",
format_address(neigh->address),
format_prefix(prefix, plen),
format_prefix(src_prefix, src_plen), hop_count);
v4 = plen >= 96 && v4mapped(prefix);
pb = v4 ? ((plen - 96) + 7) / 8 : (plen + 7) / 8;
len = 6 + 8 + pb;
if(src_plen != 0) {
spb = v4 ? ((src_plen - 96) + 7) / 8 : (src_plen + 7) / 8;
len += spb;
rc = start_unicast_message(neigh, MESSAGE_MH_REQUEST_SRC_SPECIFIC, len);
} else {
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, v4 ? src_plen - 96 : src_plen);
accumulate_unicast_bytes(neigh, id, 8);
if(prefix) {
if(v4)
accumulate_unicast_bytes(neigh, prefix + 12, pb);
else
accumulate_unicast_bytes(neigh, prefix, pb);
}
if(src_plen != 0) {
if(v4)
accumulate_unicast_bytes(neigh, src_prefix + 12, spb);
else
accumulate_unicast_bytes(neigh, src_prefix, spb);
end_unicast_message(neigh, MESSAGE_MH_REQUEST_SRC_SPECIFIC, len);
return;
}
end_unicast_message(neigh, MESSAGE_MH_REQUEST, len);
}
void
send_request_resend(struct neighbour *neigh,
const unsigned char *prefix, unsigned char plen,
const unsigned char *src_prefix, unsigned char src_plen,
unsigned short seqno, unsigned char *id)
{
if(neigh)
send_unicast_multihop_request(neigh, prefix, plen, src_prefix, src_plen,
seqno, id, 127);
else
send_multihop_request(NULL, prefix, plen, src_prefix, src_plen,
seqno, id, 127);
record_resend(RESEND_REQUEST, prefix, plen, src_prefix, src_plen, seqno, id,
neigh ? neigh->ifp : NULL, resend_delay);
}
void
handle_request(struct neighbour *neigh, const unsigned char *prefix,
unsigned char plen,
const unsigned char *src_prefix, unsigned char src_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, src_prefix, src_plen);
route = find_installed_route(prefix, plen, src_prefix, src_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, src_prefix, src_plen);
return;
}
if(route &&
(memcmp(id, route->src->id, 8) != 0 ||
seqno_compare(seqno, route->seqno) <= 0)) {
send_update(neigh->ifp, 1, prefix, plen, src_prefix, src_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, src_prefix, src_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, src_prefix, src_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, src_prefix, src_plen,
seqno, id, hop_count - 1);
record_resend(RESEND_REQUEST, prefix, plen, src_prefix, src_plen, seqno, id,
neigh->ifp, 0);
}
babeld-babeld-1.6.3-273bcc70824af88574c3724b3c6a69a9722d4cc4/message.h 0000664 0000000 0000000 00000011743 12603261366 0023256 0 ustar 00root root 0000000 0000000 /*
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
/* A registry of assigned TLV and sub-TLV types is available at
http://www.pps.univ-paris-diderot.fr/~jch/software/babel/babel-tlv-registry.text
*/
#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
/* 11 and 12 are for authentication */
#define MESSAGE_UPDATE_SRC_SPECIFIC 13
#define MESSAGE_REQUEST_SRC_SPECIFIC 14
#define MESSAGE_MH_REQUEST_SRC_SPECIFIC 15
/* Protocol extension through sub-TLVs. */
#define SUBTLV_PAD1 0
#define SUBTLV_PADN 1
#define SUBTLV_DIVERSITY 2 /* Also known as babelz. */
#define SUBTLV_TIMESTAMP 3 /* Used to compute RTT. */
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,
const unsigned char *src_prefix, unsigned char src_plen);
void send_update_resend(struct interface *ifp,
const unsigned char *prefix, unsigned char plen,
const unsigned char *src_prefix,
unsigned char src_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,
const unsigned char *src_prefix, unsigned char src_plen);
void send_unicast_request(struct neighbour *neigh,
const unsigned char *prefix, unsigned char plen,
const unsigned char *src_prefix,
unsigned char src_plen);
void send_multihop_request(struct interface *ifp,
const unsigned char *prefix, unsigned char plen,
const unsigned char *src_prefix,
unsigned char src_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,
const unsigned char *src_prefix,
unsigned char src_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,
const unsigned char *src_prefix,
unsigned char src_plen,
unsigned short seqno, unsigned char *id);
void handle_request(struct neighbour *neigh, const unsigned char *prefix,
unsigned char plen,
const unsigned char *src_prefix, unsigned char src_plen,
unsigned char hop_count,
unsigned short seqno, const unsigned char *id);
babeld-babeld-1.6.3-273bcc70824af88574c3724b3c6a69a9722d4cc4/neighbour.c 0000664 0000000 0000000 00000024463 12603261366 0023612 0 ustar 00root root 0000000 0000000 /*
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
#include
#include
#include
#include
#include
#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->hello_send_us = 0;
neigh->hello_rtt_receive_time = zero;
neigh->rtt = 0;
neigh->rtt_time = zero;
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, 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_rttcost(struct neighbour *neigh)
{
struct interface *ifp = neigh->ifp;
if(!ifp->max_rtt_penalty || !valid_rtt(neigh))
return 0;
/* Function: linear behaviour between rtt_min and rtt_max. */
if(neigh->rtt <= ifp->rtt_min) {
return 0;
} else if(neigh->rtt <= ifp->rtt_max) {
unsigned long long tmp =
(unsigned long long)ifp->max_rtt_penalty *
(neigh->rtt - ifp->rtt_min) /
(ifp->rtt_max - ifp->rtt_min);
assert((tmp & 0x7FFFFFFF) == tmp);
return tmp;
} else {
return ifp->max_rtt_penalty;
}
}
unsigned
neighbour_cost(struct neighbour *neigh)
{
unsigned a, b, cost;
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)) {
cost = 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. */
cost = (a * b + 128) >> 8;
}
cost += neighbour_rttcost(neigh);
return MIN(cost, INFINITY);
}
int
valid_rtt(struct neighbour *neigh)
{
return (timeval_minus_msec(&now, &neigh->rtt_time) < 180000) ? 1 : 0;
}
babeld-babeld-1.6.3-273bcc70824af88574c3724b3c6a69a9722d4cc4/neighbour.h 0000664 0000000 0000000 00000004632 12603261366 0023613 0 ustar 00root root 0000000 0000000 /*
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 */
/* Used for RTT estimation. */
/* Absolute time (modulo 2^32) at which the Hello was sent,
according to remote clock. */
unsigned int hello_send_us;
struct timeval hello_rtt_receive_time;
unsigned int rtt;
struct timeval rtt_time;
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_rttcost(struct neighbour *neigh);
unsigned neighbour_cost(struct neighbour *neigh);
int valid_rtt(struct neighbour *neigh);
babeld-babeld-1.6.3-273bcc70824af88574c3724b3c6a69a9722d4cc4/net.c 0000664 0000000 0000000 00000012703 12603261366 0022410 0 ustar 00root root 0000000 0000000 /*
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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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, count = 0;
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;
/* The Linux kernel can apparently keep returning EAGAIN indefinitely. */
again:
rc = sendmsg(s, &msg, 0);
if(rc < 0) {
if(errno == EINTR) {
count++;
if(count < 100)
goto again;
} else if(errno == EAGAIN) {
int rc2;
rc2 = wait_for_fd(1, s, 5);
if(rc2 > 0) {
count++;
if(count < 100)
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;
}
babeld-babeld-1.6.3-273bcc70824af88574c3724b3c6a69a9722d4cc4/net.h 0000664 0000000 0000000 00000002546 12603261366 0022421 0 ustar 00root root 0000000 0000000 /*
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);
babeld-babeld-1.6.3-273bcc70824af88574c3724b3c6a69a9722d4cc4/resend.c 0000664 0000000 0000000 00000023144 12603261366 0023103 0 ustar 00root root 0000000 0000000 /*
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
#include
#include
#include
#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,
const unsigned char *src_prefix, unsigned char src_plen)
{
return (resend->kind == kind &&
resend->plen == plen && memcmp(resend->prefix, prefix, 16) == 0 &&
resend->src_plen == src_plen &&
memcmp(resend->src_prefix, src_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,
const unsigned char *src_prefix, unsigned char src_plen,
struct resend **previous_return)
{
struct resend *current, *previous;
previous = NULL;
current = to_resend;
while(current) {
if(resend_match(current, kind, prefix, plen, src_prefix, src_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,
const unsigned char *src_prefix, unsigned char src_plen,
struct resend **previous_return)
{
return find_resend(RESEND_REQUEST, prefix, plen, src_prefix, src_plen,
previous_return);
}
int
record_resend(int kind, const unsigned char *prefix, unsigned char plen,
const unsigned char *src_prefix, unsigned char src_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, src_prefix, src_plen, NULL,
ifindex) >=
INFINITY) ||
(kind == RESEND_UPDATE &&
output_filter(NULL, prefix, plen, src_prefix, src_plen, ifindex) >=
INFINITY))
return 0;
if(delay >= 0xFFFF)
delay = 0xFFFF;
resend = find_resend(kind, prefix, plen, src_prefix, src_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;
memcpy(resend->src_prefix, src_prefix, 16);
resend->src_plen = src_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,
const unsigned char *src_prefix, unsigned char src_plen,
unsigned short seqno, const unsigned char *id)
{
struct resend *request;
request = find_request(prefix, plen, src_prefix, src_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,
const unsigned char *src_prefix, unsigned char src_plen,
unsigned short seqno, const unsigned char *id)
{
struct resend *request;
request = find_request(prefix, plen, src_prefix, src_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,
const unsigned char *src_prefix, unsigned char src_plen,
unsigned short seqno, const unsigned char *id,
struct interface *ifp)
{
struct resend *request, *previous;
request = find_request(prefix, plen, src_prefix, src_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->src_prefix, resend->src_plen,
resend->seqno, resend->id, 127);
break;
case RESEND_UPDATE:
send_update(resend->ifp, 1,
resend->prefix, resend->plen,
resend->src_prefix, resend->src_plen);
break;
default: abort();
}
resend->delay = MIN(0xFFFF, resend->delay * 2);
resend->max--;
}
}
resend = resend->next;
}
recompute_resend_time();
}
babeld-babeld-1.6.3-273bcc70824af88574c3724b3c6a69a9722d4cc4/resend.h 0000664 0000000 0000000 00000005534 12603261366 0023113 0 ustar 00root root 0000000 0000000 /*
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 char src_prefix[16];
unsigned char src_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,
const unsigned char *src_prefix, unsigned char src_plen,
struct resend **previous_return);
void flush_resends(struct neighbour *neigh);
int record_resend(int kind, const unsigned char *prefix, unsigned char plen,
const unsigned char *src_prefix, unsigned char src_plen,
unsigned short seqno, const unsigned char *id,
struct interface *ifp, int delay);
int unsatisfied_request(const unsigned char *prefix, unsigned char plen,
const unsigned char *src_prefix, unsigned char src_plen,
unsigned short seqno, const unsigned char *id);
int request_redundant(struct interface *ifp,
const unsigned char *prefix, unsigned char plen,
const unsigned char *src_prefix, unsigned char src_plen,
unsigned short seqno, const unsigned char *id);
int satisfy_request(const unsigned char *prefix, unsigned char plen,
const unsigned char *src_prefix, unsigned char src_plen,
unsigned short seqno, const unsigned char *id,
struct interface *ifp);
void expire_resend(void);
void recompute_resend_time(void);
void do_resend(void);
babeld-babeld-1.6.3-273bcc70824af88574c3724b3c6a69a9722d4cc4/route.c 0000664 0000000 0000000 00000103570 12603261366 0022763 0 ustar 00root root 0000000 0000000 /*
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
#include
#include
#include
#include
#include
#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"
#include "disambiguation.h"
struct babel_route **routes = NULL;
static int route_slots = 0, max_route_slots = 0;
int kernel_metric = 0, reflect_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;
static int smoothing_half_life = 0;
static int two_to_the_one_over_hl = 0; /* 2^(1/hl) * 0x10000 */
/* We maintain a list of "slots", ordered by prefix. Every slot
contains a linked list of the routes to this prefix, with the
installed route, if any, at the head of the list. */
static int
route_compare(const unsigned char *prefix, unsigned char plen,
const unsigned char *src_prefix, unsigned char src_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;
if(plen > route->src->plen)
return 1;
if(src_plen == 0) {
if(route->src->src_plen > 0)
return -1;
} else {
i = memcmp(src_prefix, route->src->src_prefix, 16);
if(i != 0)
return i;
if(src_plen < route->src->src_plen)
return -1;
if(src_plen > route->src->src_plen)
return 1;
}
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,
const unsigned char *src_prefix, unsigned char src_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, src_prefix, src_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,
const unsigned char *src_prefix, unsigned char src_plen,
struct neighbour *neigh, const unsigned char *nexthop)
{
struct babel_route *route;
int i = find_route_slot(prefix, plen, src_prefix, src_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,
const unsigned char *src_prefix, unsigned char src_plen)
{
int i = find_route_slot(prefix, plen, src_prefix, src_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,
route->src->src_prefix, route->src->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,
route->src->src_prefix, route->src->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[i]);
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:
;
}
}
struct route_stream {
int installed;
int index;
struct babel_route *next;
};
struct route_stream *
route_stream(int installed)
{
struct route_stream *stream;
stream = malloc(sizeof(struct route_stream));
if(stream == NULL)
return NULL;
stream->installed = installed;
stream->index = installed ? 0 : -1;
stream->next = NULL;
return stream;
}
struct babel_route *
route_stream_next(struct route_stream *stream)
{
if(stream->installed) {
while(stream->index < route_slots && !routes[stream->index]->installed)
stream->index++;
if(stream->index < route_slots)
return routes[stream->index++];
else
return NULL;
} else {
struct babel_route *next;
if(!stream->next) {
stream->index++;
if(stream->index >= route_slots)
return NULL;
stream->next = routes[stream->index];
}
next = stream->next;
stream->next = next->next;
return next;
}
}
void
route_stream_done(struct route_stream *stream)
{
free(stream);
}
int
metric_to_kernel(int metric)
{
if(metric >= INFINITY) {
return KERNEL_INFINITY;
} else if(reflect_kernel_metric) {
int r = kernel_metric + metric;
return r >= KERNEL_INFINITY ? KERNEL_INFINITY : r;
} else {
return kernel_metric;
}
}
/* 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,
route->src->src_prefix, route->src->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 = kinstall_route(route);
if(rc < 0 && errno != EEXIST)
return;
route->installed = 1;
move_installed_route(route, i);
local_notify_route(route, LOCAL_CHANGE);
}
void
uninstall_route(struct babel_route *route)
{
if(!route->installed)
return;
route->installed = 0;
kuninstall_route(route);
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 = kswitch_routes(old, new);
if(rc < 0)
return;
old->installed = 0;
new->installed = 1;
move_installed_route(new, find_route_slot(new->src->prefix, new->src->plen,
new->src->src_prefix,
new->src->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 = kchange_route_metric(route, refmetric, cost, add);
if(rc < 0)
return;
}
/* Update route->smoothed_metric using the old metric. */
route_smoothed_metric(route);
route->refmetric = refmetric;
route->cost = cost;
route->add_metric = add;
if(smoothing_half_life == 0) {
route->smoothed_metric = route_metric(route);
route->smoothed_metric_time = now.tv_sec;
}
local_notify_route(route, LOCAL_CHANGE);
}
static void
retract_route(struct babel_route *route)
{
/* We cannot simply remove the route from the kernel, as that might
cause a routing loop -- see RFC 6126 Sections 2.8 and 3.5.5. */
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));
}
void
change_smoothing_half_life(int half_life)
{
if(half_life <= 0) {
smoothing_half_life = 0;
two_to_the_one_over_hl = 0;
return;
}
smoothing_half_life = half_life;
switch(smoothing_half_life) {
case 1: two_to_the_one_over_hl = 131072; break;
case 2: two_to_the_one_over_hl = 92682; break;
case 3: two_to_the_one_over_hl = 82570; break;
case 4: two_to_the_one_over_hl = 77935; break;
default:
/* 2^(1/x) is 1 + log(2)/x + O(1/x^2) at infinity. */
two_to_the_one_over_hl = 0x10000 + 45426 / half_life;
}
}
/* Update the smoothed metric, return the new value. */
int
route_smoothed_metric(struct babel_route *route)
{
int metric = route_metric(route);
if(smoothing_half_life <= 0 || /* no smoothing */
metric >= INFINITY || /* route retracted */
route->smoothed_metric_time > now.tv_sec || /* clock stepped */
route->smoothed_metric == metric) { /* already converged */
route->smoothed_metric = metric;
route->smoothed_metric_time = now.tv_sec;
} else {
int diff;
/* We randomise the computation, to minimise global synchronisation
and hence oscillations. */
while(route->smoothed_metric_time <= now.tv_sec - smoothing_half_life) {
diff = metric - route->smoothed_metric;
route->smoothed_metric += roughly(diff) / 2;
route->smoothed_metric_time += smoothing_half_life;
}
while(route->smoothed_metric_time < now.tv_sec) {
diff = metric - route->smoothed_metric;
route->smoothed_metric +=
roughly(diff) * (two_to_the_one_over_hl - 0x10000) / 0x10000;
route->smoothed_metric_time++;
}
diff = metric - route->smoothed_metric;
if(diff > -4 && diff < 4)
route->smoothed_metric = metric;
}
/* change_route_metric relies on this */
assert(route->smoothed_metric_time == now.tv_sec);
return route->smoothed_metric;
}
static int
route_acceptable(struct babel_route *route, int feasible,
struct neighbour *exclude)
{
if(route_expired(route))
return 0;
if(feasible && !route_feasible(route))
return 0;
if(exclude && route->neigh == exclude)
return 0;
return 1;
}
/* Find the best route according to the weak ordering. Any
linearisation of the strong ordering (see consider_route) will do,
we use sm <= sm'. We could probably use a lexical ordering, but
that's probably overkill. */
struct babel_route *
find_best_route(const unsigned char *prefix, unsigned char plen,
const unsigned char *src_prefix, unsigned char src_plen,
int feasible, struct neighbour *exclude)
{
struct babel_route *route, *r;
int i = find_route_slot(prefix, plen, src_prefix, src_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_smoothed_metric(r) < route_smoothed_metric(route)))
route = r;
r = r->next;
}
return route;
}
void
update_route_metric(struct babel_route *route)
{
int oldmetric = route_metric(route);
int old_smoothed_metric = route_smoothed_metric(route);
if(route_expired(route)) {
if(route->refmetric < INFINITY) {
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,
route->src->src_prefix,
route->src->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_smoothed_metric(route) != old_smoothed_metric)
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,
const unsigned char *src_prefix, unsigned char src_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);
int is_v4;
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(nexthop));
return NULL;
}
if(src_plen != 0 && martian_prefix(src_prefix, src_plen)) {
fprintf(stderr, "Rejecting martian route to %s from %s through %s.\n",
format_prefix(prefix, plen),
format_prefix(src_prefix, src_plen), format_eui64(id));
return NULL;
}
is_v4 = v4mapped(prefix);
if(src_plen != 0 && is_v4 != v4mapped(src_prefix))
return NULL;
add_metric = input_filter(id, prefix, plen, src_prefix, src_plen,
neigh->address, neigh->ifp->ifindex);
if(add_metric >= INFINITY)
return NULL;
route = find_route(prefix, plen, src_prefix, src_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, src_prefix, src_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_eui64(route->src->id),
route->seqno, route->refmetric,
format_eui64(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;
memset(&route->channels, 0, sizeof(route->channels));
if(channels_len > 0)
memcpy(&route->channels, channels,
MIN(channels_len, DIVERSITY_HOPS));
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->smoothed_metric = MAX(route_metric(route), INFINITY / 2);
route->smoothed_metric_time = now.tv_sec;
route->installed = 0;
memset(&route->channels, 0, sizeof(route->channels));
if(channels_len > 0)
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,
src->src_prefix,
src->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->src_prefix, src->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.
This uses the strong ordering, which is defined by sm <= sm' AND
m <= m'. This ordering is not total, which is what causes
hysteresis. */
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,
route->src->src_prefix, route->src->src_plen);
if(xroute && (allow_duplicates < 0 || xroute->metric >= allow_duplicates))
return;
installed = find_installed_route(route->src->prefix, route->src->plen,
route->src->src_prefix,
route->src->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) &&
route_smoothed_metric(installed) > route_smoothed_metric(route))
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,
route->src->src_prefix, route->src->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->src->src_prefix, route->src->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,
route->src->src_prefix, route->src->src_plen);
else
send_update(NULL, urgent, route->src->prefix, route->src->plen,
route->src->src_prefix, route->src->src_plen);
if(oldmetric < INFINITY) {
if(newmetric >= oldmetric + 512) {
send_request_resend(NULL, route->src->prefix, route->src->plen,
route->src->src_prefix, route->src->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,
route->src->src_prefix, route->src->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,
route->src->src_prefix, route->src->src_plen,
1, NULL);
if(better_route && route_metric(better_route) < route_metric(route))
consider_route(better_route);
}
if(route->installed) {
/* We didn't change routes after all. */
send_triggered_update(route, oldsrc, oldmetric);
} else {
/* Reconsider routes even when their metric didn't decrease,
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,
src->src_prefix, src->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,
src->src_prefix, src->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->src_prefix, src->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->src->src_prefix, r->src->src_plen);
}
r = r->next;
}
i++;
again:
;
}
}
babeld-babeld-1.6.3-273bcc70824af88574c3724b3c6a69a9722d4cc4/route.h 0000664 0000000 0000000 00000012574 12603261366 0022773 0 ustar 00root root 0000000 0000000 /*
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 */
unsigned short smoothed_metric; /* for route selection */
time_t smoothed_metric_time;
short installed;
unsigned char channels[DIVERSITY_HOPS];
struct babel_route *next;
};
struct route_stream;
extern struct babel_route **routes;
extern int kernel_metric, allow_duplicates, reflect_kernel_metric;
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,
const unsigned char *src_prefix, unsigned char src_plen,
struct neighbour *neigh, const unsigned char *nexthop);
struct babel_route *find_installed_route(const unsigned char *prefix,
unsigned char plen, const unsigned char *src_prefix,
unsigned char src_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);
struct route_stream *route_stream(int installed);
struct babel_route *route_stream_next(struct route_stream *stream);
void route_stream_done(struct route_stream *stream);
int metric_to_kernel(int metric);
void install_route(struct babel_route *route);
void uninstall_route(struct babel_route *route);
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);
void change_smoothing_half_life(int half_life);
int route_smoothed_metric(struct babel_route *route);
struct babel_route *find_best_route(const unsigned char *prefix,
unsigned char plen,
const unsigned char *src_prefix,
unsigned char src_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,
const unsigned char *src_prefix,
unsigned char src_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);
babeld-babeld-1.6.3-273bcc70824af88574c3724b3c6a69a9722d4cc4/source.c 0000664 0000000 0000000 00000011015 12603261366 0023115 0 ustar 00root root 0000000 0000000 /*
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
#include
#include
#include
#include
#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 *prefix, unsigned char plen,
const unsigned char *src_prefix, unsigned char src_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(src->src_plen != src_plen)
continue;
if(memcmp(src->prefix, prefix, 16) != 0)
continue;
if(memcmp(src->src_prefix, src_prefix, 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, prefix, 16);
src->plen = plen;
memcpy(src->src_prefix, src_prefix, 16);
src->src_plen = src_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);
}
}
babeld-babeld-1.6.3-273bcc70824af88574c3724b3c6a69a9722d4cc4/source.h 0000664 0000000 0000000 00000003742 12603261366 0023132 0 ustar 00root root 0000000 0000000 /*
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 char src_prefix[16];
unsigned char src_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 *prefix,
unsigned char plen,
const unsigned char *src_prefix,
unsigned char src_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);
babeld-babeld-1.6.3-273bcc70824af88574c3724b3c6a69a9722d4cc4/util.c 0000664 0000000 0000000 00000027775 12603261366 0022616 0 ustar 00root root 0000000 0000000 /*
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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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;
}
/* Given a fixed-point string such as "42.1337", returns 1000 times
the value of the string, here 42133. */
int
parse_thousands(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];
}
const char *
format_thousands(unsigned int value)
{
static char buf[4][15];
static int i = 0;
i = (i + 1) % 4;
snprintf(buf[i], 15, "%d.%.3d", value / 1000, value % 1000);
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;
}
enum prefix_status
prefix_cmp(const unsigned char *p1, unsigned char plen1,
const unsigned char *p2, unsigned char plen2)
{
int plen = MIN(plen1, plen2);
if(memcmp(p1, p2, plen / 8) != 0)
return PST_DISJOINT;
if(plen % 8 != 0) {
int i = plen / 8 + 1;
unsigned char mask = (0xFF << (plen % 8)) & 0xFF;
if((p1[i] & mask) != (p2[i] & mask))
return PST_DISJOINT;
}
if(plen1 < plen2)
return PST_LESS_SPECIFIC;
else if(plen1 > plen2)
return PST_MORE_SPECIFIC;
else
return PST_EQUALS;
}
babeld-babeld-1.6.3-273bcc70824af88574c3724b3c6a69a9722d4cc4/util.h 0000664 0000000 0000000 00000014076 12603261366 0022611 0 ustar 00root root 0000000 0000000 /*
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__*/
#else
#define DO_NTOHS(_d, _s) \
do { unsigned short _dd; \
memcpy(&(_dd), (_s), 2); \
_d = ntohs(_dd); } while(0)
#define DO_NTOHL(_d, _s) \
do { unsigned int _dd; \
memcpy(&(_dd), (_s), 4); \
_d = ntohl(_dd); } while(0)
#define DO_HTONS(_d, _s) \
do { unsigned short _dd; \
_dd = htons(_s); \
memcpy((_d), &(_dd), 2); } while(0)
#define DO_HTONL(_d, _s) \
do { unsigned _dd; \
_dd = htonl(_s); \
memcpy((_d), &(_dd), 4); } 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);
}
/* Returns a time in microseconds on 32 bits (thus modulo 2^32,
i.e. about 4295 seconds). */
static inline unsigned int
time_us(const struct timeval t)
{
return (unsigned int) (t.tv_sec * 1000000 + t.tv_usec);
}
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_thousands(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);
const char *format_thousands(unsigned int value);
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);
int set_src_prefix(unsigned char *src_addr, unsigned char *src_plen);
enum prefix_status {
PST_EQUALS = 0,
PST_DISJOINT,
PST_MORE_SPECIFIC,
PST_LESS_SPECIFIC
};
enum prefix_status
prefix_cmp(const unsigned char *p1, unsigned char plen1,
const unsigned char *p2, unsigned char plen2);
/* 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
babeld-babeld-1.6.3-273bcc70824af88574c3724b3c6a69a9722d4cc4/xroute.c 0000664 0000000 0000000 00000022344 12603261366 0023152 0 ustar 00root root 0000000 0000000 /*
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
#include
#include
#include
#include
#include
#include
#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,
const unsigned char *src_prefix, unsigned char src_plen)
{
int i;
for(i = 0; i < numxroutes; i++) {
if(xroutes[i].plen == plen &&
memcmp(xroutes[i].prefix, prefix, 16) == 0 &&
xroutes[i].src_plen == src_plen &&
memcmp(xroutes[i].src_prefix, src_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 char src_prefix[16], unsigned char src_plen,
unsigned short metric, unsigned int ifindex, int proto)
{
struct xroute *xroute = find_xroute(prefix, plen, src_prefix, src_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;
memcpy(xroutes[numxroutes].src_prefix, src_prefix, 16);
xroutes[numxroutes].src_plen = src_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;
}
struct xroute_stream {
int index;
};
struct
xroute_stream *
xroute_stream()
{
struct xroute_stream *stream = malloc(sizeof(struct xroute_stream));
if(stream == NULL)
return NULL;
stream->index = 0;
return stream;
}
struct xroute *
xroute_stream_next(struct xroute_stream *stream)
{
if(stream->index < numxroutes)
return &xroutes[stream->index++];
else
return NULL;
}
void
xroute_stream_done(struct xroute_stream *stream)
{
free(stream);
}
int
check_xroutes(int send_updates)
{
int i, j, metric, export, change = 0, rc;
struct kernel_route *routes;
struct filter_result filter_result = {0};
int numroutes, numaddresses;
static int maxroutes = 8;
const int maxmaxroutes = 16 * 1024;
debugf("\nChecking kernel routes.\n");
again:
routes = calloc(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;
numaddresses = numroutes;
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;
/* Apply filter to kernel routes (e.g. change the source prefix). */
for(i = numaddresses; i < numroutes; i++) {
filter_result.src_prefix = NULL;
redistribute_filter(routes[i].prefix, routes[i].plen,
routes[i].src_prefix, routes[i].src_plen,
routes[i].ifindex, routes[i].proto,
&filter_result);
if(filter_result.src_prefix) {
memcpy(routes[i].src_prefix, filter_result.src_prefix, 16);
routes[i].src_plen = filter_result.src_plen;
}
}
/* 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].src_prefix, xroutes[i].src_plen,
xroutes[i].ifindex, xroutes[i].proto,
NULL);
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;
unsigned char src_prefix[16], src_plen;
struct babel_route *route;
memcpy(prefix, xroutes[i].prefix, 16);
plen = xroutes[i].plen;
memcpy(src_prefix, xroutes[i].src_prefix, 16);
src_plen = xroutes[i].src_plen;
flush_xroute(&xroutes[i]);
route = find_best_route(prefix, plen, src_prefix, src_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, src_prefix, src_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].src_prefix, routes[i].src_plen,
routes[i].ifindex, routes[i].proto, NULL);
if(metric < INFINITY) {
rc = add_xroute(routes[i].prefix, routes[i].plen,
routes[i].src_prefix, routes[i].src_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,
routes[i].src_prefix,
routes[i].src_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,
routes[i].src_prefix, routes[i].src_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;
}
babeld-babeld-1.6.3-273bcc70824af88574c3724b3c6a69a9722d4cc4/xroute.h 0000664 0000000 0000000 00000003604 12603261366 0023155 0 ustar 00root root 0000000 0000000 /*
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 char src_prefix[16];
unsigned char src_plen;
unsigned short metric;
unsigned int ifindex;
int proto;
};
struct xroute_stream;
struct xroute *find_xroute(const unsigned char *prefix, unsigned char plen,
const unsigned char *src_prefix, unsigned char src_plen);
void flush_xroute(struct xroute *xroute);
int add_xroute(unsigned char prefix[16], unsigned char plen,
unsigned char src_prefix[16], unsigned char src_plen,
unsigned short metric, unsigned int ifindex, int proto);
int xroutes_estimate(void);
struct xroute_stream *xroute_stream();
struct xroute *xroute_stream_next(struct xroute_stream *stream);
void xroute_stream_done(struct xroute_stream *stream);
int check_xroutes(int send_updates);