From: Guus Sliepen Date: Mon, 9 Mar 2009 18:02:24 +0000 (+0100) Subject: Merge branch 'master' into 1.1 X-Git-Tag: release-1.1pre1~121 X-Git-Url: https://www.tinc-vpn.org/git/browse?a=commitdiff_plain;h=08aabbf9317806bc50a9a6693ca866c8936ce26b;hp=-c;p=tinc Merge branch 'master' into 1.1 Conflicts: NEWS README doc/tinc.conf.5.in doc/tinc.texi po/nl.po src/conf.c src/connection.c src/event.c src/graph.c src/net.c src/net_packet.c src/net_socket.c src/node.c src/node.h src/openssl/rsagen.h src/protocol_auth.c src/protocol_key.c src/protocol_misc.c src/subnet.c src/subnet.h src/tincd.c --- 08aabbf9317806bc50a9a6693ca866c8936ce26b diff --combined NEWS index 2ad93e5a,736f5e69..a993df5c --- a/NEWS +++ b/NEWS @@@ -1,9 -1,20 +1,26 @@@ +Version 1.1-cvs Work in progress + + * Use libevent to handle I/O events and timeouts. + + * Use splay trees instead of AVL trees. + + Version 1.0.9 Dec 26 2008 + + * Fixed tinc as a service under Windows 2003. + + * Fixed reading configuration files that do not end with a newline. + + * Fixed crashes in situations where hostnames could not be resolved or hosts + would disconnect at the same time as session keys were exchanged. + + * Improved default settings of tun and tap devices on BSD platforms. + + * Make IPv6 sockets bind only to IPv6 on Linux. + + * Enable path MTU discovery by default. + + * Fixed a memory leak that occured when connections were closed. + Version 1.0.8 May 16 2007 * Fixed some memory and resource leaks. diff --combined README index b7ba7b6f,23b24581..c324e2b7 --- a/README +++ b/README @@@ -1,7 -1,7 +1,7 @@@ -This is the README file for tinc version 1.0.9. Installation +This is the README file for tinc version 1.1-cvs. Installation instructions may be found in the INSTALL file. - tinc is Copyright (C) 1998-2007 by: -tinc is Copyright (C) 1998-2008 by: ++tinc is Copyright (C) 1998-2009 by: Ivo Timmermans, Guus Sliepen , @@@ -55,7 -55,7 +55,7 @@@ should be changed into "Device", and "D Compatibility ------------- -Version 1.0.9 is compatible with 1.0pre8, 1.0 and later, but not with older +Version 1.1-cvs is compatible with 1.0pre8, 1.0 and later, but not with older versions of tinc. @@@ -78,9 -78,6 +78,9 @@@ Since 1.0, the lzo library is also use library whether or not you plan to enable compression. You can find it at http://www.oberhumer.com/opensource/lzo/. +Since 1.1, the libevent library is used for the main event loop. You can find +it at http://monkey.org/~provos/libevent/. + In order to compile tinc, you will need a GNU C compiler environment. diff --combined doc/tinc.texi index 9baf1774,ac52e7b4..f1d438fe --- a/doc/tinc.texi +++ b/doc/tinc.texi @@@ -16,7 -16,7 +16,7 @@@ This is the info manual for @value{PACKAGE} version @value{VERSION}, a Virtual Private Network daemon. - Copyright @copyright{} 1998-2006 Ivo Timmermans, + Copyright @copyright{} 1998-2009 Ivo Timmermans, Guus Sliepen and Wessel Dankers . @@@ -43,7 -43,7 +43,7 @@@ permission notice identical to this one @cindex copyright This is the info manual for @value{PACKAGE} version @value{VERSION}, a Virtual Private Network daemon. - Copyright @copyright{} 1998-2007 Ivo Timmermans, + Copyright @copyright{} 1998-2009 Ivo Timmermans, Guus Sliepen and Wessel Dankers . @@@ -71,7 -71,6 +71,7 @@@ permission notice identical to this one * Installation:: * Configuration:: * Running tinc:: +* Controlling tinc:: * Technical information:: * Platform specific information:: * About us:: @@@ -226,8 -225,7 +226,7 @@@ support tinc @section Configuring the kernel @menu - * Configuration of Linux kernels 2.1.60 up to 2.4.0:: - * Configuration of Linux kernels 2.4.0 and higher:: + * Configuration of Linux kernels:: * Configuration of FreeBSD kernels:: * Configuration of OpenBSD kernels:: * Configuration of NetBSD kernels:: @@@ -238,51 -236,11 +237,11 @@@ @c ================================================================== - @node Configuration of Linux kernels 2.1.60 up to 2.4.0 - @subsection Configuration of Linux kernels 2.1.60 up to 2.4.0 - - @cindex ethertap - For kernels up to 2.4.0, you need a kernel that supports the ethertap device. - Most distributions come with kernels that already support this. - If not, here are the options you have to turn on when configuring a new kernel: - - @example - Code maturity level options - [*] Prompt for development and/or incomplete code/drivers - Networking options - [*] Kernel/User netlink socket - Netlink device emulation - Network device support - Ethertap network tap - @end example - - If you want to run more than one instance of tinc or other programs that use - the ethertap, you have to compile the ethertap driver as a module, otherwise - you can also choose to compile it directly into the kernel. - - If you decide to build any of these as dynamic kernel modules, it's a good idea - to add these lines to @file{/etc/modules.conf}: - - @example - alias char-major-36 netlink_dev - alias tap0 ethertap - options tap0 -o tap0 unit=0 - alias tap1 ethertap - options tap1 -o tap1 unit=1 - ... - alias tap@emph{N} ethertap - options tap@emph{N} -o tap@emph{N} unit=@emph{N} - @end example - - Add as much alias/options lines as necessary. - - - @c ================================================================== - @node Configuration of Linux kernels 2.4.0 and higher - @subsection Configuration of Linux kernels 2.4.0 and higher + @node Configuration of Linux kernels + @subsection Configuration of Linux kernels @cindex Universal tun/tap - For kernels 2.4.0 and higher, you need a kernel that supports the Universal tun/tap device. + For tinc to work, you need a kernel that supports the Universal tun/tap device. Most distributions come with kernels that already support this. Here are the options you have to turn on when configuring a new kernel: @@@ -296,11 -254,6 +255,6 @@@ Network device suppor It's not necessary to compile this driver as a module, even if you are going to run more than one instance of tinc. - If you have an early 2.4 kernel, you can choose both the tun/tap driver and the - `Ethertap network tap' device. This latter is marked obsolete, and chances are - that it won't even function correctly anymore. Make sure you select the - universal tun/tap driver. - If you decide to build the tun/tap driver as a kernel module, add these lines to @file{/etc/modules.conf}: @@@ -324,9 -277,9 +278,9 @@@ Using tap devices is recommended For OpenBSD version 2.9 and higher, the tun driver is included in the default kernel configuration. There is also a kernel patch from @uref{http://diehard.n-r-g.com/stuff/openbsd/} - which adds a tap device to OpenBSD. - This should work with tinc. - + which adds a tap device to OpenBSD which should work with tinc, + but with recent versions of OpenBSD, + a tun device can act as a tap device by setting the link0 option with ifconfig. @c ================================================================== @node Configuration of NetBSD kernels @@@ -390,7 -343,6 +344,7 @@@ having them installed, configure will g * OpenSSL:: * zlib:: * lzo:: +* libevent:: @end menu @@@ -503,27 -455,6 +457,27 @@@ make sure you build development and run default). +@c ================================================================== +@node libevent +@subsection libevent + +@cindex libevent +For the main event loop, tinc uses the libevent library. + +If this library is not installed, you wil get an error when configuring +tinc for build. + +You can use your operating system's package manager to install this if +available. Make sure you install the development AND runtime versions +of this package. + +If you have to install libevent manually, you can get the source code +from @url{http://monkey.org/~provos/libevent/}. Instructions on how to configure, +build and install this package are included within the package. Please +make sure you build development and runtime libraries (which is the +default). + + @c @c @c @@@ -632,40 -563,16 +586,16 @@@ files on your system @subsection Device files @cindex device files - First, you'll need the special device file(s) that form the interface - between the kernel and the daemon. - - The permissions for these files have to be such that only the super user - may read/write to this file. You'd want this, because otherwise - eavesdropping would become a bit too easy. This does, however, imply - that you'd have to run tincd as root. + Most operating systems nowadays come with the necessary device files by default, + or they have a mechanism to create them on demand. - If you use Linux and have a kernel version prior to 2.4.0, you have to make the - ethertap devices: + If you use Linux and do not have udev installed, + you may need to create the following device file if it does not exist: @example - mknod -m 600 /dev/tap0 c 36 16 - mknod -m 600 /dev/tap1 c 36 17 - ... - mknod -m 600 /dev/tap@emph{N} c 36 @emph{N+16} + mknod -m 600 /dev/net/tun c 10 200 @end example - There is a maximum of 16 ethertap devices. - - If you use the universal tun/tap driver, you have to create the - following device file (unless it already exist): - - @example - mknod -m 600 /dev/tun c 10 200 - @end example - - If you use Linux, and you run the new 2.4 kernel using the devfs filesystem, - then the tun/tap device will probably be automatically generated as - @file{/dev/net/tun}. - - Unlike the ethertap device, you do not need multiple device files if - you are planning to run multiple tinc daemons. - @c ================================================================== @node Other files @@@ -885,6 -792,48 +815,48 @@@ Under Windows, use @var{Interface} inst Note that you can only use one device per daemon. See also @ref{Device files}. + @cindex DeviceType + @item DeviceType = (only supported on BSD platforms) + The type of the virtual network device. + Tinc will normally automatically select the right type, and this option should not be used. + However, in case tinc does not seem to correctly interpret packets received from the virtual network device, + using this option might help. + + @table @asis + @item tun + Set type to tun. + Depending on the platform, this can either be with or without an address family header (see below). + + @cindex tunnohead + @item tunnohead + Set type to tun without an address family header. + Tinc will expect packets read from the virtual network device to start with an IP header. + On some platforms IPv6 packets cannot be read from or written to the device in this mode. + + @cindex tunifhead + @item tunifhead + Set type to tun with an address family header. + Tinc will expect packets read from the virtual network device + to start with a four byte header containing the address family, + followed by an IP header. + This mode should support both IPv4 and IPv6 packets. + + @item tap + Set type to tap. + Tinc will expect packets read from the virtual network device + to start with an Ethernet header. + @end table + + @cindex GraphDumpFile + @item GraphDumpFile = <@var{filename}> [experimental] + If this option is present, + tinc will dump the current network graph to the file @var{filename} + every minute, unless there were no changes to the graph. + The file is in a format that can be read by graphviz tools. + If @var{filename} starts with a pipe symbol |, + then the rest of the filename is interpreted as a shell command + that is executed, the graph is then sent to stdin. + @cindex Hostnames @item Hostnames = (no) This option selects whether IP addresses (both real and on the VPN) @@@ -945,7 -894,8 +917,8 @@@ This only has effect when Mode is set t @cindex Name @item Name = <@var{name}> [required] - This is a symbolic name for this connection. It can be anything + This is a symbolic name for this connection. + The name should consist only of alfanumeric and underscore characters (a-z, A-Z, 0-9 and _). @cindex PingInterval @item PingInterval = <@var{seconds}> (60) @@@ -972,7 -922,7 +945,7 @@@ accidental eavesdropping if you are edi @cindex PrivateKeyFile @item PrivateKeyFile = <@var{path}> (@file{@value{sysconfdir}/tinc/@var{netname}/rsa_key.priv}) This is the full path name of the RSA private key file that was -generated by @samp{tincd --generate-keys}. It must be a full path, not a +generated by @samp{tincctl generate-keys}. It must be a full path, not a relative directory. Note that there must be exactly one of PrivateKey @@@ -1032,6 -982,15 +1005,15 @@@ The length of the message authenticatio Can be anything from 0 up to the length of the digest produced by the digest algorithm. + @cindex PMTU + @item PMTU = <@var{mtu}> (1514) + This option controls the initial path MTU to this node. + + @cindex PMTUDiscovery + @item PMTUDiscovery = (yes) + When this option is enabled, tinc will try to discover the path MTU to this node. + After the path MTU has been discovered, it will be enforced on the VPN. + @cindex Port @item Port = <@var{port}> (655) This is the port this tinc daemon listens on. @@@ -1044,7 -1003,7 +1026,7 @@@ This is the RSA public key for this hos @cindex PublicKeyFile @item PublicKeyFile = <@var{path}> [obsolete] This is the full path name of the RSA public key file that was generated -by @samp{tincd --generate-keys}. It must be a full path, not a relative +by @samp{tincctl generate-keys}. It must be a full path, not a relative directory. @cindex PEM format @@@ -1056,7 -1015,7 +1038,7 @@@ in each host configuration file, if yo connection with that host. @cindex Subnet - @item Subnet = <@var{address}[/@var{prefixlength}]> + @item Subnet = <@var{address}[/@var{prefixlength}[#@var{weight}]]> The subnet which this tinc daemon will serve. Tinc tries to look up which other daemon it should send a packet to by searching the appropiate subnet. If the packet matches a subnet, @@@ -1080,8 -1039,14 +1062,14 @@@ example: netmask 255.255.255.0 would be /22. This conforms to standard CIDR notation as described in @uref{ftp://ftp.isi.edu/in-notes/rfc1519.txt, RFC1519} + A Subnet can be given a weight to indicate its priority over identical Subnets + owned by different nodes. The default weight is 10. Lower values indicate + higher priority. Packets will be sent to the node with the highest priority, + unless that node is not reachable, in which case the node with the next highest + priority will be tried, and so on. + @cindex TCPonly - @item TCPonly = (no) [experimental] + @item TCPonly = (no) If this variable is set to yes, then the packets are tunnelled over a TCP connection instead of a UDP connection. This is especially useful for those who want to run a tinc daemon from behind a masquerading @@@ -1221,7 -1186,7 +1209,7 @@@ Now that you have already created the m you can easily create a public/private keypair by entering the following command: @example -tincd -n @var{netname} -K +tincctl -n @var{netname} generate-keys @end example Tinc will generate a public and a private key and ask you where to put them. @@@ -1450,7 -1415,7 +1438,7 @@@ Address = 4.5.6. A, B, C and D all have generated a public/private keypair with the following command: @example -tincd -n company -K +tincctl -n company generate-keys @end example The private key is stored in @file{@value{sysconfdir}/tinc/company/rsa_key.priv}, @@@ -1516,12 -1481,20 +1504,12 @@@ This will also disable the automatic re Set debug level to @var{level}. The higher the debug level, the more gets logged. Everything goes via syslog. -@item -k, --kill[=@var{signal}] -Attempt to kill a running tincd (optionally with the specified @var{signal} instead of SIGTERM) and exit. -Use it in conjunction with the -n option to make sure you kill the right tinc daemon. -Under native Windows the optional argument is ignored, -the service will always be stopped and removed. - @item -n, --net=@var{netname} Use configuration for net @var{netname}. @xref{Multiple networks}. -@item -K, --generate-keys[=@var{bits}] -Generate public/private keypair of @var{bits} length. If @var{bits} is not specified, -1024 is the default. tinc will ask where you want to store the files, -but will default to the configuration directory (you can use the -c or -n option -in combination with -K). After that, tinc will quit. +@item --controlsocket=@var{filename} +Open control socket at @var{filename}. If unspecified, the default is +@file{@value{localstatedir}/run/tinc.@var{netname}.control}. @item -L, --mlock Lock tinc into main memory. @@@ -1531,6 -1504,9 +1519,6 @@@ This will prevent sensitive data like s Write log entries to a file instead of to the system logging facility. If @var{file} is omitted, the default is @file{@value{localstatedir}/log/tinc.@var{netname}.log}. -@item --pidfile=@var{file} -Write PID to @var{file} instead of @file{@value{localstatedir}/run/tinc.@var{netname}.pid}. - @item --bypass-security Disables encryption and authentication. Only useful for debugging. @@@ -1553,11 -1529,31 +1541,11 @@@ You can also send the following signal @c from the manpage @table @samp -@item ALRM -Forces tinc to try to connect to all uplinks immediately. -Usually tinc attempts to do this itself, -but increases the time it waits between the attempts each time it failed, -and if tinc didn't succeed to connect to an uplink the first time after it started, -it defaults to the maximum time of 15 minutes. - @item HUP Partially rereads configuration files. Connections to hosts whose host config file are removed are closed. New outgoing connections specified in @file{tinc.conf} will be made. -@item INT -Temporarily increases debug level to 5. -Send this signal again to revert to the original level. - -@item USR1 -Dumps the connection list to syslog. - -@item USR2 -Dumps virtual network device statistics, all known nodes, edges and subnets to syslog. - -@item WINCH -Purges all information remembered about unreachable nodes. - @end table @c ================================================================== @@@ -1752,110 -1748,6 +1740,110 @@@ Be sure to include the following inform @item The output of any command that fails to work as it should (like ping or traceroute). @end itemize +@c ================================================================== +@node Controlling tinc +@chapter Controlling tinc + +You can control and inspect a running @samp{tincd} through the @samp{tincctl} +command. A quick example: + +@example +tincctl -n @var{netname} reload +@end example + +@menu +* tincctl runtime options:: +* tincctl commands:: +@end menu + + +@c ================================================================== +@node tincctl runtime options +@section tincctl runtime options + +@c from the manpage +@table @option +@item -c, --config=@var{path} +Read configuration options from the directory @var{path}. The default is +@file{@value{sysconfdir}/tinc/@var{netname}/}. + +@item -n, --net=@var{netname} +Use configuration for net @var{netname}. @xref{Multiple networks}. + +@item --controlsocket=@var{filename} +Open control socket at @var{filename}. If unspecified, the default is +@file{@value{localstatedir}/run/tinc.@var{netname}.control}. + +@item --help +Display a short reminder of runtime options and commands, then terminate. + +@item --version +Output version information and exit. + +@end table + + +@c ================================================================== +@node tincctl commands +@section tincctl commands + +@c from the manpage +@table @code + +@item start +Start @samp{tincd}. + +@item stop +Stop @samp{tincd}. + +@item restart +Restart @samp{tincd}. + +@item reload +Partially rereads configuration files. Connections to hosts whose host +config files are removed are closed. New outgoing connections specified +in @file{tinc.conf} will be made. + +@item pid +Shows the PID of the currently running @samp{tincd}. + +@item generate-keys [@var{bits}] +Generate public/private keypair of @var{bits} length. If @var{bits} is not specified, +1024 is the default. tinc will ask where you want to store the files, +but will default to the configuration directory (you can use the -c or -n +option). + +@item dump nodes +Dump a list of all known nodes in the VPN. + +@item dump edges +Dump a list of all known connections in the VPN. + +@item dump subnets +Dump a list of all known subnets in the VPN. + +@item dump connections +Dump a list of all meta connections with ourself. + +@item dump graph +Dump a graph of the VPN in dotty format. + +@item purge +Purges all information remembered about unreachable nodes. + +@item debug @var{level} +Sets debug level to @var{level}. + +@item retry +Forces tinc to try to connect to all uplinks immediately. +Usually tinc attempts to do this itself, +but increases the time it waits between the attempts each time it failed, +and if tinc didn't succeed to connect to an uplink the first time after it started, +it defaults to the maximum time of 15 minutes. + +@end table + + @c ================================================================== @node Technical information @chapter Technical information diff --combined src/bsd/device.c index 35038c74,2e8908af..1066858b --- a/src/bsd/device.c +++ b/src/bsd/device.c @@@ -1,7 -1,7 +1,7 @@@ /* device.c -- Interaction BSD tun/tap device Copyright (C) 2001-2005 Ivo Timmermans, - 2001-2007 Guus Sliepen + 2001-2009 Guus Sliepen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@@ -27,6 -27,7 +27,7 @@@ #include "net.h" #include "route.h" #include "utils.h" + #include "xalloc.h" #define DEFAULT_DEVICE "/dev/tun0" @@@ -37,12 -38,12 +38,12 @@@ typedef enum device_type } device_type_t; int device_fd = -1; - char *device; - char *iface; - char *device_info; + char *device = NULL; + char *iface = NULL; + static char *device_info = NULL; static int device_total_in = 0; static int device_total_out = 0; - #ifdef HAVE_OPENBSD + #if defined(HAVE_OPENBSD) || defined(HAVE_FREEBSD) static device_type_t device_type = DEVICE_TYPE_TUNIFHEAD; #else static device_type_t device_type = DEVICE_TYPE_TUN; @@@ -54,10 -55,10 +55,10 @@@ bool setup_device(void) cp(); if(!get_config_string(lookup_config(config_tree, "Device"), &device)) - device = DEFAULT_DEVICE; + device = xstrdup(DEFAULT_DEVICE); if(!get_config_string(lookup_config(config_tree, "Interface"), &iface)) - iface = rindex(device, '/') ? rindex(device, '/') + 1 : device; + iface = xstrdup(rindex(device, '/') ? rindex(device, '/') + 1 : device); if((device_fd = open(device, O_RDWR | O_NONBLOCK)) < 0) { logger(LOG_ERR, _("Could not open %s: %s"), device, strerror(errno)); @@@ -78,7 -79,7 +79,7 @@@ return false; } } else { - if(strstr(device, "tap")) + if(strstr(device, "tap") || routing_mode != RMODE_ROUTER) device_type = DEVICE_TYPE_TAP; } @@@ -139,16 -140,19 +140,19 @@@ void close_device(void) cp(); close(device_fd); + + free(device); + free(iface); } bool read_packet(vpn_packet_t *packet) { - int lenin; + int inlen; cp(); switch(device_type) { case DEVICE_TYPE_TUN: - if((lenin = read(device_fd, packet->data + 14, MTU - 14)) <= 0) { + if((inlen = read(device_fd, packet->data + 14, MTU - 14)) <= 0) { logger(LOG_ERR, _("Error while reading from %s %s: %s"), device_info, device, strerror(errno)); return false; @@@ -170,14 -174,14 +174,14 @@@ return false; } - packet->len = lenin + 14; + packet->len = inlen + 14; break; case DEVICE_TYPE_TUNIFHEAD: { u_int32_t type; - struct iovec vector[2] = {{&type, sizeof(type)}, {packet->data + 14, MTU - 14}}; + struct iovec vector[2] = {{&type, sizeof type}, {packet->data + 14, MTU - 14}}; - if((lenin = readv(device_fd, vector, 2)) <= 0) { + if((inlen = readv(device_fd, vector, 2)) <= 0) { logger(LOG_ERR, _("Error while reading from %s %s: %s"), device_info, device, strerror(errno)); return false; @@@ -201,18 -205,18 +205,18 @@@ return false; } - packet->len = lenin + 10; + packet->len = inlen + 10; break; } case DEVICE_TYPE_TAP: - if((lenin = read(device_fd, packet->data, MTU)) <= 0) { + if((inlen = read(device_fd, packet->data, MTU)) <= 0) { logger(LOG_ERR, _("Error while reading from %s %s: %s"), device_info, device, strerror(errno)); return false; } - packet->len = lenin; + packet->len = inlen; break; default: @@@ -227,7 -231,8 +231,7 @@@ return true; } -bool write_packet(vpn_packet_t *packet) -{ +bool write_packet(vpn_packet_t *packet) { cp(); ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Writing packet of %d bytes to %s"), @@@ -244,7 -249,7 +248,7 @@@ case DEVICE_TYPE_TUNIFHEAD: { u_int32_t type; - struct iovec vector[2] = {{&type, sizeof(type)}, {packet->data + 14, packet->len - 14}}; + struct iovec vector[2] = {{&type, sizeof type}, {packet->data + 14, packet->len - 14}}; int af; af = (packet->data[12] << 8) + packet->data[13]; @@@ -288,7 -293,8 +292,7 @@@ return true; } -void dump_device_stats(void) -{ +void dump_device_stats(void) { cp(); logger(LOG_DEBUG, _("Statistics for %s %s:"), device_info, device); diff --combined src/conf.c index 1b0ac641,ce776804..77942597 --- a/src/conf.c +++ b/src/conf.c @@@ -2,7 -2,7 +2,7 @@@ conf.c -- configuration code Copyright (C) 1998 Robert van der Meulen 1998-2005 Ivo Timmermans - 2000-2006 Guus Sliepen + 2000-2009 Guus Sliepen 2000 Cris van Pelt This program is free software; you can redistribute it and/or modify @@@ -24,21 -24,22 +24,21 @@@ #include "system.h" -#include "avl_tree.h" +#include "splay_tree.h" #include "conf.h" #include "logger.h" #include "netutl.h" /* for str2address */ #include "utils.h" /* for cp */ #include "xalloc.h" -avl_tree_t *config_tree; +splay_tree_t *config_tree; int pinginterval = 0; /* seconds between pings */ int pingtimeout = 0; /* seconds to wait for response */ char *confbase = NULL; /* directory in which all config files are */ char *netname = NULL; /* name of the vpn network */ -static int config_compare(const config_t *a, const config_t *b) -{ +static int config_compare(const config_t *a, const config_t *b) { int result; result = strcasecmp(a->variable, b->variable); @@@ -54,26 -55,30 +54,26 @@@ return strcmp(a->file, b->file); } -void init_configuration(avl_tree_t ** config_tree) -{ +void init_configuration(splay_tree_t ** config_tree) { cp(); - *config_tree = avl_alloc_tree((avl_compare_t) config_compare, (avl_action_t) free_config); + *config_tree = splay_alloc_tree((splay_compare_t) config_compare, (splay_action_t) free_config); } -void exit_configuration(avl_tree_t ** config_tree) -{ +void exit_configuration(splay_tree_t ** config_tree) { cp(); - avl_delete_tree(*config_tree); + splay_delete_tree(*config_tree); *config_tree = NULL; } -config_t *new_config(void) -{ +config_t *new_config(void) { cp(); return xmalloc_and_zero(sizeof(config_t)); } -void free_config(config_t *cfg) -{ +void free_config(config_t *cfg) { cp(); if(cfg->variable) @@@ -88,13 -93,15 +88,13 @@@ free(cfg); } -void config_add(avl_tree_t *config_tree, config_t *cfg) -{ +void config_add(splay_tree_t *config_tree, config_t *cfg) { cp(); - avl_insert(config_tree, cfg); + splay_insert(config_tree, cfg); } -config_t *lookup_config(avl_tree_t *config_tree, char *variable) -{ +config_t *lookup_config(splay_tree_t *config_tree, char *variable) { config_t cfg, *found; cp(); @@@ -103,7 -110,7 +103,7 @@@ cfg.file = ""; cfg.line = 0; - found = avl_search_closest_greater(config_tree, &cfg); + found = splay_search_closest_greater(config_tree, &cfg); if(!found) return NULL; @@@ -114,13 -121,14 +114,13 @@@ return found; } -config_t *lookup_config_next(avl_tree_t *config_tree, const config_t *cfg) -{ - avl_node_t *node; +config_t *lookup_config_next(splay_tree_t *config_tree, const config_t *cfg) { + splay_node_t *node; config_t *found; cp(); - node = avl_search_node(config_tree, cfg); + node = splay_search_node(config_tree, cfg); if(node) { if(node->next) { @@@ -134,7 -142,8 +134,7 @@@ return NULL; } -bool get_config_bool(const config_t *cfg, bool *result) -{ +bool get_config_bool(const config_t *cfg, bool *result) { cp(); if(!cfg) @@@ -154,7 -163,8 +154,7 @@@ return false; } -bool get_config_int(const config_t *cfg, int *result) -{ +bool get_config_int(const config_t *cfg, int *result) { cp(); if(!cfg) @@@ -169,7 -179,8 +169,7 @@@ return false; } -bool get_config_string(const config_t *cfg, char **result) -{ +bool get_config_string(const config_t *cfg, char **result) { cp(); if(!cfg) @@@ -180,7 -191,8 +180,7 @@@ return true; } -bool get_config_address(const config_t *cfg, struct addrinfo **result) -{ +bool get_config_address(const config_t *cfg, struct addrinfo **result) { struct addrinfo *ai; cp(); @@@ -201,7 -213,8 +201,7 @@@ return false; } -bool get_config_subnet(const config_t *cfg, subnet_t ** result) -{ +bool get_config_subnet(const config_t *cfg, subnet_t ** result) { subnet_t subnet = {0}; cp(); @@@ -218,9 -231,9 +218,9 @@@ /* Teach newbies what subnets are... */ if(((subnet.type == SUBNET_IPV4) - && !maskcheck(&subnet.net.ipv4.address, subnet.net.ipv4.prefixlength, sizeof(ipv4_t))) + && !maskcheck(&subnet.net.ipv4.address, subnet.net.ipv4.prefixlength, sizeof subnet.net.ipv4.address)) || ((subnet.type == SUBNET_IPV6) - && !maskcheck(&subnet.net.ipv6.address, subnet.net.ipv6.prefixlength, sizeof(ipv6_t)))) { + && !maskcheck(&subnet.net.ipv6.address, subnet.net.ipv6.prefixlength, sizeof subnet.net.ipv6.address))) { logger(LOG_ERR, _ ("Network address and prefix length do not match for configuration variable %s in %s line %d"), cfg->variable, cfg->file, cfg->line); return false; @@@ -241,7 -254,8 +241,7 @@@ given, and buf needs to be expanded, the var pointed to by buflen will be increased. */ -static char *readline(FILE * fp, char **buf, size_t *buflen) -{ +static char *readline(FILE * fp, char **buf, size_t *buflen) { char *newline = NULL; char *p; char *line; /* The array that contains everything that has been read so far */ @@@ -287,6 -301,8 +287,8 @@@ size = newsize; } else { *newline = '\0'; /* kill newline */ + if(newline > p && newline[-1] == '\r') /* and carriage return if necessary */ + newline[-1] = '\0'; break; /* yay */ } } @@@ -303,7 -319,8 +305,7 @@@ Parse a configuration file and put the results in the configuration tree starting at *base. */ -int read_config_file(avl_tree_t *config_tree, const char *fname) -{ +int read_config_file(splay_tree_t *config_tree, const char *fname) { int err = -2; /* Parse error */ FILE *fp; char *buffer, *line; @@@ -393,7 -410,8 +395,7 @@@ return err; } -bool read_server_config() -{ +bool read_server_config() { char *fname; int x; @@@ -410,3 -428,98 +412,3 @@@ return x == 0; } - -FILE *ask_and_open(const char *filename, const char *what) -{ - FILE *r; - char *directory; - char *fn; - - /* Check stdin and stdout */ - if(!isatty(0) || !isatty(1)) { - /* Argh, they are running us from a script or something. Write - the files to the current directory and let them burn in hell - for ever. */ - fn = xstrdup(filename); - } else { - /* Ask for a file and/or directory name. */ - fprintf(stdout, _("Please enter a file to save %s to [%s]: "), - what, filename); - fflush(stdout); - - fn = readline(stdin, NULL, NULL); - - if(!fn) { - fprintf(stderr, _("Error while reading stdin: %s\n"), - strerror(errno)); - return NULL; - } - - if(!strlen(fn)) - /* User just pressed enter. */ - fn = xstrdup(filename); - } - -#ifdef HAVE_MINGW - if(fn[0] != '\\' && fn[0] != '/' && !strchr(fn, ':')) { -#else - if(fn[0] != '/') { -#endif - /* The directory is a relative path or a filename. */ - char *p; - - directory = get_current_dir_name(); - asprintf(&p, "%s/%s", directory, fn); - free(fn); - free(directory); - fn = p; - } - - umask(0077); /* Disallow everything for group and other */ - - /* Open it first to keep the inode busy */ - - r = fopen(fn, "r+") ?: fopen(fn, "w+"); - - if(!r) { - fprintf(stderr, _("Error opening file `%s': %s\n"), - fn, strerror(errno)); - free(fn); - return NULL; - } - - free(fn); - - return r; -} - -bool disable_old_keys(FILE *f) { - char buf[100]; - long pos; - bool disabled = false; - - rewind(f); - pos = ftell(f); - - while(fgets(buf, sizeof buf, f)) { - if(!strncmp(buf, "-----BEGIN RSA", 14)) { - buf[11] = 'O'; - buf[12] = 'L'; - buf[13] = 'D'; - fseek(f, pos, SEEK_SET); - fputs(buf, f); - disabled = true; - } - else if(!strncmp(buf, "-----END RSA", 12)) { - buf[ 9] = 'O'; - buf[10] = 'L'; - buf[11] = 'D'; - fseek(f, pos, SEEK_SET); - fputs(buf, f); - disabled = true; - } - pos = ftell(f); - } - - return disabled; -} diff --combined src/conf.h index ee20c29c,d1eae978..dfa11231 --- a/src/conf.h +++ b/src/conf.h @@@ -1,7 -1,7 +1,7 @@@ /* conf.h -- header for conf.c Copyright (C) 1998-2005 Ivo Timmermans - 2000-2006 Guus Sliepen + 2000-2009 Guus Sliepen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@@ -23,7 -23,7 +23,7 @@@ #ifndef __TINC_CONF_H__ #define __TINC_CONF_H__ -#include "avl_tree.h" +#include "splay_tree.h" typedef struct config_t { char *variable; @@@ -34,7 -34,7 +34,7 @@@ #include "subnet.h" -extern avl_tree_t *config_tree; +extern splay_tree_t *config_tree; extern int pinginterval; extern int pingtimeout; @@@ -43,22 -43,23 +43,23 @@@ extern bool bypass_security extern char *confbase; extern char *netname; -extern void init_configuration(avl_tree_t **); -extern void exit_configuration(avl_tree_t **); +extern void init_configuration(splay_tree_t **); +extern void exit_configuration(splay_tree_t **); extern config_t *new_config(void) __attribute__ ((__malloc__)); extern void free_config(config_t *); -extern void config_add(avl_tree_t *, config_t *); -extern config_t *lookup_config(avl_tree_t *, char *); -extern config_t *lookup_config_next(avl_tree_t *, const config_t *); +extern void config_add(splay_tree_t *, config_t *); +extern config_t *lookup_config(splay_tree_t *, char *); +extern config_t *lookup_config_next(splay_tree_t *, const config_t *); extern bool get_config_bool(const config_t *, bool *); extern bool get_config_int(const config_t *, int *); extern bool get_config_string(const config_t *, char **); extern bool get_config_address(const config_t *, struct addrinfo **); extern bool get_config_subnet(const config_t *, struct subnet_t **); -extern int read_config_file(avl_tree_t *, const char *); +extern int read_config_file(splay_tree_t *, const char *); extern bool read_server_config(void); -extern FILE *ask_and_open(const char *, const char *); +extern FILE *ask_and_open(const char *, const char *, const char *); extern bool is_safe_path(const char *); + extern bool disable_old_keys(FILE *); #endif /* __TINC_CONF_H__ */ diff --combined src/connection.c index 84d2ac4d,9fd4002b..230ec4ed --- a/src/connection.c +++ b/src/connection.c @@@ -22,8 -22,7 +22,8 @@@ #include "system.h" -#include "avl_tree.h" +#include "splay_tree.h" +#include "cipher.h" #include "conf.h" #include "list.h" #include "logger.h" @@@ -33,93 -32,127 +33,96 @@@ #include "utils.h" #include "xalloc.h" -avl_tree_t *connection_tree; /* Meta connections */ +splay_tree_t *connection_tree; /* Meta connections */ connection_t *broadcast; -static int connection_compare(const connection_t *a, const connection_t *b) -{ +static int connection_compare(const connection_t *a, const connection_t *b) { return (void *)a - (void *)b; } -void init_connections(void) -{ +void init_connections(void) { cp(); - connection_tree = avl_alloc_tree((avl_compare_t) connection_compare, (avl_action_t) free_connection); + connection_tree = splay_alloc_tree((splay_compare_t) connection_compare, (splay_action_t) free_connection); broadcast = new_connection(); broadcast->name = xstrdup(_("everyone")); broadcast->hostname = xstrdup(_("BROADCAST")); } -void exit_connections(void) -{ +void exit_connections(void) { cp(); - avl_delete_tree(connection_tree); + splay_delete_tree(connection_tree); free_connection(broadcast); } -connection_t *new_connection(void) -{ - connection_t *c; - +connection_t *new_connection(void) { cp(); - c = xmalloc_and_zero(sizeof(connection_t)); - - if(!c) - return NULL; - - gettimeofday(&c->start, NULL); - - return c; + return xmalloc_and_zero(sizeof(connection_t)); } -void free_connection(connection_t *c) -{ +void free_connection(connection_t *c) { cp(); + if(!c) + return; + if(c->name) free(c->name); if(c->hostname) free(c->hostname); - if(c->inkey) - free(c->inkey); - - if(c->outkey) - free(c->outkey); - - if(c->inctx) { - EVP_CIPHER_CTX_cleanup(c->inctx); - free(c->inctx); - } - - if(c->outctx) { - EVP_CIPHER_CTX_cleanup(c->outctx); - free(c->outctx); - } - - if(c->mychallenge) - free(c->mychallenge); + cipher_close(&c->incipher); + cipher_close(&c->outcipher); if(c->hischallenge) free(c->hischallenge); + if(c->config_tree) + exit_configuration(&c->config_tree); + - if(c->outbuf) - free(c->outbuf); - - if(c->rsa_key) - RSA_free(c->rsa_key); + if(c->buffer) + bufferevent_free(c->buffer); + + if(event_initialized(&c->inevent)) + event_del(&c->inevent); free(c); } -void connection_add(connection_t *c) -{ +void connection_add(connection_t *c) { cp(); - avl_insert(connection_tree, c); + splay_insert(connection_tree, c); } -void connection_del(connection_t *c) -{ +void connection_del(connection_t *c) { cp(); - avl_delete(connection_tree, c); + splay_delete(connection_tree, c); } -void dump_connections(void) -{ - avl_node_t *node; +int dump_connections(struct evbuffer *out) { + splay_node_t *node; connection_t *c; cp(); - logger(LOG_DEBUG, _("Connections:")); - for(node = connection_tree->head; node; node = node->next) { c = node->data; - logger(LOG_DEBUG, _(" %s at %s options %lx socket %d status %04x outbuf %d/%d/%d"), - c->name, c->hostname, c->options, c->socket, c->status.value, - c->outbufsize, c->outbufstart, c->outbuflen); + if(evbuffer_add_printf(out, - _(" %s at %s options %lx socket %d status %04x\n"), - c->name, c->hostname, c->options, c->socket, - c->status.value) == -1) ++ _(" %s at %s options %lx socket %d status %04x\n"), ++ c->name, c->hostname, c->options, c->socket, ++ c->status.value) == -1) + return errno; } - logger(LOG_DEBUG, _("End of connections.")); + return 0; } -bool read_connection_config(connection_t *c) -{ +bool read_connection_config(connection_t *c) { char *fname; int x; diff --combined src/cygwin/device.c index 6b0dbea4,c6e6f353..97b2a38d --- a/src/cygwin/device.c +++ b/src/cygwin/device.c @@@ -1,7 -1,7 +1,7 @@@ /* device.c -- Interaction with Windows tap driver in a Cygwin environment Copyright (C) 2002-2005 Ivo Timmermans, - 2002-2006 Guus Sliepen + 2002-2009 Guus Sliepen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@@ -38,7 -38,7 +38,7 @@@ int device_fd = -1 static HANDLE device_handle = INVALID_HANDLE_VALUE; char *device = NULL; char *iface = NULL; - char *device_info = NULL; + static char *device_info = NULL; static int device_total_in = 0; static int device_total_out = 0; @@@ -46,7 -46,8 +46,7 @@@ static pid_t reader_pid; static int sp[2]; -bool setup_device(void) -{ +bool setup_device(void) { HKEY key, key2; int i, err; @@@ -72,18 -73,18 +72,18 @@@ } for (i = 0; ; i++) { - len = sizeof(adapterid); + len = sizeof adapterid; if(RegEnumKeyEx(key, i, adapterid, &len, 0, 0, 0, NULL)) break; /* Find out more about this adapter */ - snprintf(regpath, sizeof(regpath), "%s\\%s\\Connection", NETWORK_CONNECTIONS_KEY, adapterid); + snprintf(regpath, sizeof regpath, "%s\\%s\\Connection", NETWORK_CONNECTIONS_KEY, adapterid); if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, regpath, 0, KEY_READ, &key2)) continue; - len = sizeof(adaptername); + len = sizeof adaptername; err = RegQueryValueEx(key2, "Name", 0, 0, adaptername, &len); RegCloseKey(key2); @@@ -107,7 -108,7 +107,7 @@@ continue; } - snprintf(tapname, sizeof(tapname), USERMODEDEVICEDIR "%s" TAPSUFFIX, adapterid); + snprintf(tapname, sizeof tapname, USERMODEDEVICEDIR "%s" TAPSUFFIX, adapterid); device_handle = CreateFile(tapname, GENERIC_WRITE | GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM, 0); if(device_handle != INVALID_HANDLE_VALUE) { CloseHandle(device_handle); @@@ -129,7 -130,7 +129,7 @@@ if(!iface) iface = xstrdup(adaptername); - snprintf(tapname, sizeof(tapname), USERMODEDEVICEDIR "%s" TAPSUFFIX, device); + snprintf(tapname, sizeof tapname, USERMODEDEVICEDIR "%s" TAPSUFFIX, device); /* Now we are going to open this device twice: once for reading and once for writing. We do this because apparently it isn't possible to check for activity in the select() loop. @@@ -153,7 -154,7 +153,7 @@@ /* Get MAC address from tap device */ - if(!DeviceIoControl(device_handle, TAP_IOCTL_GET_MAC, mymac.x, sizeof(mymac.x), mymac.x, sizeof(mymac.x), &len, 0)) { + if(!DeviceIoControl(device_handle, TAP_IOCTL_GET_MAC, mymac.x, sizeof mymac.x, mymac.x, sizeof mymac.x, &len, 0)) { logger(LOG_ERR, _("Could not get MAC address from Windows tap device %s (%s): %s"), device, iface, winerror(GetLastError())); return false; } @@@ -176,7 -177,7 +176,7 @@@ It passes everything it reads to the socket. */ char buf[MTU]; - long lenin; + long inlen; CloseHandle(device_handle); @@@ -199,8 -200,8 +199,8 @@@ /* Pass packets */ for(;;) { - ReadFile(device_handle, buf, MTU, &lenin, NULL); - write(sp[1], buf, lenin); + ReadFile(device_handle, buf, MTU, &inlen, NULL); + write(sp[1], buf, inlen); } } @@@ -217,7 -218,8 +217,7 @@@ return true; } -void close_device(void) -{ +void close_device(void) { cp(); close(sp[0]); @@@ -225,20 -227,24 +225,23 @@@ CloseHandle(device_handle); kill(reader_pid, SIGKILL); + + free(device); + free(iface); } -bool read_packet(vpn_packet_t *packet) -{ - int lenin; +bool read_packet(vpn_packet_t *packet) { + int inlen; cp(); - if((lenin = read(sp[0], packet->data, MTU)) <= 0) { + if((inlen = read(sp[0], packet->data, MTU)) <= 0) { logger(LOG_ERR, _("Error while reading from %s %s: %s"), device_info, device, strerror(errno)); return false; } - packet->len = lenin; + packet->len = inlen; device_total_in += packet->len; @@@ -248,15 -254,16 +251,15 @@@ return true; } -bool write_packet(vpn_packet_t *packet) -{ - long lenout; +bool write_packet(vpn_packet_t *packet) { + long outlen; cp(); ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Writing packet of %d bytes to %s"), packet->len, device_info); - if(!WriteFile (device_handle, packet->data, packet->len, &lenout, NULL)) { + if(!WriteFile (device_handle, packet->data, packet->len, &outlen, NULL)) { logger(LOG_ERR, _("Error while writing to %s %s: %s"), device_info, device, winerror(GetLastError())); return false; } @@@ -266,7 -273,8 +269,7 @@@ return true; } -void dump_device_stats(void) -{ +void dump_device_stats(void) { cp(); logger(LOG_DEBUG, _("Statistics for %s %s:"), device_info, device); diff --combined src/graph.c index 02e8494f,e0c48d42..8e801b3d --- a/src/graph.c +++ b/src/graph.c @@@ -1,6 -1,6 +1,6 @@@ /* graph.c -- graph algorithms - Copyright (C) 2001-2006 Guus Sliepen , + Copyright (C) 2001-2009 Guus Sliepen , 2001-2005 Ivo Timmermans This program is free software; you can redistribute it and/or modify @@@ -46,7 -46,7 +46,7 @@@ #include "system.h" -#include "avl_tree.h" +#include "splay_tree.h" #include "config.h" #include "connection.h" #include "device.h" @@@ -58,16 -58,22 +58,16 @@@ #include "subnet.h" #include "utils.h" -static bool graph_changed = true; - /* Implementation of Kruskal's algorithm. - Running time: O(EN) + Running time: O(E) Please note that sorting on weight is already done by add_edge(). */ -void mst_kruskal(void) -{ - avl_node_t *node, *next; +void mst_kruskal(void) { + splay_node_t *node, *next; edge_t *e; node_t *n; connection_t *c; - int nodes = 0; - int safe_edges = 0; - bool skipped; cp(); @@@ -78,6 -84,11 +78,6 @@@ c->status.mst = false; } - /* Do we have something to do at all? */ - - if(!edge_weight_tree->head) - return; - ifdebug(SCARY_THINGS) logger(LOG_DEBUG, "Running Kruskal's algorithm:"); /* Clear visited status on nodes */ @@@ -85,16 -96,29 +85,16 @@@ for(node = node_tree->head; node; node = node->next) { n = node->data; n->status.visited = false; - nodes++; - } - - /* Starting point */ - - for(node = edge_weight_tree->head; node; node = node->next) { - e = node->data; - if(e->from->status.reachable) { - e->from->status.visited = true; - break; - } } /* Add safe edges */ - for(skipped = false, node = edge_weight_tree->head; node; node = next) { + for(node = edge_weight_tree->head; node; node = next) { next = node->next; e = node->data; - if(!e->reverse || e->from->status.visited == e->to->status.visited) { - skipped = true; + if(!e->reverse || (e->from->status.visited && e->to->status.visited)) continue; - } e->from->status.visited = true; e->to->status.visited = true; @@@ -105,148 -129,38 +105,148 @@@ if(e->reverse->connection) e->reverse->connection->status.mst = true; - safe_edges++; - ifdebug(SCARY_THINGS) logger(LOG_DEBUG, " Adding edge %s - %s weight %d", e->from->name, e->to->name, e->weight); + } +} - if(skipped) { - skipped = false; - next = edge_weight_tree->head; - continue; +/* Implementation of Dijkstra's algorithm. + Running time: O(N^2) +*/ + +void sssp_dijkstra(void) { + splay_node_t *node, *to; + edge_t *e; + node_t *n, *m; + list_t *todo_list; + list_node_t *lnode, *nnode; + bool indirect; + + cp(); + + todo_list = list_alloc(NULL); + + ifdebug(SCARY_THINGS) logger(LOG_DEBUG, "Running Dijkstra's algorithm:"); + + /* Clear visited status on nodes */ + + for(node = node_tree->head; node; node = node->next) { + n = node->data; + n->status.visited = false; + n->status.indirect = true; + n->distance = -1; + } + + /* Begin with myself */ + + myself->status.indirect = false; + myself->nexthop = myself; + myself->via = myself; + myself->distance = 0; + list_insert_head(todo_list, myself); + + /* Loop while todo_list is filled */ + + while(todo_list->head) { + n = NULL; + nnode = NULL; + + /* Select node from todo_list with smallest distance */ + + for(lnode = todo_list->head; lnode; lnode = lnode->next) { + m = lnode->data; + if(!n || m->status.indirect < n->status.indirect || m->distance < n->distance) { + n = m; + nnode = lnode; + } + } + + /* Mark this node as visited and remove it from the todo_list */ + + n->status.visited = true; + list_unlink_node(todo_list, nnode); + + /* Update distance of neighbours and add them to the todo_list */ + + for(to = n->edge_tree->head; to; to = to->next) { /* "to" is the edge connected to "from" */ + e = to->data; + + if(e->to->status.visited || !e->reverse) + continue; + + /* Situation: + + / + / + ----->(n)---e-->(e->to) + \ + \ + + Where e is an edge, (n) and (e->to) are nodes. + n->address is set to the e->address of the edge left of n to n. + We are currently examining the edge e right of n from n: + + - If e->reverse->address != n->address, then e->to is probably + not reachable for the nodes left of n. We do as if the indirectdata + flag is set on edge e. + - If edge e provides for better reachability of e->to, update e->to. + */ + + if(e->to->distance < 0) + list_insert_tail(todo_list, e->to); + + indirect = n->status.indirect || e->options & OPTION_INDIRECT || ((n != myself) && sockaddrcmp(&n->address, &e->reverse->address)); + + if(e->to->distance >= 0 && (!e->to->status.indirect || indirect) && e->to->distance <= n->distance + e->weight) + continue; + + e->to->distance = n->distance + e->weight; + e->to->status.indirect = indirect; + e->to->nexthop = (n->nexthop == myself) ? e->to : n->nexthop; + e->to->via = indirect ? n->via : e->to; + e->to->options = e->options; + + if(sockaddrcmp(&e->to->address, &e->address)) { + node = splay_unlink(node_udp_tree, e->to); + sockaddrfree(&e->to->address); + sockaddrcpy(&e->to->address, &e->address); + + if(e->to->hostname) + free(e->to->hostname); + + e->to->hostname = sockaddr2hostname(&e->to->address); + + if(node) + splay_insert_node(node_udp_tree, node); + + if(e->to->options & OPTION_PMTU_DISCOVERY) { + e->to->mtuprobes = 0; + e->to->minmtu = 0; + e->to->maxmtu = MTU; + if(e->to->status.validkey) + send_mtu_probe(e->to); + } + } + + ifdebug(SCARY_THINGS) logger(LOG_DEBUG, " Updating edge %s - %s weight %d distance %d", e->from->name, + e->to->name, e->weight, e->to->distance); } } - ifdebug(SCARY_THINGS) logger(LOG_DEBUG, "Done, counted %d nodes and %d safe edges.", nodes, - safe_edges); + list_free(todo_list); } /* Implementation of a simple breadth-first search algorithm. Running time: O(E) */ -void sssp_bfs(void) -{ - avl_node_t *node, *next, *to; +void sssp_bfs(void) { + splay_node_t *node, *to; edge_t *e; node_t *n; list_t *todo_list; list_node_t *from, *todonext; bool indirect; - char *name; - char *address, *port; - char *envp[7]; - int i; cp(); @@@ -313,7 -227,7 +313,7 @@@ e->to->options = e->options; if(sockaddrcmp(&e->to->address, &e->address)) { - node = avl_unlink(node_udp_tree, e->to); + node = splay_unlink(node_udp_tree, e->to); sockaddrfree(&e->to->address); sockaddrcpy(&e->to->address, &e->address); @@@ -323,7 -237,7 +323,7 @@@ e->to->hostname = sockaddr2hostname(&e->to->address); if(node) - avl_insert_node(node_udp_tree, node); + splay_insert_node(node_udp_tree, node); if(e->to->options & OPTION_PMTU_DISCOVERY) { e->to->mtuprobes = 0; @@@ -342,15 -256,6 +342,15 @@@ } list_free(todo_list); +} + +void check_reachability() { + splay_node_t *node, *next; + node_t *n; + char *name; + char *address, *port; + char *envp[7]; + int i; /* Check reachability status. */ @@@ -364,11 -269,11 +364,11 @@@ if(n->status.reachable) { ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Node %s (%s) became reachable"), n->name, n->hostname); - avl_insert(node_udp_tree, n); + splay_insert(node_udp_tree, n); } else { ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Node %s (%s) became unreachable"), n->name, n->hostname); - avl_delete(node_udp_tree, n); + splay_delete(node_udp_tree, n); } n->status.validkey = false; @@@ -406,44 -311,74 +406,45 @@@ } } -void graph(void) -{ - subnet_cache_flush(); - sssp_bfs(); - mst_kruskal(); - graph_changed = true; -} - - - /* Dump nodes and edges to a graphviz file. The file can be converted to an image with dot -Tpng graph_filename -o image_filename.png -Gconcentrate=true */ -void dump_graph(void) -{ - avl_node_t *node; +int dump_graph(struct evbuffer *out) { + splay_node_t *node; node_t *n; edge_t *e; - char *filename = NULL, *tmpname = NULL; - FILE *file; - - if(!graph_changed || !get_config_string(lookup_config(config_tree, "GraphDumpFile"), &filename)) - return; - - graph_changed = false; - - ifdebug(PROTOCOL) logger(LOG_NOTICE, "Dumping graph"); - - if(filename[0] == '|') { - file = popen(filename + 1, "w"); - } else { - asprintf(&tmpname, "%s.new", filename); - file = fopen(tmpname, "w"); - } - if(!file) { - logger(LOG_ERR, "Unable to open graph dump file %s: %s", filename, strerror(errno)); - free(tmpname); - return; - } - - fprintf(file, "digraph {\n"); + if(evbuffer_add_printf(out, "digraph {\n") == -1) + return errno; /* dump all nodes first */ for(node = node_tree->head; node; node = node->next) { n = node->data; - fprintf(file, " %s [label = \"%s\"];\n", n->name, n->name); + if(evbuffer_add_printf(out, " %s [label = \"%s\"];\n", + n->name, n->name) == -1) + return errno; } /* now dump all edges */ for(node = edge_weight_tree->head; node; node = node->next) { e = node->data; - fprintf(file, " %s -> %s;\n", e->from->name, e->to->name); + if(evbuffer_add_printf(out, " %s -> %s;\n", + e->from->name, e->to->name) == -1) + return errno; } - fprintf(file, "}\n"); - - if(filename[0] == '|') { - pclose(file); - } else { - fclose(file); -#ifdef HAVE_MINGW - unlink(filename); -#endif - rename(tmpname, filename); - free(tmpname); - } + if(evbuffer_add_printf(out, "}\n") == -1) + return errno; + + return 0; +} + +void graph(void) { ++ subnet_cache_flush(); + sssp_dijkstra(); + check_reachability(); + mst_kruskal(); } diff --combined src/linux/device.c index 5be210a5,2e447556..b02e88e7 --- a/src/linux/device.c +++ b/src/linux/device.c @@@ -1,7 -1,7 +1,7 @@@ /* device.c -- Interaction with Linux ethertap and tun/tap device Copyright (C) 2001-2005 Ivo Timmermans, - 2001-2006 Guus Sliepen + 2001-2009 Guus Sliepen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@@ -34,6 -34,7 +34,7 @@@ #include "net.h" #include "route.h" #include "utils.h" + #include "xalloc.h" typedef enum device_type_t { DEVICE_TYPE_ETHERTAP, @@@ -43,27 -44,28 +44,27 @@@ int device_fd = -1; static device_type_t device_type; - char *device; - char *iface; - char ifrname[IFNAMSIZ]; - char *device_info; + char *device = NULL; + char *iface = NULL; + static char ifrname[IFNAMSIZ]; + static char *device_info; static int device_total_in = 0; static int device_total_out = 0; -bool setup_device(void) -{ +bool setup_device(void) { struct ifreq ifr; cp(); if(!get_config_string(lookup_config(config_tree, "Device"), &device)) - device = DEFAULT_DEVICE; + device = xstrdup(DEFAULT_DEVICE); if(!get_config_string(lookup_config(config_tree, "Interface"), &iface)) #ifdef HAVE_LINUX_IF_TUN_H - iface = netname; + iface = xstrdup(netname); #else - iface = rindex(device, '/') ? rindex(device, '/') + 1 : device; + iface = xstrdup(rindex(device, '/') ? rindex(device, '/') + 1 : device); #endif device_fd = open(device, O_RDWR | O_NONBLOCK); @@@ -75,7 -77,7 +76,7 @@@ #ifdef HAVE_LINUX_IF_TUN_H /* Ok now check if this is an old ethertap or a new tun/tap thingie */ - memset(&ifr, 0, sizeof(ifr)); + memset(&ifr, 0, sizeof ifr); if(routing_mode == RMODE_ROUTER) { ifr.ifr_flags = IFF_TUN; device_type = DEVICE_TYPE_TUN; @@@ -91,11 -93,13 +92,13 @@@ if(!ioctl(device_fd, TUNSETIFF, &ifr)) { strncpy(ifrname, ifr.ifr_name, IFNAMSIZ); - iface = ifrname; + if(iface) free(iface); + iface = xstrdup(ifrname); } else if(!ioctl(device_fd, (('T' << 8) | 202), &ifr)) { logger(LOG_WARNING, _("Old ioctl() request was needed for %s"), device); strncpy(ifrname, ifr.ifr_name, IFNAMSIZ); - iface = ifrname; + if(iface) free(iface); + iface = xstrdup(ifrname); } else #endif { @@@ -103,7 -107,9 +106,9 @@@ overwrite_mac = true; device_info = _("Linux ethertap device"); device_type = DEVICE_TYPE_ETHERTAP; - iface = rindex(device, '/') ? rindex(device, '/') + 1 : device; + if(iface) + free(iface); + iface = xstrdup(rindex(device, '/') ? rindex(device, '/') + 1 : device); } logger(LOG_INFO, _("%s is a %s"), device, device_info); @@@ -111,50 -117,55 +116,53 @@@ return true; } -void close_device(void) -{ +void close_device(void) { cp(); close(device_fd); + + free(device); + free(iface); } -bool read_packet(vpn_packet_t *packet) -{ - int lenin; +bool read_packet(vpn_packet_t *packet) { + int inlen; cp(); switch(device_type) { case DEVICE_TYPE_TUN: - lenin = read(device_fd, packet->data + 10, MTU - 10); + inlen = read(device_fd, packet->data + 10, MTU - 10); - if(lenin <= 0) { + if(inlen <= 0) { logger(LOG_ERR, _("Error while reading from %s %s: %s"), device_info, device, strerror(errno)); return false; } - packet->len = lenin + 10; + packet->len = inlen + 10; break; case DEVICE_TYPE_TAP: - lenin = read(device_fd, packet->data, MTU); + inlen = read(device_fd, packet->data, MTU); - if(lenin <= 0) { + if(inlen <= 0) { logger(LOG_ERR, _("Error while reading from %s %s: %s"), device_info, device, strerror(errno)); return false; } - packet->len = lenin; + packet->len = inlen; break; case DEVICE_TYPE_ETHERTAP: - lenin = read(device_fd, packet->data - 2, MTU + 2); + inlen = read(device_fd, packet->data - 2, MTU + 2); - if(lenin <= 0) { + if(inlen <= 0) { logger(LOG_ERR, _("Error while reading from %s %s: %s"), device_info, device, strerror(errno)); return false; } - packet->len = lenin - 2; + packet->len = inlen - 2; break; } @@@ -166,7 -177,8 +174,7 @@@ return true; } -bool write_packet(vpn_packet_t *packet) -{ +bool write_packet(vpn_packet_t *packet) { cp(); ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Writing packet of %d bytes to %s"), @@@ -204,7 -216,8 +212,7 @@@ return true; } -void dump_device_stats(void) -{ +void dump_device_stats(void) { cp(); logger(LOG_DEBUG, _("Statistics for %s %s:"), device_info, device); diff --combined src/mingw/device.c index d6dde92d,e025cf78..915256f5 --- a/src/mingw/device.c +++ b/src/mingw/device.c @@@ -1,7 -1,7 +1,7 @@@ /* device.c -- Interaction with Windows tap driver in a MinGW environment Copyright (C) 2002-2005 Ivo Timmermans, - 2002-2007 Guus Sliepen + 2002-2009 Guus Sliepen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@@ -38,7 -38,7 +38,7 @@@ int device_fd = 0 static HANDLE device_handle = INVALID_HANDLE_VALUE; char *device = NULL; char *iface = NULL; - char *device_info = NULL; + static char *device_info = NULL; static int device_total_in = 0; static int device_total_out = 0; @@@ -52,7 -52,7 +52,7 @@@ static struct packetbuf static int nbufs = 64; - DWORD WINAPI tapreader(void *bla) { + static DWORD WINAPI tapreader(void *bla) { int sock, err, status; struct addrinfo *ai; struct addrinfo hint = { @@@ -123,7 -123,8 +123,7 @@@ } } -bool setup_device(void) -{ +bool setup_device(void) { HKEY key, key2; int i; @@@ -160,18 -161,18 +160,18 @@@ } for (i = 0; ; i++) { - len = sizeof(adapterid); + len = sizeof adapterid; if(RegEnumKeyEx(key, i, adapterid, &len, 0, 0, 0, NULL)) break; /* Find out more about this adapter */ - snprintf(regpath, sizeof(regpath), "%s\\%s\\Connection", NETWORK_CONNECTIONS_KEY, adapterid); + snprintf(regpath, sizeof regpath, "%s\\%s\\Connection", NETWORK_CONNECTIONS_KEY, adapterid); if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, regpath, 0, KEY_READ, &key2)) continue; - len = sizeof(adaptername); + len = sizeof adaptername; err = RegQueryValueEx(key2, "Name", 0, 0, adaptername, &len); RegCloseKey(key2); @@@ -195,7 -196,7 +195,7 @@@ continue; } - snprintf(tapname, sizeof(tapname), USERMODEDEVICEDIR "%s" TAPSUFFIX, adapterid); + snprintf(tapname, sizeof tapname, USERMODEDEVICEDIR "%s" TAPSUFFIX, adapterid); device_handle = CreateFile(tapname, GENERIC_WRITE | GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, 0); if(device_handle != INVALID_HANDLE_VALUE) { found = true; @@@ -219,7 -220,7 +219,7 @@@ /* Try to open the corresponding tap device */ if(device_handle == INVALID_HANDLE_VALUE) { - snprintf(tapname, sizeof(tapname), USERMODEDEVICEDIR "%s" TAPSUFFIX, device); + snprintf(tapname, sizeof tapname, USERMODEDEVICEDIR "%s" TAPSUFFIX, device); device_handle = CreateFile(tapname, GENERIC_WRITE | GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, 0); } @@@ -230,7 -231,7 +230,7 @@@ /* Get MAC address from tap device */ - if(!DeviceIoControl(device_handle, TAP_IOCTL_GET_MAC, mymac.x, sizeof(mymac.x), mymac.x, sizeof(mymac.x), &len, 0)) { + if(!DeviceIoControl(device_handle, TAP_IOCTL_GET_MAC, mymac.x, sizeof mymac.x, mymac.x, sizeof mymac.x, &len, 0)) { logger(LOG_ERR, _("Could not get MAC address from Windows tap device %s (%s): %s"), device, iface, winerror(GetLastError())); return false; } @@@ -298,7 -299,7 +298,7 @@@ /* Set media status for newer TAP-Win32 devices */ status = true; - DeviceIoControl(device_handle, TAP_IOCTL_SET_MEDIA_STATUS, &status, sizeof(status), &status, sizeof(status), &len, NULL); + DeviceIoControl(device_handle, TAP_IOCTL_SET_MEDIA_STATUS, &status, sizeof status, &status, sizeof status, &len, NULL); device_info = _("Windows tap device"); @@@ -307,13 -308,18 +307,16 @@@ return true; } -void close_device(void) -{ +void close_device(void) { cp(); CloseHandle(device_handle); + + free(device); + free(iface); } -bool read_packet(vpn_packet_t *packet) -{ +bool read_packet(vpn_packet_t *packet) { unsigned char bufno; cp(); @@@ -335,8 -341,9 +338,8 @@@ return true; } -bool write_packet(vpn_packet_t *packet) -{ - long lenout; +bool write_packet(vpn_packet_t *packet) { + long outlen; OVERLAPPED overlapped = {0}; cp(); @@@ -344,7 -351,7 +347,7 @@@ ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Writing packet of %d bytes to %s"), packet->len, device_info); - if(!WriteFile(device_handle, packet->data, packet->len, &lenout, &overlapped)) { + if(!WriteFile(device_handle, packet->data, packet->len, &outlen, &overlapped)) { logger(LOG_ERR, _("Error while writing to %s %s: %s"), device_info, device, winerror(GetLastError())); return false; } @@@ -354,7 -361,8 +357,7 @@@ return true; } -void dump_device_stats(void) -{ +void dump_device_stats(void) { cp(); logger(LOG_DEBUG, _("Statistics for %s %s:"), device_info, device); diff --combined src/net.c index 659b0784,0cdc72cc..8ee78214 --- a/src/net.c +++ b/src/net.c @@@ -1,7 -1,7 +1,7 @@@ /* net.c -- most of the network code Copyright (C) 1998-2005 Ivo Timmermans, - 2000-2006 Guus Sliepen + 2000-2009 Guus Sliepen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@@ -25,10 -25,11 +25,10 @@@ #include #include "utils.h" -#include "avl_tree.h" +#include "splay_tree.h" #include "conf.h" #include "connection.h" #include "device.h" -#include "event.h" #include "graph.h" #include "logger.h" #include "meta.h" @@@ -36,13 -37,20 +36,13 @@@ #include "netutl.h" #include "process.h" #include "protocol.h" -#include "route.h" #include "subnet.h" #include "xalloc.h" -bool do_purge = false; -volatile bool running = false; - -time_t now = 0; - /* Purge edges and subnets of unreachable nodes. Use carefully. */ -static void purge(void) -{ - avl_node_t *nnode, *nnext, *enode, *enext, *snode, *snext; +void purge(void) { + splay_node_t *nnode, *nnext, *enode, *enext, *snode, *snext; node_t *n; edge_t *e; subnet_t *s; @@@ -100,6 -108,54 +100,6 @@@ } } -/* - put all file descriptors in an fd_set array - While we're at it, purge stuff that needs to be removed. -*/ -static int build_fdset(fd_set *readset, fd_set *writeset) -{ - avl_node_t *node, *next; - connection_t *c; - int i, max = 0; - - cp(); - - FD_ZERO(readset); - FD_ZERO(writeset); - - for(node = connection_tree->head; node; node = next) { - next = node->next; - c = node->data; - - if(c->status.remove) { - connection_del(c); - if(!connection_tree->head) - purge(); - } else { - FD_SET(c->socket, readset); - if(c->outbuflen > 0) - FD_SET(c->socket, writeset); - if(c->socket > max) - max = c->socket; - } - } - - for(i = 0; i < listen_sockets; i++) { - FD_SET(listen_socket[i].tcp, readset); - if(listen_socket[i].tcp > max) - max = listen_socket[i].tcp; - FD_SET(listen_socket[i].udp, readset); - if(listen_socket[i].udp > max) - max = listen_socket[i].udp; - } - - FD_SET(device_fd, readset); - if(device_fd > max) - max = device_fd; - - return max; -} - /* Terminate a connection: - Close the socket @@@ -107,12 -163,17 +107,12 @@@ - Check if we need to retry making an outgoing connection - Deactivate the host */ -void terminate_connection(connection_t *c, bool report) -{ +void terminate_connection(connection_t *c, bool report) { cp(); - if(c->status.remove) - return; - ifdebug(CONNECTIONS) logger(LOG_NOTICE, _("Closing connection with %s (%s)"), c->name, c->hostname); - c->status.remove = true; c->status.active = false; if(c->node) @@@ -146,10 -207,16 +146,10 @@@ /* Check if this was our outgoing connection */ - if(c->outgoing) { + if(c->outgoing) retry_outgoing(c->outgoing); - c->outgoing = NULL; - } - free(c->outbuf); - c->outbuf = NULL; - c->outbuflen = 0; - c->outbufsize = 0; - c->outbufstart = 0; + connection_del(c); } /* @@@ -160,10 -227,10 +160,10 @@@ end does not reply in time, we consider them dead and close the connection. */ -static void check_dead_connections(void) -{ - avl_node_t *node, *next; +static void timeout_handler(int fd, short events, void *event) { + splay_node_t *node, *next; connection_t *c; + time_t now = time(NULL); cp(); @@@ -176,162 -243,241 +176,162 @@@ if(c->status.pinged) { ifdebug(CONNECTIONS) logger(LOG_INFO, _("%s (%s) didn't respond to PING in %ld seconds"), c->name, c->hostname, now - c->last_ping_time); - c->status.timeout = true; terminate_connection(c, true); + continue; } else if(c->last_ping_time + pinginterval < now) { send_ping(c); } } else { - if(c->status.remove) { - logger(LOG_WARNING, _("Old connection_t for %s (%s) status %04x still lingering, deleting..."), - c->name, c->hostname, c->status.value); - connection_del(c); - continue; - } - ifdebug(CONNECTIONS) logger(LOG_WARNING, _("Timeout from %s (%s) during authentication"), - c->name, c->hostname); if(c->status.connecting) { + ifdebug(CONNECTIONS) + logger(LOG_WARNING, _("Timeout while connecting to %s (%s)"), c->name, c->hostname); c->status.connecting = false; closesocket(c->socket); do_outgoing_connection(c); } else { + ifdebug(CONNECTIONS) logger(LOG_WARNING, _("Timeout from %s (%s) during authentication"), c->name, c->hostname); terminate_connection(c, false); + continue; } } } + } - if(c->outbuflen > 0 && c->last_flushed_time + pingtimeout < now) { - if(c->status.active) { - ifdebug(CONNECTIONS) logger(LOG_INFO, - _("%s (%s) could not flush for %ld seconds (%d bytes remaining)"), - c->name, c->hostname, now - c->last_flushed_time, c->outbuflen); - c->status.timeout = true; - terminate_connection(c, true); - } + event_add(event, &(struct timeval){pingtimeout, 0}); +} + +void handle_meta_connection_data(int fd, short events, void *data) { + connection_t *c = data; + int result; + socklen_t len = sizeof result; + + if(c->status.connecting) { + c->status.connecting = false; + + getsockopt(c->socket, SOL_SOCKET, SO_ERROR, &result, &len); + + if(!result) + finish_connecting(c); + else { + ifdebug(CONNECTIONS) logger(LOG_DEBUG, + _("Error while connecting to %s (%s): %s"), + c->name, c->hostname, strerror(result)); + closesocket(c->socket); + do_outgoing_connection(c); + return; } } + + if (!receive_meta(c)) { + terminate_connection(c, c->status.active); + return; + } } -/* - check all connections to see if anything - happened on their sockets -*/ -static void check_network_activity(fd_set * readset, fd_set * writeset) -{ +static void sigterm_handler(int signal, short events, void *data) { + logger(LOG_NOTICE, _("Got %s signal"), strsignal(signal)); + event_loopexit(NULL); +} + +static void sighup_handler(int signal, short events, void *data) { + logger(LOG_NOTICE, _("Got %s signal"), strsignal(signal)); + reload_configuration(); +} + +int reload_configuration(void) { connection_t *c; - avl_node_t *node; - int result, i; - socklen_t len = sizeof(result); - vpn_packet_t packet; + splay_node_t *node, *next; + char *fname; + struct stat s; + static time_t last_config_check = 0; - cp(); + /* Reread our own configuration file */ - /* check input from kernel */ - if(FD_ISSET(device_fd, readset)) { - if(read_packet(&packet)) { - packet.priority = 0; - route(myself, &packet); - } + exit_configuration(&config_tree); + init_configuration(&config_tree); + + if(!read_server_config()) { + logger(LOG_ERR, _("Unable to reread configuration file, exitting.")); + event_loopexit(NULL); + return EINVAL; } - /* check meta connections */ - for(node = connection_tree->head; node; node = node->next) { + /* Close connections to hosts that have a changed or deleted host config file */ + + for(node = connection_tree->head; node; node = next) { c = node->data; + next = node->next; + + if(c->outgoing) { + free(c->outgoing->name); + if(c->outgoing->ai) + freeaddrinfo(c->outgoing->ai); + free(c->outgoing); + c->outgoing = NULL; + } + + asprintf(&fname, "%s/hosts/%s", confbase, c->name); + if(stat(fname, &s) || s.st_mtime > last_config_check) + terminate_connection(c, c->status.active); + free(fname); + } - if(c->status.remove) - continue; + last_config_check = time(NULL); - if(FD_ISSET(c->socket, readset)) { - if(c->status.connecting) { - c->status.connecting = false; - getsockopt(c->socket, SOL_SOCKET, SO_ERROR, &result, &len); + /* Try to make outgoing connections */ + + try_outgoing_connections(); - if(!result) - finish_connecting(c); - else { - ifdebug(CONNECTIONS) logger(LOG_DEBUG, - _("Error while connecting to %s (%s): %s"), - c->name, c->hostname, strerror(result)); - closesocket(c->socket); - do_outgoing_connection(c); - continue; - } - } + return 0; +} - if(!receive_meta(c)) { - terminate_connection(c, c->status.active); - continue; - } - } +void retry(void) { + connection_t *c; + splay_node_t *node; - if(FD_ISSET(c->socket, writeset)) { - if(!flush_meta(c)) { - terminate_connection(c, c->status.active); - continue; - } + for(node = connection_tree->head; node; node = node->next) { + c = node->data; + + if(c->outgoing && !c->node) { + if(timeout_initialized(&c->outgoing->ev)) + event_del(&c->outgoing->ev); + if(c->status.connecting) + close(c->socket); + c->outgoing->timeout = 0; + do_outgoing_connection(c); } } - - for(i = 0; i < listen_sockets; i++) { - if(FD_ISSET(listen_socket[i].udp, readset)) - handle_incoming_vpn_data(listen_socket[i].udp); - - if(FD_ISSET(listen_socket[i].tcp, readset)) - handle_new_meta_connection(listen_socket[i].tcp); - } } /* this is where it all happens... */ -int main_loop(void) -{ - fd_set readset, writeset; - struct timeval tv; - int r, maxfd; - time_t last_ping_check, last_config_check, last_graph_dump; - event_t *event; +int main_loop(void) { + struct event timeout_event; + struct event sighup_event; + struct event sigterm_event; + struct event sigquit_event; cp(); - last_ping_check = now; - last_config_check = now; - last_graph_dump = now; - - srand(now); - srand48(now); - - running = true; - - while(running) { - now = time(NULL); - - // tv.tv_sec = 1 + (rand() & 7); /* Approx. 5 seconds, randomized to prevent global synchronisation effects */ - tv.tv_sec = 1; - tv.tv_usec = 0; - - maxfd = build_fdset(&readset, &writeset); - - r = select(maxfd + 1, &readset, &writeset, NULL, &tv); - - if(r < 0) { - if(errno != EINTR && errno != EAGAIN) { - logger(LOG_ERR, _("Error while waiting for input: %s"), - strerror(errno)); - cp_trace(); - dump_connections(); - return 1; - } - - continue; - } - - check_network_activity(&readset, &writeset); - - if(do_purge) { - purge(); - do_purge = false; - } - - /* Let's check if everybody is still alive */ - - if(last_ping_check + pingtimeout < now) { - check_dead_connections(); - last_ping_check = now; - - if(routing_mode == RMODE_SWITCH) - age_subnets(); - - age_past_requests(); - - /* Should we regenerate our key? */ - - if(keyexpires < now) { - ifdebug(STATUS) logger(LOG_INFO, _("Regenerating symmetric key")); - - RAND_pseudo_bytes((unsigned char *)myself->key, myself->keylength); - if(myself->cipher) - EVP_DecryptInit_ex(&packet_ctx, myself->cipher, NULL, (unsigned char *)myself->key, (unsigned char *)myself->key + myself->cipher->key_len); - send_key_changed(broadcast, myself); - keyexpires = now + keylifetime; - } - } - - if(sigalrm) { - logger(LOG_INFO, _("Flushing event queue")); - expire_events(); - sigalrm = false; - } - - while((event = get_expired_event())) { - event->handler(event->data); - free_event(event); - } - - if(sighup) { - connection_t *c; - avl_node_t *node; - char *fname; - struct stat s; - - sighup = false; - - /* Reread our own configuration file */ - - exit_configuration(&config_tree); - init_configuration(&config_tree); - - if(!read_server_config()) { - logger(LOG_ERR, _("Unable to reread configuration file, exitting.")); - return 1; - } - - /* Close connections to hosts that have a changed or deleted host config file */ - - for(node = connection_tree->head; node; node = node->next) { - c = node->data; - - asprintf(&fname, "%s/hosts/%s", confbase, c->name); - if(stat(fname, &s) || s.st_mtime > last_config_check) - terminate_connection(c, c->status.active); - free(fname); - } - - last_config_check = now; - - /* Try to make outgoing connections */ - - try_outgoing_connections(); - } - - /* Dump graph if wanted every 60 seconds*/ - - if(last_graph_dump + 60 < now) { - dump_graph(); - last_graph_dump = now; - } + timeout_set(&timeout_event, timeout_handler, &timeout_event); + event_add(&timeout_event, &(struct timeval){pingtimeout, 0}); + signal_set(&sighup_event, SIGHUP, sighup_handler, NULL); + signal_add(&sighup_event, NULL); + signal_set(&sigterm_event, SIGTERM, sigterm_handler, NULL); + signal_add(&sigterm_event, NULL); + signal_set(&sigquit_event, SIGQUIT, sigterm_handler, NULL); + signal_add(&sigquit_event, NULL); + + if(event_loop(0) < 0) { + logger(LOG_ERR, _("Error while waiting for input: %s"), strerror(errno)); + return 1; } + signal_del(&sighup_event); + signal_del(&sigterm_event); + signal_del(&sigquit_event); + event_del(&timeout_event); + return 0; } diff --combined src/net.h index 1f7b4576,e07e6465..d320c15a --- a/src/net.h +++ b/src/net.h @@@ -1,7 -1,7 +1,7 @@@ /* net.h -- header for net.c Copyright (C) 1998-2005 Ivo Timmermans - 2000-2006 Guus Sliepen + 2000-2009 Guus Sliepen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@@ -23,9 -23,9 +23,9 @@@ #ifndef __TINC_NET_H__ #define __TINC_NET_H__ -#include - #include "ipv6.h" +#include "cipher.h" +#include "digest.h" #ifdef ENABLE_JUMBOGRAMS #define MTU 9018 /* 9000 bytes payload + 14 bytes ethernet header + 4 bytes VLAN tag */ @@@ -33,13 -33,11 +33,11 @@@ #define MTU 1518 /* 1500 bytes payload + 14 bytes ethernet header + 4 bytes VLAN tag */ #endif -#define MAXSIZE (MTU + 4 + EVP_MAX_BLOCK_LENGTH + EVP_MAX_MD_SIZE + MTU/64 + 20) /* MTU + seqno + padding + HMAC + compressor overhead */ +#define MAXSIZE (MTU + 4 + CIPHER_MAX_BLOCK_SIZE + DIGEST_MAX_SIZE + MTU/64 + 20) /* MTU + seqno + padding + HMAC + compressor overhead */ #define MAXBUFSIZE ((MAXSIZE > 2048 ? MAXSIZE : 2048) + 128) /* Enough room for a request with a MAXSIZEd packet or a 8192 bits RSA key */ -#define MAXSOCKETS 128 /* Overkill... */ +#define MAXSOCKETS 8 /* Probably overkill... */ - #define MAXQUEUELENGTH 8 /* Maximum number of packats in a single queue */ - typedef struct mac_t { uint8_t x[6]; } mac_t; @@@ -87,26 -85,14 +85,16 @@@ typedef struct vpn_packet_t uint8_t data[MAXSIZE]; } vpn_packet_t; - typedef struct queue_element_t { - void *packet; - struct queue_element_t *prev; - struct queue_element_t *next; - } queue_element_t; - - typedef struct packet_queue_t { - queue_element_t *head; - queue_element_t *tail; - } packet_queue_t; - typedef struct listen_socket_t { + struct event ev_tcp; + struct event ev_udp; int tcp; int udp; sockaddr_t sa; } listen_socket_t; #include "conf.h" + #include "list.h" typedef struct outgoing_t { char *name; @@@ -114,28 -100,33 +102,30 @@@ struct config_t *cfg; struct addrinfo *ai; struct addrinfo *aip; + struct event ev; } outgoing_t; + extern list_t *outgoing_list; + extern int maxoutbufsize; extern int seconds_till_retry; extern int addressfamily; extern listen_socket_t listen_socket[MAXSOCKETS]; extern int listen_sockets; -extern int keyexpires; extern int keylifetime; extern bool do_prune; -extern bool do_purge; extern char *myport; -extern time_t now; -extern EVP_CIPHER_CTX packet_ctx; /* Yes, very strange placement indeed, but otherwise the typedefs get all tangled up */ #include "connection.h" #include "node.h" extern void retry_outgoing(outgoing_t *); -extern void handle_incoming_vpn_data(int); +extern void handle_incoming_vpn_data(int, short, void *); extern void finish_connecting(struct connection_t *); extern void do_outgoing_connection(struct connection_t *); -extern bool handle_new_meta_connection(int); +extern void handle_new_meta_connection(int, short, void *); extern int setup_listen_socket(const sockaddr_t *); extern int setup_vpn_in_socket(const sockaddr_t *); extern void send_packet(const struct node_t *, vpn_packet_t *); @@@ -150,12 -141,6 +140,12 @@@ extern void terminate_connection(struc extern void flush_queue(struct node_t *); extern bool read_rsa_public_key(struct connection_t *); extern void send_mtu_probe(struct node_t *); +extern void handle_device_data(int, short, void *); +extern void handle_meta_connection_data(int, short, void *); +extern void regenerate_key(); +extern void purge(void); +extern void retry(void); +extern int reload_configuration(void); #ifndef HAVE_MINGW #define closesocket(s) close(s) diff --combined src/net_packet.c index 754e6699,544bbde7..7d640cb6 --- a/src/net_packet.c +++ b/src/net_packet.c @@@ -1,7 -1,7 +1,7 @@@ /* net_packet.c -- Handles in- and outgoing VPN packets Copyright (C) 1998-2005 Ivo Timmermans, - 2000-2006 Guus Sliepen + 2000-2009 Guus Sliepen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@@ -22,17 -22,21 +22,17 @@@ #include "system.h" -#include -#include -#include -#include -#include - #include #include LZO1X_H -#include "avl_tree.h" +#include "splay_tree.h" +#include "cipher.h" #include "conf.h" #include "connection.h" +#include "crypto.h" +#include "digest.h" #include "device.h" #include "ethernet.h" -#include "event.h" #include "graph.h" #include "list.h" #include "logger.h" @@@ -49,20 -53,23 +49,20 @@@ #endif int keylifetime = 0; -int keyexpires = 0; -EVP_CIPHER_CTX packet_ctx; static char lzo_wrkmem[LZO1X_999_MEM_COMPRESS > LZO1X_1_MEM_COMPRESS ? LZO1X_999_MEM_COMPRESS : LZO1X_1_MEM_COMPRESS]; static void send_udppacket(node_t *, vpn_packet_t *); #define MAX_SEQNO 1073741824 -void send_mtu_probe(node_t *n) -{ +static void send_mtu_probe_handler(int fd, short events, void *data) { + node_t *n = data; vpn_packet_t packet; int len, i; cp(); n->mtuprobes++; - n->mtuevent = NULL; if(n->mtuprobes >= 10 && !n->minmtu) { ifdebug(TRAFFIC) logger(LOG_INFO, _("No response to MTU probes from %s (%s)"), n->name, n->hostname); @@@ -81,21 -88,20 +81,22 @@@ len = 64; memset(packet.data, 0, 14); - RAND_pseudo_bytes(packet.data + 14, len - 14); + randomize(packet.data + 14, len - 14); packet.len = len; + packet.priority = 0; ifdebug(TRAFFIC) logger(LOG_INFO, _("Sending MTU probe length %d to %s (%s)"), len, n->name, n->hostname); send_udppacket(n, &packet); } - n->mtuevent = new_event(); - n->mtuevent->handler = (event_handler_t)send_mtu_probe; - n->mtuevent->data = n; - n->mtuevent->time = now + 1; - event_add(n->mtuevent); + event_add(&n->mtuevent, &(struct timeval){1, 0}); +} + +void send_mtu_probe(node_t *n) { + if(!timeout_initialized(&n->mtuevent)) + timeout_set(&n->mtuevent, send_mtu_probe_handler, n); + send_mtu_probe_handler(0, 0, n); } void mtu_probe_h(node_t *n, vpn_packet_t *packet) { @@@ -110,7 -116,8 +111,7 @@@ } } -static length_t compress_packet(uint8_t *dest, const uint8_t *source, length_t len, int level) -{ +static length_t compress_packet(uint8_t *dest, const uint8_t *source, length_t len, int level) { if(level == 10) { lzo_uint lzolen = MAXSIZE; lzo1x_1_compress(source, len, dest, &lzolen, lzo_wrkmem); @@@ -130,7 -137,8 +131,7 @@@ return -1; } -static length_t uncompress_packet(uint8_t *dest, const uint8_t *source, length_t len, int level) -{ +static length_t uncompress_packet(uint8_t *dest, const uint8_t *source, length_t len, int level) { if(level > 9) { lzo_uint lzolen = MAXSIZE; if(lzo1x_decompress_safe(source, len, dest, &lzolen, NULL) == LZO_E_OK) @@@ -150,7 -158,8 +151,7 @@@ /* VPN packet I/O */ -static void receive_packet(node_t *n, vpn_packet_t *packet) -{ +static void receive_packet(node_t *n, vpn_packet_t *packet) { cp(); ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Received packet of %d bytes from %s (%s)"), @@@ -159,19 -168,21 +160,19 @@@ route(n, packet); } -static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) -{ +static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) { vpn_packet_t pkt1, pkt2; vpn_packet_t *pkt[] = { &pkt1, &pkt2, &pkt1, &pkt2 }; int nextpkt = 0; vpn_packet_t *outpkt = pkt[0]; - int outlen, outpad; - unsigned char hmac[EVP_MAX_MD_SIZE]; + size_t outlen; int i; cp(); /* Check packet length */ - if(inpkt->len < sizeof(inpkt->seqno) + myself->maclength) { + if(inpkt->len < sizeof inpkt->seqno + digest_length(&myself->digest)) { ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Got too short packet from %s (%s)"), n->name, n->hostname); return; @@@ -179,56 -190,66 +180,56 @@@ /* Check the message authentication code */ - if(myself->digest && myself->maclength) { - inpkt->len -= myself->maclength; - HMAC(myself->digest, myself->key, myself->keylength, - (unsigned char *) &inpkt->seqno, inpkt->len, (unsigned char *)hmac, NULL); - - if(memcmp(hmac, (char *) &inpkt->seqno + inpkt->len, myself->maclength)) { - ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Got unauthenticated packet from %s (%s)"), - n->name, n->hostname); - return; - } + if(digest_active(&myself->digest) && !digest_verify(&myself->digest, &inpkt->seqno, inpkt->len, &inpkt->seqno + inpkt->len)) { + ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Got unauthenticated packet from %s (%s)"), n->name, n->hostname); + return; } /* Decrypt the packet */ - if(myself->cipher) { + if(cipher_active(&myself->cipher)) { outpkt = pkt[nextpkt++]; + outlen = MAXSIZE; - if(!EVP_DecryptInit_ex(&packet_ctx, NULL, NULL, NULL, NULL) - || !EVP_DecryptUpdate(&packet_ctx, (unsigned char *) &outpkt->seqno, &outlen, - (unsigned char *) &inpkt->seqno, inpkt->len) - || !EVP_DecryptFinal_ex(&packet_ctx, (unsigned char *) &outpkt->seqno + outlen, &outpad)) { - ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Error decrypting packet from %s (%s): %s"), - n->name, n->hostname, ERR_error_string(ERR_get_error(), NULL)); + if(!cipher_decrypt(&myself->cipher, &inpkt->seqno, inpkt->len, &outpkt->seqno, &outlen, true)) { + ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Error decrypting packet from %s (%s)"), n->name, n->hostname); return; } - outpkt->len = outlen + outpad; + outpkt->len = outlen; inpkt = outpkt; } /* Check the sequence number */ - inpkt->len -= sizeof(inpkt->seqno); + inpkt->len -= sizeof inpkt->seqno; inpkt->seqno = ntohl(inpkt->seqno); if(inpkt->seqno != n->received_seqno + 1) { - if(inpkt->seqno >= n->received_seqno + sizeof(n->late) * 8) { + if(inpkt->seqno >= n->received_seqno + sizeof n->late * 8) { logger(LOG_WARNING, _("Lost %d packets from %s (%s)"), inpkt->seqno - n->received_seqno - 1, n->name, n->hostname); - memset(n->late, 0, sizeof(n->late)); + memset(n->late, 0, sizeof n->late); } else if (inpkt->seqno <= n->received_seqno) { - if((n->received_seqno >= sizeof(n->late) * 8 && inpkt->seqno <= n->received_seqno - sizeof(n->late) * 8) || !(n->late[(inpkt->seqno / 8) % sizeof(n->late)] & (1 << inpkt->seqno % 8))) { + if((n->received_seqno >= sizeof n->late * 8 && inpkt->seqno <= n->received_seqno - sizeof n->late * 8) || !(n->late[(inpkt->seqno / 8) % sizeof n->late] & (1 << inpkt->seqno % 8))) { logger(LOG_WARNING, _("Got late or replayed packet from %s (%s), seqno %d, last received %d"), n->name, n->hostname, inpkt->seqno, n->received_seqno); return; } } else { for(i = n->received_seqno + 1; i < inpkt->seqno; i++) - n->late[(i / 8) % sizeof(n->late)] |= 1 << i % 8; + n->late[(i / 8) % sizeof n->late] |= 1 << i % 8; } } - n->late[(inpkt->seqno / 8) % sizeof(n->late)] &= ~(1 << inpkt->seqno % 8); + n->late[(inpkt->seqno / 8) % sizeof n->late] &= ~(1 << inpkt->seqno % 8); if(inpkt->seqno > n->received_seqno) n->received_seqno = inpkt->seqno; if(n->received_seqno > MAX_SEQNO) - keyexpires = 0; + regenerate_key(); /* Decompress the packet */ @@@ -244,32 -265,42 +245,37 @@@ inpkt = outpkt; } + inpkt->priority = 0; + - if(n->connection) - n->connection->last_ping_time = now; - if(!inpkt->data[12] && !inpkt->data[13]) mtu_probe_h(n, inpkt); else receive_packet(n, inpkt); } -void receive_tcppacket(connection_t *c, char *buffer, int len) -{ +void receive_tcppacket(connection_t *c, char *buffer, int len) { vpn_packet_t outpkt; cp(); outpkt.len = len; + if(c->options & OPTION_TCPONLY) + outpkt.priority = 0; + else + outpkt.priority = -1; memcpy(outpkt.data, buffer, len); receive_packet(c->node, &outpkt); } -static void send_udppacket(node_t *n, vpn_packet_t *origpkt) -{ +static void send_udppacket(node_t *n, vpn_packet_t *origpkt) { vpn_packet_t pkt1, pkt2; vpn_packet_t *pkt[] = { &pkt1, &pkt2, &pkt1, &pkt2 }; vpn_packet_t *inpkt = origpkt; int nextpkt = 0; vpn_packet_t *outpkt; int origlen; - int outlen, outpad; + size_t outlen; - vpn_packet_t *copy; static int priority = 0; int origpriority; int sock; @@@ -280,26 -311,27 +286,27 @@@ if(!n->status.validkey) { ifdebug(TRAFFIC) logger(LOG_INFO, - _("No valid key known yet for %s (%s), queueing packet"), + _("No valid key known yet for %s (%s), forwarding via TCP"), n->name, n->hostname); - /* Since packet is on the stack of handle_tap_input(), we have to make a copy of it first. */ - - *(copy = xmalloc(sizeof *copy)) = *inpkt; - - list_insert_tail(n->queue, copy); - - if(n->queue->count > MAXQUEUELENGTH) - list_delete_head(n->queue); - if(!n->status.waitingforkey) send_req_key(n->nexthop->connection, myself, n); n->status.waitingforkey = true; + send_tcppacket(n->nexthop->connection, origpkt); + return; } + if(!n->minmtu && (inpkt->data[12] | inpkt->data[13])) { + ifdebug(TRAFFIC) logger(LOG_INFO, + _("No minimum MTU established yet for %s (%s), forwarding via TCP"), + n->name, n->hostname); + + send_tcppacket(n->nexthop->connection, origpkt); + } + origlen = inpkt->len; origpriority = inpkt->priority; @@@ -320,28 -352,32 +327,28 @@@ /* Add sequence number */ inpkt->seqno = htonl(++(n->sent_seqno)); - inpkt->len += sizeof(inpkt->seqno); + inpkt->len += sizeof inpkt->seqno; /* Encrypt the packet */ - if(n->cipher) { + if(cipher_active(&n->cipher)) { outpkt = pkt[nextpkt++]; + outlen = MAXSIZE; - if(!EVP_EncryptInit_ex(&n->packet_ctx, NULL, NULL, NULL, NULL) - || !EVP_EncryptUpdate(&n->packet_ctx, (unsigned char *) &outpkt->seqno, &outlen, - (unsigned char *) &inpkt->seqno, inpkt->len) - || !EVP_EncryptFinal_ex(&n->packet_ctx, (unsigned char *) &outpkt->seqno + outlen, &outpad)) { - ifdebug(TRAFFIC) logger(LOG_ERR, _("Error while encrypting packet to %s (%s): %s"), - n->name, n->hostname, ERR_error_string(ERR_get_error(), NULL)); + if(!cipher_encrypt(&n->cipher, &inpkt->seqno, inpkt->len, &outpkt->seqno, &outlen, true)) { + ifdebug(TRAFFIC) logger(LOG_ERR, _("Error while encrypting packet to %s (%s)"), n->name, n->hostname); goto end; } - outpkt->len = outlen + outpad; + outpkt->len = outlen; inpkt = outpkt; } /* Add the message authentication code */ - if(n->digest && n->maclength) { - HMAC(n->digest, n->key, n->keylength, (unsigned char *) &inpkt->seqno, - inpkt->len, (unsigned char *) &inpkt->seqno + inpkt->len, NULL); - inpkt->len += n->maclength; + if(digest_active(&n->digest)) { + digest_create(&n->digest, &inpkt->seqno, inpkt->len, &inpkt->seqno + inpkt->len); + inpkt->len += digest_length(&n->digest); } /* Determine which socket we have to use */ @@@ -360,7 -396,7 +367,7 @@@ && listen_socket[sock].sa.sa.sa_family == AF_INET) { priority = origpriority; ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Setting outgoing packet priority to %d"), priority); - if(setsockopt(listen_socket[sock].udp, SOL_IP, IP_TOS, &priority, sizeof(priority))) /* SO_PRIORITY doesn't seem to work */ + if(setsockopt(listen_socket[sock].udp, SOL_IP, IP_TOS, &priority, sizeof priority)) /* SO_PRIORITY doesn't seem to work */ logger(LOG_ERR, _("System call `%s' failed: %s"), "setsockopt", strerror(errno)); } #endif @@@ -382,7 -418,8 +389,7 @@@ end /* send a packet to the given vpn ip. */ -void send_packet(const node_t *n, vpn_packet_t *packet) -{ +void send_packet(const node_t *n, vpn_packet_t *packet) { node_t *via; cp(); @@@ -403,13 -440,13 +410,13 @@@ return; } - via = (n->via == myself) ? n->nexthop : n->via; + via = (packet->priority == -1 || n->via == myself) ? n->nexthop : n->via; if(via != n) - ifdebug(TRAFFIC) logger(LOG_ERR, _("Sending packet to %s via %s (%s)"), + ifdebug(TRAFFIC) logger(LOG_INFO, _("Sending packet to %s via %s (%s)"), n->name, via->name, n->via->hostname); - if((myself->options | via->options) & OPTION_TCPONLY) { + if(packet->priority == -1 || ((myself->options | via->options) & OPTION_TCPONLY)) { if(!send_tcppacket(via->connection, packet)) terminate_connection(via->connection, true); } else @@@ -418,8 -455,9 +425,8 @@@ /* Broadcast a packet using the minimum spanning tree */ -void broadcast_packet(const node_t *from, vpn_packet_t *packet) -{ - avl_node_t *node; +void broadcast_packet(const node_t *from, vpn_packet_t *packet) { + splay_node_t *node; connection_t *c; cp(); @@@ -438,25 -476,12 +445,12 @@@ } } - void flush_queue(node_t *n) { - list_node_t *node, *next; - - cp(); - - ifdebug(TRAFFIC) logger(LOG_INFO, _("Flushing queue for %s (%s)"), n->name, n->hostname); - - for(node = n->queue->head; node; node = next) { - next = node->next; - send_udppacket(n, node->data); - list_delete_node(n->queue, node); - } - } - - void handle_incoming_vpn_data(int sock, short events, void *data) { -void handle_incoming_vpn_data(int sock) ++void handle_incoming_vpn_data(int sock, short events, void *data) + { vpn_packet_t pkt; char *hostname; sockaddr_t from; - socklen_t fromlen = sizeof(from); + socklen_t fromlen = sizeof from; node_t *n; cp(); @@@ -482,10 -507,3 +476,10 @@@ receive_udppacket(n, &pkt); } + +void handle_device_data(int sock, short events, void *data) { + vpn_packet_t packet; + + if(read_packet(&packet)) + route(myself, &packet); +} diff --combined src/net_setup.c index 033bf376,3eb56441..43adbc84 --- a/src/net_setup.c +++ b/src/net_setup.c @@@ -1,7 -1,7 +1,7 @@@ /* net_setup.c -- Setup. Copyright (C) 1998-2005 Ivo Timmermans, - 2000-2006 Guus Sliepen + 2000-2009 Guus Sliepen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@@ -22,13 -22,17 +22,13 @@@ #include "system.h" -#include -#include -#include -#include -#include - -#include "avl_tree.h" +#include "splay_tree.h" +#include "cipher.h" #include "conf.h" #include "connection.h" +#include "control.h" #include "device.h" -#include "event.h" +#include "digest.h" #include "graph.h" #include "logger.h" #include "net.h" @@@ -36,77 -40,136 +36,77 @@@ #include "process.h" #include "protocol.h" #include "route.h" +#include "rsa.h" #include "subnet.h" #include "utils.h" #include "xalloc.h" char *myport; +static struct event device_ev; -bool read_rsa_public_key(connection_t *c) -{ +bool read_rsa_public_key(connection_t *c) { FILE *fp; char *fname; - char *key; + char *n; + bool result; cp(); - if(!c->rsa_key) { - c->rsa_key = RSA_new(); -// RSA_blinding_on(c->rsa_key, NULL); - } - /* First, check for simple PublicKey statement */ - if(get_config_string(lookup_config(c->config_tree, "PublicKey"), &key)) { - BN_hex2bn(&c->rsa_key->n, key); - BN_hex2bn(&c->rsa_key->e, "FFFF"); - free(key); - return true; + if(get_config_string(lookup_config(c->config_tree, "PublicKey"), &n)) { + result = rsa_set_hex_public_key(&c->rsa, n, "FFFF"); + free(n); + return result; } /* Else, check for PublicKeyFile statement and read it */ - if(get_config_string(lookup_config(c->config_tree, "PublicKeyFile"), &fname)) { - fp = fopen(fname, "r"); - - if(!fp) { - logger(LOG_ERR, _("Error reading RSA public key file `%s': %s"), - fname, strerror(errno)); - free(fname); - return false; - } - - free(fname); - c->rsa_key = PEM_read_RSAPublicKey(fp, &c->rsa_key, NULL, NULL); - fclose(fp); - - if(c->rsa_key) - return true; /* Woohoo. */ - - /* If it fails, try PEM_read_RSA_PUBKEY. */ - fp = fopen(fname, "r"); + if(!get_config_string(lookup_config(c->config_tree, "PublicKeyFile"), &fname)) + asprintf(&fname, "%s/hosts/%s", confbase, c->name); - if(!fp) { - logger(LOG_ERR, _("Error reading RSA public key file `%s': %s"), - fname, strerror(errno)); - free(fname); - return false; - } - - free(fname); - c->rsa_key = PEM_read_RSA_PUBKEY(fp, &c->rsa_key, NULL, NULL); - fclose(fp); - - if(c->rsa_key) { -// RSA_blinding_on(c->rsa_key, NULL); - return true; - } + fp = fopen(fname, "r"); - logger(LOG_ERR, _("Reading RSA public key file `%s' failed: %s"), + if(!fp) { + logger(LOG_ERR, _("Error reading RSA public key file `%s': %s"), fname, strerror(errno)); + free(fname); return false; } - /* Else, check if a harnessed public key is in the config file */ - - asprintf(&fname, "%s/hosts/%s", confbase, c->name); - fp = fopen(fname, "r"); - - if(fp) { - c->rsa_key = PEM_read_RSAPublicKey(fp, &c->rsa_key, NULL, NULL); - fclose(fp); - } - - free(fname); - - if(c->rsa_key) - return true; - - /* Try again with PEM_read_RSA_PUBKEY. */ - - asprintf(&fname, "%s/hosts/%s", confbase, c->name); - fp = fopen(fname, "r"); - - if(fp) { - c->rsa_key = PEM_read_RSA_PUBKEY(fp, &c->rsa_key, NULL, NULL); -// RSA_blinding_on(c->rsa_key, NULL); - fclose(fp); - } + result = rsa_read_pem_public_key(&c->rsa, fp); + fclose(fp); + if(!result) + logger(LOG_ERR, _("Reading RSA public key file `%s' failed: %s"), fname, strerror(errno)); free(fname); - - if(c->rsa_key) - return true; - - logger(LOG_ERR, _("No public key for %s specified!"), c->name); - - return false; + return result; } -bool read_rsa_private_key(void) -{ +bool read_rsa_private_key() { FILE *fp; - char *fname, *key, *pubkey; - struct stat s; + char *fname; + char *n, *d; + bool result; cp(); - if(get_config_string(lookup_config(config_tree, "PrivateKey"), &key)) { - if(!get_config_string(lookup_config(myself->connection->config_tree, "PublicKey"), &pubkey)) { + /* First, check for simple PrivateKey statement */ + + if(get_config_string(lookup_config(config_tree, "PrivateKey"), &d)) { + if(!get_config_string(lookup_config(myself->connection->config_tree, "PublicKey"), &n)) { logger(LOG_ERR, _("PrivateKey used but no PublicKey found!")); + free(d); return false; } - myself->connection->rsa_key = RSA_new(); -// RSA_blinding_on(myself->connection->rsa_key, NULL); - BN_hex2bn(&myself->connection->rsa_key->d, key); - BN_hex2bn(&myself->connection->rsa_key->n, pubkey); - BN_hex2bn(&myself->connection->rsa_key->e, "FFFF"); - free(key); - free(pubkey); + result = rsa_set_hex_private_key(&myself->connection->rsa, n, "FFFF", d); + free(n); + free(d); return true; } + /* Else, check for PrivateKeyFile statement and read it */ + if(!get_config_string(lookup_config(config_tree, "PrivateKeyFile"), &fname)) asprintf(&fname, "%s/rsa_key.priv", confbase); @@@ -120,10 -183,9 +120,10 @@@ } #if !defined(HAVE_MINGW) && !defined(HAVE_CYGWIN) + struct stat s; + if(fstat(fileno(fp), &s)) { - logger(LOG_ERR, _("Could not stat RSA private key file `%s': %s'"), - fname, strerror(errno)); + logger(LOG_ERR, _("Could not stat RSA private key file `%s': %s'"), fname, strerror(errno)); free(fname); return false; } @@@ -132,43 -194,25 +132,43 @@@ logger(LOG_WARNING, _("Warning: insecure file permissions for RSA private key file `%s'!"), fname); #endif - myself->connection->rsa_key = PEM_read_RSAPrivateKey(fp, NULL, NULL, NULL); + result = rsa_read_pem_private_key(&myself->connection->rsa, fp); fclose(fp); - if(!myself->connection->rsa_key) { - logger(LOG_ERR, _("Reading RSA private key file `%s' failed: %s"), - fname, strerror(errno)); - free(fname); - return false; + if(!result) + logger(LOG_ERR, _("Reading RSA private key file `%s' failed: %s"), fname, strerror(errno)); + free(fname); + return result; +} + +static struct event keyexpire_event; + +static void keyexpire_handler(int fd, short events, void *data) { + regenerate_key(); +} + +void regenerate_key() { + ifdebug(STATUS) logger(LOG_INFO, _("Regenerating symmetric key")); + + if(!cipher_regenerate_key(&myself->cipher, true)) { + logger(LOG_ERR, _("Error regenerating key!")); + abort(); } - free(fname); - return true; + if(timeout_initialized(&keyexpire_event)) { + event_del(&keyexpire_event); + send_key_changed(broadcast, myself); + } else { + timeout_set(&keyexpire_event, keyexpire_handler, NULL); + } + + event_add(&keyexpire_event, &(struct timeval){keylifetime, 0}); } /* Configure node_t myself and set up the local sockets (listen only) */ -bool setup_myself(void) -{ +bool setup_myself(void) { config_t *cfg; subnet_t *subnet; char *name, *hostname, *mode, *afname, *cipher, *digest; @@@ -242,9 -286,6 +242,6 @@@ if(get_config_bool(lookup_config(myself->connection->config_tree, "TCPOnly"), &choice) && choice) myself->options |= OPTION_TCPONLY; - if(get_config_bool(lookup_config(myself->connection->config_tree, "PMTUDiscovery"), &choice) && choice) - myself->options |= OPTION_PMTU_DISCOVERY; - if(myself->options & OPTION_TCPONLY) myself->options |= OPTION_INDIRECT; @@@ -265,6 -306,10 +262,10 @@@ } else routing_mode = RMODE_ROUTER; + if(routing_mode == RMODE_ROUTER) + if(!get_config_bool(lookup_config(myself->connection->config_tree, "PMTUDiscovery"), &choice) || choice) + myself->options |= OPTION_PMTU_DISCOVERY; + get_config_bool(lookup_config(config_tree, "PriorityInheritance"), &priorityinheritance); #if !defined(SOL_IP) || !defined(IP_TOS) @@@ -301,44 -346,85 +302,44 @@@ /* Generate packet encryption key */ - if(get_config_string - (lookup_config(myself->connection->config_tree, "Cipher"), &cipher)) { - if(!strcasecmp(cipher, "none")) { - myself->cipher = NULL; - } else { - myself->cipher = EVP_get_cipherbyname(cipher); - - if(!myself->cipher) { - logger(LOG_ERR, _("Unrecognized cipher type!")); - return false; - } - } - } else - myself->cipher = EVP_bf_cbc(); - - if(myself->cipher) - myself->keylength = myself->cipher->key_len + myself->cipher->iv_len; - else - myself->keylength = 1; + if(!get_config_string(lookup_config(myself->connection->config_tree, "Cipher"), &cipher)) + cipher = xstrdup("blowfish"); - myself->connection->outcipher = EVP_bf_ofb(); - - myself->key = xmalloc(myself->keylength); - RAND_pseudo_bytes((unsigned char *)myself->key, myself->keylength); + if(!cipher_open_by_name(&myself->cipher, cipher)) { + logger(LOG_ERR, _("Unrecognized cipher type!")); + return false; + } if(!get_config_int(lookup_config(config_tree, "KeyExpire"), &keylifetime)) keylifetime = 3600; - keyexpires = now + keylifetime; - - if(myself->cipher) { - EVP_CIPHER_CTX_init(&packet_ctx); - if(!EVP_DecryptInit_ex(&packet_ctx, myself->cipher, NULL, (unsigned char *)myself->key, (unsigned char *)myself->key + myself->cipher->key_len)) { - logger(LOG_ERR, _("Error during initialisation of cipher for %s (%s): %s"), - myself->name, myself->hostname, ERR_error_string(ERR_get_error(), NULL)); - return false; - } + regenerate_key(); + + /* Check if we want to use message authentication codes... */ + + if(!get_config_string(lookup_config(myself->connection->config_tree, "Digest"), &digest)) + digest = xstrdup("sha1"); + if(!digest_open_by_name(&myself->digest, digest)) { + logger(LOG_ERR, _("Unrecognized digest type!")); + return false; } - /* Check if we want to use message authentication codes... */ + if(!get_config_int(lookup_config(myself->connection->config_tree, "MACLength"), &myself->maclength)) - if(get_config_string - (lookup_config(myself->connection->config_tree, "Digest"), &digest)) { - if(!strcasecmp(digest, "none")) { - myself->digest = NULL; - } else { - myself->digest = EVP_get_digestbyname(digest); - - if(!myself->digest) { - logger(LOG_ERR, _("Unrecognized digest type!")); - return false; - } - } - } else - myself->digest = EVP_sha1(); - - myself->connection->outdigest = EVP_sha1(); - - if(get_config_int(lookup_config(myself->connection->config_tree, "MACLength"), - &myself->maclength)) { - if(myself->digest) { - if(myself->maclength > myself->digest->md_size) { - logger(LOG_ERR, _("MAC length exceeds size of digest!")); - return false; - } else if(myself->maclength < 0) { - logger(LOG_ERR, _("Bogus MAC length!")); - return false; - } + if(digest_active(&myself->digest)) { + if(myself->maclength > digest_length(&myself->digest)) { + logger(LOG_ERR, _("MAC length exceeds size of digest!")); + return false; + } else if(myself->maclength < 0) { + logger(LOG_ERR, _("Bogus MAC length!")); + return false; } - } else - myself->maclength = 4; - - myself->connection->outmaclength = 0; + } /* Compression */ - if(get_config_int(lookup_config(myself->connection->config_tree, "Compression"), - &myself->compression)) { + if(get_config_int(lookup_config(myself->connection->config_tree, "Compression"), &myself->compression)) { if(myself->compression < 0 || myself->compression > 11) { logger(LOG_ERR, _("Bogus compression level!")); return false; @@@ -362,14 -448,6 +363,14 @@@ if(!setup_device()) return false; + event_set(&device_ev, device_fd, EV_READ|EV_PERSIST, handle_device_data, NULL); + + if (event_add(&device_ev, NULL) < 0) { + logger(LOG_ERR, _("event_add failed: %s"), strerror(errno)); + close_device(); + return false; + } + /* Run tinc-up script to further initialize the tap interface */ asprintf(&envp[0], "NETNAME=%s", netname ? : ""); asprintf(&envp[1], "DEVICE=%s", device ? : ""); @@@ -415,28 -493,8 +416,28 @@@ listen_socket[listen_sockets].udp = setup_vpn_in_socket((sockaddr_t *) aip->ai_addr); - if(listen_socket[listen_sockets].udp < 0) + if(listen_socket[listen_sockets].udp < 0) { + close(listen_socket[listen_sockets].tcp); continue; + } + + event_set(&listen_socket[listen_sockets].ev_tcp, + listen_socket[listen_sockets].tcp, + EV_READ|EV_PERSIST, + handle_new_meta_connection, NULL); + if(event_add(&listen_socket[listen_sockets].ev_tcp, NULL) < 0) { + logger(LOG_EMERG, _("event_add failed: %s"), strerror(errno)); + abort(); + } + + event_set(&listen_socket[listen_sockets].ev_udp, + listen_socket[listen_sockets].udp, + EV_READ|EV_PERSIST, + handle_incoming_vpn_data, NULL); + if(event_add(&listen_socket[listen_sockets].ev_udp, NULL) < 0) { + logger(LOG_EMERG, _("event_add failed: %s"), strerror(errno)); + abort(); + } ifdebug(CONNECTIONS) { hostname = sockaddr2hostname((sockaddr_t *) aip->ai_addr); @@@ -446,11 -504,6 +447,11 @@@ memcpy(&listen_socket[listen_sockets].sa, aip->ai_addr, aip->ai_addrlen); listen_sockets++; + + if(listen_sockets >= MAXSOCKETS) { + logger(LOG_WARNING, _("Maximum of %d listening sockets reached"), MAXSOCKETS); + break; + } } freeaddrinfo(ai); @@@ -468,9 -521,13 +469,9 @@@ /* setup all initial network connections */ -bool setup_network_connections(void) -{ +bool setup_network_connections(void) { cp(); - now = time(NULL); - - init_events(); init_connections(); init_subnets(); init_nodes(); @@@ -490,7 -547,7 +491,7 @@@ pingtimeout = pinginterval; if(!get_config_int(lookup_config(config_tree, "MaxOutputBufferSize"), &maxoutbufsize)) - maxoutbufsize = 4 * MTU; + maxoutbufsize = 10 * MTU; if(!setup_myself()) return false; @@@ -503,8 -560,9 +504,8 @@@ /* close all open network connections */ -void close_network_connections(void) -{ - avl_node_t *node, *next; +void close_network_connections(void) { + splay_node_t *node, *next; connection_t *c; char *envp[5]; int i; @@@ -514,26 -572,19 +515,21 @@@ for(node = connection_tree->head; node; node = next) { next = node->next; c = node->data; - - if(c->outgoing) { - if(c->outgoing->ai) - freeaddrinfo(c->outgoing->ai); - free(c->outgoing->name); - free(c->outgoing); - c->outgoing = NULL; - } - + c->outgoing = false; terminate_connection(c, false); } + list_delete_list(outgoing_list); + if(myself && myself->connection) { subnet_update(myself, NULL, false); terminate_connection(myself->connection, false); + free_connection(myself->connection); } for(i = 0; i < listen_sockets; i++) { + event_del(&listen_socket[i].ev_tcp); + event_del(&listen_socket[i].ev_udp); close(listen_socket[i].tcp); close(listen_socket[i].udp); } @@@ -549,9 -600,14 +545,11 @@@ exit_subnets(); exit_nodes(); exit_connections(); - exit_events(); execute_script("tinc-down", envp); + if(myport) free(myport); + - EVP_CIPHER_CTX_cleanup(&packet_ctx); - for(i = 0; i < 4; i++) free(envp[i]); diff --combined src/net_socket.c index 808e1c66,82213e91..43b8ada2 --- a/src/net_socket.c +++ b/src/net_socket.c @@@ -1,7 -1,7 +1,7 @@@ /* net_socket.c -- Handle various kinds of sockets. Copyright (C) 1998-2005 Ivo Timmermans, - 2000-2007 Guus Sliepen + 2000-2009 Guus Sliepen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@@ -22,9 -22,10 +22,9 @@@ #include "system.h" -#include "avl_tree.h" +#include "splay_tree.h" #include "conf.h" #include "connection.h" -#include "event.h" #include "logger.h" #include "meta.h" #include "net.h" @@@ -48,10 -49,12 +48,11 @@@ int seconds_till_retry = 5 listen_socket_t listen_socket[MAXSOCKETS]; int listen_sockets; + list_t *outgoing_list = NULL; /* Setup sockets */ -static void configure_tcp(connection_t *c) -{ +static void configure_tcp(connection_t *c) { int option; #ifdef O_NONBLOCK @@@ -70,16 -73,17 +71,16 @@@ #if defined(SOL_TCP) && defined(TCP_NODELAY) option = 1; - setsockopt(c->socket, SOL_TCP, TCP_NODELAY, &option, sizeof(option)); + setsockopt(c->socket, SOL_TCP, TCP_NODELAY, &option, sizeof option); #endif #if defined(SOL_IP) && defined(IP_TOS) && defined(IPTOS_LOWDELAY) option = IPTOS_LOWDELAY; - setsockopt(c->socket, SOL_IP, IP_TOS, &option, sizeof(option)); + setsockopt(c->socket, SOL_IP, IP_TOS, &option, sizeof option); #endif } -int setup_listen_socket(const sockaddr_t *sa) -{ +int setup_listen_socket(const sockaddr_t *sa) { int nfd; char *addrstr; int option; @@@ -97,7 -101,7 +98,7 @@@ /* Optimize TCP settings */ option = 1; - setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option)); + setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof option); #if defined(SOL_IPV6) && defined(IPV6_V6ONLY) if(sa->sa.sa_family == AF_INET6) @@@ -109,10 -113,10 +110,10 @@@ #if defined(SOL_SOCKET) && defined(SO_BINDTODEVICE) struct ifreq ifr; - memset(&ifr, 0, sizeof(ifr)); + memset(&ifr, 0, sizeof ifr); strncpy(ifr.ifr_ifrn.ifrn_name, iface, IFNAMSIZ); - if(setsockopt(nfd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr))) { + if(setsockopt(nfd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof ifr)) { closesocket(nfd); logger(LOG_ERR, _("Can't bind to interface %s: %s"), iface, strerror(errno)); @@@ -142,7 -146,8 +143,7 @@@ return nfd; } -int setup_vpn_in_socket(const sockaddr_t *sa) -{ +int setup_vpn_in_socket(const sockaddr_t *sa) { int nfd; char *addrstr; int option; @@@ -180,7 -185,7 +181,7 @@@ #endif option = 1; - setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option)); + setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof option); #if defined(SOL_IPV6) && defined(IPV6_V6ONLY) if(sa->sa.sa_family == AF_INET6) @@@ -188,24 -193,16 +189,16 @@@ #endif #if defined(SOL_IP) && defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DO) - { - bool choice; - - if(get_config_bool(lookup_config(myself->connection->config_tree, "PMTUDiscovery"), &choice) && choice) { - option = IP_PMTUDISC_DO; - setsockopt(nfd, SOL_IP, IP_MTU_DISCOVER, &option, sizeof option); - } + if(myself->options & OPTION_PMTU_DISCOVERY) { + option = IP_PMTUDISC_DO; + setsockopt(nfd, SOL_IP, IP_MTU_DISCOVER, &option, sizeof(option)); } #endif #if defined(SOL_IPV6) && defined(IPV6_MTU_DISCOVER) && defined(IPV6_PMTUDISC_DO) - { - bool choice; - - if(get_config_bool(lookup_config(myself->connection->config_tree, "PMTUDiscovery"), &choice) && choice) { - option = IPV6_PMTUDISC_DO; - setsockopt(nfd, SOL_IPV6, IPV6_MTU_DISCOVER, &option, sizeof option); - } + if(myself->options & OPTION_PMTU_DISCOVERY) { + option = IPV6_PMTUDISC_DO; + setsockopt(nfd, SOL_IPV6, IPV6_MTU_DISCOVER, &option, sizeof(option)); } #endif @@@ -215,10 -212,10 +208,10 @@@ struct ifreq ifr; if(get_config_string(lookup_config(config_tree, "BindToInterface"), &iface)) { - memset(&ifr, 0, sizeof(ifr)); + memset(&ifr, 0, sizeof ifr); strncpy(ifr.ifr_ifrn.ifrn_name, iface, IFNAMSIZ); - if(setsockopt(nfd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr))) { + if(setsockopt(nfd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof ifr)) { closesocket(nfd); logger(LOG_ERR, _("Can't bind to interface %s: %s"), iface, strerror(errno)); @@@ -240,11 -237,10 +233,11 @@@ return nfd; } -void retry_outgoing(outgoing_t *outgoing) -{ - event_t *event; +static void retry_outgoing_handler(int fd, short events, void *data) { + setup_outgoing_connection(data); +} +void retry_outgoing(outgoing_t *outgoing) { cp(); outgoing->timeout += 5; @@@ -252,28 -248,32 +245,28 @@@ if(outgoing->timeout > maxtimeout) outgoing->timeout = maxtimeout; - event = new_event(); - event->handler = (event_handler_t) setup_outgoing_connection; - event->time = now + outgoing->timeout; - event->data = outgoing; - event_add(event); + timeout_set(&outgoing->ev, retry_outgoing_handler, outgoing); + event_add(&outgoing->ev, &(struct timeval){outgoing->timeout, 0}); ifdebug(CONNECTIONS) logger(LOG_NOTICE, _("Trying to re-establish outgoing connection in %d seconds"), outgoing->timeout); } -void finish_connecting(connection_t *c) -{ +void finish_connecting(connection_t *c) { cp(); ifdebug(CONNECTIONS) logger(LOG_INFO, _("Connected to %s (%s)"), c->name, c->hostname); configure_tcp(c); - c->last_ping_time = now; + c->last_ping_time = time(NULL); + c->status.connecting = false; send_id(c); } -void do_outgoing_connection(connection_t *c) -{ +void do_outgoing_connection(connection_t *c) { char *address, *port; int result; @@@ -284,9 -284,8 +277,9 @@@ begin if(!c->outgoing->cfg) { ifdebug(CONNECTIONS) logger(LOG_ERR, _("Could not set up a meta connection to %s"), c->name); - c->status.remove = true; retry_outgoing(c->outgoing); + c->outgoing = NULL; + connection_del(c); return; } @@@ -366,22 -365,8 +359,22 @@@ return; } -void setup_outgoing_connection(outgoing_t *outgoing) -{ +void handle_meta_read(struct bufferevent *event, void *data) { + logger(LOG_EMERG, _("handle_meta_read() called")); + abort(); +} + +void handle_meta_write(struct bufferevent *event, void *data) { + ifdebug(META) logger(LOG_DEBUG, _("handle_meta_write() called")); +} + +void handle_meta_connection_error(struct bufferevent *event, short what, void *data) { + connection_t *c = data; + logger(LOG_EMERG, _("handle_meta_connection_error() called: %d: %s"), what, strerror(errno)); + terminate_connection(c, c->status.active); +} + +void setup_outgoing_connection(outgoing_t *outgoing) { connection_t *c; node_t *n; @@@ -412,45 -397,36 +405,43 @@@ if(!outgoing->cfg) { logger(LOG_ERR, _("No address specified for %s"), c->name); free_connection(c); - free(outgoing->name); - free(outgoing); return; } c->outgoing = outgoing; - c->last_ping_time = now; + c->last_ping_time = time(NULL); connection_add(c); do_outgoing_connection(c); + + event_set(&c->inevent, c->socket, EV_READ | EV_PERSIST, handle_meta_connection_data, c); + event_add(&c->inevent, NULL); + c->buffer = bufferevent_new(c->socket, handle_meta_read, handle_meta_write, handle_meta_connection_error, c); + if(!c->buffer) { + logger(LOG_EMERG, _("bufferevent_new() failed: %s"), strerror(errno)); + abort(); + } + bufferevent_disable(c->buffer, EV_READ); } /* accept a new tcp connect and create a new connection */ -bool handle_new_meta_connection(int sock) -{ +void handle_new_meta_connection(int sock, short events, void *data) { connection_t *c; sockaddr_t sa; int fd; - socklen_t len = sizeof(sa); + socklen_t len = sizeof sa; cp(); fd = accept(sock, &sa.sa, &len); if(fd < 0) { - logger(LOG_ERR, _("Accepting a new connection failed: %s"), - strerror(errno)); - return false; + logger(LOG_ERR, _("Accepting a new connection failed: %s"), strerror(errno)); + return; } sockaddrunmap(&sa); @@@ -465,34 -441,51 +456,58 @@@ c->address = sa; c->hostname = sockaddr2hostname(&sa); c->socket = fd; - c->last_ping_time = now; + c->last_ping_time = time(NULL); ifdebug(CONNECTIONS) logger(LOG_NOTICE, _("Connection from %s"), c->hostname); + event_set(&c->inevent, c->socket, EV_READ | EV_PERSIST, handle_meta_connection_data, c); + event_add(&c->inevent, NULL); + c->buffer = bufferevent_new(c->socket, NULL, handle_meta_write, handle_meta_connection_error, c); + if(!c->buffer) { + logger(LOG_EMERG, _("bufferevent_new() failed: %s"), strerror(errno)); + abort(); + } + bufferevent_disable(c->buffer, EV_READ); + configure_tcp(c); connection_add(c); c->allow_request = ID; send_id(c); - - return true; } - void try_outgoing_connections(void) { + void free_outgoing(outgoing_t *outgoing) { + if(outgoing->ai) + freeaddrinfo(outgoing->ai); + + if(outgoing->name) + free(outgoing->name); + + free(outgoing); + } + + void try_outgoing_connections(void) + { static config_t *cfg = NULL; char *name; outgoing_t *outgoing; - + connection_t *c; - avl_node_t *node; ++ splay_node_t *node; + cp(); + if(outgoing_list) { + for(node = connection_tree->head; node; node = node->next) { + c = node->data; + c->outgoing = NULL; + } + + list_delete_list(outgoing_list); + } + + outgoing_list = list_alloc((list_action_t)free_outgoing); + for(cfg = lookup_config(config_tree, "ConnectTo"); cfg; cfg = lookup_config_next(config_tree, cfg)) { get_config_string(cfg, &name); @@@ -504,8 -497,9 +519,9 @@@ continue; } - outgoing = xmalloc_and_zero(sizeof(*outgoing)); + outgoing = xmalloc_and_zero(sizeof *outgoing); outgoing->name = name; + list_insert_tail(outgoing_list, outgoing); setup_outgoing_connection(outgoing); } } diff --combined src/node.c index a71a8735,4ee9ce72..80d28d64 --- a/src/node.c +++ b/src/node.c @@@ -1,6 -1,6 +1,6 @@@ /* node.c -- node tree management - Copyright (C) 2001-2006 Guus Sliepen , + Copyright (C) 2001-2009 Guus Sliepen , 2001-2005 Ivo Timmermans This program is free software; you can redistribute it and/or modify @@@ -22,7 -22,7 +22,7 @@@ #include "system.h" -#include "avl_tree.h" +#include "splay_tree.h" #include "logger.h" #include "net.h" #include "netutl.h" @@@ -30,16 -30,18 +30,16 @@@ #include "utils.h" #include "xalloc.h" -avl_tree_t *node_tree; /* Known nodes, sorted by name */ -avl_tree_t *node_udp_tree; /* Known nodes, sorted by address and port */ +splay_tree_t *node_tree; /* Known nodes, sorted by name */ +splay_tree_t *node_udp_tree; /* Known nodes, sorted by address and port */ node_t *myself; -static int node_compare(const node_t *a, const node_t *b) -{ +static int node_compare(const node_t *a, const node_t *b) { return strcmp(a->name, b->name); } -static int node_udp_compare(const node_t *a, const node_t *b) -{ +static int node_udp_compare(const node_t *a, const node_t *b) { int result; cp(); @@@ -52,40 -54,44 +52,36 @@@ return (a->name && b->name) ? strcmp(a->name, b->name) : 0; } -void init_nodes(void) -{ +void init_nodes(void) { cp(); - node_tree = avl_alloc_tree((avl_compare_t) node_compare, (avl_action_t) free_node); - node_udp_tree = avl_alloc_tree((avl_compare_t) node_udp_compare, NULL); + node_tree = splay_alloc_tree((splay_compare_t) node_compare, (splay_action_t) free_node); + node_udp_tree = splay_alloc_tree((splay_compare_t) node_udp_compare, NULL); } -void exit_nodes(void) -{ +void exit_nodes(void) { cp(); - avl_delete_tree(node_udp_tree); - avl_delete_tree(node_tree); + splay_delete_tree(node_udp_tree); + splay_delete_tree(node_tree); } -node_t *new_node(void) -{ - node_t *n = xmalloc_and_zero(sizeof(*n)); +node_t *new_node(void) { + node_t *n = xmalloc_and_zero(sizeof *n); cp(); n->subnet_tree = new_subnet_tree(); n->edge_tree = new_edge_tree(); - n->queue = list_alloc((list_action_t) free); - EVP_CIPHER_CTX_init(&n->packet_ctx); n->mtu = MTU; n->maxmtu = MTU; return n; } -void free_node(node_t *n) -{ +void free_node(node_t *n) { cp(); - if(n->queue) - list_delete_list(n->queue); - if(n->key) - free(n->key); -- if(n->subnet_tree) free_subnet_tree(n->subnet_tree); @@@ -94,10 -100,10 +90,10 @@@ sockaddrfree(&n->address); - EVP_CIPHER_CTX_cleanup(&n->packet_ctx); + cipher_close(&n->cipher); + digest_close(&n->digest); - if(n->mtuevent) - event_del(n->mtuevent); + event_del(&n->mtuevent); if(n->hostname) free(n->hostname); @@@ -108,14 -114,16 +104,14 @@@ free(n); } -void node_add(node_t *n) -{ +void node_add(node_t *n) { cp(); - avl_insert(node_tree, n); + splay_insert(node_tree, n); } -void node_del(node_t *n) -{ - avl_node_t *node, *next; +void node_del(node_t *n) { + splay_node_t *node, *next; edge_t *e; subnet_t *s; @@@ -133,20 -141,22 +129,20 @@@ edge_del(e); } - avl_delete(node_tree, n); + splay_delete(node_tree, n); } -node_t *lookup_node(char *name) -{ +node_t *lookup_node(char *name) { node_t n = {0}; cp(); n.name = name; - return avl_search(node_tree, &n); + return splay_search(node_tree, &n); } -node_t *lookup_node_udp(const sockaddr_t *sa) -{ +node_t *lookup_node_udp(const sockaddr_t *sa) { node_t n = {0}; cp(); @@@ -154,24 -164,26 +150,24 @@@ n.address = *sa; n.name = NULL; - return avl_search(node_udp_tree, &n); + return splay_search(node_udp_tree, &n); } -void dump_nodes(void) -{ - avl_node_t *node; +int dump_nodes(struct evbuffer *out) { + splay_node_t *node; node_t *n; cp(); - logger(LOG_DEBUG, _("Nodes:")); - for(node = node_tree->head; node; node = node->next) { n = node->data; - logger(LOG_DEBUG, _(" %s at %s cipher %d digest %d maclength %d compression %d options %lx status %04x nexthop %s via %s pmtu %d (min %d max %d)"), - n->name, n->hostname, n->cipher ? n->cipher->nid : 0, - n->digest ? n->digest->type : 0, n->maclength, n->compression, + if(evbuffer_add_printf(out, _(" %s at %s cipher %d digest %d maclength %d compression %d options %lx status %04x nexthop %s via %s distance %d pmtu %d (min %d max %d)\n"), + n->name, n->hostname, cipher_get_nid(&n->cipher), + digest_get_nid(&n->digest), n->maclength, n->compression, n->options, *(uint32_t *)&n->status, n->nexthop ? n->nexthop->name : "-", - n->via ? n->via->name : "-", n->mtu, n->minmtu, n->maxmtu); + n->via ? n->via->name : "-", n->distance, n->mtu, n->minmtu, n->maxmtu) == -1) + return errno; } - logger(LOG_DEBUG, _("End of nodes.")); + return 0; } diff --combined src/node.h index 7b777288,55a1b530..f4fc88bb --- a/src/node.h +++ b/src/node.h @@@ -1,6 -1,6 +1,6 @@@ /* node.h -- header for node.c - Copyright (C) 2001-2006 Guus Sliepen , + Copyright (C) 2001-2009 Guus Sliepen , 2001-2005 Ivo Timmermans This program is free software; you can redistribute it and/or modify @@@ -23,12 -23,9 +23,12 @@@ #ifndef __TINC_NODE_H__ #define __TINC_NODE_H__ -#include "avl_tree.h" +#include + +#include "splay_tree.h" +#include "cipher.h" #include "connection.h" -#include "event.h" +#include "digest.h" #include "list.h" #include "subnet.h" @@@ -54,21 -51,22 +54,19 @@@ typedef struct node_t node_status_t status; - const EVP_CIPHER *cipher; /* Cipher type for UDP packets */ - char *key; /* Cipher key and iv */ - int keylength; /* Cipher key and iv length */ - EVP_CIPHER_CTX packet_ctx; /* Cipher context */ - - const EVP_MD *digest; /* Digest type for MAC */ - int maclength; /* Length of MAC */ + cipher_t cipher; /* Cipher for UDP packets */ + digest_t digest; /* Digest for UDP packets */ + int maclength; /* Portion of digest to use */ int compression; /* Compressionlevel, 0 = no compression */ - list_t *queue; /* Queue for packets awaiting to be encrypted */ - + int distance; struct node_t *nexthop; /* nearest node from us to him */ struct node_t *via; /* next hop for UDP packets */ - avl_tree_t *subnet_tree; /* Pointer to a tree of subnets belonging to this node */ + splay_tree_t *subnet_tree; /* Pointer to a tree of subnets belonging to this node */ - avl_tree_t *edge_tree; /* Edges with this node as one of the endpoints */ + splay_tree_t *edge_tree; /* Edges with this node as one of the endpoints */ struct connection_t *connection; /* Connection associated with this node (if a direct connection exists) */ @@@ -80,12 -78,12 +78,12 @@@ length_t minmtu; /* Probed minimum MTU */ length_t maxmtu; /* Probed maximum MTU */ int mtuprobes; /* Number of probes */ - event_t *mtuevent; /* Probe event */ + struct event mtuevent; /* Probe event */ } node_t; extern struct node_t *myself; -extern avl_tree_t *node_tree; -extern avl_tree_t *node_udp_tree; +extern splay_tree_t *node_tree; +extern splay_tree_t *node_udp_tree; extern void init_nodes(void); extern void exit_nodes(void); @@@ -95,6 -93,6 +93,6 @@@ extern void node_add(node_t *) extern void node_del(node_t *); extern node_t *lookup_node(char *); extern node_t *lookup_node_udp(const sockaddr_t *); -extern void dump_nodes(void); +extern int dump_nodes(struct evbuffer *); #endif /* __TINC_NODE_H__ */ diff --combined src/process.c index aaddcbc1,c2940bc5..29cd486d --- a/src/process.c +++ b/src/process.c @@@ -1,7 -1,7 +1,7 @@@ /* process.c -- process management functions Copyright (C) 1999-2005 Ivo Timmermans, - 2000-2006 Guus Sliepen + 2000-2007 Guus Sliepen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@@ -24,11 -24,11 +24,11 @@@ #include "conf.h" #include "connection.h" +#include "control.h" #include "device.h" #include "edge.h" #include "logger.h" #include "node.h" -#include "pidfile.h" #include "process.h" #include "subnet.h" #include "utils.h" @@@ -36,15 -36,21 +36,15 @@@ /* If zero, don't detach from the terminal. */ bool do_detach = true; -bool sighup = false; bool sigalrm = false; extern char *identname; -extern char *pidfilename; extern char **g_argv; extern bool use_logfile; -extern volatile bool running; sigset_t emptysigset; -static int saved_debug_level = -1; - -static void memory_full(int size) -{ +static void memory_full(int size) { logger(LOG_ERR, _("Memory exhausted (couldn't allocate %d bytes), exitting."), size); cp_trace(); exit(1); @@@ -162,14 -168,23 +162,14 @@@ DWORD WINAPI controlhandler(DWORD reque return ERROR_CALL_NOT_IMPLEMENTED; } - if(running) { - running = false; - status.dwWaitHint = 30000; - status.dwCurrentState = SERVICE_STOP_PENDING; - SetServiceStatus(statushandle, &status); - return NO_ERROR; - } else { - status.dwWaitHint = 0; - status.dwCurrentState = SERVICE_STOPPED; - SetServiceStatus(statushandle, &status); - exit(1); - } - + event_loopexit(NULL); + status.dwWaitHint = 30000; + status.dwCurrentState = SERVICE_STOP_PENDING; + SetServiceStatus(statushandle, &status); + return NO_ERROR; } -VOID WINAPI run_service(DWORD argc, LPTSTR* argv) -{ +VOID WINAPI run_service(DWORD argc, LPTSTR* argv) { int err = 1; extern int main2(int argc, char **argv); @@@ -223,15 -238,95 +223,15 @@@ bool init_service(void) } #endif -#ifndef HAVE_MINGW -/* - check for an existing tinc for this net, and write pid to pidfile -*/ -static bool write_pidfile(void) -{ - pid_t pid; - - cp(); - - pid = check_pid(pidfilename); - - if(pid) { - if(netname) - fprintf(stderr, _("A tincd is already running for net `%s' with pid %ld.\n"), - netname, (long)pid); - else - fprintf(stderr, _("A tincd is already running with pid %ld.\n"), (long)pid); - return false; - } - - /* if it's locked, write-protected, or whatever */ - if(!write_pid(pidfilename)) { - fprintf(stderr, _("Could write pid file %s: %s\n"), pidfilename, strerror(errno)); - return false; - } - - return true; -} -#endif - -/* - kill older tincd for this net -*/ -bool kill_other(int signal) -{ -#ifndef HAVE_MINGW - pid_t pid; - - cp(); - - pid = read_pid(pidfilename); - - if(!pid) { - if(netname) - fprintf(stderr, _("No other tincd is running for net `%s'.\n"), - netname); - else - fprintf(stderr, _("No other tincd is running.\n")); - return false; - } - - errno = 0; /* No error, sometimes errno is only changed on error */ - - /* ESRCH is returned when no process with that pid is found */ - if(kill(pid, signal) && errno == ESRCH) { - if(netname) - fprintf(stderr, _("The tincd for net `%s' is no longer running. "), - netname); - else - fprintf(stderr, _("The tincd is no longer running. ")); - - fprintf(stderr, _("Removing stale lock file.\n")); - remove_pid(pidfilename); - } - - return true; -#else - return remove_service(); -#endif -} - /* - Detach from current terminal, write pidfile, kill parent + Detach from current terminal */ -bool detach(void) -{ +bool detach(void) { cp(); setup_signals(); - /* First check if we can open a fresh new pidfile */ - #ifndef HAVE_MINGW - if(!write_pidfile()) - return false; - - /* If we succeeded in doing that, detach */ - closelogger(); #endif @@@ -242,6 -337,13 +242,6 @@@ strerror(errno)); return false; } - - /* Now UPDATE the pid in the pidfile, because we changed it... */ - - if(!write_pid(pidfilename)) { - fprintf(stderr, _("Could not write pid file %s: %s\n"), pidfilename, strerror(errno)); - return false; - } #else if(!statushandle) exit(install_service()); @@@ -258,7 -360,8 +258,7 @@@ return true; } -bool execute_script(const char *name, char **envp) -{ +bool execute_script(const char *name, char **envp) { #ifdef HAVE_SYSTEM int status, len; struct stat s; @@@ -341,14 -444,34 +341,14 @@@ */ #ifndef HAVE_MINGW -static RETSIGTYPE sigterm_handler(int a) -{ - logger(LOG_NOTICE, _("Got %s signal"), "TERM"); - if(running) - running = false; - else - exit(1); -} - -static RETSIGTYPE sigquit_handler(int a) -{ - logger(LOG_NOTICE, _("Got %s signal"), "QUIT"); - if(running) - running = false; - else - exit(1); -} - -static RETSIGTYPE fatal_signal_square(int a) -{ +static RETSIGTYPE fatal_signal_square(int a) { logger(LOG_ERR, _("Got another fatal signal %d (%s): not restarting."), a, strsignal(a)); cp_trace(); exit(1); } -static RETSIGTYPE fatal_signal_handler(int a) -{ +static RETSIGTYPE fatal_signal_handler(int a) { struct sigaction act; logger(LOG_ERR, _("Got fatal signal %d (%s)"), a, strsignal(a)); cp_trace(); @@@ -363,7 -486,7 +363,7 @@@ close_network_connections(); sleep(5); - remove_pid(pidfilename); + exit_control(); execvp(g_argv[0], g_argv); } else { logger(LOG_NOTICE, _("Not restarting.")); @@@ -371,12 -494,62 +371,12 @@@ } } -static RETSIGTYPE sighup_handler(int a) -{ - logger(LOG_NOTICE, _("Got %s signal"), "HUP"); - sighup = true; -} - -static RETSIGTYPE sigint_handler(int a) -{ - logger(LOG_NOTICE, _("Got %s signal"), "INT"); - - if(saved_debug_level != -1) { - logger(LOG_NOTICE, _("Reverting to old debug level (%d)"), - saved_debug_level); - debug_level = saved_debug_level; - saved_debug_level = -1; - } else { - logger(LOG_NOTICE, - _("Temporarily setting debug level to 5. Kill me with SIGINT again to go back to level %d."), - debug_level); - saved_debug_level = debug_level; - debug_level = 5; - } -} - -static RETSIGTYPE sigalrm_handler(int a) -{ - logger(LOG_NOTICE, _("Got %s signal"), "ALRM"); - sigalrm = true; -} - -static RETSIGTYPE sigusr1_handler(int a) -{ - dump_connections(); -} - -static RETSIGTYPE sigusr2_handler(int a) -{ - dump_device_stats(); - dump_nodes(); - dump_edges(); - dump_subnets(); -} - -static RETSIGTYPE sigwinch_handler(int a) -{ - do_purge = true; -} - -static RETSIGTYPE unexpected_signal_handler(int a) -{ +static RETSIGTYPE unexpected_signal_handler(int a) { logger(LOG_WARNING, _("Got unexpected signal %d (%s)"), a, strsignal(a)); cp_trace(); } -static RETSIGTYPE ignore_signal_handler(int a) -{ +static RETSIGTYPE ignore_signal_handler(int a) { ifdebug(SCARY_THINGS) logger(LOG_DEBUG, _("Ignored signal %d (%s)"), a, strsignal(a)); } @@@ -384,16 -557,25 +384,16 @@@ static struct int signal; void (*handler)(int); } sighandlers[] = { - {SIGHUP, sighup_handler}, - {SIGTERM, sigterm_handler}, - {SIGQUIT, sigquit_handler}, {SIGSEGV, fatal_signal_handler}, {SIGBUS, fatal_signal_handler}, {SIGILL, fatal_signal_handler}, {SIGPIPE, ignore_signal_handler}, - {SIGINT, sigint_handler}, - {SIGUSR1, sigusr1_handler}, - {SIGUSR2, sigusr2_handler}, {SIGCHLD, ignore_signal_handler}, - {SIGALRM, sigalrm_handler}, - {SIGWINCH, sigwinch_handler}, {0, NULL} }; #endif -void setup_signals(void) -{ +void setup_signals(void) { #ifndef HAVE_MINGW int i; struct sigaction act; @@@ -415,7 -597,7 +415,7 @@@ /* If we didn't detach, allow coredumps */ if(!do_detach) - sighandlers[3].handler = SIG_DFL; + sighandlers[0].handler = SIG_DFL; /* Then, for each known signal that we want to catch, assign a handler to the signal, with error checking this time. */ diff --combined src/protocol_auth.c index 1fb37fcf,5e453600..2109cd4d --- a/src/protocol_auth.c +++ b/src/protocol_auth.c @@@ -1,7 -1,7 +1,7 @@@ /* protocol_auth.c -- handle the meta-protocol, authentication Copyright (C) 1999-2005 Ivo Timmermans, - 2000-2007 Guus Sliepen + 2000-2009 Guus Sliepen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@@ -22,10 -22,14 +22,10 @@@ #include "system.h" -#include -#include -#include -#include - -#include "avl_tree.h" +#include "splay_tree.h" #include "conf.h" #include "connection.h" +#include "crypto.h" #include "edge.h" #include "graph.h" #include "logger.h" @@@ -33,25 -37,24 +33,25 @@@ #include "netutl.h" #include "node.h" #include "protocol.h" +#include "rsa.h" #include "utils.h" #include "xalloc.h" -bool send_id(connection_t *c) -{ +bool send_id(connection_t *c) { cp(); + gettimeofday(&c->start, NULL); + return send_request(c, "%d %s %d", ID, myself->connection->name, myself->connection->protocol_version); } -bool id_h(connection_t *c) -{ +bool id_h(connection_t *c, char *request) { char name[MAX_STRING_SIZE]; cp(); - if(sscanf(c->buffer, "%*d " MAX_STRING " %d", name, &c->protocol_version) != 2) { + if(sscanf(request, "%*d " MAX_STRING " %d", name, &c->protocol_version) != 2) { logger(LOG_ERR, _("Got bad %s from %s (%s)"), "ID", c->name, c->hostname); return false; @@@ -113,23 -116,29 +113,23 @@@ return send_metakey(c); } -bool send_metakey(connection_t *c) -{ - char *buffer; - int len; - bool x; +bool send_metakey(connection_t *c) { + size_t len = rsa_size(&c->rsa); + char key[len]; + char enckey[len]; + char hexkey[2 * len + 1]; cp(); - len = RSA_size(c->rsa_key); - - /* Allocate buffers for the meta key */ - - buffer = alloca(2 * len + 1); + if(!cipher_open_blowfish_ofb(&c->outcipher)) + return false; - if(!c->outkey) - c->outkey = xmalloc(len); + if(!digest_open_sha1(&c->outdigest)) + return false; - if(!c->outctx) - c->outctx = xmalloc_and_zero(sizeof(*c->outctx)); - cp(); - /* Copy random data to the buffer */ + /* Create a random key */ - RAND_pseudo_bytes((unsigned char *)c->outkey, len); + randomize(key, len); /* The message we send must be smaller than the modulus of the RSA key. By definition, for a key of k bits, the following formula holds: @@@ -141,14 -150,13 +141,14 @@@ This can be done by setting the most significant bit to zero. */ - c->outkey[0] &= 0x7F; + key[0] &= 0x7F; + + cipher_set_key_from_rsa(&c->outcipher, key, len, true); ifdebug(SCARY_THINGS) { - bin2hex(c->outkey, buffer, len); - buffer[len * 2] = '\0'; - logger(LOG_DEBUG, _("Generated random meta key (unencrypted): %s"), - buffer); + bin2hex(key, hexkey, len); + hexkey[len * 2] = '\0'; + logger(LOG_DEBUG, _("Generated random meta key (unencrypted): %s"), hexkey); } /* Encrypt the random data @@@ -158,96 -166,163 +158,96 @@@ with a length equal to that of the modulus of the RSA key. */ - if(RSA_public_encrypt(len, (unsigned char *)c->outkey, (unsigned char *)buffer, c->rsa_key, RSA_NO_PADDING) != len) { - logger(LOG_ERR, _("Error during encryption of meta key for %s (%s)"), - c->name, c->hostname); + if(!rsa_public_encrypt(&c->rsa, key, len, enckey)) { + logger(LOG_ERR, _("Error during encryption of meta key for %s (%s)"), c->name, c->hostname); return false; } /* Convert the encrypted random data to a hexadecimal formatted string */ - bin2hex(buffer, buffer, len); - buffer[len * 2] = '\0'; + bin2hex(enckey, hexkey, len); + hexkey[len * 2] = '\0'; /* Send the meta key */ - x = send_request(c, "%d %d %d %d %d %s", METAKEY, - c->outcipher ? c->outcipher->nid : 0, - c->outdigest ? c->outdigest->type : 0, c->outmaclength, - c->outcompression, buffer); - - /* Further outgoing requests are encrypted with the key we just generated */ - - if(c->outcipher) { - if(!EVP_EncryptInit(c->outctx, c->outcipher, - (unsigned char *)c->outkey + len - c->outcipher->key_len, - (unsigned char *)c->outkey + len - c->outcipher->key_len - - c->outcipher->iv_len)) { - logger(LOG_ERR, _("Error during initialisation of cipher for %s (%s): %s"), - c->name, c->hostname, ERR_error_string(ERR_get_error(), NULL)); - return false; - } - - c->status.encryptout = true; - } - - return x; + bool result = send_request(c, "%d %d %d %d %d %s", METAKEY, + cipher_get_nid(&c->outcipher), + digest_get_nid(&c->outdigest), c->outmaclength, + c->outcompression, hexkey); + + c->status.encryptout = true; + return result; } -bool metakey_h(connection_t *c) -{ - char buffer[MAX_STRING_SIZE]; +bool metakey_h(connection_t *c, char *request) { + char hexkey[MAX_STRING_SIZE]; int cipher, digest, maclength, compression; - int len; + size_t len = rsa_size(&myself->connection->rsa); + char enckey[len]; + char key[len]; cp(); - if(sscanf(c->buffer, "%*d %d %d %d %d " MAX_STRING, &cipher, &digest, &maclength, &compression, buffer) != 5) { - logger(LOG_ERR, _("Got bad %s from %s (%s)"), "METAKEY", c->name, - c->hostname); + if(sscanf(request, "%*d %d %d %d %d " MAX_STRING, &cipher, &digest, &maclength, &compression, hexkey) != 5) { + logger(LOG_ERR, _("Got bad %s from %s (%s)"), "METAKEY", c->name, c->hostname); return false; } - len = RSA_size(myself->connection->rsa_key); - /* Check if the length of the meta key is all right */ - if(strlen(buffer) != len * 2) { + if(strlen(hexkey) != len * 2) { logger(LOG_ERR, _("Possible intruder %s (%s): %s"), c->name, c->hostname, "wrong keylength"); return false; } - /* Allocate buffers for the meta key */ - - if(!c->inkey) - c->inkey = xmalloc(len); - - if(!c->inctx) - c->inctx = xmalloc_and_zero(sizeof(*c->inctx)); - /* Convert the challenge from hexadecimal back to binary */ - hex2bin(buffer, buffer, len); + hex2bin(hexkey, enckey, len); /* Decrypt the meta key */ - if(RSA_private_decrypt(len, (unsigned char *)buffer, (unsigned char *)c->inkey, myself->connection->rsa_key, RSA_NO_PADDING) != len) { /* See challenge() */ - logger(LOG_ERR, _("Error during decryption of meta key for %s (%s)"), - c->name, c->hostname); + if(!rsa_private_decrypt(&myself->connection->rsa, enckey, len, key)) { + logger(LOG_ERR, _("Error during decryption of meta key for %s (%s)"), c->name, c->hostname); return false; } ifdebug(SCARY_THINGS) { - bin2hex(c->inkey, buffer, len); - buffer[len * 2] = '\0'; - logger(LOG_DEBUG, _("Received random meta key (unencrypted): %s"), buffer); + bin2hex(key, hexkey, len); + hexkey[len * 2] = '\0'; + logger(LOG_DEBUG, _("Received random meta key (unencrypted): %s"), hexkey); } - /* All incoming requests will now be encrypted. */ - /* Check and lookup cipher and digest algorithms */ - if(cipher) { - c->incipher = EVP_get_cipherbynid(cipher); - - if(!c->incipher) { - logger(LOG_ERR, _("%s (%s) uses unknown cipher!"), c->name, c->hostname); - return false; - } - - if(!EVP_DecryptInit(c->inctx, c->incipher, - (unsigned char *)c->inkey + len - c->incipher->key_len, - (unsigned char *)c->inkey + len - c->incipher->key_len - - c->incipher->iv_len)) { - logger(LOG_ERR, _("Error during initialisation of cipher from %s (%s): %s"), - c->name, c->hostname, ERR_error_string(ERR_get_error(), NULL)); - return false; - } - - c->status.decryptin = true; - } else { - c->incipher = NULL; + if(!cipher_open_by_nid(&c->incipher, cipher) || !cipher_set_key_from_rsa(&c->incipher, key, len, false)) { + logger(LOG_ERR, _("Error during initialisation of cipher from %s (%s)"), c->name, c->hostname); + return false; } - c->inmaclength = maclength; - - if(digest) { - c->indigest = EVP_get_digestbynid(digest); - - if(!c->indigest) { - logger(LOG_ERR, _("Node %s (%s) uses unknown digest!"), c->name, c->hostname); - return false; - } - - if(c->inmaclength > c->indigest->md_size || c->inmaclength < 0) { - logger(LOG_ERR, _("%s (%s) uses bogus MAC length!"), c->name, c->hostname); - return false; - } - } else { - c->indigest = NULL; + if(!digest_open_by_nid(&c->indigest, digest)) { + logger(LOG_ERR, _("Error during initialisation of digest from %s (%s)"), c->name, c->hostname); + return false; } - c->incompression = compression; + c->status.decryptin = true; c->allow_request = CHALLENGE; return send_challenge(c); } -bool send_challenge(connection_t *c) -{ - char *buffer; - int len; +bool send_challenge(connection_t *c) { + size_t len = rsa_size(&c->rsa); + char buffer[len * 2 + 1]; cp(); - /* CHECKME: what is most reasonable value for len? */ - - len = RSA_size(c->rsa_key); - - /* Allocate buffers for the challenge */ - - buffer = alloca(2 * len + 1); - if(!c->hischallenge) c->hischallenge = xmalloc(len); /* Copy random data to the buffer */ - RAND_pseudo_bytes((unsigned char *)c->hischallenge, len); + randomize(c->hischallenge, len); /* Convert to hex */ @@@ -259,54 -334,81 +259,54 @@@ return send_request(c, "%d %s", CHALLENGE, buffer); } -bool challenge_h(connection_t *c) -{ +bool challenge_h(connection_t *c, char *request) { char buffer[MAX_STRING_SIZE]; - int len; + size_t len = rsa_size(&myself->connection->rsa); + size_t digestlen = digest_length(&c->outdigest); + char digest[digestlen]; cp(); - if(sscanf(c->buffer, "%*d " MAX_STRING, buffer) != 1) { - logger(LOG_ERR, _("Got bad %s from %s (%s)"), "CHALLENGE", c->name, - c->hostname); + if(sscanf(request, "%*d " MAX_STRING, buffer) != 1) { + logger(LOG_ERR, _("Got bad %s from %s (%s)"), "CHALLENGE", c->name, c->hostname); return false; } - len = RSA_size(myself->connection->rsa_key); - /* Check if the length of the challenge is all right */ if(strlen(buffer) != len * 2) { - logger(LOG_ERR, _("Possible intruder %s (%s): %s"), c->name, - c->hostname, "wrong challenge length"); + logger(LOG_ERR, _("Possible intruder %s (%s): %s"), c->name, c->hostname, "wrong challenge length"); return false; } - /* Allocate buffers for the challenge */ - - if(!c->mychallenge) - c->mychallenge = xmalloc(len); - /* Convert the challenge from hexadecimal back to binary */ - hex2bin(buffer, c->mychallenge, len); + hex2bin(buffer, buffer, len); c->allow_request = CHAL_REPLY; - /* Rest is done by send_chal_reply() */ - - return send_chal_reply(c); -} - -bool send_chal_reply(connection_t *c) -{ - char hash[EVP_MAX_MD_SIZE * 2 + 1]; - EVP_MD_CTX ctx; - cp(); /* Calculate the hash from the challenge we received */ - if(!EVP_DigestInit(&ctx, c->indigest) - || !EVP_DigestUpdate(&ctx, c->mychallenge, RSA_size(myself->connection->rsa_key)) - || !EVP_DigestFinal(&ctx, (unsigned char *)hash, NULL)) { - logger(LOG_ERR, _("Error during calculation of response for %s (%s): %s"), - c->name, c->hostname, ERR_error_string(ERR_get_error(), NULL)); - return false; - } + digest_create(&c->indigest, buffer, len, digest); /* Convert the hash to a hexadecimal formatted string */ - bin2hex(hash, hash, c->indigest->md_size); - hash[c->indigest->md_size * 2] = '\0'; + bin2hex(digest, buffer, digestlen); + buffer[digestlen * 2] = '\0'; /* Send the reply */ - return send_request(c, "%d %s", CHAL_REPLY, hash); + return send_request(c, "%d %s", CHAL_REPLY, buffer); } -bool chal_reply_h(connection_t *c) -{ +bool chal_reply_h(connection_t *c, char *request) { char hishash[MAX_STRING_SIZE]; - char myhash[EVP_MAX_MD_SIZE]; - EVP_MD_CTX ctx; cp(); - if(sscanf(c->buffer, "%*d " MAX_STRING, hishash) != 1) { + if(sscanf(request, "%*d " MAX_STRING, hishash) != 1) { logger(LOG_ERR, _("Got bad %s from %s (%s)"), "CHAL_REPLY", c->name, c->hostname); return false; @@@ -314,19 -416,38 +314,19 @@@ /* Check if the length of the hash is all right */ - if(strlen(hishash) != c->outdigest->md_size * 2) { - logger(LOG_ERR, _("Possible intruder %s (%s): %s"), c->name, - c->hostname, _("wrong challenge reply length")); + if(strlen(hishash) != digest_length(&c->outdigest) * 2) { + logger(LOG_ERR, _("Possible intruder %s (%s): %s"), c->name, c->hostname, _("wrong challenge reply length")); return false; } /* Convert the hash to binary format */ - hex2bin(hishash, hishash, c->outdigest->md_size); + hex2bin(hishash, hishash, digest_length(&c->outdigest)); - /* Calculate the hash from the challenge we sent */ - - if(!EVP_DigestInit(&ctx, c->outdigest) - || !EVP_DigestUpdate(&ctx, c->hischallenge, RSA_size(c->rsa_key)) - || !EVP_DigestFinal(&ctx, (unsigned char *)myhash, NULL)) { - logger(LOG_ERR, _("Error during calculation of response from %s (%s): %s"), - c->name, c->hostname, ERR_error_string(ERR_get_error(), NULL)); - return false; - } - - /* Verify the incoming hash with the calculated hash */ - - if(memcmp(hishash, myhash, c->outdigest->md_size)) { - logger(LOG_ERR, _("Possible intruder %s (%s): %s"), c->name, - c->hostname, _("wrong challenge reply")); - - ifdebug(SCARY_THINGS) { - bin2hex(myhash, hishash, SHA_DIGEST_LENGTH); - hishash[SHA_DIGEST_LENGTH * 2] = '\0'; - logger(LOG_DEBUG, _("Expected challenge reply: %s"), hishash); - } + /* Verify the hash */ + if(!digest_verify(&c->outdigest, c->hischallenge, rsa_size(&c->rsa), hishash)) { + logger(LOG_ERR, _("Possible intruder %s (%s): %s"), c->name, c->hostname, _("wrong challenge reply")); return false; } @@@ -334,14 -455,13 +334,14 @@@ Send an acknowledgement with the rest of the information needed. */ + free(c->hischallenge); + c->hischallenge = NULL; c->allow_request = ACK; return send_ack(c); } -bool send_ack(connection_t *c) -{ +bool send_ack(connection_t *c) { /* ACK message contains rest of the information the other end needs to create node_t and edge_t structures. */ @@@ -363,7 -483,7 +363,7 @@@ if((get_config_bool(lookup_config(c->config_tree, "TCPOnly"), &choice) && choice) || myself->options & OPTION_TCPONLY) c->options |= OPTION_TCPONLY | OPTION_INDIRECT; - if((get_config_bool(lookup_config(c->config_tree, "PMTUDiscovery"), &choice) && choice) || myself->options & OPTION_PMTU_DISCOVERY) + if(myself->options & OPTION_PMTU_DISCOVERY) c->options |= OPTION_PMTU_DISCOVERY; get_config_int(lookup_config(c->config_tree, "Weight"), &c->estimated_weight); @@@ -371,8 -491,9 +371,8 @@@ return send_request(c, "%d %s %d %lx", ACK, myport, c->estimated_weight, c->options); } -static void send_everything(connection_t *c) -{ - avl_node_t *node, *node2; +static void send_everything(connection_t *c) { + splay_node_t *node, *node2; node_t *n; subnet_t *s; edge_t *e; @@@ -403,7 -524,8 +403,7 @@@ } } -bool ack_h(connection_t *c) -{ +bool ack_h(connection_t *c, char *request) { char hisport[MAX_STRING_SIZE]; char *hisaddress, *dummy; int weight, mtu; @@@ -412,7 -534,7 +412,7 @@@ cp(); - if(sscanf(c->buffer, "%*d " MAX_STRING " %d %lx", hisport, &weight, &options) != 3) { + if(sscanf(request, "%*d " MAX_STRING " %d %lx", hisport, &weight, &options) != 3) { logger(LOG_ERR, _("Got bad %s from %s (%s)"), "ACK", c->name, c->hostname); return false; @@@ -429,17 -551,8 +429,17 @@@ } else { if(n->connection) { /* Oh dear, we already have a connection to this node. */ - ifdebug(CONNECTIONS) logger(LOG_DEBUG, _("Established a second connection with %s (%s), closing old connection"), - n->name, n->hostname); + ifdebug(CONNECTIONS) logger(LOG_DEBUG, _("Established a second connection with %s (%s), closing old connection"), n->connection->name, n->connection->hostname); + + if(n->connection->outgoing) { + if(c->outgoing) + logger(LOG_WARNING, _("Two outgoing connections to the same node!")); + else + c->outgoing = n->connection->outgoing; + + n->connection->outgoing = NULL; + } + terminate_connection(n->connection, false); /* Run graph algorithm to purge key and make sure up/down scripts are rerun with new IP addresses and stuff */ graph(); @@@ -448,6 -561,10 +448,10 @@@ n->connection = c; c->node = n; + if(!(c->options & options & OPTION_PMTU_DISCOVERY)) { + c->options &= ~OPTION_PMTU_DISCOVERY; + options &= ~OPTION_PMTU_DISCOVERY; + } c->options |= options; if(get_config_int(lookup_config(c->config_tree, "PMTU"), &mtu) && mtu < n->mtu) diff --combined src/protocol_key.c index 0cd840be,06c6d336..3cf4ab11 --- a/src/protocol_key.c +++ b/src/protocol_key.c @@@ -1,7 -1,7 +1,7 @@@ /* protocol_key.c -- handle the meta-protocol, key exchange Copyright (C) 1999-2005 Ivo Timmermans, - 2000-2006 Guus Sliepen + 2000-2009 Guus Sliepen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@@ -22,8 -22,10 +22,8 @@@ #include "system.h" -#include -#include - -#include "avl_tree.h" +#include "splay_tree.h" +#include "cipher.h" #include "connection.h" #include "logger.h" #include "net.h" @@@ -33,9 -35,10 +33,9 @@@ #include "utils.h" #include "xalloc.h" -bool mykeyused = false; +static bool mykeyused = false; -bool send_key_changed(connection_t *c, const node_t *n) -{ +bool send_key_changed(connection_t *c, const node_t *n) { cp(); /* Only send this message if some other daemon requested our key previously. @@@ -48,19 -51,20 +48,19 @@@ return send_request(c, "%d %lx %s", KEY_CHANGED, random(), n->name); } -bool key_changed_h(connection_t *c) -{ +bool key_changed_h(connection_t *c, char *request) { char name[MAX_STRING_SIZE]; node_t *n; cp(); - if(sscanf(c->buffer, "%*d %*x " MAX_STRING, name) != 1) { + if(sscanf(request, "%*d %*x " MAX_STRING, name) != 1) { logger(LOG_ERR, _("Got bad %s from %s (%s)"), "KEY_CHANGED", c->name, c->hostname); return false; } - if(seen_request(c->buffer)) + if(seen_request(request)) return true; n = lookup_node(name); @@@ -77,25 -81,27 +77,25 @@@ /* Tell the others */ if(!tunnelserver) - forward_request(c); + forward_request(c, request); return true; } -bool send_req_key(connection_t *c, const node_t *from, const node_t *to) -{ +bool send_req_key(connection_t *c, const node_t *from, const node_t *to) { cp(); return send_request(c, "%d %s %s", REQ_KEY, from->name, to->name); } -bool req_key_h(connection_t *c) -{ +bool req_key_h(connection_t *c, char *request) { char from_name[MAX_STRING_SIZE]; char to_name[MAX_STRING_SIZE]; node_t *from, *to; cp(); - if(sscanf(c->buffer, "%*d " MAX_STRING " " MAX_STRING, from_name, to_name) != 2) { + if(sscanf(request, "%*d " MAX_STRING " " MAX_STRING, from_name, to_name) != 2) { logger(LOG_ERR, _("Got bad %s from %s (%s)"), "REQ_KEY", c->name, c->hostname); return false; @@@ -122,7 -128,7 +122,7 @@@ if(to == myself) { /* Yes, send our own key back */ mykeyused = true; from->received_seqno = 0; - memset(from->late, 0, sizeof(from->late)); + memset(from->late, 0, sizeof from->late); send_ans_key(c, myself, from); } else { if(tunnelserver) @@@ -140,24 -146,25 +140,24 @@@ return true; } -bool send_ans_key(connection_t *c, const node_t *from, const node_t *to) -{ - char *key; +bool send_ans_key(connection_t *c, const node_t *from, const node_t *to) { + size_t keylen = cipher_keylength(&from->cipher); + char key[keylen * 2 + 1]; cp(); - key = alloca(2 * from->keylength + 1); - bin2hex(from->key, key, from->keylength); - key[from->keylength * 2] = '\0'; + cipher_get_key(&from->cipher, key); + bin2hex(key, key, keylen); + key[keylen * 2] = '\0'; return send_request(c, "%d %s %s %s %d %d %d %d", ANS_KEY, from->name, to->name, key, - from->cipher ? from->cipher->nid : 0, - from->digest ? from->digest->type : 0, from->maclength, + cipher_get_nid(&from->cipher), + digest_get_nid(&from->digest), from->maclength, from->compression); } -bool ans_key_h(connection_t *c) -{ +bool ans_key_h(connection_t *c, char *request) { char from_name[MAX_STRING_SIZE]; char to_name[MAX_STRING_SIZE]; char key[MAX_STRING_SIZE]; @@@ -166,7 -173,7 +166,7 @@@ cp(); - if(sscanf(c->buffer, "%*d "MAX_STRING" "MAX_STRING" "MAX_STRING" %d %d %d %d", + if(sscanf(request, "%*d "MAX_STRING" "MAX_STRING" "MAX_STRING" %d %d %d %d", from_name, to_name, key, &cipher, &digest, &maclength, &compression) != 7) { logger(LOG_ERR, _("Got bad %s from %s (%s)"), "ANS_KEY", c->name, @@@ -198,35 -205,65 +198,35 @@@ if(!to->status.reachable) { logger(LOG_WARNING, _("Got %s from %s (%s) destination %s which is not reachable"), - "ANS_KEY", c->name, c->hostname, to_name); + "ANS_KEY", c->name, c->hostname, to_name); return true; } - return send_request(to->nexthop->connection, "%s", c->buffer); + return send_request(to->nexthop->connection, "%s", request); } - /* Update our copy of the origin's packet key */ - - if(from->key) - free(from->key); - - from->key = xstrdup(key); - from->keylength = strlen(key) / 2; - hex2bin(from->key, from->key, from->keylength); - from->key[from->keylength] = '\0'; - - from->status.validkey = true; - from->status.waitingforkey = false; - from->sent_seqno = 0; - /* Check and lookup cipher and digest algorithms */ - if(cipher) { - from->cipher = EVP_get_cipherbynid(cipher); - - if(!from->cipher) { - logger(LOG_ERR, _("Node %s (%s) uses unknown cipher!"), from->name, - from->hostname); - return false; - } + if(!cipher_open_by_nid(&from->cipher, cipher)) { + logger(LOG_ERR, _("Node %s (%s) uses unknown cipher!"), from->name, from->hostname); + return false; + } - if(from->keylength != from->cipher->key_len + from->cipher->iv_len) { - logger(LOG_ERR, _("Node %s (%s) uses wrong keylength!"), from->name, - from->hostname); - return false; - } - } else { - from->cipher = NULL; + if(strlen(key) / 2 != cipher_keylength(&from->cipher)) { + logger(LOG_ERR, _("Node %s (%s) uses wrong keylength!"), from->name, from->hostname); + return false; } from->maclength = maclength; - if(digest) { - from->digest = EVP_get_digestbynid(digest); - - if(!from->digest) { - logger(LOG_ERR, _("Node %s (%s) uses unknown digest!"), from->name, - from->hostname); - return false; - } + if(!digest_open_by_nid(&from->digest, digest)) { + logger(LOG_ERR, _("Node %s (%s) uses unknown digest!"), from->name, from->hostname); + return false; + } - if(from->maclength > from->digest->md_size || from->maclength < 0) { - logger(LOG_ERR, _("Node %s (%s) uses bogus MAC length!"), - from->name, from->hostname); - return false; - } - } else { - from->digest = NULL; + if(from->maclength > digest_length(&from->digest) || from->maclength < 0) { + logger(LOG_ERR, _("Node %s (%s) uses bogus MAC length!"), from->name, from->hostname); + return false; } if(compression < 0 || compression > 11) { @@@ -236,19 -273,15 +236,17 @@@ from->compression = compression; - if(from->cipher) - if(!EVP_EncryptInit_ex(&from->packet_ctx, from->cipher, NULL, (unsigned char *)from->key, (unsigned char *)from->key + from->cipher->key_len)) { - logger(LOG_ERR, _("Error during initialisation of key from %s (%s): %s"), - from->name, from->hostname, ERR_error_string(ERR_get_error(), NULL)); - return false; - } + /* Update our copy of the origin's packet key */ + + hex2bin(key, key, cipher_keylength(&from->cipher)); + cipher_set_key(&from->cipher, key, false); + + from->status.validkey = true; + from->status.waitingforkey = false; + from->sent_seqno = 0; if(from->options & OPTION_PMTU_DISCOVERY && !from->mtuprobes) send_mtu_probe(from); - flush_queue(from); - return true; } diff --combined src/protocol_misc.c index 95a240e1,8f56aee5..02e38598 --- a/src/protocol_misc.c +++ b/src/protocol_misc.c @@@ -45,14 -45,14 +45,14 @@@ bool send_status(connection_t *c, int s return send_request(c, "%d %d %s", STATUS, statusno, statusstring); } -bool status_h(connection_t *c) +bool status_h(connection_t *c, char *request) { int statusno; char statusstring[MAX_STRING_SIZE]; cp(); - if(sscanf(c->buffer, "%*d %d " MAX_STRING, &statusno, statusstring) != 2) { + if(sscanf(request, "%*d %d " MAX_STRING, &statusno, statusstring) != 2) { logger(LOG_ERR, _("Got bad %s from %s (%s)"), "STATUS", c->name, c->hostname); return false; @@@ -74,14 -74,14 +74,14 @@@ bool send_error(connection_t *c, int er return send_request(c, "%d %d %s", ERROR, err, errstring); } -bool error_h(connection_t *c) +bool error_h(connection_t *c, char *request) { int err; char errorstring[MAX_STRING_SIZE]; cp(); - if(sscanf(c->buffer, "%*d %d " MAX_STRING, &err, errorstring) != 2) { + if(sscanf(request, "%*d %d " MAX_STRING, &err, errorstring) != 2) { logger(LOG_ERR, _("Got bad %s from %s (%s)"), "ERROR", c->name, c->hostname); return false; @@@ -90,7 -90,9 +90,7 @@@ ifdebug(ERROR) logger(LOG_NOTICE, _("Error message from %s (%s): %d: %s"), c->name, c->hostname, err, errorstring); - terminate_connection(c, c->status.active); - - return true; + return false; } bool send_termreq(connection_t *c) @@@ -100,11 -102,13 +100,11 @@@ return send_request(c, "%d", TERMREQ); } -bool termreq_h(connection_t *c) +bool termreq_h(connection_t *c, char *request) { cp(); - terminate_connection(c, c->status.active); - - return true; + return false; } bool send_ping(connection_t *c) @@@ -112,12 -116,12 +112,12 @@@ cp(); c->status.pinged = true; - c->last_ping_time = now; + c->last_ping_time = time(NULL); return send_request(c, "%d", PING); } -bool ping_h(connection_t *c) +bool ping_h(connection_t *c, char *request) { cp(); @@@ -131,7 -135,7 +131,7 @@@ bool send_pong(connection_t *c return send_request(c, "%d", PONG); } -bool pong_h(connection_t *c) +bool pong_h(connection_t *c, char *request) { cp(); @@@ -151,9 -155,10 +151,10 @@@ bool send_tcppacket(connection_t *c, vp { cp(); - /* If there already is a lot of data in the outbuf buffer, discard this packet. */ + /* If there already is a lot of data in the outbuf buffer, discard this packet. + We use a very simple Random Early Drop algorithm. */ - if(c->buffer->output->off > maxoutbufsize) - if(2.0 * c->outbuflen / (double)maxoutbufsize - 1 > drand48()) ++ if(2.0 * c->buffer->output->off / (double)maxoutbufsize - 1 > drand48()) return true; if(!send_request(c, "%d %hd", PACKET, packet->len)) @@@ -162,13 -167,13 +163,13 @@@ return send_meta(c, (char *)packet->data, packet->len); } -bool tcppacket_h(connection_t *c) +bool tcppacket_h(connection_t *c, char *request) { short int len; cp(); - if(sscanf(c->buffer, "%*d %hd", &len) != 1) { + if(sscanf(request, "%*d %hd", &len) != 1) { logger(LOG_ERR, _("Got bad %s from %s (%s)"), "PACKET", c->name, c->hostname); return false; diff --combined src/protocol_subnet.c index b1d71ab7,6e7d706e..00cdde67 --- a/src/protocol_subnet.c +++ b/src/protocol_subnet.c @@@ -1,7 -1,7 +1,7 @@@ /* protocol_subnet.c -- handle the meta-protocol, subnets Copyright (C) 1999-2005 Ivo Timmermans, - 2000-2006 Guus Sliepen + 2000-2009 Guus Sliepen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@@ -45,7 -45,7 +45,7 @@@ bool send_add_subnet(connection_t *c, c return send_request(c, "%d %lx %s %s", ADD_SUBNET, random(), subnet->owner->name, netstr); } -bool add_subnet_h(connection_t *c) +bool add_subnet_h(connection_t *c, char *request) { char subnetstr[MAX_STRING_SIZE]; char name[MAX_STRING_SIZE]; @@@ -54,7 -54,7 +54,7 @@@ cp(); - if(sscanf(c->buffer, "%*d %*x " MAX_STRING " " MAX_STRING, name, subnetstr) != 2) { + if(sscanf(request, "%*d %*x " MAX_STRING " " MAX_STRING, name, subnetstr) != 2) { logger(LOG_ERR, _("Got bad %s from %s (%s)"), "ADD_SUBNET", c->name, c->hostname); return false; @@@ -76,22 -76,26 +76,26 @@@ return false; } - if(seen_request(c->buffer)) + if(seen_request(request)) return true; /* Check if the owner of the new subnet is in the connection list */ owner = lookup_node(name); + if(tunnelserver && owner != myself && owner != c->node) { + /* in case of tunnelserver, ignore indirect subnet registrations */ + ifdebug(PROTOCOL) logger(LOG_WARNING, _("Ignoring indirect %s from %s (%s) for %s"), + "ADD_SUBNET", c->name, c->hostname, subnetstr); + return true; + } + if(!owner) { owner = new_node(); owner->name = xstrdup(name); node_add(owner); } - if(tunnelserver && owner != myself && owner != c->node) - return false; - /* Check if we already know this subnet */ if(lookup_subnet(owner, &s)) @@@ -140,7 -144,7 +144,7 @@@ /* Tell the rest */ if(!tunnelserver) - forward_request(c); + forward_request(c, request); return true; } @@@ -157,7 -161,7 +161,7 @@@ bool send_del_subnet(connection_t *c, c return send_request(c, "%d %lx %s %s", DEL_SUBNET, random(), s->owner->name, netstr); } -bool del_subnet_h(connection_t *c) +bool del_subnet_h(connection_t *c, char *request) { char subnetstr[MAX_STRING_SIZE]; char name[MAX_STRING_SIZE]; @@@ -166,7 -170,7 +170,7 @@@ cp(); - if(sscanf(c->buffer, "%*d %*x " MAX_STRING " " MAX_STRING, name, subnetstr) != 2) { + if(sscanf(request, "%*d %*x " MAX_STRING " " MAX_STRING, name, subnetstr) != 2) { logger(LOG_ERR, _("Got bad %s from %s (%s)"), "DEL_SUBNET", c->name, c->hostname); return false; @@@ -201,7 -205,7 +205,7 @@@ return false; } - if(seen_request(c->buffer)) + if(seen_request(request)) return true; /* If everything is correct, delete the subnet from the list of the owner */ @@@ -228,7 -232,7 +232,7 @@@ /* Tell the rest */ if(!tunnelserver) - forward_request(c); + forward_request(c, request); /* Finally, delete it. */ diff --combined src/raw_socket/device.c index 3694cb12,f2a135d3..412ba41b --- a/src/raw_socket/device.c +++ b/src/raw_socket/device.c @@@ -1,7 -1,7 +1,7 @@@ /* device.c -- raw socket Copyright (C) 2002-2005 Ivo Timmermans, - 2002-2006 Guus Sliepen + 2002-2009 Guus Sliepen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@@ -29,28 -29,29 +29,28 @@@ #include "logger.h" #include "utils.h" #include "route.h" + #include "xalloc.h" int device_fd = -1; - char *device; - char *iface; - char ifrname[IFNAMSIZ]; - char *device_info; + char *device = NULL; + char *iface = NULL; + static char ifrname[IFNAMSIZ]; + static char *device_info; static int device_total_in = 0; static int device_total_out = 0; -bool setup_device(void) -{ +bool setup_device(void) { struct ifreq ifr; struct sockaddr_ll sa; cp(); - if(!get_config_string - (lookup_config(config_tree, "Interface"), &iface)) - iface = "eth0"; + if(!get_config_string(lookup_config(config_tree, "Interface"), &iface)) + iface = xstrdup("eth0"); if(!get_config_string(lookup_config(config_tree, "Device"), &device)) - device = iface; + device = xstrdup(iface); device_info = _("raw socket"); @@@ -60,7 -61,7 +60,7 @@@ return false; } - memset(&ifr, 0, sizeof(ifr)); + memset(&ifr, 0, sizeof ifr); strncpy(ifr.ifr_ifrn.ifrn_name, iface, IFNAMSIZ); if(ioctl(device_fd, SIOCGIFINDEX, &ifr)) { close(device_fd); @@@ -69,12 -70,12 +69,12 @@@ return false; } - memset(&sa, '0', sizeof(sa)); + memset(&sa, '0', sizeof sa); sa.sll_family = AF_PACKET; sa.sll_protocol = htons(ETH_P_ALL); sa.sll_ifindex = ifr.ifr_ifindex; - if(bind(device_fd, (struct sockaddr *) &sa, (socklen_t) sizeof(sa))) { + if(bind(device_fd, (struct sockaddr *) &sa, (socklen_t) sizeof sa)) { logger(LOG_ERR, _("Could not bind %s to %s: %s"), device, iface, strerror(errno)); return false; } @@@ -84,24 -85,29 +84,27 @@@ return true; } -void close_device(void) -{ +void close_device(void) { cp(); close(device_fd); + + free(device); + free(iface); } -bool read_packet(vpn_packet_t *packet) -{ - int lenin; +bool read_packet(vpn_packet_t *packet) { + int inlen; cp(); - if((lenin = read(device_fd, packet->data, MTU)) <= 0) { + if((inlen = read(device_fd, packet->data, MTU)) <= 0) { logger(LOG_ERR, _("Error while reading from %s %s: %s"), device_info, device, strerror(errno)); return false; } - packet->len = lenin; + packet->len = inlen; device_total_in += packet->len; @@@ -111,7 -117,8 +114,7 @@@ return true; } -bool write_packet(vpn_packet_t *packet) -{ +bool write_packet(vpn_packet_t *packet) { cp(); ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Writing packet of %d bytes to %s"), @@@ -128,7 -135,8 +131,7 @@@ return true; } -void dump_device_stats(void) -{ +void dump_device_stats(void) { cp(); logger(LOG_DEBUG, _("Statistics for %s %s:"), device_info, device); diff --combined src/route.c index f3622f59,e79fae51..8acabb1b --- a/src/route.c +++ b/src/route.c @@@ -1,7 -1,7 +1,7 @@@ /* route.c -- routing Copyright (C) 2000-2005 Ivo Timmermans, - 2000-2006 Guus Sliepen + 2000-2009 Guus Sliepen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@@ -22,7 -22,7 +22,7 @@@ #include "system.h" -#include "avl_tree.h" +#include "splay_tree.h" #include "connection.h" #include "ethernet.h" #include "ipv4.h" @@@ -51,8 -51,6 +51,8 @@@ static const size_t icmp6_size = sizeof static const size_t ns_size = sizeof(struct nd_neighbor_solicit); static const size_t opt_size = sizeof(struct nd_opt_hdr); +static struct event age_subnets_event; + /* RFC 1071 */ static uint16_t inet_checksum(void *data, int len, uint16_t prevsum) @@@ -77,7 -75,6 +77,7 @@@ static bool ratelimit(int frequency) { static time_t lasttime = 0; static int count = 0; + time_t now = time(NULL); if(lasttime == now) { if(++count > frequency) @@@ -98,47 -95,10 +98,47 @@@ static bool checklength(node_t *source return true; } +static void age_subnets(int fd, short events, void *data) +{ + subnet_t *s; + connection_t *c; + splay_node_t *node, *next, *node2; + bool left = false; + time_t now = time(NULL); + + cp(); + + for(node = myself->subnet_tree->head; node; node = next) { + next = node->next; + s = node->data; + if(s->expires && s->expires < now) { + ifdebug(TRAFFIC) { + char netstr[MAXNETSTR]; + if(net2str(netstr, sizeof netstr, s)) + logger(LOG_INFO, _("Subnet %s expired"), netstr); + } + + for(node2 = connection_tree->head; node2; node2 = node2->next) { + c = node2->data; + if(c->status.active) + send_del_subnet(c, s); + } + + subnet_del(myself, s); + } else { + if(s->expires) + left = true; + } + } + + if(left) + event_add(&age_subnets_event, &(struct timeval){10, 0}); +} + static void learn_mac(mac_t *address) { subnet_t *subnet; - avl_node_t *node; + splay_node_t *node; connection_t *c; cp(); @@@ -154,7 -114,7 +154,7 @@@ subnet = new_subnet(); subnet->type = SUBNET_MAC; - subnet->expires = now + macexpire; + subnet->expires = time(NULL) + macexpire; subnet->net.mac.address = *address; subnet_add(myself, subnet); @@@ -165,13 -125,38 +165,13 @@@ if(c->status.active) send_add_subnet(c, subnet); } - } - - if(subnet->expires) - subnet->expires = now + macexpire; -} - -void age_subnets(void) -{ - subnet_t *s; - connection_t *c; - avl_node_t *node, *next, *node2; - cp(); - - for(node = myself->subnet_tree->head; node; node = next) { - next = node->next; - s = node->data; - if(s->expires && s->expires < now) { - ifdebug(TRAFFIC) { - char netstr[MAXNETSTR]; - if(net2str(netstr, sizeof netstr, s)) - logger(LOG_INFO, _("Subnet %s expired"), netstr); - } - - for(node2 = connection_tree->head; node2; node2 = node2->next) { - c = node2->data; - if(c->status.active) - send_del_subnet(c, s); - } - - subnet_del(myself, s); - } + if(!timeout_initialized(&age_subnets_event)) + timeout_set(&age_subnets_event, age_subnets, NULL); + event_add(&age_subnets_event, &(struct timeval){10, 0}); + } else { + if(subnet->expires) + subnet->expires = time(NULL) + macexpire; } } @@@ -391,7 -376,14 +391,14 @@@ static void route_ipv4(node_t *source, if(!checklength(source, packet, ether_size + ip_size)) return; - route_ipv4_unicast(source, packet); + if(((packet->data[30] & 0xf0) == 0xe0) || ( + packet->data[30] == 255 && + packet->data[31] == 255 && + packet->data[32] == 255 && + packet->data[33] == 255)) + broadcast_packet(source, packet); + else + route_ipv4_unicast(source, packet); } /* RFC 2463 */ @@@ -457,7 -449,7 +464,7 @@@ static void route_ipv6_unreachable(node /* Generate checksum */ - checksum = inet_checksum(&pseudo, sizeof(pseudo), ~0); + checksum = inet_checksum(&pseudo, sizeof pseudo, ~0); checksum = inet_checksum(&icmp6, icmp6_size, checksum); checksum = inet_checksum(packet->data + ether_size + ip6_size + icmp6_size, ntohl(pseudo.length) - icmp6_size, checksum); @@@ -529,6 -521,7 +536,7 @@@ static void route_neighborsol(node_t *s struct nd_opt_hdr opt; subnet_t *subnet; uint16_t checksum; + bool has_opt; struct { struct in6_addr ip6_src; /* source address */ @@@ -539,9 -532,11 +547,11 @@@ cp(); - if(!checklength(source, packet, ether_size + ip6_size + ns_size + opt_size + ETH_ALEN)) + if(!checklength(source, packet, ether_size + ip6_size + ns_size)) return; + has_opt = packet->len >= ether_size + ip6_size + ns_size + opt_size + ETH_ALEN; + if(source != myself) { ifdebug(TRAFFIC) logger(LOG_WARNING, _("Got neighbor solicitation request from %s (%s) while in router mode!"), source->name, source->hostname); return; @@@ -551,7 -546,8 +561,8 @@@ memcpy(&ip6, packet->data + ether_size, ip6_size); memcpy(&ns, packet->data + ether_size + ip6_size, ns_size); - memcpy(&opt, packet->data + ether_size + ip6_size + ns_size, opt_size); + if(has_opt) + memcpy(&opt, packet->data + ether_size + ip6_size + ns_size, opt_size); /* First, snatch the source address from the neighbor solicitation packet */ @@@ -561,7 -557,7 +572,7 @@@ /* Check if this is a valid neighbor solicitation request */ if(ns.nd_ns_hdr.icmp6_type != ND_NEIGHBOR_SOLICIT || - opt.nd_opt_type != ND_OPT_SOURCE_LINKADDR) { + (has_opt && opt.nd_opt_type != ND_OPT_SOURCE_LINKADDR)) { ifdebug(TRAFFIC) logger(LOG_WARNING, _("Cannot route packet: received unknown type neighbor solicitation request")); return; } @@@ -570,15 -566,20 +581,20 @@@ pseudo.ip6_src = ip6.ip6_src; pseudo.ip6_dst = ip6.ip6_dst; - pseudo.length = htonl(ns_size + opt_size + ETH_ALEN); + if(has_opt) + pseudo.length = htonl(ns_size + opt_size + ETH_ALEN); + else + pseudo.length = htonl(ns_size); pseudo.next = htonl(IPPROTO_ICMPV6); /* Generate checksum */ - checksum = inet_checksum(&pseudo, sizeof(pseudo), ~0); + checksum = inet_checksum(&pseudo, sizeof pseudo, ~0); checksum = inet_checksum(&ns, ns_size, checksum); - checksum = inet_checksum(&opt, opt_size, checksum); - checksum = inet_checksum(packet->data + ether_size + ip6_size + ns_size + opt_size, ETH_ALEN, checksum); + if(has_opt) { + checksum = inet_checksum(&opt, opt_size, checksum); + checksum = inet_checksum(packet->data + ether_size + ip6_size + ns_size + opt_size, ETH_ALEN, checksum); + } if(checksum) { ifdebug(TRAFFIC) logger(LOG_WARNING, _("Cannot route packet: checksum error for neighbor solicitation request")); @@@ -616,7 -617,8 +632,8 @@@ ip6.ip6_dst = ip6.ip6_src; /* swap destination and source protocoll address */ ip6.ip6_src = ns.nd_ns_target; - memcpy(packet->data + ether_size + ip6_size + ns_size + opt_size, packet->data + ETH_ALEN, ETH_ALEN); /* add fake source hard addr */ + if(has_opt) + memcpy(packet->data + ether_size + ip6_size + ns_size + opt_size, packet->data + ETH_ALEN, ETH_ALEN); /* add fake source hard addr */ ns.nd_ns_cksum = 0; ns.nd_ns_type = ND_NEIGHBOR_ADVERT; @@@ -627,15 -629,20 +644,20 @@@ pseudo.ip6_src = ip6.ip6_src; pseudo.ip6_dst = ip6.ip6_dst; - pseudo.length = htonl(ns_size + opt_size + ETH_ALEN); + if(has_opt) + pseudo.length = htonl(ns_size + opt_size + ETH_ALEN); + else + pseudo.length = htonl(ns_size); pseudo.next = htonl(IPPROTO_ICMPV6); /* Generate checksum */ - checksum = inet_checksum(&pseudo, sizeof(pseudo), ~0); + checksum = inet_checksum(&pseudo, sizeof pseudo, ~0); checksum = inet_checksum(&ns, ns_size, checksum); - checksum = inet_checksum(&opt, opt_size, checksum); - checksum = inet_checksum(packet->data + ether_size + ip6_size + ns_size + opt_size, ETH_ALEN, checksum); + if(has_opt) { + checksum = inet_checksum(&opt, opt_size, checksum); + checksum = inet_checksum(packet->data + ether_size + ip6_size + ns_size + opt_size, ETH_ALEN, checksum); + } ns.nd_ns_hdr.icmp6_cksum = checksum; @@@ -643,7 -650,8 +665,8 @@@ memcpy(packet->data + ether_size, &ip6, ip6_size); memcpy(packet->data + ether_size + ip6_size, &ns, ns_size); - memcpy(packet->data + ether_size + ip6_size + ns_size, &opt, opt_size); + if(has_opt) + memcpy(packet->data + ether_size + ip6_size + ns_size, &opt, opt_size); send_packet(source, packet); } @@@ -660,7 -668,10 +683,10 @@@ static void route_ipv6(node_t *source, return; } - route_ipv6_unicast(source, packet); + if(packet->data[38] == 255) + broadcast_packet(source, packet); + else + route_ipv6_unicast(source, packet); } /* RFC 826 */ @@@ -693,7 -704,7 +719,7 @@@ static void route_arp(node_t *source, v /* Check if this is a valid ARP request */ if(ntohs(arp.arp_hrd) != ARPHRD_ETHER || ntohs(arp.arp_pro) != ETH_P_IP || - arp.arp_hln != ETH_ALEN || arp.arp_pln != sizeof(addr) || ntohs(arp.arp_op) != ARPOP_REQUEST) { + arp.arp_hln != ETH_ALEN || arp.arp_pln != sizeof addr || ntohs(arp.arp_op) != ARPOP_REQUEST) { ifdebug(TRAFFIC) logger(LOG_WARNING, _("Cannot route packet: received unknown type ARP request")); return; } @@@ -717,9 -728,9 +743,9 @@@ memcpy(packet->data, packet->data + ETH_ALEN, ETH_ALEN); /* copy destination address */ packet->data[ETH_ALEN * 2 - 1] ^= 0xFF; /* mangle source address so it looks like it's not from us */ - memcpy(&addr, arp.arp_tpa, sizeof(addr)); /* save protocol addr */ - memcpy(arp.arp_tpa, arp.arp_spa, sizeof(addr)); /* swap destination and source protocol address */ - memcpy(arp.arp_spa, &addr, sizeof(addr)); /* ... */ + memcpy(&addr, arp.arp_tpa, sizeof addr); /* save protocol addr */ + memcpy(arp.arp_tpa, arp.arp_spa, sizeof addr); /* swap destination and source protocol address */ + memcpy(arp.arp_spa, &addr, sizeof addr); /* ... */ memcpy(arp.arp_tha, arp.arp_sha, ETH_ALEN); /* set target hard/proto addr */ memcpy(arp.arp_sha, packet->data + ETH_ALEN, ETH_ALEN); /* add fake source hard addr */ diff --combined src/solaris/device.c index 25f66f38,748b5459..295a2f88 --- a/src/solaris/device.c +++ b/src/solaris/device.c @@@ -1,7 -1,7 +1,7 @@@ /* device.c -- Interaction with Solaris tun device Copyright (C) 2001-2005 Ivo Timmermans, - 2001-2006 Guus Sliepen + 2001-2009 Guus Sliepen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@@ -31,18 -31,20 +31,19 @@@ #include "logger.h" #include "net.h" #include "utils.h" + #include "xalloc.h" #define DEFAULT_DEVICE "/dev/tun" int device_fd = -1; char *device = NULL; char *iface = NULL; - char *device_info = NULL; + static char *device_info = NULL; static int device_total_in = 0; static int device_total_out = 0; -bool setup_device(void) -{ +bool setup_device(void) { int ip_fd = -1, if_fd = -1; int ppa; char *ptr; @@@ -50,7 -52,7 +51,7 @@@ cp(); if(!get_config_string(lookup_config(config_tree, "Device"), &device)) - device = DEFAULT_DEVICE; + device = xstrdup(DEFAULT_DEVICE); if((device_fd = open(device, O_RDWR | O_NONBLOCK)) < 0) { logger(LOG_ERR, _("Could not open %s: %s"), device, strerror(errno)); @@@ -107,18 -109,23 +108,21 @@@ return true; } -void close_device(void) -{ +void close_device(void) { cp(); close(device_fd); + + free(device); + free(iface); } -bool read_packet(vpn_packet_t *packet) -{ - int lenin; +bool read_packet(vpn_packet_t *packet) { + int inlen; cp(); - if((lenin = read(device_fd, packet->data + 14, MTU - 14)) <= 0) { + if((inlen = read(device_fd, packet->data + 14, MTU - 14)) <= 0) { logger(LOG_ERR, _("Error while reading from %s %s: %s"), device_info, device, strerror(errno)); return false; @@@ -140,7 -147,7 +144,7 @@@ return false; } - packet->len = lenin + 14; + packet->len = inlen + 14; device_total_in += packet->len; @@@ -150,7 -157,8 +154,7 @@@ return true; } -bool write_packet(vpn_packet_t *packet) -{ +bool write_packet(vpn_packet_t *packet) { cp(); ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Writing packet of %d bytes to %s"), @@@ -167,7 -175,8 +171,7 @@@ return true; } -void dump_device_stats(void) -{ +void dump_device_stats(void) { cp(); logger(LOG_DEBUG, _("Statistics for %s %s:"), device_info, device); diff --combined src/subnet.c index 446d38b1,9547829f..75fe31f8 --- a/src/subnet.c +++ b/src/subnet.c @@@ -1,6 -1,6 +1,6 @@@ /* subnet.c -- handle subnet lookups and lists - Copyright (C) 2000-2006 Guus Sliepen , + Copyright (C) 2000-2009 Guus Sliepen , 2000-2005 Ivo Timmermans This program is free software; you can redistribute it and/or modify @@@ -22,7 -22,7 +22,7 @@@ #include "system.h" -#include "avl_tree.h" +#include "splay_tree.h" #include "device.h" #include "logger.h" #include "net.h" @@@ -35,16 -35,38 +35,38 @@@ /* lists type of subnet */ -avl_tree_t *subnet_tree; +splay_tree_t *subnet_tree; + /* Subnet lookup cache */ + + static ipv4_t cache_ipv4_address[2]; + static subnet_t *cache_ipv4_subnet[2]; + static bool cache_ipv4_valid[2]; + static int cache_ipv4_slot; + + static ipv6_t cache_ipv6_address[2]; + static subnet_t *cache_ipv6_subnet[2]; + static bool cache_ipv6_valid[2]; + static int cache_ipv6_slot; + + void subnet_cache_flush() { + cache_ipv4_valid[0] = cache_ipv4_valid[1] = false; + cache_ipv6_valid[0] = cache_ipv6_valid[1] = false; + } + /* Subnet comparison */ static int subnet_compare_mac(const subnet_t *a, const subnet_t *b) { int result; - result = memcmp(&a->net.mac.address, &b->net.mac.address, sizeof(mac_t)); + result = memcmp(&a->net.mac.address, &b->net.mac.address, sizeof a->net.mac.address); + if(result) + return result; + + result = a->weight - b->weight; + if(result || !a->owner || !b->owner) return result; @@@ -55,12 -77,17 +77,17 @@@ static int subnet_compare_ipv4(const su { int result; - result = memcmp(&a->net.ipv4.address, &b->net.ipv4.address, sizeof a->net.ipv4.address); + result = b->net.ipv4.prefixlength - a->net.ipv4.prefixlength; if(result) return result; - result = a->net.ipv4.prefixlength - b->net.ipv4.prefixlength; + result = memcmp(&a->net.ipv4.address, &b->net.ipv4.address, sizeof(ipv4_t)); + + if(result) + return result; + + result = a->weight - b->weight; if(result || !a->owner || !b->owner) return result; @@@ -72,12 -99,17 +99,17 @@@ static int subnet_compare_ipv6(const su { int result; - result = memcmp(&a->net.ipv6.address, &b->net.ipv6.address, sizeof a->net.ipv6.address); + result = b->net.ipv6.prefixlength - a->net.ipv6.prefixlength; if(result) return result; + + result = memcmp(&a->net.ipv6.address, &b->net.ipv6.address, sizeof(ipv6_t)); - result = a->net.ipv6.prefixlength - b->net.ipv6.prefixlength; + if(result) + return result; + + result = a->weight - b->weight; if(result || !a->owner || !b->owner) return result; @@@ -117,28 -149,30 +149,30 @@@ void init_subnets(void { cp(); - subnet_tree = avl_alloc_tree((avl_compare_t) subnet_compare, (avl_action_t) free_subnet); + subnet_tree = splay_alloc_tree((splay_compare_t) subnet_compare, (splay_action_t) free_subnet); + + subnet_cache_flush(); } void exit_subnets(void) { cp(); - avl_delete_tree(subnet_tree); + splay_delete_tree(subnet_tree); } -avl_tree_t *new_subnet_tree(void) +splay_tree_t *new_subnet_tree(void) { cp(); - return avl_alloc_tree((avl_compare_t) subnet_compare, NULL); + return splay_alloc_tree((splay_compare_t) subnet_compare, NULL); } -void free_subnet_tree(avl_tree_t *subnet_tree) +void free_subnet_tree(splay_tree_t *subnet_tree) { cp(); - avl_delete_tree(subnet_tree); + splay_delete_tree(subnet_tree); } /* Allocating and freeing space for subnets */ @@@ -165,16 -199,20 +199,20 @@@ void subnet_add(node_t *n, subnet_t *su subnet->owner = n; - avl_insert(subnet_tree, subnet); - avl_insert(n->subnet_tree, subnet); + splay_insert(subnet_tree, subnet); + splay_insert(n->subnet_tree, subnet); + + subnet_cache_flush(); } void subnet_del(node_t *n, subnet_t *subnet) { cp(); - avl_delete(n->subnet_tree, subnet); - avl_delete(subnet_tree, subnet); + splay_delete(n->subnet_tree, subnet); + splay_delete(subnet_tree, subnet); + + subnet_cache_flush(); } /* Ascii representation of subnets */ @@@ -183,16 -221,18 +221,18 @@@ bool str2net(subnet_t *subnet, const ch { int i, l; uint16_t x[8]; + int weight = 10; cp(); - if(sscanf(subnetstr, "%hu.%hu.%hu.%hu/%d", - &x[0], &x[1], &x[2], &x[3], &l) == 5) { + if(sscanf(subnetstr, "%hu.%hu.%hu.%hu/%d#%d", + &x[0], &x[1], &x[2], &x[3], &l, &weight) >= 5) { if(l < 0 || l > 32) return false; subnet->type = SUBNET_IPV4; subnet->net.ipv4.prefixlength = l; + subnet->weight = weight; for(i = 0; i < 4; i++) { if(x[i] > 255) @@@ -203,14 -243,15 +243,15 @@@ return true; } - if(sscanf(subnetstr, "%hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx/%d", + if(sscanf(subnetstr, "%hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx/%d#%d", &x[0], &x[1], &x[2], &x[3], &x[4], &x[5], &x[6], &x[7], - &l) == 9) { + &l, &weight) >= 9) { if(l < 0 || l > 128) return false; subnet->type = SUBNET_IPV6; subnet->net.ipv6.prefixlength = l; + subnet->weight = weight; for(i = 0; i < 8; i++) subnet->net.ipv6.address.x[i] = htons(x[i]); @@@ -218,9 -259,10 +259,10 @@@ return true; } - if(sscanf(subnetstr, "%hu.%hu.%hu.%hu", &x[0], &x[1], &x[2], &x[3]) == 4) { + if(sscanf(subnetstr, "%hu.%hu.%hu.%hu#%d", &x[0], &x[1], &x[2], &x[3], &weight) >= 4) { subnet->type = SUBNET_IPV4; subnet->net.ipv4.prefixlength = 32; + subnet->weight = weight; for(i = 0; i < 4; i++) { if(x[i] > 255) @@@ -231,10 -273,11 +273,11 @@@ return true; } - if(sscanf(subnetstr, "%hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx", - &x[0], &x[1], &x[2], &x[3], &x[4], &x[5], &x[6], &x[7]) == 8) { + if(sscanf(subnetstr, "%hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx#%d", + &x[0], &x[1], &x[2], &x[3], &x[4], &x[5], &x[6], &x[7], &weight) >= 8) { subnet->type = SUBNET_IPV6; subnet->net.ipv6.prefixlength = 128; + subnet->weight = weight; for(i = 0; i < 8; i++) subnet->net.ipv6.address.x[i] = htons(x[i]); @@@ -242,9 -285,10 +285,10 @@@ return true; } - if(sscanf(subnetstr, "%hx:%hx:%hx:%hx:%hx:%hx", - &x[0], &x[1], &x[2], &x[3], &x[4], &x[5]) == 6) { + if(sscanf(subnetstr, "%hx:%hx:%hx:%hx:%hx:%hx#%d", + &x[0], &x[1], &x[2], &x[3], &x[4], &x[5], &weight) >= 6) { subnet->type = SUBNET_MAC; + subnet->weight = weight; for(i = 0; i < 6; i++) subnet->net.mac.address.x[i] = x[i]; @@@ -266,24 -310,28 +310,28 @@@ bool net2str(char *netstr, int len, con switch (subnet->type) { case SUBNET_MAC: - snprintf(netstr, len, "%hx:%hx:%hx:%hx:%hx:%hx", + snprintf(netstr, len, "%hx:%hx:%hx:%hx:%hx:%hx#%d", subnet->net.mac.address.x[0], subnet->net.mac.address.x[1], subnet->net.mac.address.x[2], subnet->net.mac.address.x[3], - subnet->net.mac.address.x[4], subnet->net.mac.address.x[5]); + subnet->net.mac.address.x[4], + subnet->net.mac.address.x[5], + subnet->weight); break; case SUBNET_IPV4: - snprintf(netstr, len, "%hu.%hu.%hu.%hu/%d", + snprintf(netstr, len, "%hu.%hu.%hu.%hu/%d#%d", subnet->net.ipv4.address.x[0], subnet->net.ipv4.address.x[1], subnet->net.ipv4.address.x[2], - subnet->net.ipv4.address.x[3], subnet->net.ipv4.prefixlength); + subnet->net.ipv4.address.x[3], + subnet->net.ipv4.prefixlength, + subnet->weight); break; case SUBNET_IPV6: - snprintf(netstr, len, "%hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx/%d", + snprintf(netstr, len, "%hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx/%d#%d", ntohs(subnet->net.ipv6.address.x[0]), ntohs(subnet->net.ipv6.address.x[1]), ntohs(subnet->net.ipv6.address.x[2]), @@@ -292,7 -340,8 +340,8 @@@ ntohs(subnet->net.ipv6.address.x[5]), ntohs(subnet->net.ipv6.address.x[6]), ntohs(subnet->net.ipv6.address.x[7]), - subnet->net.ipv6.prefixlength); + subnet->net.ipv6.prefixlength, + subnet->weight); break; default: @@@ -312,7 -361,7 +361,7 @@@ subnet_t *lookup_subnet(const node_t *o { cp(); - return avl_search(owner->subnet_tree, subnet); + return splay_search(owner->subnet_tree, subnet); } subnet_t *lookup_subnet_mac(const mac_t *address) @@@ -325,91 -374,107 +374,107 @@@ subnet.net.mac.address = *address; subnet.owner = NULL; - p = avl_search(subnet_tree, &subnet); + p = splay_search(subnet_tree, &subnet); return p; } subnet_t *lookup_subnet_ipv4(const ipv4_t *address) { - subnet_t *p, subnet = {0}; + subnet_t *p, *r = NULL, subnet = {0}; - avl_node_t *n; ++ splay_node_t *n; + int i; cp(); + // Check if this address is cached + + for(i = 0; i < 2; i++) { + if(!cache_ipv4_valid[i]) + continue; + if(!memcmp(address, &cache_ipv4_address[i], sizeof *address)) + return cache_ipv4_subnet[i]; + } + + // Search all subnets for a matching one + subnet.type = SUBNET_IPV4; subnet.net.ipv4.address = *address; subnet.net.ipv4.prefixlength = 32; subnet.owner = NULL; - do { - /* Go find subnet */ + for(n = subnet_tree->head; n; n = n->next) { + p = n->data; + + if(!p || p->type != subnet.type) + continue; - p = splay_search_closest_smaller(subnet_tree, &subnet); - - /* Check if the found subnet REALLY matches */ - - if(p) { - if(p->type != SUBNET_IPV4) { - p = NULL; - break; - } - - if(!maskcmp(address, &p->net.ipv4.address, p->net.ipv4.prefixlength)) + if(!maskcmp(address, &p->net.ipv4.address, p->net.ipv4.prefixlength)) { + r = p; + if(p->owner->status.reachable) break; - else { - /* Otherwise, see if there is a bigger enclosing subnet */ - - subnet.net.ipv4.prefixlength = p->net.ipv4.prefixlength - 1; - if(subnet.net.ipv4.prefixlength < 0 || subnet.net.ipv4.prefixlength > 32) - return NULL; - maskcpy(&subnet.net.ipv4.address, &p->net.ipv4.address, subnet.net.ipv4.prefixlength, sizeof subnet.net.ipv4.address); - } } - } while(p); + } - return p; + // Cache the result + + cache_ipv4_slot = !cache_ipv4_slot; + memcpy(&cache_ipv4_address[cache_ipv4_slot], address, sizeof *address); + cache_ipv4_subnet[cache_ipv4_slot] = r; + cache_ipv4_valid[cache_ipv4_slot] = true; + + return r; } subnet_t *lookup_subnet_ipv6(const ipv6_t *address) { - subnet_t *p, subnet = {0}; + subnet_t *p, *r = NULL, subnet = {0}; - avl_node_t *n; ++ splay_node_t *n; + int i; cp(); + // Check if this address is cached + + for(i = 0; i < 2; i++) { + if(!cache_ipv6_valid[i]) + continue; + if(!memcmp(address, &cache_ipv6_address[i], sizeof *address)) + return cache_ipv6_subnet[i]; + } + + // Search all subnets for a matching one + subnet.type = SUBNET_IPV6; subnet.net.ipv6.address = *address; subnet.net.ipv6.prefixlength = 128; subnet.owner = NULL; - do { - /* Go find subnet */ - - p = splay_search_closest_smaller(subnet_tree, &subnet); - - /* Check if the found subnet REALLY matches */ + for(n = subnet_tree->head; n; n = n->next) { + p = n->data; + + if(!p || p->type != subnet.type) + continue; - if(p) { - if(p->type != SUBNET_IPV6) - return NULL; - - if(!maskcmp(address, &p->net.ipv6.address, p->net.ipv6.prefixlength)) + if(!maskcmp(address, &p->net.ipv6.address, p->net.ipv6.prefixlength)) { + r = p; + if(p->owner->status.reachable) break; - else { - /* Otherwise, see if there is a bigger enclosing subnet */ - - subnet.net.ipv6.prefixlength = p->net.ipv6.prefixlength - 1; - if(subnet.net.ipv6.prefixlength < 0 || subnet.net.ipv6.prefixlength > 128) - return NULL; - maskcpy(&subnet.net.ipv6.address, &p->net.ipv6.address, subnet.net.ipv6.prefixlength, sizeof subnet.net.ipv6.address); - } } - } while(p); + } - return p; + // Cache the result + + cache_ipv6_slot = !cache_ipv6_slot; + memcpy(&cache_ipv6_address[cache_ipv6_slot], address, sizeof *address); + cache_ipv6_subnet[cache_ipv6_slot] = r; + cache_ipv6_valid[cache_ipv6_slot] = true; + + return r; } void subnet_update(node_t *owner, subnet_t *subnet, bool up) { - avl_node_t *node; + splay_node_t *node; int i; char *envp[8]; char netstr[MAXNETSTR + 7] = "SUBNET="; @@@ -454,22 -519,22 +519,22 @@@ } } -void dump_subnets(void) +int dump_subnets(struct evbuffer *out) { char netstr[MAXNETSTR]; subnet_t *subnet; - avl_node_t *node; + splay_node_t *node; cp(); - logger(LOG_DEBUG, _("Subnet list:")); - for(node = subnet_tree->head; node; node = node->next) { subnet = node->data; if(!net2str(netstr, sizeof netstr, subnet)) continue; - logger(LOG_DEBUG, _(" %s owner %s"), netstr, subnet->owner->name); + if(evbuffer_add_printf(out, _(" %s owner %s\n"), + netstr, subnet->owner->name) == -1) + return errno; } - logger(LOG_DEBUG, _("End of subnet list.")); + return 0; } diff --combined src/subnet.h index 48eec520,3efed1be..22af0d3a --- a/src/subnet.h +++ b/src/subnet.h @@@ -1,6 -1,6 +1,6 @@@ /* subnet.h -- header for subnet.c - Copyright (C) 2000-2006 Guus Sliepen , + Copyright (C) 2000-2009 Guus Sliepen , 2000-2005 Ivo Timmermans This program is free software; you can redistribute it and/or modify @@@ -53,6 -53,7 +53,7 @@@ typedef struct subnet_t subnet_type_t type; /* subnet type (IPv4? IPv6? MAC? something even weirder?) */ time_t expires; /* expiry time */ + int weight; /* weight (higher value is higher priority) */ /* And now for the actual subnet: */ @@@ -70,8 -71,8 +71,8 @@@ extern subnet_t *new_subnet(void) __att extern void free_subnet(subnet_t *); extern void init_subnets(void); extern void exit_subnets(void); -extern avl_tree_t *new_subnet_tree(void) __attribute__ ((__malloc__)); -extern void free_subnet_tree(avl_tree_t *); +extern splay_tree_t *new_subnet_tree(void) __attribute__ ((__malloc__)); +extern void free_subnet_tree(splay_tree_t *); extern void subnet_add(struct node_t *, subnet_t *); extern void subnet_del(struct node_t *, subnet_t *); extern void subnet_update(struct node_t *, subnet_t *, bool); @@@ -81,6 -82,7 +82,7 @@@ extern subnet_t *lookup_subnet(const st extern subnet_t *lookup_subnet_mac(const mac_t *); extern subnet_t *lookup_subnet_ipv4(const ipv4_t *); extern subnet_t *lookup_subnet_ipv6(const ipv6_t *); -extern void dump_subnets(void); +extern int dump_subnets(struct evbuffer *); + extern void subnet_cache_flush(void); #endif /* __TINC_SUBNET_H__ */ diff --combined src/tincd.c index 3e3b7b0d,f909d9a0..ac0cbc6c --- a/src/tincd.c +++ b/src/tincd.c @@@ -1,7 -1,7 +1,7 @@@ /* tincd.c -- the main file for tincd Copyright (C) 1998-2005 Ivo Timmermans - 2000-2007 Guus Sliepen + 2000-2009 Guus Sliepen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@@ -31,13 -31,18 +31,13 @@@ #include #endif -#include -#include -#include -#include -#include - #include LZO1X_H #include -#include "pidfile.h" #include "conf.h" +#include "control.h" +#include "crypto.h" #include "device.h" #include "logger.h" #include "net.h" @@@ -56,6 -61,12 +56,6 @@@ bool show_help = false /* If nonzero, print the version on standard output and exit. */ bool show_version = false; -/* If nonzero, it will attempt to kill a running tincd and exit. */ -int kill_tincd = 0; - -/* If nonzero, generate public/private keypair for this host/net. */ -int generate_keys = 0; - /* If nonzero, use null ciphers and skip all key exchanges. */ bool bypass_security = false; @@@ -66,7 -77,7 +66,7 @@@ bool do_mlock = false bool use_logfile = false; char *identname = NULL; /* program name for syslog */ -char *pidfilename = NULL; /* pid file location */ +char *controlsocketname = NULL; /* control socket location */ char *logfilename = NULL; /* log file location */ char **g_argv; /* a copy of the cmdline arguments */ @@@ -74,15 -85,17 +74,15 @@@ static int status static struct option const long_options[] = { {"config", required_argument, NULL, 'c'}, - {"kill", optional_argument, NULL, 'k'}, {"net", required_argument, NULL, 'n'}, {"help", no_argument, NULL, 1}, {"version", no_argument, NULL, 2}, {"no-detach", no_argument, NULL, 'D'}, - {"generate-keys", optional_argument, NULL, 'K'}, {"debug", optional_argument, NULL, 'd'}, {"bypass-security", no_argument, NULL, 3}, {"mlock", no_argument, NULL, 'L'}, {"logfile", optional_argument, NULL, 4}, - {"pidfile", required_argument, NULL, 5}, + {"controlsocket", required_argument, NULL, 5}, {NULL, 0, NULL, 0} }; @@@ -97,16 -110,17 +97,16 @@@ static void usage(bool status program_name); else { printf(_("Usage: %s [option]...\n\n"), program_name); - printf(_(" -c, --config=DIR Read configuration options from DIR.\n" - " -D, --no-detach Don't fork and detach.\n" - " -d, --debug[=LEVEL] Increase debug level or set it to LEVEL.\n" - " -k, --kill[=SIGNAL] Attempt to kill a running tincd and exit.\n" - " -n, --net=NETNAME Connect to net NETNAME.\n" - " -K, --generate-keys[=BITS] Generate public/private RSA keypair.\n" - " -L, --mlock Lock tinc into main memory.\n" - " --logfile[=FILENAME] Write log entries to a logfile.\n" - " --pidfile=FILENAME Write PID to FILENAME.\n" - " --help Display this help and exit.\n" - " --version Output version information and exit.\n\n")); + printf(_( " -c, --config=DIR Read configuration options from DIR.\n" + " -D, --no-detach Don't fork and detach.\n" + " -d, --debug[=LEVEL] Increase debug level or set it to LEVEL.\n" + " -n, --net=NETNAME Connect to net NETNAME.\n" + " -L, --mlock Lock tinc into main memory.\n" + " --logfile[=FILENAME] Write log entries to a logfile.\n" + " --controlsocket=FILENAME Open control socket at FILENAME.\n" + " --bypass-security Disables meta protocol security, for debugging.\n" + " --help Display this help and exit.\n" + " --version Output version information and exit.\n\n")); printf(_("Report bugs to tinc@tinc-vpn.org.\n")); } } @@@ -116,7 -130,7 +116,7 @@@ static bool parse_options(int argc, cha int r; int option_index = 0; - while((r = getopt_long(argc, argv, "c:DLd::k::n:K::", long_options, &option_index)) != EOF) { + while((r = getopt_long(argc, argv, "c:DLd::n:", long_options, &option_index)) != EOF) { switch (r) { case 0: /* long option */ break; @@@ -140,10 -154,62 +140,10 @@@ debug_level++; break; - case 'k': /* kill old tincds */ -#ifndef HAVE_MINGW - if(optarg) { - if(!strcasecmp(optarg, "HUP")) - kill_tincd = SIGHUP; - else if(!strcasecmp(optarg, "TERM")) - kill_tincd = SIGTERM; - else if(!strcasecmp(optarg, "KILL")) - kill_tincd = SIGKILL; - else if(!strcasecmp(optarg, "USR1")) - kill_tincd = SIGUSR1; - else if(!strcasecmp(optarg, "USR2")) - kill_tincd = SIGUSR2; - else if(!strcasecmp(optarg, "WINCH")) - kill_tincd = SIGWINCH; - else if(!strcasecmp(optarg, "INT")) - kill_tincd = SIGINT; - else if(!strcasecmp(optarg, "ALRM")) - kill_tincd = SIGALRM; - else { - kill_tincd = atoi(optarg); - - if(!kill_tincd) { - fprintf(stderr, _("Invalid argument `%s'; SIGNAL must be a number or one of HUP, TERM, KILL, USR1, USR2, WINCH, INT or ALRM.\n"), - optarg); - usage(true); - return false; - } - } - } else - kill_tincd = SIGTERM; -#else - kill_tincd = 1; -#endif - break; - case 'n': /* net name given */ netname = xstrdup(optarg); break; - case 'K': /* generate public/private keypair */ - if(optarg) { - generate_keys = atoi(optarg); - - if(generate_keys < 512) { - fprintf(stderr, _("Invalid argument `%s'; BITS must be a number equal to or greater than 512.\n"), - optarg); - usage(true); - return false; - } - - generate_keys &= ~7; /* Round it to bytes */ - } else - generate_keys = 1024; - break; - case 1: /* show help */ show_help = true; break; @@@ -162,8 -228,8 +162,8 @@@ logfilename = xstrdup(optarg); break; - case 5: /* write PID to a file */ - pidfilename = xstrdup(optarg); + case 5: /* open control socket here */ + controlsocketname = xstrdup(optarg); break; case '?': @@@ -178,6 -244,110 +178,6 @@@ return true; } -/* This function prettyprints the key generation process */ - -static void indicator(int a, int b, void *p) -{ - switch (a) { - case 0: - fprintf(stderr, "."); - break; - - case 1: - fprintf(stderr, "+"); - break; - - case 2: - fprintf(stderr, "-"); - break; - - case 3: - switch (b) { - case 0: - fprintf(stderr, " p\n"); - break; - - case 1: - fprintf(stderr, " q\n"); - break; - - default: - fprintf(stderr, "?"); - } - break; - - default: - fprintf(stderr, "?"); - } -} - -/* - Generate a public/private RSA keypair, and ask for a file to store - them in. -*/ -static bool keygen(int bits) -{ - RSA *rsa_key; - FILE *f; - char *name = NULL; - char *filename; - - get_config_string(lookup_config(config_tree, "Name"), &name); - - if(name && !check_id(name)) { - fprintf(stderr, _("Invalid name for myself!\n")); - return false; - } - - fprintf(stderr, _("Generating %d bits keys:\n"), bits); - rsa_key = RSA_generate_key(bits, 0x10001, indicator, NULL); - - if(!rsa_key) { - fprintf(stderr, _("Error during key generation!\n")); - return false; - } else - fprintf(stderr, _("Done.\n")); - - asprintf(&filename, "%s/rsa_key.priv", confbase); - f = ask_and_open(filename, _("private RSA key")); - - if(!f) - return false; - - if(disable_old_keys(f)) - fprintf(stderr, _("Warning: old key(s) found and disabled.\n")); - -#ifdef HAVE_FCHMOD - /* Make it unreadable for others. */ - fchmod(fileno(f), 0600); -#endif - - PEM_write_RSAPrivateKey(f, rsa_key, NULL, NULL, 0, NULL, NULL); - fclose(f); - free(filename); - - if(name) - asprintf(&filename, "%s/hosts/%s", confbase, name); - else - asprintf(&filename, "%s/rsa_key.pub", confbase); - - f = ask_and_open(filename, _("public RSA key")); - - if(!f) - return false; - - if(disable_old_keys(f)) - fprintf(stderr, _("Warning: old key(s) found and disabled.\n")); - - PEM_write_RSAPublicKey(f, rsa_key); - fclose(f); - free(filename); - if(name) - free(name); - - return true; -} - /* Set all files and paths according to netname */ @@@ -186,7 -356,7 +186,7 @@@ static void make_names(void #ifdef HAVE_MINGW HKEY key; char installdir[1024] = ""; - long len = sizeof(installdir); + long len = sizeof installdir; #endif if(netname) @@@ -212,8 -382,8 +212,8 @@@ } #endif - if(!pidfilename) - asprintf(&pidfilename, LOCALSTATEDIR "/run/%s.pid", identname); + if(!controlsocketname) + asprintf(&controlsocketname, "%s/run/%s.control/socket", LOCALSTATEDIR, identname); if(!logfilename) asprintf(&logfilename, LOCALSTATEDIR "/log/%s.log", identname); @@@ -229,6 -399,14 +229,14 @@@ } } + static void free_names() { + if (identname) free(identname); + if (netname) free(netname); - if (pidfilename) free(pidfilename); ++ if (controlsocketname) free(controlsocketname); + if (logfilename) free(logfilename); + if (confbase) free(confbase); + } + int main(int argc, char **argv) { program_name = argv[0]; @@@ -245,7 -423,7 +253,7 @@@ if(show_version) { printf(_("%s version %s (built %s %s, protocol %d)\n"), PACKAGE, VERSION, __DATE__, __TIME__, PROT_CURRENT); - printf(_("Copyright (C) 1998-2007 Ivo Timmermans, Guus Sliepen and others.\n" + printf(_("Copyright (C) 1998-2009 Ivo Timmermans, Guus Sliepen and others.\n" "See the AUTHORS file for a complete list.\n\n" "tinc comes with ABSOLUTELY NO WARRANTY. This is free software,\n" "and you are welcome to redistribute it under certain conditions;\n" @@@ -259,16 -437,11 +267,16 @@@ return 0; } - if(kill_tincd) - return !kill_other(kill_tincd); - openlogger("tinc", use_logfile?LOGMODE_FILE:LOGMODE_STDERR); + if(!event_init()) { + logger(LOG_ERR, _("Error initializing libevent!")); + return 1; + } + + if(!init_control()) + return 1; + /* Lock all pages into memory if requested */ if(do_mlock) @@@ -289,8 -462,17 +297,8 @@@ /* Slllluuuuuuurrrrp! */ - RAND_load_file("/dev/urandom", 1024); - - ENGINE_load_builtin_engines(); - ENGINE_register_all_complete(); - - OpenSSL_add_all_algorithms(); - - if(generate_keys) { - read_server_config(); - return !keygen(generate_keys); - } + srand(time(NULL)); + crypto_init(); if(!read_server_config()) return 1; @@@ -331,19 -513,26 +339,19 @@@ int main2(int argc, char **argv /* Shutdown properly. */ - close_network_connections(); - ifdebug(CONNECTIONS) dump_device_stats(); + close_network_connections(); + end: logger(LOG_NOTICE, _("Terminating")); #ifndef HAVE_MINGW - remove_pid(pidfilename); + exit_control(); #endif - EVP_cleanup(); - ENGINE_cleanup(); - CRYPTO_cleanup_all_ex_data(); - ERR_remove_state(0); - ERR_free_strings(); + crypto_exit(); - exit_configuration(&config_tree); - free_names(); - return status; } diff --combined src/uml_socket/device.c index 27fdd337,62a6f2eb..30194d9c --- a/src/uml_socket/device.c +++ b/src/uml_socket/device.c @@@ -1,7 -1,7 +1,7 @@@ /* device.c -- UML network socket Copyright (C) 2002-2005 Ivo Timmermans, - 2002-2006 Guus Sliepen + 2002-2009 Guus Sliepen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@@ -36,9 -36,9 +36,9 @@@ static int request_fd = -1 static int data_fd = -1; static int write_fd = -1; static int state = 0; - char *device; + char *device = NULL; char *iface = NULL; - char *device_info; + static char *device_info; extern char *identname; extern bool running; @@@ -57,7 -57,8 +57,7 @@@ static struct request static struct sockaddr_un data_sun; -bool setup_device(void) -{ +bool setup_device(void) { struct sockaddr_un listen_sun; static const int one = 1; struct { @@@ -153,7 -154,8 +153,7 @@@ return true; } -void close_device(void) -{ +void close_device(void) { cp(); if(listen_fd >= 0) @@@ -169,10 -171,14 +169,13 @@@ close(write_fd); unlink(device); + + free(device); + if(iface) free(iface); } -bool read_packet(vpn_packet_t *packet) -{ - int lenin; +bool read_packet(vpn_packet_t *packet) { + int inlen; cp(); @@@ -202,7 -208,7 +205,7 @@@ } case 1: { - if((lenin = read(request_fd, &request, sizeof request)) != sizeof request) { + if((inlen = read(request_fd, &request, sizeof request)) != sizeof request) { logger(LOG_ERR, _("Error while reading request from %s %s: %s"), device_info, device, strerror(errno)); running = false; @@@ -232,14 -238,14 +235,14 @@@ } case 2: { - if((lenin = read(data_fd, packet->data, MTU)) <= 0) { + if((inlen = read(data_fd, packet->data, MTU)) <= 0) { logger(LOG_ERR, _("Error while reading from %s %s: %s"), device_info, device, strerror(errno)); running = false; return false; } - packet->len = lenin; + packet->len = inlen; device_total_in += packet->len; @@@ -251,7 -257,8 +254,7 @@@ } } -bool write_packet(vpn_packet_t *packet) -{ +bool write_packet(vpn_packet_t *packet) { cp(); if(state != 2) { @@@ -277,7 -284,8 +280,7 @@@ return true; } -void dump_device_stats(void) -{ +void dump_device_stats(void) { cp(); logger(LOG_DEBUG, _("Statistics for %s %s:"), device_info, device);