pax_global_header00006660000000000000000000000064126544464250014526gustar00rootroot0000000000000052 comment=8fc3c521918b2b508094dd895d56af8ab880e7e6 babeld-1.7.0/000077500000000000000000000000001265444642500127445ustar00rootroot00000000000000babeld-1.7.0/CHANGES000066400000000000000000000450711265444642500137460ustar00rootroot000000000000003 February 2016: babeld-1.7.0 * Added the ability to choose the kernel routing table on a per-route basis. Thanks to Matthieu Boutier. * Refactored the disambiguation code to live above the kernel interface. Thanks to Matthieu Boutier. * Reworked the source table to function in log time. * Optimised the disambiguation code to avoid scanning all routes in non-source-specific networks. Thanks to Matthieu Boutier. * Modified the triggered updates logic to no longer send multihop requests. This makes babeld slightly less noisy, at the cost of slightly longer reconvergence after mobility. * Increased the token bucket limits, which some large meshes are starting to hit. * Increased the size of the netlink socket buffer. 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-1.7.0/LICENCE000066400000000000000000000020601265444642500137270ustar00rootroot00000000000000Copyright (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-1.7.0/Makefile000066400000000000000000000027451265444642500144140ustar00rootroot00000000000000PREFIX = /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 rule.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 rule.o babeld: $(OBJS) $(CC) $(CFLAGS) $(LDFLAGS) -o babeld $(OBJS) $(LDLIBS) babeld.o: babeld.c version.h local.o: local.c version.h kernel.o: kernel_netlink.c kernel_socket.c version.h: ./generate-version.sh > version.h .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 version.h *.o *~ core TAGS gmon.out babeld-1.7.0/README000066400000000000000000000064511265444642500136320ustar00rootroot00000000000000Babel ===== 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-1.7.0/babeld.c000066400000000000000000000767631265444642500143440ustar00rootroot00000000000000/* 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" #include "rule.h" #include "version.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 void init_signals(void); static void dump_tables(FILE *out); static int reopen_logfile(void); static int kernel_route_notify(struct kernel_route *route, void *closure) { kernel_routes_changed = 1; return -1; } static int kernel_addr_notify(struct kernel_addr *addr, void *closure) { kernel_addr_changed = 1; return -1; } static int kernel_link_notify(struct kernel_link *link, void *closure) { struct interface *ifp; FOR_ALL_INTERFACES(ifp) { if(strcmp(ifp->name, link->ifname) == 0) { kernel_link_changed = 1; return -1; } } return 0; } static int kernel_rule_notify(struct kernel_rule *rule, void *closure) { int i; if(martian_prefix(rule->src, rule->src_plen)) return 0; i = rule->priority - src_table_prio; if(i < 0 || SRC_TABLE_NUM <= i) return 0; kernel_rules_changed = 1; return -1; } 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:V"); 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; case 'V': fprintf(stderr, "%s\n", BABELD_VERSION); exit(0); 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"); rc = check_rules(); if(rc < 0) fprintf(stderr, "Warning: couldn't check rules.\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)) { struct kernel_filter filter = {0}; filter.route = kernel_route_notify; filter.addr = kernel_addr_notify; filter.link = kernel_link_notify; filter.rule = kernel_rule_notify; kernel_callback(&filter); } 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"); rc = check_rules(); if(rc < 0) fprintf(stderr, "Warning: couldn't check rules.\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); } release_tables(); 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, "%s\n" "Syntax: babeld " "[-V] [-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", BABELD_VERSION); 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(ROUTE_ALL); 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; } babeld-1.7.0/babeld.h000066400000000000000000000060521265444642500143310ustar00rootroot00000000000000/* 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-1.7.0/babeld.man000066400000000000000000000423051265444642500146560ustar00rootroot00000000000000.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 \-V Display babeld's version and quit. .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 , .BR redistribute , or .BR install . 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 . .TP .BI table " table" In an .B install filter, specify the kernel routing table to use. For source-specific routes, this only works reliably for IPv6, and only when .B ipv6-subtrees is true. .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-1.7.0/configuration.c000066400000000000000000000760551265444642500157740ustar00rootroot00000000000000/* 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" #include "rule.h" struct filter *input_filters = NULL; struct filter *output_filters = NULL; struct filter *redistribute_filters = NULL; struct filter *install_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 if(strcmp(token, "table") == 0) { int table; c = getint(c, &table, gnc, closure); if(c < -1) goto error; if(table <= 0 || table > INFINITY) goto error; filter->action.table = table; } 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, "install") == 0) { struct filter *filter; c = parse_filter(c, gnc, closure, &filter); if(c < -1) return -1; add_filter(filter, &install_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); renumber_filter(install_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 install_filter(const unsigned char *prefix, unsigned short plen, const unsigned char *src_prefix, unsigned short src_plen, struct filter_result *result) { int res; res = do_filter(install_filters, NULL, prefix, plen, src_prefix, src_plen, NULL, 0, 0, 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-1.7.0/configuration.h000066400000000000000000000054401265444642500157670ustar00rootroot00000000000000/* 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; unsigned int table; }; 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 install_filter(const unsigned char *prefix, unsigned short plen, const unsigned char *src_prefix, unsigned short src_plen, struct filter_result *result); int finalise_config(void); babeld-1.7.0/disambiguation.c000066400000000000000000000356071265444642500161220ustar00rootroot00000000000000/* 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" #include "rule.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(ROUTE_INSTALLED); 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; /* Having a conflict requires at least one specific route. */ stream1 = route_stream(ROUTE_SS_INSTALLED); if(!stream1) { return NULL; } while(1) { rt1 = route_stream_next(stream1); if(rt1 == NULL) break; stream2 = route_stream(ROUTE_INSTALLED); 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) { int table = find_table(zone->dst_prefix, zone->dst_plen, zone->src_prefix, zone->src_plen); return kernel_route(ROUTE_ADD, table, 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, 0); } static int del_route(const struct zone *zone, const struct babel_route *route) { int table = find_table(zone->dst_prefix, zone->dst_plen, zone->src_prefix, zone->src_plen); return kernel_route(ROUTE_FLUSH, table, 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, 0); } static int chg_route(const struct zone *zone, const struct babel_route *old, const struct babel_route *new) { int table = find_table(zone->dst_prefix, zone->dst_plen, zone->src_prefix, zone->src_plen); return kernel_route(ROUTE_MODIFY, table, 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)), table); } static int chg_route_metric(const struct zone *zone, const struct babel_route *route, int old_metric, int new_metric) { int table = find_table(zone->dst_prefix, zone->dst_plen, zone->src_prefix, zone->src_plen); return kernel_route(ROUTE_MODIFY, table, 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, table); } 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(ROUTE_INSTALLED); 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(ROUTE_INSTALLED); 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(ROUTE_INSTALLED); 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(ROUTE_INSTALLED); 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-1.7.0/disambiguation.h000066400000000000000000000025541265444642500161220ustar00rootroot00000000000000/* 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-1.7.0/generate-version.sh000077500000000000000000000003331265444642500165570ustar00rootroot00000000000000#!/bin/sh set -e if [ -d .git ] ; then version="$(git describe --dirty)" elif [ -f version ] ; then version="$(cat version)" else version="(unknown version)" fi echo "#define BABELD_VERSION \"$version\"" babeld-1.7.0/interface.c000066400000000000000000000341231265444642500150530ustar00rootroot00000000000000/* 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" #include "xroute.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->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-1.7.0/interface.h000066400000000000000000000100721265444642500150550ustar00rootroot00000000000000/* 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[8]; 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-1.7.0/kernel.c000066400000000000000000000065501265444642500143760ustar00rootroot00000000000000/* 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 /* 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-1.7.0/kernel.h000066400000000000000000000074461265444642500144100ustar00rootroot00000000000000/* 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]; }; struct kernel_addr { struct in6_addr addr; unsigned int ifindex; }; struct kernel_link { char *ifname; }; struct kernel_rule { unsigned int priority; unsigned int table; unsigned char src[16]; unsigned char src_plen; }; struct kernel_filter { /* return -1 to interrupt search. */ int (*addr)(struct kernel_addr *, void *); void *addr_closure; int (*route)(struct kernel_route *, void *); void *route_closure; int (*link)(struct kernel_link *, void *); void *link_closure; int (*rule)(struct kernel_rule *, void *); void *rule_closure; }; #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); 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, int table, 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 newtable); int kernel_dump(int operation, struct kernel_filter *filter); int kernel_callback(struct kernel_filter *filter); 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); int add_rule(int prio, const unsigned char *src_prefix, int src_plen, int table); int flush_rule(int prio, int family); int change_rule(int new_prio, int old_prio, const unsigned char *src, int plen, int table); babeld-1.7.0/kernel_netlink.c000066400000000000000000001270311265444642500161200ustar00rootroot00000000000000/* 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" #include "configuration.h" #ifndef MAX_INTERFACES #define MAX_INTERFACES 20 #endif #define GET_PLEN(p, v4) (v4) ? (p) + 96 : (p) #define COPY_ADDR(d, rta, v4) \ do { \ if(UNLIKELY(RTA_PAYLOAD(rta) < (v4 ? 4 : 16))) { \ fprintf(stderr, "truncated message."); \ return -1; \ } \ if(v4) \ v4tov6(d, RTA_DATA(rta)); \ else \ memcpy(d, RTA_DATA(rta), 16); \ } while(0) 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 filter_netlink(struct nlmsghdr *nh, struct kernel_filter *filter); /* 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; int rcvsize = 512 * 1024; 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; #ifdef SO_RCVBUFFORCE rc = setsockopt(nl->sock, SOL_SOCKET, SO_RCVBUFFORCE, &rcvsize, sizeof(rcvsize)); #else rc = -1; #endif if(rc < 0) { rc = setsockopt(nl->sock, SOL_SOCKET, SO_RCVBUF, &rcvsize, sizeof(rcvsize)); if(rc < 0) { perror("setsockopt(SO_RCVBUF)"); } } 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, struct kernel_filter *filter) { /* '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 : success */ int err; struct msghdr msg; struct sockaddr_nl nladdr; struct iovec iov; struct nlmsghdr *nh; int len; int done = 0; int skip = 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 -1; } 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 -1; } kdebugf("Netlink message: "); for(nh = (struct nlmsghdr *)buf; NLMSG_OK(nh, len); nh = NLMSG_NEXT(nh, len)) { kdebugf("%s{seq:%d}", (nh->nlmsg_flags & NLM_F_MULTI) ? "[multi] " : "", nh->nlmsg_seq); 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(skip) { kdebugf("(skip)"); } if(filter) { kdebugf("(msg -> \""); err = filter_netlink(nh, filter); kdebugf("\" %d), ", err); if(err < 0) skip = 1; continue; } kdebugf(", "); } kdebugf("\n"); if(msg.msg_flags & MSG_TRUNC) fprintf(stderr, "netlink_read: message truncated\n"); } while(!done); return 0; 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; kdebugf("Sending seqno %d from address %p (talk)\n", nl_command.seqno, &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); /* 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); kdebugf("Sending seqno %d from address %p (dump)\n", nl_command.seqno, &nl_command.seqno); 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 { 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, int table, 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 newtable) { union { char raw[1024]; struct nlmsghdr nh; } buf; struct rtmsg *rtm; struct rtattr *rta; int len = sizeof(buf.raw); int rc, ipv4, 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, table, dest, plen, src, src_plen, gate, ifindex, metric, NULL, 0, 0, 0); rc = kernel_route(ROUTE_ADD, newtable, dest, plen, src, src_plen, newgate, newifindex, newmetric, NULL, 0, 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); use_src = (src_plen != 0 && kernel_disambiguate(ipv4)); 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, is_v4; 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; is_v4 = rtm->rtm_family == AF_INET; while(RTA_OK(rta, len)) { switch(rta->rta_type) { case RTA_DST: route->plen = GET_PLEN(rtm->rtm_dst_len, is_v4); COPY_ADDR(route->prefix, rta, is_v4); break; case RTA_SRC: route->src_plen = GET_PLEN(rtm->rtm_src_len, is_v4); COPY_ADDR(route->src_prefix, rta, is_v4); break; case RTA_GATEWAY: COPY_ADDR(route->gw, rta, is_v4); 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); } 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, struct kernel_route *route) { int rc, len; struct rtmsg *rtm; len = nh->nlmsg_len; if(nh->nlmsg_type != RTM_NEWROUTE && 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; rc = parse_kernel_route_rta(rtm, len, route); if(rc < 0) return 0; /* Ignore default unreachable routes; no idea where they come from. */ if(route->plen == 0 && route->metric >= KERNEL_INFINITY) return 0; if(debug >= 2) { if(rc >= 0) { print_kernel_route(nh->nlmsg_type, rtm->rtm_protocol, rtm->rtm_type, route); } } return 1; } /* This function should not return routes installed by us. */ int kernel_dump(int operation, struct kernel_filter *filter) { int i, rc; int families[2] = { AF_INET6, AF_INET }; struct rtgenmsg g; if(!nl_setup) { fprintf(stderr,"kernel_dump: netlink not initialized.\n"); errno = EIO; return -1; } if(nl_command.sock < 0) { rc = netlink_socket(&nl_command, 0); if(rc < 0) { int save = errno; perror("kernel_dump: netlink_socket()"); errno = save; return -1; } } for(i = 0; i < 2; i++) { memset(&g, 0, sizeof(g)); g.rtgen_family = families[i]; if(operation & CHANGE_ROUTE) { rc = netlink_send_dump(RTM_GETROUTE, &g, sizeof(g)); if(rc < 0) return -1; rc = netlink_read(&nl_command, NULL, 1, filter); if(rc < 0) return -1; } memset(&g, 0, sizeof(g)); g.rtgen_family = families[i]; if(operation & CHANGE_RULE) { rc = netlink_send_dump(RTM_GETRULE, &g, sizeof(g)); if(rc < 0) return -1; rc = netlink_read(&nl_command, NULL, 1, filter); if(rc < 0) return -1; } } if(operation & CHANGE_ADDR) { 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); if(rc < 0) return -1; } return 0; } 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, struct kernel_link *link) { struct ifinfomsg *info; int len; int ifindex; unsigned int ifflags; 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; link->ifname = parse_ifname_rta(info, len); if(link->ifname == NULL) return 0; kdebugf("filter_interfaces: link change on if %s(%d): 0x%x\n", link->ifname, ifindex, (unsigned)ifflags); return 1; } /* 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, struct kernel_addr *addr) { int rc; int len; struct ifaddrmsg *ifa; char ifname[IFNAMSIZ]; len = nh->nlmsg_len; if(nh->nlmsg_type != RTM_NEWADDR && nh->nlmsg_type != RTM_DELADDR) return 0; ifa = (struct ifaddrmsg *)NLMSG_DATA(nh); len -= NLMSG_LENGTH(0); rc = parse_addr_rta(ifa, len, &addr->addr); if(rc < 0) return 0; addr->ifindex = ifa->ifa_index; kdebugf("found address on interface %s(%d): %s\n", if_indextoname(ifa->ifa_index, ifname), ifa->ifa_index, format_address(addr->addr.s6_addr)); return 1; } static int filter_kernel_rules(struct nlmsghdr *nh, struct kernel_rule *rule) { int len, has_priority = 0, has_table = 0; struct rtmsg *rtm = NULL; struct rtattr *rta = NULL; int is_v4 = 0; len = nh->nlmsg_len; rtm = (struct rtmsg*)NLMSG_DATA(nh); len -= NLMSG_LENGTH(0); rule->src_plen = rtm->rtm_src_len; memset(rule->src, 0, sizeof(rule->src)); rule->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)); for(; RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) { switch(rta->rta_type) { case FRA_UNSPEC: break; case FRA_SRC: rule->src_plen = GET_PLEN(rtm->rtm_src_len, is_v4); COPY_ADDR(rule->src, rta, is_v4); break; case FRA_PRIORITY: rule->priority = *(unsigned int*)RTA_DATA(rta); has_priority = 1; break; case FRA_TABLE: rule->table = *(int*)RTA_DATA(rta); has_table = 1; break; default: kdebugf("filter_rules: Unknown rule attribute: %d.\n", rta->rta_type); break; } } kdebugf("filter_rules: from %s prio %d table %d\n", format_prefix(rule->src, rule->src_plen), has_priority ? rule->priority : -1, rule->table); if(!has_priority || !has_table) return 0; return 1; } static int filter_netlink(struct nlmsghdr *nh, struct kernel_filter *filter) { int rc; union { struct kernel_route route; struct kernel_addr addr; struct kernel_link link; struct kernel_rule rule; } u; switch(nh->nlmsg_type) { case RTM_NEWROUTE: case RTM_DELROUTE: if(!filter->route) break; rc = filter_kernel_routes(nh, &u.route); if(rc <= 0) break; return filter->route(&u.route, filter->route_closure); case RTM_NEWLINK: case RTM_DELLINK: if(!filter->link) break; rc = filter_link(nh, &u.link); if(rc <= 0) break; return filter->link(&u.link, filter->link_closure); case RTM_NEWADDR: case RTM_DELADDR: if(!filter->addr) break; rc = filter_addresses(nh, &u.addr); if(rc <= 0) break; return filter->addr(&u.addr, filter->addr_closure); case RTM_NEWRULE: case RTM_DELRULE: if(!filter->rule) break; rc = filter_kernel_rules(nh, &u.rule); if(rc <= 0) break; return filter->rule(&u.rule, filter->rule_closure); default: kdebugf("filter_netlink: unexpected message type %d\n", nh->nlmsg_type); break; } return 0; } int kernel_callback(struct kernel_filter *filter) { int rc; 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); if(rc < 0 && nl_listen.sock < 0) kernel_setup_socket(1); return 0; } /* Routing table's rules */ 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); } 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); } int change_rule(int new_prio, int old_prio, const unsigned char *src, int plen, int table) { 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); } babeld-1.7.0/kernel_socket.c000066400000000000000000000552351265444642500157520ustar00rootroot00000000000000/* 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, int table, 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 newtable) { 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, table, dest, plen, src, src_plen, gate, ifindex, metric, NULL, 0, 0, 0); return kernel_route(ROUTE_ADD, table, dest, plen, src, src_plen, newgate, newifindex, newmetric, NULL, 0, 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; } static int kernel_routes(struct kernel_filter *filter) { int mib[6]; char *buf, *p; size_t len; struct rt_msghdr *rtm; int rc; 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; } for(p = buf; p < buf + len; p += rtm->rtm_msglen) { struct kernel_route route; rtm = (struct rt_msghdr*)p; rc = parse_kernel_route(rtm, &route); if(rc < 0) continue; if(debug > 2) print_kernel_route(1, &route); rc = filter->route(&route, filter->route_closure); if(rc < 0) break; } free(buf); return 0; fail: free(buf); return -1; } static int socket_read(int sock, struct kernel_filter *filter) { 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; filter->route(&route, filter->route_closure); if(debug > 2) print_kernel_route(1,&route); return 1; } return 0; } static int kernel_addresses(struct kernel_filter *filter) { struct ifaddrs *ifa, *ifap; int rc; rc = getifaddrs(&ifa); if(rc < 0) return -1; for(ifap = ifa; ifap != NULL; ifap = ifap->ifa_next) { struct kernel_addr addr; addr.ifindex = if_nametoindex(ifap->ifa_name); if(!addr.ifindex) continue; if(ifap->ifa_addr->sa_family == AF_INET6) { struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)ifap->ifa_addr; memcpy(&addr.addr, &sin6->sin6_addr, 16); if(IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) /* 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(((char*)&addr.addr) + 2, 0, 2); } else if(ifap->ifa_addr->sa_family == AF_INET) { struct sockaddr_in *sin = (struct sockaddr_in*)ifap->ifa_addr; #if defined(IN_LINKLOCAL) if(IN_LINKLOCAL(htonl(sin->sin_addr.s_addr))) continue; #endif v4tov6((void*)&addr.addr, (void*) &sin->sin_addr); } else { continue; } filter->addr(&addr, filter->addr_closure); } freeifaddrs(ifa); return 0; } int kernel_dump(int operation, struct kernel_filter *filter) { switch(operation) { case CHANGE_ROUTE: return kernel_routes(filter); case CHANGE_ADDR: return kernel_addresses(filter); default: break; } return -1; } int kernel_callback(struct kernel_filter *filter) { if(kernel_socket < 0) kernel_setup_socket(1); kdebugf("Reading kernel table modification."); socket_read(kernel_socket, filter); return 0; } int add_rule(int prio, const unsigned char *src_prefix, int src_plen, int table) { errno = ENOSYS; return -1; } int flush_rule(int prio, int family) { errno = ENOSYS; return -1; } int change_rule(int new_prio, int old_prio, const unsigned char *src, int plen, int table) { errno = ENOSYS; return -1; } /* Local Variables: */ /* c-basic-offset: 4 */ /* indent-tabs-mode: nil */ /* End: */ babeld-1.7.0/local.c000066400000000000000000000173671265444642500142200ustar00rootroot00000000000000/* 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 "kernel.h" #include "xroute.h" #include "route.h" #include "util.h" #include "local.h" #include "version.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"; char buf[512]; struct xroute_stream *xroutes; struct route_stream *routes; rc = write_timeout(s, header, strlen(header)); if(rc < 0) goto fail; rc = snprintf(buf, 512, "version %s\n", BABELD_VERSION); if(rc < 0 || rc >= 512) goto fail; rc = write_timeout(s, buf, rc); 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(ROUTE_ALL); 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-1.7.0/local.h000066400000000000000000000034711265444642500142140ustar00rootroot00000000000000/* 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-1.7.0/message.c000066400000000000000000002130751265444642500145440ustar00rootroot00000000000000/* 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 "kernel.h" #include "xroute.h" #include "resend.h" #include "message.h" #include "configuration.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, 8); 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(ROUTE_INSTALLED); 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)) 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-1.7.0/message.h000066400000000000000000000115171265444642500145460ustar00rootroot00000000000000/* 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 4000 #define BUCKET_TOKENS_PER_SEC 1000 #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-1.7.0/neighbour.c000066400000000000000000000244631265444642500151030ustar00rootroot00000000000000/* 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-1.7.0/neighbour.h000066400000000000000000000046321265444642500151040ustar00rootroot00000000000000/* 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-1.7.0/net.c000066400000000000000000000127031265444642500137010ustar00rootroot00000000000000/* 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-1.7.0/net.h000066400000000000000000000025461265444642500137120ustar00rootroot00000000000000/* 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-1.7.0/resend.c000066400000000000000000000231441265444642500143740ustar00rootroot00000000000000/* 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-1.7.0/resend.h000066400000000000000000000055341265444642500144040ustar00rootroot00000000000000/* 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-1.7.0/route.c000066400000000000000000001044621265444642500142550ustar00rootroot00000000000000/* 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 */ static int check_specific_first(void) { /* All source-specific routes are in front of the list */ int specific = 1; int i; for(i = 0; i < route_slots; i++) { if(routes[i]->src->src_plen == 0) { specific = 0; } else if(!specific) { return 0; } } return 1; } /* 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; /* Put all source-specific routes in the front of the list. */ if(src_plen == 0 && route->src->src_plen > 0) { return 1; } else if(src_plen > 0 && route->src->src_plen == 0) { return -1; } 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 which) { struct route_stream *stream; if(!check_specific_first()) fprintf(stderr, "Invariant failed: specific routes first in RIB.\n"); stream = malloc(sizeof(struct route_stream)); if(stream == NULL) return NULL; stream->installed = which; stream->index = which == ROUTE_ALL ? -1 : 0; stream->next = NULL; return stream; } struct babel_route * route_stream_next(struct route_stream *stream) { if(stream->installed) { while(stream->index < route_slots) if(stream->installed == ROUTE_SS_INSTALLED && routes[stream->index]->src->src_plen == 0) return NULL; else if(routes[stream->index]->installed) break; else 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 + 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-1.7.0/route.h000066400000000000000000000127031265444642500142560ustar00rootroot00000000000000/* 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; }; #define ROUTE_ALL 0 #define ROUTE_INSTALLED 1 #define ROUTE_SS_INSTALLED 2 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 which); 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-1.7.0/rule.c000066400000000000000000000204751265444642500140670ustar00rootroot00000000000000/* Copyright (c) 2015 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 "babeld.h" #include "util.h" #include "kernel.h" #include "configuration.h" #include "rule.h" int src_table_idx = 10; int src_table_prio = 100; /* The table used for non-specific routes is "export_table", therefore, we can take the convention of plen == 0 <=> empty table. */ struct rule { unsigned char src[16]; unsigned char plen; unsigned char table; }; /* rules 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 rule rules[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 int get_free_table(void) { int i; for(i = 0; i < SRC_TABLE_NUM; i++) if(!used_tables[i]) { used_tables[i] = 1; return i + src_table_idx; } return -1; } static void release_table(int i) { used_tables[i - src_table_idx] = 0; } static int find_hole_around(int i) { int j; for(j = i; j < SRC_TABLE_NUM; j++) if(rules[j].plen == 0) return j; for(j = i - 1; j >= 0; j--) if(rules[j].plen == 0) return j; return -1; } static int shift_rule(int from, int to) { int dec = (from < to) ? 1 /* right */ : -1 /* left */; int rc; while(to != from) { to -= dec; rc = change_rule(to + dec + src_table_prio, to + src_table_prio, rules[to].src, rules[to].plen, rules[to].table); if(rc < 0) { perror("change_table_priority"); return -1; } rules[to+dec] = rules[to]; rules[to].plen = 0; } return 0; } /* Return a new table at index [idx] of rules. If cell at that index is not free, we need to shift cells (and rules). If it's full, return NULL. */ static struct rule * insert_table(const unsigned char *src, unsigned short src_plen, int idx) { int table; int rc; int hole; if(idx < 0 || idx >= SRC_TABLE_NUM) { fprintf(stderr, "Incorrect table number %d\n", idx); return NULL; } table = get_free_table(); if(table < 0) { kdebugf("All allowed routing tables are used!\n"); return NULL; } hole = find_hole_around(idx); if(hole < 0) { fprintf(stderr, "Have free table but not free rule.\n"); goto fail; } rc = shift_rule(idx, hole); if(rc < 0) goto fail; rc = add_rule(idx + src_table_prio, src, src_plen, table); if(rc < 0) { perror("add rule"); goto fail; } memcpy(rules[idx].src, src, 16); rules[idx].plen = src_plen; rules[idx].table = table; return &rules[idx]; fail: release_table(table); return NULL; } /* Sorting rules in a well ordered fashion will increase code complexity and decrease performances, because more rule shifts will be required, so more system calls invoked. */ static int find_table_slot(const unsigned char *src, unsigned short src_plen, int *found) { struct rule *kr = NULL; int i; *found = 0; for(i = 0; i < SRC_TABLE_NUM; i++) { kr = &rules[i]; if(kr->plen == 0) return i; switch(prefix_cmp(src, src_plen, kr->src, kr->plen)) { case PST_LESS_SPECIFIC: case PST_DISJOINT: continue; case PST_MORE_SPECIFIC: return i; case PST_EQUALS: *found = 1; return i; } } return -1; } int find_table(const unsigned char *dest, unsigned short plen, const unsigned char *src, unsigned short src_plen) { struct filter_result filter_result = {0}; struct rule *kr = NULL; int i, found; install_filter(dest, plen, src, src_plen, &filter_result); if(filter_result.table) { return filter_result.table; } else if(src_plen == 0) { return export_table; } else if(kernel_disambiguate(v4mapped(dest))) { return export_table; } i = find_table_slot(src, src_plen, &found); if(found) return rules[i].table; if(i < 0) return -1; kr = insert_table(src, src_plen, i); return kr == NULL ? -1 : kr->table; } void release_tables(void) { int i; for(i = 0; i < SRC_TABLE_NUM; i++) { if(rules[i].plen != 0) { flush_rule(i + src_table_prio, v4mapped(rules[i].src) ? AF_INET : AF_INET6); rules[i].plen = 0; } used_tables[i] = 0; } } static int filter_rule(struct kernel_rule *rule, void *data) { int i; char (*rule_exists)[2][SRC_TABLE_NUM] = data; int is_v4 = v4mapped(rule->src); int r = is_v4 ? 0 : 1; if(martian_prefix(rule->src, rule->src_plen)) return 0; i = rule->priority - src_table_prio; if(i < 0 || SRC_TABLE_NUM <= i) return 0; if(prefix_cmp(rule->src, rule->src_plen, rules[i].src, rules[i].plen) == PST_EQUALS && rule->table == rules[i].table && (*rule_exists)[r][i] == 0) (*rule_exists)[r][i] = 1; else (*rule_exists)[r][i] = -1; 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++) { int priority = i + src_table_prio; if(rule_exists[i] == 1) continue; if(rule_exists[i] != 0) { int rc; do { rc = flush_rule(priority, v4 ? AF_INET : AF_INET6); } while(rc >= 0); if(errno != ENOENT && errno != EEXIST) fprintf(stderr, "Cannot remove rule %d: from %s table %d (%s)\n", priority, format_prefix(rules[i].src, rules[i].plen), rules[i].table, strerror(errno)); } /* Be wise, our priority are both for v4 and v6 (does not overlap). */ if(!!v4mapped(rules[i].src) == !!v4 && rules[i].plen != 0) { rc = add_rule(priority, rules[i].src, rules[i].plen, rules[i].table); if(rc < 0) fprintf(stderr, "Cannot install rule %d: from %s table %d (%s)\n", priority, format_prefix(rules[i].src, rules[i].plen), rules[i].table, strerror(errno)); } } } int check_rules(void) { int rc; char rule_exists[2][SRC_TABLE_NUM]; /* v4, v6 */ struct kernel_filter filter = {0}; filter.rule = filter_rule; filter.rule_closure = (void*) rule_exists; memset(rule_exists, 0, sizeof(rule_exists)); rc = kernel_dump(CHANGE_RULE, &filter); if(rc < 0) return -1; install_missing_rules(rule_exists[0], 1); install_missing_rules(rule_exists[1], 0); return 0; } babeld-1.7.0/rule.h000066400000000000000000000027511265444642500140710ustar00rootroot00000000000000/* Copyright (c) 2015 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. */ #define SRC_TABLE_NUM 10 extern int src_table_idx; /* number of the first table */ extern int src_table_prio; /* first prio range */ /* Return the number of the table using src_plen, allocate the table in the kernel if necessary. */ int find_table(const unsigned char *dest, unsigned short plen, const unsigned char *src, unsigned short src_plen); void release_tables(void); int check_rules(void); babeld-1.7.0/source.c000066400000000000000000000140261265444642500144130ustar00rootroot00000000000000/* 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" static struct source **sources = NULL; static int source_slots = 0, max_source_slots = 0; static int source_compare(const unsigned char *id, const unsigned char *prefix, unsigned char plen, const unsigned char *src_prefix, unsigned char src_plen, const struct source *src) { int rc; rc = memcmp(id, src->id, 8); if(rc != 0) return rc; if(plen < src->plen) return -1; if(plen > src->plen) return 1; rc = memcmp(prefix, src->prefix, 16); if(rc != 0) return rc; rc = memcmp(src_prefix, src->src_prefix, 16); if(rc != 0) return rc; return 0; } static int find_source_slot(const unsigned char *id, 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(source_slots < 1) { if(new_return) *new_return = 0; return -1; } p = 0; g = source_slots - 1; do { m = (p + g) / 2; c = source_compare(id, prefix, plen, src_prefix, src_plen, sources[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; } static int resize_source_table(int new_slots) { struct source **new_sources; assert(new_slots >= source_slots); if(new_slots == 0) { new_sources = NULL; free(sources); } else { new_sources = realloc(sources, new_slots * sizeof(struct source*)); if(new_sources == NULL) return -1; } max_source_slots = new_slots; sources = new_sources; return 1; } 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) { int n = -1; int i = find_source_slot(id, prefix, plen, src_prefix, src_plen, &n); struct source *src; if(i >= 0) return sources[i]; 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; if(source_slots >= max_source_slots) resize_source_table(max_source_slots < 1 ? 8 : 2 * max_source_slots); if(source_slots >= max_source_slots) { free(src); return NULL; } if(n < source_slots) memmove(sources + n + 1, sources + n, (source_slots - n) * sizeof(struct source*)); source_slots++; sources[n] = 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--; } 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() { int i = 0, j = 0; while(i < source_slots) { struct source *src = sources[i]; if(src->time > now.tv_sec) /* clock stepped */ src->time = now.tv_sec; if(src->route_count == 0 && src->time < now.tv_sec - SOURCE_GC_TIME) { free(src); sources[i] = NULL; i++; } else { if(j < i) { sources[j] = sources[i]; sources[i] = NULL; } i++; j++; } } source_slots = j; } void check_sources_released(void) { int i; for(i = 0; i < source_slots; i++) { struct source *src = sources[i]; 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-1.7.0/source.h000066400000000000000000000036431265444642500144230ustar00rootroot00000000000000/* 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 { 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); void update_source(struct source *src, unsigned short seqno, unsigned short metric); void expire_sources(void); void check_sources_released(void); babeld-1.7.0/util.c000066400000000000000000000277751265444642500141070ustar00rootroot00000000000000/* 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-1.7.0/util.h000066400000000000000000000140761265444642500141020ustar00rootroot00000000000000/* 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-1.7.0/xroute.c000066400000000000000000000264301265444642500144430ustar00rootroot00000000000000/* 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); } static int filter_route(struct kernel_route *route, void *data) { void **args = (void**)data; int maxroutes = *(int*)args[0]; struct kernel_route *routes = (struct kernel_route *)args[1]; int *found = (int*)args[2]; if(*found >= maxroutes) return -1; if(martian_prefix(route->prefix, route->plen) || martian_prefix(route->src_prefix, route->src_plen)) return 0; routes[*found] = *route; ++ *found; return 0; } static int kernel_routes(struct kernel_route *routes, int maxroutes) { int found = 0; void *data[3] = { &maxroutes, routes, &found }; struct kernel_filter filter = {0}; filter.route = filter_route; filter.route_closure = data; kernel_dump(CHANGE_ROUTE, &filter); return found; } static int filter_address(struct kernel_addr *addr, void *data) { void **args = (void **)data; int maxroutes = *(int *)args[0]; struct kernel_route *routes = (struct kernel_route*)args[1]; int *found = (int *)args[2]; int ifindex = *(int*)args[3]; int ll = args[4] ? !!*(int*)args[4] : 0; struct kernel_route *route = NULL; if(*found >= maxroutes) return 0; if(ll == !IN6_IS_ADDR_LINKLOCAL(&addr->addr)) return 0; /* ifindex may be 0 -- see kernel_addresses */ if(ifindex && addr->ifindex != ifindex) return 0; route = &routes[*found]; memcpy(route->prefix, addr->addr.s6_addr, 16); route->plen = 128; route->metric = 0; route->ifindex = addr->ifindex; route->proto = RTPROT_BABEL_LOCAL; memset(route->gw, 0, 16); ++ *found; return 1; } /* ifindex is 0 for all interfaces. ll indicates whether we are interested in link-local or global addresses. */ int kernel_addresses(int ifindex, int ll, struct kernel_route *routes, int maxroutes) { int found = 0; void *data[5] = { &maxroutes, routes, &found, &ifindex, &ll }; struct kernel_filter filter = {0}; filter.addr = filter_address; filter.addr_closure = data; kernel_dump(CHANGE_ADDR, &filter); return found; } 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(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-1.7.0/xroute.h000066400000000000000000000037601265444642500144510ustar00rootroot00000000000000/* 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 kernel_addresses(int ifindex, int ll, struct kernel_route *routes, int maxroutes); int check_xroutes(int send_updates); babeld-1.7.0/version0000644000175000017500000000001512654446620013737 0ustar00jchjch00000000000000babeld-1.7.0