Merge branch 'master' of git://tinc-vpn.org/tinc into 1.1
authorGuus Sliepen <guus@tinc-vpn.org>
Mon, 6 Jun 2011 18:42:15 +0000 (20:42 +0200)
committerGuus Sliepen <guus@tinc-vpn.org>
Mon, 6 Jun 2011 18:42:15 +0000 (20:42 +0200)
Conflicts:
NEWS
configure.in
doc/tincd.8.in
lib/pidfile.c
lib/pidfile.h
lib/xalloc.h
lib/xmalloc.c
src/conf.c
src/conf.h
src/connection.c
src/connection.h
src/event.c
src/graph.c
src/graph.h
src/net.c
src/net.h
src/node.h
src/openssl/crypto.c
src/process.c
src/protocol.c
src/protocol_key.c
src/route.c

27 files changed:
1  2 
NEWS
configure.in
doc/tinc.texi
doc/tincd.8.in
src/bsd/device.c
src/conf.c
src/connection.c
src/connection.h
src/cygwin/device.c
src/graph.c
src/linux/device.c
src/logger.c
src/mingw/device.c
src/net.c
src/net.h
src/net_packet.c
src/node.h
src/process.c
src/protocol.c
src/protocol.h
src/protocol_misc.c
src/raw_socket/device.c
src/solaris/device.c
src/tincd.c
src/uml_socket/device.c
src/utils.c
src/utils.h

diff --combined NEWS
--- 1/NEWS
--- 2/NEWS
+++ b/NEWS
@@@ -1,9 -1,7 +1,11 @@@
 -Version 1.0.15               not released yet
 +Version 1.1-cvs              Work in progress
 +
 + * Use libevent to handle I/O events and timeouts.
 +
 + * Use splay trees instead of AVL trees.
  
+  * Fix ProcessPriority option under Windows.
  Version 1.0.14               May  8 2011
  
   * Fixed reading configuration files that do not end with a newline. Again.
diff --combined configure.in
@@@ -3,8 -3,7 +3,8 @@@ dnl Process this file with autoconf to 
  AC_PREREQ(2.61)
  AC_INIT
  AC_CONFIG_SRCDIR([src/tincd.c])
 -AM_INIT_AUTOMAKE(tinc, 1.0.14+git)
 +AC_GNU_SOURCE
 +AM_INIT_AUTOMAKE(tinc, 1.1-cvs)
  AC_CONFIG_HEADERS([config.h])
  AM_MAINTAINER_MODE
  
@@@ -96,12 -95,14 +96,12 @@@ if test -d /sw/lib ; the
    LIBS="$LIBS -L/sw/lib"
  fi
  
 -dnl Checks for libraries.
 -
  dnl Checks for header files.
  dnl We do this in multiple stages, because unlike Linux all the other operating systems really suck and don't include their own dependencies.
  
  AC_HEADER_STDC
 -AC_CHECK_HEADERS([stdbool.h syslog.h sys/file.h sys/ioctl.h sys/mman.h sys/param.h sys/resource.h sys/socket.h sys/time.h time.h sys/uio.h sys/wait.h netdb.h arpa/inet.h dirent.h])
 -AC_CHECK_HEADERS([net/if.h net/if_types.h linux/if_tun.h net/if_tun.h net/tun/if_tun.h net/if_tap.h net/tap/if_tap.h net/ethernet.h net/if_arp.h netinet/in_systm.h netinet/in.h netinet/in6.h],
 +AC_CHECK_HEADERS([stdbool.h syslog.h sys/file.h sys/ioctl.h sys/mman.h sys/param.h sys/resource.h sys/socket.h sys/time.h sys/uio.h sys/un.h sys/wait.h netdb.h arpa/inet.h dirent.h])
 +AC_CHECK_HEADERS([net/if.h net/if_types.h linux/if_tun.h net/if_tun.h net/tun/if_tun.h net/if_tap.h net/tap/if_tap.h net/ethernet.h net/if_arp.h netinet/in_systm.h netinet/in.h netinet/in6.h time.h],
    [], [], [#include "have.h"]
  )
  AC_CHECK_HEADERS([netinet/if_ether.h netinet/ip.h netinet/ip6.h],
@@@ -126,10 -127,14 +126,10 @@@ AC_CHECK_TYPES([socklen_t, struct ether
  )
  
  dnl Checks for library functions.
 -AC_FUNC_MEMCMP
 -AC_FUNC_ALLOCA
  AC_TYPE_SIGNAL
- AC_CHECK_FUNCS([asprintf daemon fchmod flock ftime fork get_current_dir_name gettimeofday mlockall putenv random select strdup strerror strsignal strtol system time usleep unsetenv vsyslog writev],
 -AC_CHECK_FUNCS([asprintf daemon fchmod flock ftime fork get_current_dir_name gettimeofday mlockall pselect putenv random select strdup strerror strsignal strtol system unsetenv usleep vsyslog writev],
++AC_CHECK_FUNCS([asprintf daemon fchmod flock ftime fork get_current_dir_name gettimeofday mlockall pselect putenv random select strdup strerror strsignal strtol system time usleep unsetenv vsyslog writev],
    [], [], [#include "have.h"]
  )
 -AC_FUNC_MALLOC
 -AC_FUNC_REALLOC
  
  dnl Support for SunOS
  
@@@ -148,21 -153,9 +148,21 @@@ AC_CACHE_SAV
  
  dnl These are defined in files in m4/
  
 +AC_ARG_WITH(libgcrypt, AC_HELP_STRING([--with-libgcrypt], [enable use of libgcrypt instead of OpenSSL])], [])
 +
 +tinc_CURSES
 +tinc_LIBEVENT
  tinc_ZLIB
  tinc_LZO
 -tinc_OPENSSL
 +
 +if test "$with_libgcrypt" = yes; then
 +      AM_PATH_LIBGCRYPT([1.4.0], [], [])
 +      ln -sf gcrypt/cipher.c gcrypt/cipher.h gcrypt/crypto.c gcrypt/crypto.h gcrypt/digest.c gcrypt/digest.h gcrypt/rsa.c gcrypt/rsa.h gcrypt/rsagen.c gcrypt/rsagen.h src/
 +else
 +      tinc_OPENSSL
 +      ln -sf openssl/cipher.c openssl/cipher.h openssl/crypto.c openssl/crypto.h openssl/digest.c openssl/digest.h openssl/rsa.c openssl/rsa.h openssl/rsagen.c openssl/rsagen.h src/
 +fi
 +      
  
  dnl Check if support for jumbograms is requested 
  AC_ARG_ENABLE(jumbograms,
  
  AC_SUBST(INCLUDES)
  
 -AC_CONFIG_FILES([Makefile src/Makefile doc/Makefile lib/Makefile m4/Makefile])
 +AC_CONFIG_FILES([Makefile src/Makefile doc/Makefile m4/Makefile])
  
  AC_OUTPUT
diff --combined doc/tinc.texi
@@@ -37,7 -37,6 +37,7 @@@ permission notice identical to this one
  
  @page
  @vskip 0pt plus 1filll
 +@cindex copyright
  This is the info manual for @value{PACKAGE} version @value{VERSION}, a Virtual Private Network daemon.
  
  Copyright @copyright{} 1998-2011 Ivo Timmermans,
@@@ -55,7 -54,7 +55,7 @@@ permission notice identical to this one
  
  @end titlepage
  
 -@ifnottex
 +@ifinfo
  @c ==================================================================
  @node Top
  @top Top
  * Installation::
  * Configuration::
  * Running tinc::
 +* Controlling tinc::
  * Technical information::
  * Platform specific information::
  * About us::
  * Concept Index::               All used terms explained
  @end menu
 -@end ifnottex
 +@end ifinfo
  
  @c ==================================================================
  @node    Introduction
@@@ -339,7 -337,6 +339,7 @@@ having them installed, configure will g
  * OpenSSL::
  * zlib::
  * lzo::
 +* libevent::
  @end menu
  
  
@@@ -452,27 -449,6 +452,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
@@@ -974,7 -950,7 +974,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
@@@ -1096,7 -1072,7 +1096,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
@@@ -1132,6 -1108,7 +1132,6 @@@ 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}
  
 -@cindex Subnet weight
  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,
@@@ -1139,12 -1116,15 +1139,12 @@@ unless that node is not reachable, in w
  priority will be tried, and so on.
  
  @cindex TCPonly
 -@item TCPonly = <yes|no> (no) [deprecated]
 +@item TCPonly = <yes|no> (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
  firewall, or if UDP packet routing is disabled somehow.
  Setting this options also implicitly sets IndirectData.
 -
 -Since version 1.0.10, tinc will automatically detect whether communication via
 -UDP is possible or not.
  @end table
  
  
@@@ -1233,6 -1213,10 +1233,6 @@@ this is set to the port number it uses 
  @item SUBNET
  When a subnet becomes (un)reachable, this is set to the subnet.
  
 -@cindex WEIGHT
 -@item WEIGHT
 -When a subnet becomes (un)reachable, this is set to the subnet weight.
 -
  @end table
  
  
@@@ -1279,7 -1263,7 +1279,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.
@@@ -1508,7 -1492,7 +1508,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},
@@@ -1574,6 -1558,12 +1574,6 @@@ 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}.
  This will let tinc read all configuration files from
  Specifying . for @var{netname} is the same as not specifying any @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,
 -2048 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.
@@@ -1593,6 -1585,9 +1593,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.
@@@ -1632,11 -1627,33 +1632,13 @@@ 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.
+ If the --logfile option is used, this will also close and reopen the log file,
+ useful when log rotation is used.
  
 -@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 ==================================================================
@@@ -1700,7 -1717,7 +1702,7 @@@ Do you have a firewall or a NAT device 
  If so, check that it allows TCP and UDP traffic on port 655.
  If it masquerades and the host running tinc is behind it, make sure that it forwards TCP and UDP traffic to port 655 to the host running tinc.
  You can add @samp{TCPOnly = yes} to your host config file to force tinc to only use a single TCP connection,
 -this works through most firewalls and NATs. Since version 1.0.10, tinc will automatically fall back to TCP if direct communication via UDP is not possible.
 +this works through most firewalls and NATs.
  
  @end itemize
  
@@@ -1799,8 -1816,6 +1801,8 @@@ or if that is not the case, try changin
  
  @itemize
  @item If you see this only sporadically, it is harmless and caused by a node sending packets using an old key.
 +@item If you see this often and another node is not reachable anymore, then a NAT (masquerading firewall) is changing the source address of UDP packets.
 +You can add @samp{TCPOnly = yes} to host configuration files to force all VPN traffic to go over a TCP connection.
  @end itemize
  
  @item Got bad/bogus/unauthorized REQUEST from foo (1.2.3.4 port 12345)
@@@ -1831,110 -1846,6 +1833,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 doc/tincd.8.in
@@@ -8,13 -8,16 +8,13 @@@
  .Nd tinc VPN daemon
  .Sh SYNOPSIS
  .Nm
 -.Op Fl cdDkKnLRU
 +.Op Fl cdDKnLRU
  .Op Fl -config Ns = Ns Ar DIR
  .Op Fl -no-detach
  .Op Fl -debug Ns Op = Ns Ar LEVEL
 -.Op Fl -kill Ns Op = Ns Ar SIGNAL
  .Op Fl -net Ns = Ns Ar NETNAME
 -.Op Fl -generate-keys Ns Op = Ns Ar BITS
  .Op Fl -mlock
  .Op Fl -logfile Ns Op = Ns Ar FILE
 -.Op Fl -pidfile Ns = Ns Ar FILE
  .Op Fl -bypass-security
  .Op Fl -chroot
  .Op Fl -user Ns = Ns Ar USER
@@@ -50,6 -53,14 +50,6 @@@ If not mentioned otherwise, this will s
  Increase debug level or set it to
  .Ar LEVEL
  (see below).
 -.It Fl k, -kill Ns Op = Ns Ar SIGNAL
 -Attempt to kill a running
 -.Nm
 -(optionally with the specified
 -.Ar SIGNAL
 -instead of SIGTERM) and exit.
 -Under Windows (not Cygwin) the optional argument is ignored,
 -the service will always be stopped and removed.
  .It Fl n, -net Ns = Ns Ar NETNAME
  Connect to net
  .Ar NETNAME .
@@@ -61,6 -72,13 +61,6 @@@ fo
  .Ar NETNAME
  is the same as not specifying any
  .Ar NETNAME .
 -.It Fl K, -generate-keys Ns Op = Ns Ar BITS
 -Generate public/private RSA keypair and exit.
 -If
 -.Ar BITS
 -is omitted, the default length will be 2048 bits.
 -When saving keys to existing files, tinc will not delete the old keys,
 -you have to remove them manually.
  .It Fl L, -mlock
  Lock tinc into main memory.
  This will prevent sensitive data like shared private keys to be written to the system swap files/partitions.
@@@ -70,13 -88,12 +70,13 @@@ I
  .Ar FILE
  is omitted, the default is
  .Pa @localstatedir@/log/tinc. Ns Ar NETNAME Ns Pa .log.
 -.It Fl -pidfile Ns = Ns Ar FILE
 -Write PID to
 +.It Fl -controlsocket Ns = Ns Ar FILENAME
 +Open control socket at
 +.Ar FILENAME .
 +If
  .Ar FILE
 -instead of
 -.Pa @localstatedir@/run/tinc. Ns Ar NETNAME Ns Pa .pid.
 -Under Windows this option will be ignored.
 +is omitted, the default is
 +.Pa @localstatedir@/run/tinc. Ns Ar NETNAME Ns Pa .control.
  .It Fl -bypass-security
  Disables encryption and authentication of the meta protocol.
  Only useful for debugging.
@@@ -95,12 -112,37 +95,25 @@@ Output version information and exit
  .El
  .Sh SIGNALS
  .Bl -tag -width indent
 -.It ALRM
 -Forces
 -.Nm
 -to try to connect to all uplinks immediately.
 -Usually
 -.Nm
 -attempts to do this itself,
 -but increases the time it waits between the attempts each time it failed,
 -and if
 -.Nm
 -didn't succeed to connect to an uplink the first time after it started,
 -it defaults to the maximum time of 15 minutes.
  .It HUP
  Partially rereads configuration files.
  Connections to hosts whose host config file are removed are closed.
  New outgoing connections specified in
  .Pa tinc.conf
  will be made.
+ If the
+ .Fl -logfile
+ option is used, this will also close and reopen the log file,
+ useful when log rotation is used.
+ .It INT
+ Temporarily increases debug level to 5.
+ Send this signal again to revert to the original level.
+ .It USR1
+ Dumps the connection list to syslog.
+ .It USR2
+ Dumps virtual network device statistics, all known nodes, edges and subnets to syslog.
+ .It WINCH
+ Purges all information remembered about unreachable nodes.
  .El
  .Sh DEBUG LEVELS
  The tinc daemon can send a lot of messages to the syslog.
@@@ -147,7 -189,6 +160,7 @@@ If you find any bugs, report them to ti
  .Sh TODO
  A lot, especially security auditing.
  .Sh SEE ALSO
 +.Xr tincctl 8 ,
  .Xr tinc.conf 5 ,
  .Pa http://www.tinc-vpn.org/ ,
  .Pa http://www.cabal.org/ .
diff --combined src/bsd/device.c
@@@ -22,6 -22,7 +22,7 @@@
  #include "system.h"
  
  #include "conf.h"
+ #include "device.h"
  #include "logger.h"
  #include "net.h"
  #include "route.h"
@@@ -190,19 -191,19 +191,19 @@@ void close_device(void) 
  }
  
  bool read_packet(vpn_packet_t *packet) {
 -      int lenin;
 +      int inlen;
  
        switch(device_type) {
                case DEVICE_TYPE_TUN:
  #ifdef HAVE_TUNEMU
                case DEVICE_TYPE_TUNEMU:
                        if(device_type == DEVICE_TYPE_TUNEMU)
 -                              lenin = tunemu_read(device_fd, packet->data + 14, MTU - 14);
 +                              inlen = tunemu_read(device_fd, packet->data + 14, MTU - 14);
                        else
  #endif
 -                              lenin = read(device_fd, packet->data + 14, MTU - 14);
 +                              inlen = read(device_fd, packet->data + 14, MTU - 14);
  
 -                      if(lenin <= 0) {
 +                      if(inlen <= 0) {
                                logger(LOG_ERR, "Error while reading from %s %s: %s", device_info,
                                           device, strerror(errno));
                                return false;
                                        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;
                                        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:
@@@ -296,7 -297,7 +297,7 @@@ bool write_packet(vpn_packet_t *packet
  
                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];
diff --combined src/conf.c
  
  #include "system.h"
  
 -#include "avl_tree.h"
 +#include "splay_tree.h"
  #include "connection.h"
  #include "conf.h"
+ #include "list.h"
  #include "logger.h"
  #include "netutl.h"                           /* for str2address */
  #include "protocol.h"
  #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 */
@@@ -62,12 -63,12 +63,12 @@@ static int config_compare(const config_
                return a->file ? strcmp(a->file, b->file) : 0;
  }
  
 -void init_configuration(avl_tree_t ** config_tree) {
 -      *config_tree = avl_alloc_tree((avl_compare_t) config_compare, (avl_action_t) free_config);
 +void init_configuration(splay_tree_t ** config_tree) {
 +      *config_tree = splay_alloc_tree((splay_compare_t) config_compare, (splay_action_t) free_config);
  }
  
 -void exit_configuration(avl_tree_t ** config_tree) {
 -      avl_delete_tree(*config_tree);
 +void exit_configuration(splay_tree_t ** config_tree) {
 +      splay_delete_tree(*config_tree);
        *config_tree = NULL;
  }
  
@@@ -88,18 -89,18 +89,18 @@@ void free_config(config_t *cfg) 
        free(cfg);
  }
  
 -void config_add(avl_tree_t *config_tree, config_t *cfg) {
 -      avl_insert(config_tree, cfg);
 +void config_add(splay_tree_t *config_tree, config_t *cfg) {
 +      splay_insert(config_tree, cfg);
  }
  
 -config_t *lookup_config(const avl_tree_t *config_tree, char *variable) {
 +config_t *lookup_config(splay_tree_t *config_tree, char *variable) {
        config_t cfg, *found;
  
        cfg.variable = variable;
        cfg.file = NULL;
        cfg.line = 0;
  
 -      found = avl_search_closest_greater(config_tree, &cfg);
 +      found = splay_search_closest_greater(config_tree, &cfg);
  
        if(!found)
                return NULL;
        return found;
  }
  
 -config_t *lookup_config_next(const 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;
  
 -      node = avl_search_node(config_tree, cfg);
 +      node = splay_search_node(config_tree, cfg);
  
        if(node) {
                if(node->next) {
@@@ -202,9 -203,9 +203,9 @@@ bool get_config_subnet(const config_t *
        /* 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;
@@@ -285,7 -286,7 +286,7 @@@ config_t *parse_config_line(char *line
    Parse a configuration file and put the results in the configuration tree
    starting at *base.
  */
 -bool read_config_file(avl_tree_t *config_tree, const char *fname) {
 +bool read_config_file(splay_tree_t *config_tree, const char *fname) {
        FILE *fp;
        char buffer[MAX_STRING_SIZE];
        char *line;
        return result;
  }
  
 -void read_config_options(avl_tree_t *config_tree, const char *prefix) {
 +void read_config_options(splay_tree_t *config_tree, const char *prefix) {
        list_node_t *node, *next;
        size_t prefix_len = prefix ? strlen(prefix) : 0;
  
@@@ -399,6 -400,66 +400,6 @@@ bool read_connection_config(connection_
        return x;
  }
  
 -FILE *ask_and_open(const char *filename, const char *what) {
 -      FILE *r;
 -      char *directory;
 -      char line[PATH_MAX];
 -      const 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 = 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, line, sizeof line);
 -
 -              if(!fn) {
 -                      fprintf(stderr, "Error while reading stdin: %s\n",
 -                                      strerror(errno));
 -                      return NULL;
 -              }
 -
 -              if(!strlen(fn))
 -                      /* User just pressed enter. */
 -                      fn = 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();
 -              xasprintf(&p, "%s/%s", directory, 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));
 -              return NULL;
 -      }
 -
 -      return r;
 -}
 -
  bool disable_old_keys(FILE *f) {
        char buf[100];
        long pos;
diff --combined src/connection.c
  
  #include "system.h"
  
 -#include "avl_tree.h"
 +#include "splay_tree.h"
 +#include "cipher.h"
  #include "conf.h"
 +#include "control_common.h"
 +#include "list.h"
  #include "logger.h"
- #include "net.h"                              /* Don't ask. */
- #include "netutl.h"
  #include "subnet.h"
  #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) {
  }
  
  void init_connections(void) {
 -      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) {
 -      avl_delete_tree(connection_tree);
 +      splay_delete_tree(connection_tree);
        free_connection(broadcast);
  }
  
  connection_t *new_connection(void) {
 -      connection_t *c;
 -
 -      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) {
 +      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);
 +      digest_close(&c->indigest);
 +      cipher_close(&c->outcipher);
 +      digest_close(&c->outdigest);
  
        if(c->hischallenge)
                free(c->hischallenge);
        if(c->config_tree)
                exit_configuration(&c->config_tree);
  
 -      if(c->outbuf)
 -              free(c->outbuf);
 +      buffer_clear(&c->inbuf);
 +      buffer_clear(&c->outbuf);
 +      
 +      if(event_initialized(&c->inevent))
 +              event_del(&c->inevent);
  
 -      if(c->rsa_key)
 -              RSA_free(c->rsa_key);
 +      if(event_initialized(&c->outevent))
 +              event_del(&c->outevent);
  
        free(c);
  }
  
  void connection_add(connection_t *c) {
 -      avl_insert(connection_tree, c);
 +      splay_insert(connection_tree, c);
  }
  
  void connection_del(connection_t *c) {
 -      avl_delete(connection_tree, c);
 +      splay_delete(connection_tree, c);
  }
  
 -void dump_connections(void) {
 -      avl_node_t *node;
 +bool dump_connections(connection_t *cdump) {
 +      splay_node_t *node;
        connection_t *c;
  
 -      logger(LOG_DEBUG, "Connections:");
 -
        for(node = connection_tree->head; node; node = node->next) {
                c = node->data;
 -              logger(LOG_DEBUG, " %s at %s options %x socket %d status %04x outbuf %d/%d/%d",
 -                         c->name, c->hostname, c->options, c->socket, bitfield_to_int(&c->status, sizeof c->status),
 -                         c->outbufsize, c->outbufstart, c->outbuflen);
 +              send_request(cdump, "%d %d %s at %s options %x socket %d status %04x",
 +                              CONTROL, REQ_DUMP_CONNECTIONS,
 +                              c->name, c->hostname, c->options, c->socket,
 +                              bitfield_to_int(&c->status, sizeof c->status));
        }
  
 -      logger(LOG_DEBUG, "End of connections.");
 +      return send_request(cdump, "%d %d", CONTROL, REQ_DUMP_CONNECTIONS);
  }
diff --combined src/connection.h
  #ifndef __TINC_CONNECTION_H__
  #define __TINC_CONNECTION_H__
  
 -#include <openssl/rsa.h>
 -#include <openssl/evp.h>
 -
 -#include "avl_tree.h"
 +#include "buffer.h"
 +#include "cipher.h"
 +#include "digest.h"
 +#include "rsa.h"
 +#include "splay_tree.h"
  
  #define OPTION_INDIRECT               0x0001
  #define OPTION_TCPONLY                0x0002
  #define OPTION_CLAMP_MSS      0x0008
  
  typedef struct connection_status_t {
 -      unsigned int pinged:1;                          /* sent ping */
 -      unsigned int active:1;                          /* 1 if active.. */
 -      unsigned int connecting:1;                      /* 1 if we are waiting for a non-blocking connect() to finish */
 -      unsigned int termreq:1;                         /* the termination of this connection was requested */
 -      unsigned int remove:1;                          /* Set to 1 if you want this connection removed */
 -      unsigned int timeout:1;                         /* 1 if gotten timeout */
 -      unsigned int encryptout:1;                      /* 1 if we can encrypt outgoing traffic */
 -      unsigned int decryptin:1;                       /* 1 if we have to decrypt incoming traffic */
 -      unsigned int mst:1;                             /* 1 if this connection is part of a minimum spanning tree */
 -      unsigned int unused:23;
 +              unsigned int pinged:1;                  /* sent ping */
 +              unsigned int active:1;                  /* 1 if active.. */
 +              unsigned int connecting:1;              /* 1 if we are waiting for a non-blocking connect() to finish */
 +              unsigned int termreq:1;                 /* the termination of this connection was requested */
 +              unsigned int remove_unused:1;           /* Set to 1 if you want this connection removed */
 +              unsigned int timeout_unused:1;          /* 1 if gotten timeout */
 +              unsigned int encryptout:1;              /* 1 if we can encrypt outgoing traffic */
 +              unsigned int decryptin:1;               /* 1 if we have to decrypt incoming traffic */
 +              unsigned int mst:1;                     /* 1 if this connection is part of a minimum spanning tree */
 +              unsigned int control:1;
 +              unsigned int pcap:1;
 +              unsigned int unused:21;
  } connection_status_t;
  
  #include "edge.h"
- #include "list.h"
  #include "net.h"
  #include "node.h"
  
@@@ -69,32 -65,42 +68,32 @@@ typedef struct connection_t 
        struct node_t *node;            /* node associated with the other end */
        struct edge_t *edge;            /* edge associated with this connection */
  
 -      RSA *rsa_key;                           /* his public/private key */
 -      const EVP_CIPHER *incipher;     /* Cipher he will use to send data to us */
 -      const EVP_CIPHER *outcipher;    /* Cipher we will use to send data to him */
 -      EVP_CIPHER_CTX *inctx;          /* Context of encrypted meta data that will come from him to us */
 -      EVP_CIPHER_CTX *outctx;         /* Context of encrypted meta data that will be sent from us to him */
 -      char *inkey;                            /* His symmetric meta key + iv */
 -      char *outkey;                           /* Our symmetric meta key + iv */
 -      int inkeylength;                        /* Length of his key + iv */
 -      int outkeylength;                       /* Length of our key + iv */
 -      const EVP_MD *indigest;
 -      const EVP_MD *outdigest;
 +      rsa_t rsa;                      /* his public/private key */
 +      cipher_t incipher;              /* Cipher he will use to send data to us */
 +      cipher_t outcipher;             /* Cipher we will use to send data to him */
 +      digest_t indigest;
 +      digest_t outdigest;
 +
        int inmaclength;
        int outmaclength;
        int incompression;
        int outcompression;
 -      char *mychallenge;                      /* challenge we received from him */
 -      char *hischallenge;                     /* challenge we sent to him */
  
 -      char buffer[MAXBUFSIZE];        /* metadata input buffer */
 -      int buflen;                                     /* bytes read into buffer */
 -      int reqlen;                                     /* length of incoming request */
 +      char *hischallenge;             /* The challenge we sent to him */
 +
 +      struct buffer_t inbuf;
 +      struct buffer_t outbuf;
 +      struct event inevent;                           /* input event on this metadata connection */
 +      struct event outevent;                          /* output event on this metadata connection */
        int tcplen;                                     /* length of incoming TCPpacket */
        int allow_request;                      /* defined if there's only one request possible */
  
 -      char *outbuf;                           /* metadata output buffer */
 -      int outbufstart;                        /* index of first meaningful byte in output buffer */
 -      int outbuflen;                          /* number of meaningful bytes in output buffer */
 -      int outbufsize;                         /* number of bytes allocated to output buffer */
 -
        time_t last_ping_time;          /* last time we saw some activity from the other end or pinged them */
 -      time_t last_flushed_time;       /* last time buffer was empty. Only meaningful if outbuflen > 0 */
  
 -      avl_tree_t *config_tree;        /* Pointer to configuration tree belonging to him */
 +      splay_tree_t *config_tree;      /* Pointer to configuration tree belonging to him */
  } connection_t;
  
 -extern avl_tree_t *connection_tree;
 +extern splay_tree_t *connection_tree;
  extern connection_t *broadcast;
  
  extern void init_connections(void);
@@@ -103,6 -109,6 +102,6 @@@ extern connection_t *new_connection(voi
  extern void free_connection(connection_t *);
  extern void connection_add(connection_t *);
  extern void connection_del(connection_t *);
 -extern void dump_connections(void);
 +extern bool dump_connections(struct connection_t *);
  
  #endif                                                        /* __TINC_CONNECTION_H__ */
diff --combined src/cygwin/device.c
@@@ -24,6 -24,7 +24,7 @@@
  #include <w32api/winioctl.h>
  
  #include "conf.h"
+ #include "device.h"
  #include "logger.h"
  #include "net.h"
  #include "route.h"
@@@ -68,18 -69,18 +69,18 @@@ bool setup_device(void) 
        }
  
        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);
                                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);
        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.
  
        /* 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;
        }
                   It passes everything it reads to the socket. */
        
                char buf[MTU];
 -              long lenin;
 +              long inlen;
  
                CloseHandle(device_handle);
  
                /* 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);
                }
        }
  
@@@ -225,15 -226,15 +226,15 @@@ void close_device(void) 
  }
  
  bool read_packet(vpn_packet_t *packet) {
 -      int lenin;
 +      int inlen;
  
 -      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;
  
  }
  
  bool write_packet(vpn_packet_t *packet) {
 -      long lenout;
 +      long outlen;
  
        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;
        }
diff --combined src/graph.c
  
  #include "system.h"
  
 -#include "avl_tree.h"
 +#include "splay_tree.h"
  #include "config.h"
  #include "connection.h"
  #include "device.h"
  #include "edge.h"
+ #include "graph.h"
  #include "logger.h"
  #include "netutl.h"
  #include "node.h"
  #include "subnet.h"
  #include "utils.h"
  #include "xalloc.h"
 -
 -static bool graph_changed = true;
 +#include "graph.h"
  
  /* 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().
  */
  
 -static 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;
  
        /* Clear MST status on connections */
  
                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 */
        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;
                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)
 +*/
 +
 +static 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;
 +
 +      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);
-                               }
-                       }
++                      if(e->to->address.sa.sa_family == AF_UNSPEC && e->address.sa.sa_family != AF_UNKNOWN)
++                              update_node_udp(e->to, &e->address);
 +
 +                      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)
  */
  
 -static 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;
  
        todo_list = list_alloc(NULL);
  
        }
  
        list_free(todo_list);
 +}
 +
 +static void check_reachability(void) {
 +      splay_node_t *node, *next;
 +      node_t *n;
 +      char *name;
 +      char *address, *port;
 +      char *envp[7];
 +      int i;
  
        /* Check reachability status. */
  
                        n->minmtu = 0;
                        n->mtuprobes = 0;
  
 -                      if(n->mtuevent) {
 -                              event_del(n->mtuevent);
 -                              n->mtuevent = NULL;
 -                      }
 +                      if(timeout_initialized(&n->mtuevent))
 +                              event_del(&n->mtuevent);
  
                        xasprintf(&envp[0], "NETNAME=%s", netname ? : "");
                        xasprintf(&envp[1], "DEVICE=%s", device ? : "");
  
  void graph(void) {
        subnet_cache_flush();
 -      sssp_bfs();
 +      sssp_dijkstra();
 +      check_reachability();
        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;
 -      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 {
 -              xasprintf(&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");
 -      
 -      /* 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);
 -      }
 -
 -      /* 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);
 -      }
 -
 -      fprintf(file, "}\n");   
 -      
 -      if(filename[0] == '|') {
 -              pclose(file);
 -      } else {
 -              fclose(file);
 -#ifdef HAVE_MINGW
 -              unlink(filename);
 -#endif
 -              rename(tmpname, filename);
 -              free(tmpname);
 -      }
  }
diff --combined src/linux/device.c
  
  #include "system.h"
  
 -#ifdef HAVE_LINUX_IF_TUN_H
  #include <linux/if_tun.h>
  #define DEFAULT_DEVICE "/dev/net/tun"
 -#else
 -#define DEFAULT_DEVICE "/dev/tap0"
 -#endif
  
  #include "conf.h"
+ #include "device.h"
  #include "logger.h"
  #include "net.h"
  #include "route.h"
  #include "utils.h"
  #include "xalloc.h"
 +#include "device.h"
  
  typedef enum device_type_t {
 -      DEVICE_TYPE_ETHERTAP,
        DEVICE_TYPE_TUN,
        DEVICE_TYPE_TAP,
  } device_type_t;
@@@ -43,12 -48,13 +44,12 @@@ char *iface = NULL
  static char ifrname[IFNAMSIZ];
  static char *device_info;
  
 -static uint64_t device_total_in = 0;
 -static uint64_t device_total_out = 0;
 +uint64_t device_in_packets = 0;
 +uint64_t device_in_bytes = 0;
 +uint64_t device_out_packets = 0;
 +uint64_t device_out_bytes = 0;
  
  bool setup_device(void) {
 -      struct ifreq ifr;
 -      bool t1q = false;
 -
        if(!get_config_string(lookup_config(config_tree, "Device"), &device))
                device = xstrdup(DEFAULT_DEVICE);
  
                return false;
        }
  
 -#ifdef HAVE_LINUX_IF_TUN_H
 -      /* Ok now check if this is an old ethertap or a new tun/tap thingie */
 +      struct ifreq ifr = {{{0}}};
  
 -      memset(&ifr, 0, sizeof(ifr));
        if(routing_mode == RMODE_ROUTER) {
                ifr.ifr_flags = IFF_TUN;
                device_type = DEVICE_TYPE_TUN;
@@@ -80,8 -88,6 +81,8 @@@
  
  #ifdef IFF_ONE_QUEUE
        /* Set IFF_ONE_QUEUE flag... */
 +
 +      bool t1q = false;
        if(get_config_bool(lookup_config(config_tree, "IffOneQueue"), &t1q) && t1q)
                ifr.ifr_flags |= IFF_ONE_QUEUE;
  #endif
                strncpy(ifrname, ifr.ifr_name, IFNAMSIZ);
                if(iface) free(iface);
                iface = xstrdup(ifrname);
 -      } else
 -#endif
 -      {
 -              if(routing_mode == RMODE_ROUTER)
 -                      overwrite_mac = true;
 -              device_info = "Linux ethertap device";
 -              device_type = DEVICE_TYPE_ETHERTAP;
 -              if(iface)
 -                      free(iface);
 -              iface = xstrdup(strrchr(device, '/') ? strrchr(device, '/') + 1 : device);
        }
  
        logger(LOG_INFO, "%s is a %s", device, device_info);
@@@ -113,37 -129,45 +114,37 @@@ void close_device(void) 
  }
  
  bool read_packet(vpn_packet_t *packet) {
 -      int lenin;
 +      int inlen;
        
        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);
 -
 -                      if(lenin <= 0) {
 -                              logger(LOG_ERR, "Error while reading from %s %s: %s",
 -                                         device_info, device, strerror(errno));
 -                              return false;
 -                      }
 -
 -                      packet->len = lenin;
 -                      break;
 -              case DEVICE_TYPE_ETHERTAP:
 -                      lenin = read(device_fd, packet->data - 2, MTU + 2);
 +                      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 - 2;
 +                      packet->len = inlen;
                        break;
 +              default:
 +                      abort();
        }
  
 -      device_total_in += packet->len;
 +      device_in_packets++;
 +      device_in_bytes += packet->len;
  
        ifdebug(TRAFFIC) logger(LOG_DEBUG, "Read packet of %d bytes from %s", packet->len,
                           device_info);
@@@ -171,18 -195,24 +172,18 @@@ bool write_packet(vpn_packet_t *packet
                                return false;
                        }
                        break;
 -              case DEVICE_TYPE_ETHERTAP:
 -                      *(short int *)(packet->data - 2) = packet->len;
 -
 -                      if(write(device_fd, packet->data - 2, packet->len + 2) < 0) {
 -                              logger(LOG_ERR, "Can't write to %s %s: %s", device_info, device,
 -                                         strerror(errno));
 -                              return false;
 -                      }
 -                      break;
 +              default:
 +                      abort();
        }
  
 -      device_total_out += packet->len;
 +      device_out_packets++;
 +      device_out_bytes += packet->len;
  
        return true;
  }
  
  void dump_device_stats(void) {
        logger(LOG_DEBUG, "Statistics for %s %s:", device_info, device);
 -      logger(LOG_DEBUG, " total bytes in:  %10"PRIu64, device_total_in);
 -      logger(LOG_DEBUG, " total bytes out: %10"PRIu64, device_total_out);
 +      logger(LOG_DEBUG, " total bytes in:  %10"PRIu64, device_in_bytes);
 +      logger(LOG_DEBUG, " total bytes out: %10"PRIu64, device_out_bytes);
  }
diff --combined src/logger.c
@@@ -44,14 -44,18 +44,18 @@@ void openlogger(const char *ident, logm
                case LOGMODE_FILE:
                        logpid = getpid();
                        logfile = fopen(logfilename, "a");
-                       if(!logfile)
+                       if(!logfile) {
+                               fprintf(stderr, "Could not open log file %s: %s\n", logfilename, strerror(errno));
                                logmode = LOGMODE_NULL;
+                       }
                        break;
                case LOGMODE_SYSLOG:
  #ifdef HAVE_MINGW
                        loghandle = RegisterEventSource(NULL, logident);
-                       if(!loghandle)
+                       if(!loghandle) {
+                               fprintf(stderr, "Could not open log handle!");
                                logmode = LOGMODE_NULL;
+                       }
                        break;
  #else
  #ifdef HAVE_SYSLOG_H
        }
  }
  
+ void reopenlogger() {
+       if(logmode != LOGMODE_FILE)
+               return;
+       fflush(logfile);
+       FILE *newfile = fopen(logfilename, "a");
+       if(!newfile) {
+               logger(LOG_ERR, "Unable to reopen log file %s: %s\n", logfilename, strerror(errno));
+               return;
+       }
+       fclose(logfile);
+       logfile = newfile;
+ }
  void logger(int priority, const char *format, ...) {
        va_list ap;
+       char timestr[32] = "";
+       time_t now;
  
        va_start(ap, format);
  
@@@ -76,7 -96,9 +96,9 @@@
                        fflush(stderr);
                        break;
                case LOGMODE_FILE:
-                       fprintf(logfile, "%ld %s[%ld]: ", time(NULL), logident, (long)logpid);
+                       now = time(NULL);
+                       strftime(timestr, sizeof timestr, "%Y-%m-%d %H:%M:%S", localtime(&now));
+                       fprintf(logfile, "%s %s[%ld]: ", timestr, logident, (long)logpid);
                        vfprintf(logfile, format, ap);
                        fprintf(logfile, "\n");
                        fflush(logfile);
                        {
                                char message[4096];
                                const char *messages[] = {message};
 -                              vsnprintf(message, sizeof(message), format, ap);
 +                              vsnprintf(message, sizeof message, format, ap);
                                ReportEvent(loghandle, priority, 0, 0, NULL, 1, 0, messages, NULL);
                        }
  #else
  #else
                        {
                                char message[4096];
 -                              vsnprintf(message, sizeof(message), format, ap);
 +                              vsnprintf(message, sizeof message, format, ap);
                                syslog(priority, "%s", message);
                        }
  #endif
diff --combined src/mingw/device.c
@@@ -24,6 -24,7 +24,7 @@@
  #include <winioctl.h>
  
  #include "conf.h"
+ #include "device.h"
  #include "logger.h"
  #include "net.h"
  #include "route.h"
@@@ -109,18 -110,18 +110,18 @@@ bool setup_device(void) 
        }
  
        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);
                                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;
        /* 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);
        }
        
  
        /* 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;
        }
        /* 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";
  
@@@ -221,13 -222,13 +222,13 @@@ bool read_packet(vpn_packet_t *packet) 
  }
  
  bool write_packet(vpn_packet_t *packet) {
 -      long lenout;
 +      long outlen;
        OVERLAPPED overlapped = {0};
  
        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;
        }
diff --combined src/net.c
+++ b/src/net.c
@@@ -3,6 -3,7 +3,7 @@@
      Copyright (C) 1998-2005 Ivo Timmermans,
                    2000-2011 Guus Sliepen <guus@tinc-vpn.org>
                    2006      Scott Lamb <slamb@slamb.org>
+                 2011      Loïc Grenié <loic.grenie@gmail.com>
  
      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
  
  #include "system.h"
  
 -#include <openssl/rand.h>
 -
  #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"
  #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;
 -#ifdef HAVE_PSELECT
 -bool graph_dump = false;
 -#endif
 -
 -time_t now = 0;
  int contradicting_add_edge = 0;
  int contradicting_del_edge = 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;
        }
  }
  
 -/*
 -  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;
 -
 -      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;
 -      }
 -
 -      if(device_fd >= 0)
 -              FD_SET(device_fd, readset);
 -      if(device_fd > max)
 -              max = device_fd;
 -      
 -      return max;
 -}
 -
  /*
    Terminate a connection:
    - Close the socket
    - Deactivate the host
  */
  void terminate_connection(connection_t *c, bool report) {
 -      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)
  
        /* 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);
  }
  
  /*
    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);
  
        for(node = connection_tree->head; node; node = next) {
                next = node->next;
                c = node->data;
  
-               if(c->last_ping_time + pingtimeout < now) {
+               if(c->last_ping_time + pingtimeout <= now) {
                        if(c->status.active) {
                                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);
-                               } else if(c->last_ping_time + pinginterval < now) {
 +                                      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, bitfield_to_int(&c->status, sizeof c->status));
 -                                      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);
 -                      }
 -              }
 -      }
 -}
 -
 -/*
 -  check all connections to see if anything
 -  happened on their sockets
 -*/
 -static void check_network_activity(fd_set * readset, fd_set * writeset) {
 -      connection_t *c;
 -      avl_node_t *node;
 -      int result, i;
 -      socklen_t len = sizeof(result);
 -      vpn_packet_t packet;
 -      static int errors = 0;
 -
 -      /* check input from kernel */
 -      if(device_fd >= 0 && FD_ISSET(device_fd, readset)) {
 -              if(read_packet(&packet)) {
 -                      errors = 0;
 -                      packet.priority = 0;
 -                      route(myself, &packet);
 -              } else {
 -                      usleep(errors * 50000);
 -                      errors++;
 -                      if(errors > 10) {
 -                              logger(LOG_ERR, "Too many errors from %s, exiting!", device);
 -                              running = false;
 -                      }
 -              }
        }
  
 -      /* check meta connections */
 -      for(node = connection_tree->head; node; node = node->next) {
 -              c = node->data;
 -
 -              if(c->status.remove)
 -                      continue;
 +      if(contradicting_del_edge && contradicting_add_edge) {
 +              logger(LOG_WARNING, "Possible node with same Name as us!");
  
 -              if(FD_ISSET(c->socket, readset)) {
 -                      if(c->status.connecting) {
 -                              c->status.connecting = false;
 -                              getsockopt(c->socket, SOL_SOCKET, SO_ERROR, (void *)&result, &len);
 +              if(rand() % 3 == 0) {
 +                      logger(LOG_ERR, "Shutting down, check configuration of all nodes for duplicate Names!");
 +                      event_loopexit(NULL);
 +                      return;
 +              }
  
 -                              if(!result)
 -                                      finish_connecting(c);
 -                              else {
 -                                      ifdebug(CONNECTIONS) logger(LOG_DEBUG,
 -                                                         "Error while connecting to %s (%s): %s",
 -                                                         c->name, c->hostname, sockstrerror(result));
 -                                      closesocket(c->socket);
 -                                      do_outgoing_connection(c);
 -                                      continue;
 -                              }
 -                      }
 +              contradicting_add_edge = 0;
 +              contradicting_del_edge = 0;
 +      }
  
 -                      if(!receive_meta(c)) {
 -                              terminate_connection(c, c->status.active);
 -                              continue;
 -                      }
 -              }
 +      event_add(event, &(struct timeval){pingtimeout, 0});
 +}
  
 -              if(FD_ISSET(c->socket, writeset)) {
 -                      if(!flush_meta(c)) {
 -                              terminate_connection(c, c->status.active);
 -                              continue;
 -                      }
 +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, sockstrerror(result));
 +                      closesocket(c->socket);
 +                      do_outgoing_connection(c);
 +                      return;
                }
        }
  
 -      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);
 +      if (!receive_meta(c)) {
 +              terminate_connection(c, c->status.active);
 +              return;
        }
  }
  
 -/*
 -  this is where it all happens...
 -*/
 -int main_loop(void) {
 -      fd_set readset, writeset;
 -#ifdef HAVE_PSELECT
 -      struct timespec tv;
 -      sigset_t omask, block_mask;
 -      time_t next_event;
 -#else
 -      struct timeval tv;
 -#endif
 -      int r, maxfd;
 -      time_t last_ping_check, last_config_check, last_graph_dump;
 -      event_t *event;
 +static void sigterm_handler(int signal, short events, void *data) {
 +      logger(LOG_NOTICE, "Got %s signal", strsignal(signal));
 +      event_loopexit(NULL);
 +}
  
 -      last_ping_check = now;
 -      last_config_check = now;
 -      last_graph_dump = now;
 -      
 -      srand(now);
 -
 -#ifdef HAVE_PSELECT
 -      if(lookup_config(config_tree, "GraphDumpFile"))
 -              graph_dump = true;
 -      /* Block SIGHUP & SIGALRM */
 -      sigemptyset(&block_mask);
 -      sigaddset(&block_mask, SIGHUP);
 -      sigaddset(&block_mask, SIGALRM);
 -      sigprocmask(SIG_BLOCK, &block_mask, &omask);
 -#endif
 +static void sighup_handler(int signal, short events, void *data) {
 +      logger(LOG_NOTICE, "Got %s signal", strsignal(signal));
 +      reload_configuration();
 +}
  
 -      running = true;
 -
 -      while(running) {
 -#ifdef HAVE_PSELECT
 -              next_event = last_ping_check + pingtimeout;
 -              if(graph_dump && next_event > last_graph_dump + 60)
 -                      next_event = last_graph_dump + 60;
 -
 -              if((event = peek_next_event()) && next_event > event->time)
 -                      next_event = event->time;
 -
 -              if(next_event <= now)
 -                      tv.tv_sec = 0;
 -              else
 -                      tv.tv_sec = next_event - now;
 -              tv.tv_nsec = 0;
 -#else
 -              tv.tv_sec = 1;
 -              tv.tv_usec = 0;
 -#endif
 +int reload_configuration(void) {
 +      connection_t *c;
 +      splay_node_t *node, *next;
 +      char *fname;
 +      struct stat s;
 +      static time_t last_config_check = 0;
  
 -              maxfd = build_fdset(&readset, &writeset);
 +      /* Reread our own configuration file */
  
 -#ifdef HAVE_MINGW
 -              LeaveCriticalSection(&mutex);
 -#endif
 -#ifdef HAVE_PSELECT
 -              r = pselect(maxfd + 1, &readset, &writeset, NULL, &tv, &omask);
 -#else
 -              r = select(maxfd + 1, &readset, &writeset, NULL, &tv);
 -#endif
 -              now = time(NULL);
 -#ifdef HAVE_MINGW
 -              EnterCriticalSection(&mutex);
 -#endif
 +      exit_configuration(&config_tree);
 +      init_configuration(&config_tree);
  
 -              if(r < 0) {
 -                      if(!sockwouldblock(sockerrno)) {
 -                              logger(LOG_ERR, "Error while waiting for input: %s", sockstrerror(sockerrno));
 -                              dump_connections();
 -                              return 1;
 -                      }
 -              }
 -
 -              if(r > 0)
 -                      check_network_activity(&readset, &writeset);
 +      if(!read_server_config()) {
 +              logger(LOG_ERR, "Unable to reread configuration file, exitting.");
 +              event_loopexit(NULL);
 +              return EINVAL;
 +      }
  
 -              if(do_purge) {
 -                      purge();
 -                      do_purge = false;
 +      /* 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;
                }
 +              
 +              xasprintf(&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);
 +      }
  
 -              /* 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) {
 -                              avl_node_t *node;
 -                              node_t *n;
 -
 -                              ifdebug(STATUS) logger(LOG_INFO, "Expiring symmetric keys");
 +      last_config_check = time(NULL);
  
 -                              for(node = node_tree->head; node; node = node->next) {
 -                                      n = node->data;
 -                                      if(n->inkey) {
 -                                              free(n->inkey);
 -                                              n->inkey = NULL;
 -                                      }
 -                              }
 +      /* If StrictSubnet is set, expire deleted Subnets and read new ones in */
  
 -                              send_key_changed();
 -                              keyexpires = now + keylifetime;
 -                      }
 +      if(strictsubnets) {
 +              subnet_t *subnet;
  
 -                      if(contradicting_del_edge > 10 && contradicting_add_edge > 10) {
 -                              logger(LOG_WARNING, "Possible node with same Name as us!");
  
 -                              if(rand() % 3 == 0) {
 -                                      logger(LOG_ERR, "Shutting down, check configuration of all nodes for duplicate Names!");
 -                                      running = false;
 -                                      break;
 -                              }
 -
 -                              contradicting_add_edge = 0;
 -                              contradicting_del_edge = 0;
 -                      }
 +              for(node = subnet_tree->head; node; node = node->next) {
 +                      subnet = node->data;
 +                      subnet->expires = 1;
                }
  
 -              if(sigalrm) {
 -                      avl_node_t *node;
 -                      logger(LOG_INFO, "Flushing event queue");
 -                      expire_events();
 -                      for(node = connection_tree->head; node; node = node->next) {
 -                              connection_t *c = node->data;
 -                              send_ping(c);
 +              load_all_subnets();
 +
 +              for(node = subnet_tree->head; node; node = next) {
 +                      next = node->next;
 +                      subnet = node->data;
 +                      if(subnet->expires == 1) {
 +                              send_del_subnet(broadcast, subnet);
 +                              if(subnet->owner->status.reachable)
 +                                      subnet_update(subnet->owner, subnet, false);
 +                              subnet_del(subnet->owner, subnet);
 +                      } else if(subnet->expires == -1) {
 +                              subnet->expires = 0;
 +                      } else {
 +                              send_add_subnet(broadcast, subnet);
 +                              if(subnet->owner->status.reachable)
 +                                      subnet_update(subnet->owner, subnet, true);
                        }
 -                      sigalrm = false;
                }
 +      }
  
 -              while((event = get_expired_event())) {
 -                      event->handler(event->data);
 -                      free_event(event);
 -              }
 -
 -              if(sighup) {
 -                      connection_t *c;
 -                      avl_node_t *node, *next;
 -                      char *fname;
 -                      struct stat s;
 -                      
 -                      sighup = false;
 -
 -                      reopenlogger();
 -                      
 -                      /* 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;
 -                      }
 -
 -                      /* Cancel non-active outgoing connections */
 -
 -                      for(node = connection_tree->head; node; node = next) {
 -                              next = node->next;
 -                              c = node->data;
 -
 -                              c->outgoing = NULL;
 -
 -                              if(c->status.connecting) {
 -                                      terminate_connection(c, false);
 -                                      connection_del(c);
 -                              }
 -                      }
 -
 -                      /* Wipe list of outgoing connections */
 -
 -                      for(list_node_t *node = outgoing_list->head; node; node = node->next) {
 -                              outgoing_t *outgoing = node->data;
 -
 -                              if(outgoing->event)
 -                                      event_del(outgoing->event);
 -                      }
 -
 -                      list_delete_list(outgoing_list);
 -
 -                      /* 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;
 -                              
 -                              xasprintf(&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);
 -                      }
 +      /* Try to make outgoing connections */
 +      
 +      try_outgoing_connections();
  
 -                      last_config_check = now;
 +      return 0;
 +}
  
 -                      /* If StrictSubnet is set, expire deleted Subnets and read new ones in */
 +void retry(void) {
 +      connection_t *c;
 +      splay_node_t *node;
  
 -                      if(strictsubnets) {
 -                              subnet_t *subnet;
 +      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(node = subnet_tree->head; node; node = node->next) {
 -                                      subnet = node->data;
 -                                      subnet->expires = 1;
 -                              }
 +/*
 +  this is where it all happens...
 +*/
 +int main_loop(void) {
 +      struct event timeout_event;
 +      struct event sighup_event;
 +      struct event sigterm_event;
 +      struct event sigquit_event;
  
 -                              load_all_subnets();
 -
 -                              for(node = subnet_tree->head; node; node = next) {
 -                                      next = node->next;
 -                                      subnet = node->data;
 -                                      if(subnet->expires == 1) {
 -                                              send_del_subnet(broadcast, subnet);
 -                                              if(subnet->owner->status.reachable)
 -                                                      subnet_update(subnet->owner, subnet, false);
 -                                              subnet_del(subnet->owner, subnet);
 -                                      } else if(subnet->expires == -1) {
 -                                              subnet->expires = 0;
 -                                      } else {
 -                                              send_add_subnet(broadcast, subnet);
 -                                              if(subnet->owner->status.reachable)
 -                                                      subnet_update(subnet->owner, subnet, true);
 -                                      }
 -                              }
 -                      }
 +      timeout_set(&timeout_event, timeout_handler, &timeout_event);
 +      event_add(&timeout_event, &(struct timeval){pingtimeout, 0});
  
 -                      /* Try to make outgoing connections */
 -                      
 -                      try_outgoing_connections();
 -              }
 -              
 -              /* Dump graph if wanted every 60 seconds*/
 +#ifdef SIGHUP
 +      signal_set(&sighup_event, SIGHUP, sighup_handler, NULL);
 +      signal_add(&sighup_event, NULL);
 +#endif
 +#ifdef SIGTERM
 +      signal_set(&sigterm_event, SIGTERM, sigterm_handler, NULL);
 +      signal_add(&sigterm_event, NULL);
 +#endif
 +#ifdef SIGQUIT
 +      signal_set(&sigquit_event, SIGQUIT, sigterm_handler, NULL);
 +      signal_add(&sigquit_event, NULL);
 +#endif
  
 -              if(last_graph_dump + 60 <= now) {
 -                      dump_graph();
 -                      last_graph_dump = now;
 -              }
 +      if(event_loop(0) < 0) {
 +              logger(LOG_ERR, "Error while waiting for input: %s", strerror(errno));
 +              return 1;
        }
  
 -#ifdef HAVE_PSELECT
 -      /* Restore SIGHUP & SIGALARM mask */
 -      sigprocmask(SIG_SETMASK, &omask, NULL);
 -#endif
 +      signal_del(&sighup_event);
 +      signal_del(&sigterm_event);
 +      signal_del(&sigquit_event);
 +
 +      if(timeout_initialized(&timeout_event))
 +              event_del(&timeout_event);
  
        return 0;
  }
diff --combined src/net.h
+++ b/src/net.h
@@@ -21,9 -21,9 +21,9 @@@
  #ifndef __TINC_NET_H__
  #define __TINC_NET_H__
  
 -#include <openssl/evp.h>
 -
  #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 */
  #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... */
  
  typedef struct mac_t {
        uint8_t x[6];
@@@ -84,8 -84,6 +84,8 @@@ typedef struct vpn_packet_t 
  } vpn_packet_t;
  
  typedef struct listen_socket_t {
 +      struct event ev_tcp;
 +      struct event ev_udp;
        int tcp;
        int udp;
        sockaddr_t sa;
@@@ -100,7 -98,7 +100,7 @@@ typedef struct outgoing_t 
        struct config_t *cfg;
        struct addrinfo *ai;
        struct addrinfo *aip;
 -      struct event *event;
 +      struct event ev;
  } outgoing_t;
  
  extern list_t *outgoing_list;
@@@ -112,27 -110,32 +112,27 @@@ extern unsigned replaywin
  
  extern listen_socket_t listen_socket[MAXSOCKETS];
  extern int listen_sockets;
 -extern int keyexpires;
  extern int keylifetime;
  extern int udp_rcvbuf;
  extern int udp_sndbuf;
  extern bool do_prune;
 -extern bool do_purge;
  extern char *myport;
 -extern time_t now;
  extern int contradicting_add_edge;
  extern int contradicting_del_edge;
  
 -extern volatile bool running;
 -
  /* 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 bool do_outgoing_connection(struct connection_t *);
 +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 *);
 +extern void send_packet(struct node_t *, vpn_packet_t *);
- extern void receive_tcppacket(struct connection_t *, char *, int);
+ extern void receive_tcppacket(struct connection_t *, const char *, int);
  extern void broadcast_packet(const struct node_t *, vpn_packet_t *);
  extern bool setup_network(void);
  extern void setup_outgoing_connection(struct outgoing_t *);
@@@ -143,13 -146,7 +143,13 @@@ 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 regenerate_key();
 +extern void handle_device_data(int, short, void *);
 +extern void handle_meta_connection_data(int, short, void *);
- extern void load_all_subnets();
++extern void regenerate_key(void);
 +extern void purge(void);
 +extern void retry(void);
 +extern int reload_configuration(void);
+ extern void load_all_subnets(void);
  
  #ifndef HAVE_MINGW
  #define closesocket(s) close(s)
diff --combined src/net_packet.c
  #include LZO1X_H
  #endif
  
 -#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"
  #include "net.h"
  #include "netutl.h"
@@@ -56,6 -53,7 +55,6 @@@
  #include "xalloc.h"
  
  int keylifetime = 0;
 -int keyexpires = 0;
  #ifdef HAVE_LZO
  static char lzo_wrkmem[LZO1X_999_MEM_COMPRESS > LZO1X_1_MEM_COMPRESS ? LZO1X_999_MEM_COMPRESS : LZO1X_1_MEM_COMPRESS];
  #endif
@@@ -71,13 -69,13 +70,13 @@@ unsigned replaywin = 16
  // mtuprobes ==    32: send 1 burst, sleep pingtimeout second
  // mtuprobes ==    33: no response from other side, restart PMTU discovery process
  
 -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;
        int timeout = 1;
        
        n->mtuprobes++;
 -      n->mtuevent = NULL;
  
        if(!n->status.reachable || !n->status.validkey) {
                ifdebug(TRAFFIC) logger(LOG_INFO, "Trying to send MTU probe to unreachable or rekeying node %s (%s)", n->name, n->hostname);
                        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;
  
        }
  
  end:
 -      n->mtuevent = new_event();
 -      n->mtuevent->handler = (event_handler_t)send_mtu_probe;
 -      n->mtuevent->data = n;
 -      n->mtuevent->time = now + timeout;
 -      event_add(n->mtuevent);
 +      event_add(&n->mtuevent, &(struct timeval){timeout, 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, length_t len) {
 +static void mtu_probe_h(node_t *n, vpn_packet_t *packet, length_t len) {
        ifdebug(TRAFFIC) logger(LOG_INFO, "Got MTU probe length %d from %s (%s)", packet->len, n->name, n->hostname);
  
        if(!packet->data[0]) {
@@@ -235,17 -231,18 +234,17 @@@ static void receive_packet(node_t *n, v
        ifdebug(TRAFFIC) logger(LOG_DEBUG, "Received packet of %d bytes from %s (%s)",
                           packet->len, n->name, n->hostname);
  
 +      n->in_packets++;
 +      n->in_bytes += packet->len;
 +
        route(n, packet);
  }
  
 -static bool try_mac(const node_t *n, const vpn_packet_t *inpkt) {
 -      unsigned char hmac[EVP_MAX_MD_SIZE];
 -
 -      if(!n->indigest || !n->inmaclength || !n->inkey || inpkt->len < sizeof inpkt->seqno + n->inmaclength)
 +static bool try_mac(node_t *n, const vpn_packet_t *inpkt) {
 +      if(!digest_active(&n->indigest) || inpkt->len < sizeof inpkt->seqno + digest_length(&n->indigest))
                return false;
  
 -      HMAC(n->indigest, n->inkey, n->inkeylength, (unsigned char *) &inpkt->seqno, inpkt->len - n->inmaclength, (unsigned char *)hmac, NULL);
 -
 -      return !memcmp(hmac, (char *) &inpkt->seqno + inpkt->len - n->inmaclength, n->inmaclength);
 +      return digest_verify(&n->indigest, &inpkt->seqno, inpkt->len - n->indigest.maclength, (const char *)&inpkt->seqno + inpkt->len - n->indigest.maclength);
  }
  
  static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) {
        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];
 -      int i;
 +      size_t outlen;
  
 -      if(!n->inkey) {
 +      if(!cipher_active(&n->incipher)) {
                ifdebug(TRAFFIC) logger(LOG_DEBUG, "Got packet from %s (%s) but he hasn't got our key yet",
                                        n->name, n->hostname);
                return;
  
        /* Check packet length */
  
 -      if(inpkt->len < sizeof(inpkt->seqno) + n->inmaclength) {
 +      if(inpkt->len < sizeof inpkt->seqno + digest_length(&n->indigest)) {
                ifdebug(TRAFFIC) logger(LOG_DEBUG, "Got too short packet from %s (%s)",
                                        n->name, n->hostname);
                return;
  
        /* Check the message authentication code */
  
 -      if(n->indigest && n->inmaclength) {
 -              inpkt->len -= n->inmaclength;
 -              HMAC(n->indigest, n->inkey, n->inkeylength,
 -                       (unsigned char *) &inpkt->seqno, inpkt->len, (unsigned char *)hmac, NULL);
 -
 -              if(memcmp(hmac, (char *) &inpkt->seqno + inpkt->len, n->inmaclength)) {
 -                      ifdebug(TRAFFIC) logger(LOG_DEBUG, "Got unauthenticated packet from %s (%s)",
 -                                         n->name, n->hostname);
 +      if(digest_active(&n->indigest)) {
 +              inpkt->len -= n->indigest.maclength;
 +              if(!digest_verify(&n->indigest, &inpkt->seqno, inpkt->len, (const char *)&inpkt->seqno + inpkt->len)) {
 +                      ifdebug(TRAFFIC) logger(LOG_DEBUG, "Got unauthenticated packet from %s (%s)", n->name, n->hostname);
                        return;
                }
        }
 -
        /* Decrypt the packet */
  
 -      if(n->incipher) {
 +      if(cipher_active(&n->incipher)) {
                outpkt = pkt[nextpkt++];
 +              outlen = MAXSIZE;
  
 -              if(!EVP_DecryptInit_ex(&n->inctx, NULL, NULL, NULL, NULL)
 -                              || !EVP_DecryptUpdate(&n->inctx, (unsigned char *) &outpkt->seqno, &outlen,
 -                                      (unsigned char *) &inpkt->seqno, inpkt->len)
 -                              || !EVP_DecryptFinal_ex(&n->inctx, (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(&n->incipher, &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(replaywin) {
                                        return;
                                }
                        } else {
 -                              for(i = n->received_seqno + 1; i < inpkt->seqno; i++)
 +                              for(int i = n->received_seqno + 1; i < inpkt->seqno; i++)
                                        n->late[(i / 8) % replaywin] |= 1 << i % 8;
                        }
                }
                n->received_seqno = inpkt->seqno;
                        
        if(n->received_seqno > MAX_SEQNO)
 -              keyexpires = 0;
 +              regenerate_key();
  
        /* Decompress the packet */
  
                receive_packet(n, inpkt);
  }
  
- void receive_tcppacket(connection_t *c, char *buffer, int len) {
+ void receive_tcppacket(connection_t *c, const char *buffer, int len) {
        vpn_packet_t outpkt;
  
        outpkt.len = len;
@@@ -376,12 -383,12 +375,12 @@@ static void send_udppacket(node_t *n, v
        vpn_packet_t *inpkt = origpkt;
        int nextpkt = 0;
        vpn_packet_t *outpkt;
 -      int origlen;
 -      int outlen, outpad;
 +      int origlen = origpkt->len;
 +      size_t outlen;
  #if defined(SOL_IP) && defined(IP_TOS)
        static int priority = 0;
 +      int origpriority = origpkt->priority;
  #endif
 -      int origpriority;
        int sock;
  
        if(!n->status.reachable) {
        /* Make sure we have a valid key */
  
        if(!n->status.validkey) {
 +              time_t now = time(NULL);
 +
                ifdebug(TRAFFIC) logger(LOG_INFO,
                                   "No valid key known yet for %s (%s), forwarding via TCP",
                                   n->name, n->hostname);
  
-               if(n->last_req_key + 10 < now) {
+               if(n->last_req_key + 10 <= now) {
                        send_req_key(n);
                        n->last_req_key = now;
                }
                return;
        }
  
 -      origlen = inpkt->len;
 -      origpriority = inpkt->priority;
 -
        /* Compress the packet */
  
        if(n->outcompression) {
        /* Add sequence number */
  
        inpkt->seqno = htonl(++(n->sent_seqno));
 -      inpkt->len += sizeof(inpkt->seqno);
 +      inpkt->len += sizeof inpkt->seqno;
  
        /* Encrypt the packet */
  
 -      if(n->outcipher) {
 +      if(cipher_active(&n->outcipher)) {
                outpkt = pkt[nextpkt++];
 +              outlen = MAXSIZE;
  
 -              if(!EVP_EncryptInit_ex(&n->outctx, NULL, NULL, NULL, NULL)
 -                              || !EVP_EncryptUpdate(&n->outctx, (unsigned char *) &outpkt->seqno, &outlen,
 -                                      (unsigned char *) &inpkt->seqno, inpkt->len)
 -                              || !EVP_EncryptFinal_ex(&n->outctx, (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->outcipher, &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->outdigest && n->outmaclength) {
 -              HMAC(n->outdigest, n->outkey, n->outkeylength, (unsigned char *) &inpkt->seqno,
 -                       inpkt->len, (unsigned char *) &inpkt->seqno + inpkt->len, NULL);
 -              inpkt->len += n->outmaclength;
 +      if(digest_active(&n->outdigest)) {
 +              digest_create(&n->outdigest, &inpkt->seqno, inpkt->len, (char *)&inpkt->seqno + inpkt->len);
 +              inpkt->len += digest_length(&n->outdigest);
        }
  
        /* Determine which socket we have to use */
           && 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
@@@ -500,14 -512,12 +499,14 @@@ end
  /*
    send a packet to the given vpn ip.
  */
 -void send_packet(const node_t *n, vpn_packet_t *packet) {
 +void send_packet(node_t *n, vpn_packet_t *packet) {
        node_t *via;
  
        if(n == myself) {
                if(overwrite_mac)
                         memcpy(packet->data, mymac.x, ETH_ALEN);
 +              n->out_packets++;
 +              n->out_bytes += packet->len;
                write_packet(packet);
                return;
        }
                return;
        }
  
 +      n->out_packets++;
 +      n->out_bytes += packet->len;
 +
        via = (packet->priority == -1 || n->via == myself) ? n->nexthop : n->via;
  
        if(via != n)
  /* Broadcast a packet using the minimum spanning tree */
  
  void broadcast_packet(const node_t *from, vpn_packet_t *packet) {
 -      avl_node_t *node;
 +      splay_node_t *node;
        connection_t *c;
  
        ifdebug(TRAFFIC) logger(LOG_INFO, "Broadcasting packet of %d bytes from %s (%s)",
  }
  
  static node_t *try_harder(const sockaddr_t *from, const vpn_packet_t *pkt) {
 -      avl_node_t *node;
 +      splay_node_t *node;
        edge_t *e;
        node_t *n = NULL;
        bool hard = false;
        static time_t last_hard_try = 0;
 +      time_t now = time(NULL);
 +
 +      if(last_hard_try == now)
 +              return NULL;
 +      else
 +              last_hard_try = now;
  
        for(node = edge_weight_tree->head; node; node = node->next) {
                e = node->data;
        return n;
  }
  
 -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;
 +      int len;
  
 -      pkt.len = recvfrom(sock, (char *) &pkt.seqno, MAXSIZE, 0, &from.sa, &fromlen);
 +      len = recvfrom(sock, (char *) &pkt.seqno, MAXSIZE, 0, &from.sa, &fromlen);
  
 -      if(pkt.len < 0) {
 +      if(len <= 0 || len > MAXSIZE) {
                if(!sockwouldblock(sockerrno))
                        logger(LOG_ERR, "Receiving packet failed: %s", sockstrerror(sockerrno));
                return;
        }
  
 +      pkt.len = len;
 +
        sockaddrunmap(&from);           /* Some braindead IPv6 implementations do stupid things. */
  
        n = lookup_node_udp(&from);
  
        receive_udppacket(n, &pkt);
  }
 +
 +void handle_device_data(int sock, short events, void *data) {
 +      vpn_packet_t packet;
 +
 +      packet.priority = 0;
 +
 +      if(read_packet(&packet)) {
 +              myself->in_packets++;
 +              myself->in_bytes += packet.len;
 +              route(myself, &packet);
 +      }
 +}
diff --combined src/node.h
  #ifndef __TINC_NODE_H__
  #define __TINC_NODE_H__
  
 -#include "avl_tree.h"
 +#include "splay_tree.h"
 +#include "cipher.h"
  #include "connection.h"
 -#include "event.h"
 +#include "digest.h"
- #include "list.h"
  #include "subnet.h"
  
  typedef struct node_status_t {
 -      unsigned int unused_active:1;                   /* 1 if active (not used for nodes) */
 -      unsigned int validkey:1;                                /* 1 if we currently have a valid key for him */
 -      unsigned int unused_waitingforkey:1;            /* 1 if we already sent out a request */
 -      unsigned int visited:1;                         /* 1 if this node has been visited by one of the graph algorithms */
 -      unsigned int reachable:1;                       /* 1 if this node is reachable in the graph */
 -      unsigned int indirect:1;                                /* 1 if this node is not directly reachable by us */
 +      unsigned int unused_active:1;           /* 1 if active (not used for nodes) */
 +      unsigned int validkey:1;                /* 1 if we currently have a valid key for him */
 +      unsigned int unused_waitingforkey:1;    /* 1 if we already sent out a request */
 +      unsigned int visited:1;                 /* 1 if this node has been visited by one of the graph algorithms */
 +      unsigned int reachable:1;               /* 1 if this node is reachable in the graph */
 +      unsigned int indirect:1;                /* 1 if this node is not directly reachable by us */
        unsigned int unused:26;
  } node_status_t;
  
@@@ -48,22 -46,31 +47,22 @@@ typedef struct node_t 
        node_status_t status;
        time_t last_req_key;
  
 -      const EVP_CIPHER *incipher;             /* Cipher type for UDP packets received from him */
 -      char *inkey;                            /* Cipher key and iv */
 -      int inkeylength;                        /* Cipher key and iv length */
 -      EVP_CIPHER_CTX inctx;                   /* Cipher context */
 -      
 -      const EVP_CIPHER *outcipher;            /* Cipher type for UDP packets sent to him*/
 -      char *outkey;                           /* Cipher key and iv */
 -      int outkeylength;                       /* Cipher key and iv length */
 -      EVP_CIPHER_CTX outctx;                  /* Cipher context */
 -      
 -      const EVP_MD *indigest;                 /* Digest type for MAC of packets received from him */
 -      int inmaclength;                        /* Length of MAC */
 -
 -      const EVP_MD *outdigest;                /* Digest type for MAC of packets sent to him*/
 -      int outmaclength;                       /* Length of MAC */
 +      cipher_t incipher;                        /* Cipher for UDP packets */
 +      digest_t indigest;                        /* Digest for UDP packets */  
 +
 +      cipher_t outcipher;                        /* Cipher for UDP packets */
 +      digest_t outdigest;                        /* Digest for UDP packets */ 
  
        int incompression;                      /* Compressionlevel, 0 = no compression */
        int outcompression;                     /* Compressionlevel, 0 = no compression */
  
 +      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) */
  
        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 */
 +
 +      uint64_t in_packets;
 +      uint64_t in_bytes;
 +      uint64_t out_packets;
 +      uint64_t out_bytes;
  } 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);
@@@ -96,8 -98,7 +95,8 @@@ 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 bool dump_nodes(struct connection_t *);
 +extern bool dump_traffic(struct connection_t *);
  extern void update_node_udp(node_t *, const sockaddr_t *);
 -extern void dump_nodes(void);
  
  #endif                                                        /* __TINC_NODE_H__ */
diff --combined src/process.c
  
  #include "conf.h"
  #include "connection.h"
 +#include "control.h"
  #include "device.h"
  #include "edge.h"
  #include "logger.h"
+ #include "net.h"
  #include "node.h"
 -#include "pidfile.h"
  #include "process.h"
  #include "subnet.h"
  #include "utils.h"
  
  /* 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;
  
 -#ifndef HAVE_MINGW
 -static sigset_t emptysigset;
 -#endif
 -
 -static int saved_debug_level = -1;
 -
 -static void memory_full(int size) {
 -      logger(LOG_ERR, "Memory exhausted (couldn't allocate %d bytes), exitting.", size);
 -      exit(1);
 -}
 -
  /* Some functions the less gifted operating systems might lack... */
  
  #ifdef HAVE_MINGW
@@@ -157,11 -171,19 +158,11 @@@ 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) {
@@@ -218,16 -240,85 +219,16 @@@ 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;
 -
 -      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;
 -
 -      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) {
 -      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 */
 +      signal(SIGALRM, SIG_IGN);
 +      signal(SIGPIPE, SIG_IGN);
 +      signal(SIGUSR1, SIG_IGN);
 +      signal(SIGUSR2, SIG_IGN);
 +      signal(SIGWINCH, SIG_IGN);
  
        closelogger();
  #endif
                                        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());
        logger(LOG_NOTICE, "tincd %s (%s %s) starting, debug level %d",
                           VERSION, __DATE__, __TIME__, debug_level);
  
 -      xalloc_fail_func = memory_full;
 -
        return true;
  }
  
@@@ -328,3 -428,161 +329,3 @@@ bool execute_script(const char *name, c
  #endif
        return true;
  }
 -
 -
 -/*
 -  Signal handlers.
 -*/
 -
 -#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) {
 -      logger(LOG_ERR, "Got another fatal signal %d (%s): not restarting.", a,
 -                 strsignal(a));
 -      exit(1);
 -}
 -
 -static RETSIGTYPE fatal_signal_handler(int a) {
 -      struct sigaction act;
 -      logger(LOG_ERR, "Got fatal signal %d (%s)", a, strsignal(a));
 -
 -      if(do_detach) {
 -              logger(LOG_NOTICE, "Trying to re-execute in 5 seconds...");
 -
 -              act.sa_handler = fatal_signal_square;
 -              act.sa_mask = emptysigset;
 -              act.sa_flags = 0;
 -              sigaction(SIGSEGV, &act, NULL);
 -
 -              close_network_connections();
 -              sleep(5);
 -              remove_pid(pidfilename);
 -              execvp(g_argv[0], g_argv);
 -      } else {
 -              logger(LOG_NOTICE, "Not restarting.");
 -              exit(1);
 -      }
 -}
 -
 -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) {
 -      logger(LOG_WARNING, "Got unexpected signal %d (%s)", a, strsignal(a));
 -}
 -
 -static RETSIGTYPE ignore_signal_handler(int a) {
 -      ifdebug(SCARY_THINGS) logger(LOG_DEBUG, "Ignored signal %d (%s)", a, strsignal(a));
 -}
 -
 -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},
 -      {SIGABRT, SIG_DFL},
 -      {0, NULL}
 -};
 -#endif
 -
 -void setup_signals(void) {
 -#ifndef HAVE_MINGW
 -      int i;
 -      struct sigaction act;
 -
 -      sigemptyset(&emptysigset);
 -      act.sa_handler = NULL;
 -      act.sa_mask = emptysigset;
 -      act.sa_flags = 0;
 -
 -      /* Set a default signal handler for every signal, errors will be
 -         ignored. */
 -      for(i = 1; i < NSIG; i++) {
 -              if(!do_detach)
 -                      act.sa_handler = SIG_DFL;
 -              else
 -                      act.sa_handler = unexpected_signal_handler;
 -              sigaction(i, &act, NULL);
 -      }
 -
 -      /* If we didn't detach, allow coredumps */
 -      if(!do_detach)
 -              sighandlers[3].handler = SIG_DFL;
 -
 -      /* Then, for each known signal that we want to catch, assign a
 -         handler to the signal, with error checking this time. */
 -      for(i = 0; sighandlers[i].signal; i++) {
 -              act.sa_handler = sighandlers[i].handler;
 -              if(sigaction(sighandlers[i].signal, &act, NULL) < 0)
 -                      fprintf(stderr, "Installing signal handler for signal %d (%s) failed: %s\n",
 -                                      sighandlers[i].signal, strsignal(sighandlers[i].signal),
 -                                      strerror(errno));
 -      }
 -#endif
 -}
diff --combined src/protocol.c
@@@ -33,13 -33,13 +33,13 @@@ bool strictsubnets = false
  
  /* Jumptable for the request handlers */
  
 -static bool (*request_handlers[])(connection_t *) = {
 +static bool (*request_handlers[])(connection_t *, char *) = {
                id_h, metakey_h, challenge_h, chal_reply_h, ack_h,
                status_h, error_h, termreq_h,
                ping_h, pong_h,
                add_subnet_h, del_subnet_h,
                add_edge_h, del_edge_h,
 -              key_changed_h, req_key_h, ans_key_h, tcppacket_h,
 +              key_changed_h, req_key_h, ans_key_h, tcppacket_h, control_h,
  };
  
  /* Request names */
@@@ -49,10 -49,10 +49,10 @@@ static char (*request_name[]) = 
                "STATUS", "ERROR", "TERMREQ",
                "PING", "PONG",
                "ADD_SUBNET", "DEL_SUBNET",
 -              "ADD_EDGE", "DEL_EDGE", "KEY_CHANGED", "REQ_KEY", "ANS_KEY", "PACKET",
 +              "ADD_EDGE", "DEL_EDGE", "KEY_CHANGED", "REQ_KEY", "ANS_KEY", "PACKET", "CONTROL",
  };
  
 -static avl_tree_t *past_request_tree;
 +static splay_tree_t *past_request_tree;
  
  bool check_id(const char *id) {
        for(; *id; id++)
  
  bool send_request(connection_t *c, const char *format, ...) {
        va_list args;
 -      char buffer[MAXBUFSIZE];
 -      int len, request;
 +      char request[MAXBUFSIZE];
 +      int len;
  
        /* Use vsnprintf instead of vxasprintf: faster, no memory
           fragmentation, cleanup is automatic, and there is a limit on the
           input buffer anyway */
  
        va_start(args, format);
 -      len = vsnprintf(buffer, MAXBUFSIZE, format, args);
 +      len = vsnprintf(request, MAXBUFSIZE, format, args);
        va_end(args);
  
        if(len < 0 || len > MAXBUFSIZE - 1) {
        }
  
        ifdebug(PROTOCOL) {
 -              sscanf(buffer, "%d", &request);
                ifdebug(META)
                        logger(LOG_DEBUG, "Sending %s to %s (%s): %s",
 -                                 request_name[request], c->name, c->hostname, buffer);
 +                                 request_name[atoi(request)], c->name, c->hostname, request);
                else
 -                      logger(LOG_DEBUG, "Sending %s to %s (%s)", request_name[request],
 +                      logger(LOG_DEBUG, "Sending %s to %s (%s)", request_name[atoi(request)],
                                   c->name, c->hostname);
        }
  
 -      buffer[len++] = '\n';
 +      request[len++] = '\n';
  
        if(c == broadcast) {
 -              broadcast_meta(NULL, buffer, len);
 +              broadcast_meta(NULL, request, len);
                return true;
        } else
 -              return send_meta(c, buffer, len);
 +              return send_meta(c, request, len);
  }
  
 -void forward_request(connection_t *from) {
 -      int request;
 -
 +void forward_request(connection_t *from, char *request) {
 +      /* Note: request is not zero terminated anymore after a call to this function! */
        ifdebug(PROTOCOL) {
 -              sscanf(from->buffer, "%d", &request);
                ifdebug(META)
                        logger(LOG_DEBUG, "Forwarding %s from %s (%s): %s",
 -                                 request_name[request], from->name, from->hostname,
 -                                 from->buffer);
 +                                 request_name[atoi(request)], from->name, from->hostname, request);
                else
                        logger(LOG_DEBUG, "Forwarding %s from %s (%s)",
 -                                 request_name[request], from->name, from->hostname);
 +                                 request_name[atoi(request)], from->name, from->hostname);
        }
  
 -      from->buffer[from->reqlen - 1] = '\n';
 -
 -      broadcast_meta(from, from->buffer, from->reqlen);
 +      int len = strlen(request);
 +      request[len++] = '\n';
 +      broadcast_meta(from, request, len);
  }
  
 -bool receive_request(connection_t *c) {
 -      int request;
 +bool receive_request(connection_t *c, char *request) {
 +      int reqno = atoi(request);
  
 -      if(sscanf(c->buffer, "%d", &request) == 1) {
 -              if((request < 0) || (request >= LAST) || !request_handlers[request]) {
 +      if(reqno || *request == '0') {
 +              if((reqno < 0) || (reqno >= LAST) || !request_handlers[reqno]) {
                        ifdebug(META)
                                logger(LOG_DEBUG, "Unknown request from %s (%s): %s",
 -                                         c->name, c->hostname, c->buffer);
 +                                         c->name, c->hostname, request);
                        else
                                logger(LOG_ERR, "Unknown request from %s (%s)",
                                           c->name, c->hostname);
                        ifdebug(PROTOCOL) {
                                ifdebug(META)
                                        logger(LOG_DEBUG, "Got %s from %s (%s): %s",
 -                                                 request_name[request], c->name, c->hostname,
 -                                                 c->buffer);
 +                                                 request_name[reqno], c->name, c->hostname, request);
                                else
                                        logger(LOG_DEBUG, "Got %s from %s (%s)",
 -                                                 request_name[request], c->name, c->hostname);
 +                                                 request_name[reqno], c->name, c->hostname);
                        }
                }
  
 -              if((c->allow_request != ALL) && (c->allow_request != request)) {
 +              if((c->allow_request != ALL) && (c->allow_request != reqno)) {
                        logger(LOG_ERR, "Unauthorized request from %s (%s)", c->name,
                                   c->hostname);
                        return false;
                }
  
 -              if(!request_handlers[request](c)) {
 +              if(!request_handlers[reqno](c, request)) {
                        /* Something went wrong. Probably scriptkiddies. Terminate. */
  
                        logger(LOG_ERR, "Error while processing %s from %s (%s)",
 -                                 request_name[request], c->name, c->hostname);
 +                                 request_name[reqno], c->name, c->hostname);
                        return false;
                }
        } else {
@@@ -175,38 -180,42 +175,38 @@@ static void free_past_request(past_requ
        free(r);
  }
  
 -void init_requests(void) {
 -      past_request_tree = avl_alloc_tree((avl_compare_t) past_request_compare, (avl_action_t) free_past_request);
 -}
 -
 -void exit_requests(void) {
 -      avl_delete_tree(past_request_tree);
 -}
 +static struct event past_request_event;
  
  bool seen_request(char *request) {
        past_request_t *new, p = {NULL};
  
        p.request = request;
  
 -      if(avl_search(past_request_tree, &p)) {
 +      if(splay_search(past_request_tree, &p)) {
                ifdebug(SCARY_THINGS) logger(LOG_DEBUG, "Already seen request");
                return true;
        } else {
 -              new = xmalloc(sizeof(*new));
 +              new = xmalloc(sizeof *new);
                new->request = xstrdup(request);
 -              new->firstseen = now;
 -              avl_insert(past_request_tree, new);
 +              new->firstseen = time(NULL);
 +              splay_insert(past_request_tree, new);
 +              event_add(&past_request_event, &(struct timeval){10, 0});
                return false;
        }
  }
  
 -void age_past_requests(void) {
 -      avl_node_t *node, *next;
 +static void age_past_requests(int fd, short events, void *data) {
 +      splay_node_t *node, *next;
        past_request_t *p;
        int left = 0, deleted = 0;
 +      time_t now = time(NULL);
  
        for(node = past_request_tree->head; node; node = next) {
                next = node->next;
                p = node->data;
  
-               if(p->firstseen + pinginterval < now)
+               if(p->firstseen + pinginterval <= now)
 -                      avl_delete_node(past_request_tree, node), deleted++;
 +                      splay_delete_node(past_request_tree, node), deleted++;
                else
                        left++;
        }
        if(left || deleted)
                ifdebug(SCARY_THINGS) logger(LOG_DEBUG, "Aging past requests: deleted %d, left %d",
                           deleted, left);
 +
 +      if(left)
 +              event_add(&past_request_event, &(struct timeval){10, 0});
 +}
 +
 +void init_requests(void) {
 +      past_request_tree = splay_alloc_tree((splay_compare_t) past_request_compare, (splay_action_t) free_past_request);
 +
 +      timeout_set(&past_request_event, age_past_requests, NULL);
 +}
 +
 +void exit_requests(void) {
 +      splay_delete_tree(past_request_tree);
 +
 +      if(timeout_initialized(&past_request_event))
 +              event_del(&past_request_event);
  }
diff --combined src/protocol.h
@@@ -44,7 -44,6 +44,7 @@@ typedef enum request_t 
        ADD_EDGE, DEL_EDGE,
        KEY_CHANGED, REQ_KEY, ANS_KEY,
        PACKET,
 +      CONTROL,
        LAST                                            /* Guardian for the highest request number */
  } request_t;
  
@@@ -72,13 -71,14 +72,13 @@@ extern bool strictsubnets
  /* Basic functions */
  
  extern bool send_request(struct connection_t *, const char *, ...) __attribute__ ((__format__(printf, 2, 3)));
 -extern void forward_request(struct connection_t *);
 -extern bool receive_request(struct connection_t *);
 +extern void forward_request(struct connection_t *, char *);
 +extern bool receive_request(struct connection_t *, char *);
  extern bool check_id(const char *);
  
  extern void init_requests(void);
  extern void exit_requests(void);
  extern bool seen_request(char *);
 -extern void age_past_requests(void);
  
  /* Requests */
  
@@@ -96,31 -96,30 +96,31 @@@ extern bool send_add_subnet(struct conn
  extern bool send_del_subnet(struct connection_t *, const struct subnet_t *);
  extern bool send_add_edge(struct connection_t *, const struct edge_t *);
  extern bool send_del_edge(struct connection_t *, const struct edge_t *);
- extern void send_key_changed();
+ extern void send_key_changed(void);
  extern bool send_req_key(struct node_t *);
  extern bool send_ans_key(struct node_t *);
- extern bool send_tcppacket(struct connection_t *, struct vpn_packet_t *);
+ extern bool send_tcppacket(struct connection_t *, const struct vpn_packet_t *);
  
  /* Request handlers  */
  
 -extern bool id_h(struct connection_t *);
 -extern bool metakey_h(struct connection_t *);
 -extern bool challenge_h(struct connection_t *);
 -extern bool chal_reply_h(struct connection_t *);
 -extern bool ack_h(struct connection_t *);
 -extern bool status_h(struct connection_t *);
 -extern bool error_h(struct connection_t *);
 -extern bool termreq_h(struct connection_t *);
 -extern bool ping_h(struct connection_t *);
 -extern bool pong_h(struct connection_t *);
 -extern bool add_subnet_h(struct connection_t *);
 -extern bool del_subnet_h(struct connection_t *);
 -extern bool add_edge_h(struct connection_t *);
 -extern bool del_edge_h(struct connection_t *);
 -extern bool key_changed_h(struct connection_t *);
 -extern bool req_key_h(struct connection_t *);
 -extern bool ans_key_h(struct connection_t *);
 -extern bool tcppacket_h(struct connection_t *);
 +extern bool id_h(struct connection_t *, char *);
 +extern bool metakey_h(struct connection_t *, char *);
 +extern bool challenge_h(struct connection_t *, char *);
 +extern bool chal_reply_h(struct connection_t *, char *);
 +extern bool ack_h(struct connection_t *, char *);
 +extern bool status_h(struct connection_t *, char *);
 +extern bool error_h(struct connection_t *, char *);
 +extern bool termreq_h(struct connection_t *, char *);
 +extern bool ping_h(struct connection_t *, char *);
 +extern bool pong_h(struct connection_t *, char *);
 +extern bool add_subnet_h(struct connection_t *, char *);
 +extern bool del_subnet_h(struct connection_t *, char *);
 +extern bool add_edge_h(struct connection_t *, char *);
 +extern bool del_edge_h(struct connection_t *, char *);
 +extern bool key_changed_h(struct connection_t *, char *);
 +extern bool req_key_h(struct connection_t *, char *);
 +extern bool ans_key_h(struct connection_t *, char *);
 +extern bool tcppacket_h(struct connection_t *, char *);
 +extern bool control_h(struct connection_t *, char *);
  
  #endif                                                        /* __TINC_PROTOCOL_H__ */
diff --combined src/protocol_misc.c
@@@ -40,11 -40,11 +40,11 @@@ 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];
  
 -      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;
@@@ -63,11 -63,11 +63,11 @@@ 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];
  
 -      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;
        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) {
        return send_request(c, "%d", TERMREQ);
  }
  
 -bool termreq_h(connection_t *c) {
 -      terminate_connection(c, c->status.active);
 -
 -      return true;
 +bool termreq_h(connection_t *c, char *request) {
 +      return false;
  }
  
  bool send_ping(connection_t *c) {
        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) {
        return send_pong(c);
  }
  
@@@ -102,7 -106,7 +102,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) {
        c->status.pinged = false;
  
        /* Succesful connection, reset timeout if this is an outgoing connection. */
  
  /* Sending and receiving packets via TCP */
  
- bool send_tcppacket(connection_t *c, vpn_packet_t *packet) {
+ bool send_tcppacket(connection_t *c, const vpn_packet_t *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(2.0 * c->outbuflen / (float)maxoutbufsize - 1 > (float)rand()/(float)RAND_MAX)
 +      if(2.0 * c->outbuf.len / (float)maxoutbufsize - 1 > (float)rand()/(float)RAND_MAX)
                return true;
  
        if(!send_request(c, "%d %hd", PACKET, packet->len))
        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;
  
 -      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/raw_socket/device.c
@@@ -23,6 -23,7 +23,7 @@@
  #include <netpacket/packet.h>
  
  #include "conf.h"
+ #include "device.h"
  #include "net.h"
  #include "logger.h"
  #include "utils.h"
@@@ -56,7 -57,7 +57,7 @@@ bool setup_device(void) 
                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);
                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;
        }
@@@ -88,15 -89,15 +89,15 @@@ void close_device(void) 
  }
  
  bool read_packet(vpn_packet_t *packet) {
 -      int lenin;
 +      int inlen;
  
 -      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;
  
diff --combined src/solaris/device.c
@@@ -26,6 -26,7 +26,7 @@@
  #include <net/if_tun.h>
  
  #include "conf.h"
+ #include "device.h"
  #include "logger.h"
  #include "net.h"
  #include "utils.h"
@@@ -114,9 -115,9 +115,9 @@@ void close_device(void) 
  }
  
  bool read_packet(vpn_packet_t *packet) {
 -      int lenin;
 +      int inlen;
  
 -      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;
                        return false;
        }
  
 -      packet->len = lenin + 14;
 +      packet->len = inlen + 14;
  
        device_total_in += packet->len;
  
diff --combined src/tincd.c
  #endif
  
  #include <getopt.h>
 -#include "pidfile.h"
  
  #include "conf.h"
 +#include "control.h"
 +#include "crypto.h"
  #include "device.h"
  #include "logger.h"
  #include "net.h"
  char *program_name = NULL;
  
  /* If nonzero, display usage information and exit. */
 -bool show_help = false;
 +static 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;
 +static bool show_version = false;
  
  /* If nonzero, use null ciphers and skip all key exchanges. */
  bool bypass_security = false;
  
  /* If nonzero, disable swapping for this process. */
 -bool do_mlock = false;
 +static bool do_mlock = false;
  
  /* If nonzero, chroot to netdir after startup. */
  static bool do_chroot = false;
@@@ -88,25 -93,27 +88,25 @@@ static const char *switchuser = NULL
  bool use_logfile = false;
  
  char *identname = NULL;                               /* program name for syslog */
 -char *pidfilename = NULL;                     /* pid file location */
  char *logfilename = NULL;                     /* log file location */
 +char *controlcookiename = NULL;
  char **g_argv;                                        /* a copy of the cmdline arguments */
  
  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'},
        {"chroot", no_argument, NULL, 'R'},
        {"user", required_argument, NULL, 'U'},
        {"logfile", optional_argument, NULL, 4},
 -      {"pidfile", required_argument, NULL, 5},
 +      {"controlcookie", required_argument, NULL, 5},
        {NULL, 0, NULL, 0}
  };
  
@@@ -122,18 -129,20 +122,18 @@@ 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"
 -                              "  -o [HOST.]KEY=VALUE        Set global/host configuration value.\n"
 -                              "  -R, --chroot               chroot to NET dir at startup.\n"
 -                              "  -U, --user=USER            setuid to given USER at startup.\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"
 +                              "      --controlcookie=FILENAME  Write control socket cookie to FILENAME.\n"
 +                              "      --bypass-security         Disables meta protocol security, for debugging.\n"
 +                              "  -o [HOST.]KEY=VALUE           Set global/host configuration value.\n"
 +                              "  -R, --chroot                  chroot to NET dir at startup.\n"
 +                              "  -U, --user=USER               setuid to given USER at startup.\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");
        }
  }
@@@ -146,7 -155,7 +146,7 @@@ static bool parse_options(int argc, cha
  
        cmdline_conf = list_alloc((list_action_t)free_config);
  
 -      while((r = getopt_long(argc, argv, "c:DLd::k::n:o:K::RU:", long_options, &option_index)) != EOF) {
 +      while((r = getopt_long(argc, argv, "c:DLd::n:o:RU:", long_options, &option_index)) != EOF) {
                switch (r) {
                        case 0:                         /* long option */
                                break;
                                        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 if(!strcasecmp(optarg, "ABRT"))
 -                                              kill_tincd = SIGABRT;
 -                                      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 "." is special: a "top-level name" */
                                netname = strcmp(optarg, ".") != 0 ?
                                list_insert_tail(cmdline_conf, cfg);
                                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 = 2048;
 -                              break;
 -
                        case 'R':                               /* chroot to NETNAME dir */
                                do_chroot = true;
                                break;
                                        logfilename = xstrdup(optarg);
                                break;
  
 -                      case 5:                                 /* write PID to a file */
 -                              pidfilename = xstrdup(optarg);
 +                      case 5:                                 /* open control socket here */
 +                              controlcookiename = xstrdup(optarg);
                                break;
  
                        case '?':
        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");
 -
 -      xasprintf(&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
 -              
 -      fputc('\n', f);
 -      PEM_write_RSAPrivateKey(f, rsa_key, NULL, NULL, 0, NULL, NULL);
 -      fclose(f);
 -      free(filename);
 -
 -      if(name)
 -              xasprintf(&filename, "%s/hosts/%s", confbase, name);
 -      else
 -              xasprintf(&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");
 -
 -      fputc('\n', f);
 -      PEM_write_RSAPublicKey(f, rsa_key);
 -      fclose(f);
 -      free(filename);
 -      if(name)
 -              free(name);
 -
 -      return true;
 -}
 -
  /*
    Set all files and paths according to netname
  */
@@@ -237,7 -404,7 +237,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)
                                else
                                        xasprintf(&confbase, "%s", installdir);
                        }
 +                      if(!controlcookiename)
 +                              xasprintf(&controlcookiename, "%s/cookie", confbase);
                }
                RegCloseKey(key);
                if(*installdir)
        }
  #endif
  
 -      if(!pidfilename)
 -              xasprintf(&pidfilename, LOCALSTATEDIR "/run/%s.pid", identname);
 -
        if(!logfilename)
                xasprintf(&logfilename, LOCALSTATEDIR "/log/%s.log", identname);
  
 +      if(!controlcookiename)
 +              xasprintf(&controlcookiename, LOCALSTATEDIR "/run/%s.cookie", identname);
 +
        if(netname) {
                if(!confbase)
                        xasprintf(&confbase, CONFDIR "/tinc/%s", netname);
        }
  }
  
 -static void free_names() {
 +static void free_names(void) {
        if (identname) free(identname);
        if (netname) free(netname);
 -      if (pidfilename) free(pidfilename);
 +      if (controlcookiename) free(controlcookiename);
        if (logfilename) free(logfilename);
        if (confbase) free(confbase);
  }
  
 -static bool drop_privs() {
 +static bool drop_privs(void) {
  #ifdef HAVE_MINGW
        if (switchuser) {
                logger(LOG_ERR, "%s not supported on this platform", "-U");
  }
  
  #ifdef HAVE_MINGW
- # define setpriority(level) SetPriorityClass(GetCurrentProcess(), (level))
+ # define setpriority(level) !SetPriorityClass(GetCurrentProcess(), (level))
  #else
  # define NORMAL_PRIORITY_CLASS 0
  # define BELOW_NORMAL_PRIORITY_CLASS 10
@@@ -372,28 -537,28 +372,28 @@@ int main(int argc, char **argv) 
                return 0;
        }
  
 -      if(kill_tincd)
 -              return !kill_other(kill_tincd);
 +#ifdef HAVE_MINGW
 +      if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
 +              logger(LOG_ERR, "System call `%s' failed: %s", "WSAStartup", winerror(GetLastError()));
 +              return 1;
 +      }
 +#endif
  
        openlogger("tinc", use_logfile?LOGMODE_FILE:LOGMODE_STDERR);
  
 +      if(!event_init()) {
 +              logger(LOG_ERR, "Error initializing libevent!");
 +              return 1;
 +      }
 +
        g_argv = argv;
  
        init_configuration(&config_tree);
  
        /* 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;
  #endif
  
  #ifdef HAVE_MINGW
 -      if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
 -              logger(LOG_ERR, "System call `%s' failed: %s", "WSAStartup", winerror(GetLastError()));
 -              return 1;
 -      }
 -
        if(!do_detach || !init_service())
                return main2(argc, argv);
        else
@@@ -436,16 -606,13 +436,16 @@@ int main2(int argc, char **argv) 
        if(!setup_network())
                goto end;
  
 +      if(!init_control())
 +              return 1;
 +
        /* Initiate all outgoing connections. */
  
        try_outgoing_connections();
  
        /* Change process priority */
  
 -        char *priority = 0;
 +        char *priority = NULL;
  
          if(get_config_string(lookup_config(config_tree, "ProcessPriority"), &priority)) {
                  if(!strcasecmp(priority, "Normal")) {
  end:
        logger(LOG_NOTICE, "Terminating");
  
 -#ifndef HAVE_MINGW
 -      remove_pid(pidfilename);
 -#endif
 +      exit_control();
  
 -      EVP_cleanup();
 -      ENGINE_cleanup();
 -      CRYPTO_cleanup_all_ex_data();
 -      ERR_remove_state(0);
 -      ERR_free_strings();
 +      crypto_exit();
  
        exit_configuration(&config_tree);
        free_names();
diff --combined src/uml_socket/device.c
@@@ -23,6 -23,7 +23,7 @@@
  #include <sys/un.h>
  
  #include "conf.h"
+ #include "device.h"
  #include "net.h"
  #include "logger.h"
  #include "utils.h"
@@@ -169,7 -170,7 +170,7 @@@ void close_device(void) 
  }
  
  bool read_packet(vpn_packet_t *packet) {
 -      int lenin;
 +      int inlen;
  
        switch(state) {
                case 0: {
                }
  
                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;
                }
  
                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;
  
diff --combined src/utils.c
@@@ -67,7 -67,7 +67,7 @@@ const char *winerror(int err) 
  }
  #endif
  
- unsigned int bitfield_to_int(void *bitfield, size_t size) {
+ unsigned int bitfield_to_int(const void *bitfield, size_t size) {
        unsigned int value = 0;
        if(size > sizeof value)
                size = sizeof value;
diff --combined src/utils.h
@@@ -32,16 -32,14 +32,16 @@@ extern const char *winerror(int)
  #define sockwouldblock(x) ((x) == WSAEWOULDBLOCK || (x) == WSAEINTR)
  #define sockmsgsize(x) ((x) == WSAEMSGSIZE)
  #define sockinprogress(x) ((x) == WSAEINPROGRESS || (x) == WSAEWOULDBLOCK)
 +#define sockinuse(x) ((x) == WSAEADDRINUSE)
  #else
  #define sockerrno errno
  #define sockstrerror(x) strerror(x)
  #define sockwouldblock(x) ((x) == EWOULDBLOCK || (x) == EINTR)
  #define sockmsgsize(x) ((x) == EMSGSIZE)
  #define sockinprogress(x) ((x) == EINPROGRESS)
 +#define sockinuse(x) ((x) == EADDRINUSE)
  #endif
  
- extern unsigned int bitfield_to_int(void *bitfield, size_t size);
+ extern unsigned int bitfield_to_int(const void *bitfield, size_t size);
  
  #endif                                                        /* __TINC_UTILS_H__ */