Merge branch 'master' of git://tinc-vpn.org/tinc into 1.1
authorGuus Sliepen <guus@tinc-vpn.org>
Sun, 25 Mar 2012 22:35:31 +0000 (23:35 +0100)
committerGuus Sliepen <guus@tinc-vpn.org>
Sun, 25 Mar 2012 22:35:31 +0000 (23:35 +0100)
Conflicts:
NEWS
README
configure.in
src/Makefile.am
src/conf.c
src/conf.h
src/connection.c
src/net.c
src/tincd.c

29 files changed:
1  2 
NEWS
README
doc/tinc.conf.5.in
doc/tinc.texi
src/Makefile.am
src/conf.c
src/conf.h
src/connection.c
src/connection.h
src/device.h
src/graph.c
src/ipv4.h
src/ipv6.h
src/multicast_device.c
src/net.c
src/net.h
src/net_packet.c
src/net_setup.c
src/node.h
src/protocol.c
src/protocol_auth.c
src/protocol_edge.c
src/protocol_key.c
src/protocol_misc.c
src/route.c
src/route.h
src/subnet.c
src/tincd.c
src/vde_device.c

diff --cc NEWS
--- 1/NEWS
--- 2/NEWS
+++ b/NEWS
@@@ -1,33 -1,29 +1,59 @@@
 +Version 1.1pre2              Juli 17 2011
 +
 + * .cookie files are renamed to .pid files, which are compatible with 1.0.x.
 +
 + * Experimental protocol enhancements that can be enabled with the option
 +   ExperimentalProtocol = yes:
 +
 +   * Ephemeral ECDH key exchange will be used for both the meta protocol and
 +     UDP session keys.
 +   * Key exchanges are signed with ECDSA.
 +   * ECDSA public keys are automatically exchanged after RSA authentication if
 +     nodes do not know each other's ECDSA public key yet.
 +
 +Version 1.1pre1              June 25 2011
 +
 + * Control interface allows control of a running tinc daemon. Used by:
 +   * tincctl, a commandline utility
 +   * tinc-gui, a preliminary GUI implemented in Python/wxWidgets
 +
 + * Code cleanups and reorganization. 
 +
 + * Repleacable cryptography backend, currently supports OpenSSL and libgcrypt.
 +
 + * Use libevent to handle I/O events and timeouts.
 +
 + * Use splay trees instead of AVL trees to manage internal datastructures.
 +
 + Thanks to Scott Lamb and Sven-Haegar Koch for their contributions to this
 + version of tinc.
 +
+ Version 1.0.18               March 25 2012
+  * Fixed IPv6 in switch mode by turning off DecrementTTL by default.
+  * Allow a port number to be specified in BindToAddress, which also allows tinc
+    to listen on multiple ports.
+  * Add support for multicast communication with UML/QEMU/KVM.
+ Version 1.0.17               March 10 2012
+  * The DeviceType option can now be used to select dummy, raw socket, UML and
+    VDE devices without needing to recompile tinc.
+  * Allow multiple BindToAddress statements.
+  * Decrement TTL value of IPv4 and IPv6 packets.
+  * Add LocalDiscovery option allowing tinc to detect peers that are behind the
+    same NAT.
+  * Accept Subnets passed with the -o option when StrictSubnets = yes.
+  * Disabling old RSA keys when generating new ones now also works properly on
+    Windows.
  Version 1.0.16               July 23 2011
  
   * Fixed a performance issue with TCP communication under Windows.
diff --cc README
--- 1/README
--- 2/README
+++ b/README
@@@ -1,7 -1,7 +1,7 @@@
 -This is the README file for tinc version 1.0.18. Installation
 +This is the README file for tinc version 1.1pre2. Installation
  instructions may be found in the INSTALL file.
  
- tinc is Copyright (C) 1998-2011 by:
+ tinc is Copyright (C) 1998-2012 by:
  
  Ivo Timmermans,
  Guus Sliepen <guus@tinc-vpn.org>,
Simple merge
diff --cc doc/tinc.texi
Simple merge
diff --cc src/Makefile.am
@@@ -1,15 -1,13 +1,15 @@@
  ## Produce this file with automake to get Makefile.in
  
 -sbin_PROGRAMS = tincd
 +sbin_PROGRAMS = tincd tincctl sptps_test
  
 -EXTRA_DIST = linux/device.c bsd/device.c solaris/device.c cygwin/device.c mingw/device.c mingw/common.h
 +EXTRA_DIST = linux bsd solaris cygwin mingw openssl gcrypt
  
 -tincd_SOURCES = conf.c connection.c edge.c event.c graph.c logger.c meta.c net.c net_packet.c net_setup.c     \
 -      net_socket.c netutl.c node.c process.c protocol.c protocol_auth.c protocol_edge.c protocol_misc.c       \
 -      protocol_key.c protocol_subnet.c route.c subnet.c tincd.c \
 +tincd_SOURCES = \
 +      utils.c getopt.c getopt1.c list.c splay_tree.c dropin.c fake-getaddrinfo.c fake-getnameinfo.c \
 +      buffer.c conf.c connection.c control.c edge.c graph.c logger.c meta.c net.c net_packet.c net_setup.c \
 +      net_socket.c netutl.c node.c process.c protocol.c protocol_auth.c protocol_edge.c protocol_misc.c \
 +      protocol_key.c protocol_subnet.c route.c sptps.c subnet.c tincd.c \
-       dummy_device.c raw_socket_device.c
+       dummy_device.c raw_socket_device.c multicast_device.c
        
  if UML
  tincd_SOURCES += uml_device.c
diff --cc src/conf.c
@@@ -400,60 -400,130 +400,21 @@@ bool read_connection_config(connection_
        return x;
  }
  
 -static void disable_old_keys(const char *filename) {
 -      char tmpfile[PATH_MAX] = "";
 -      char buf[1024];
 -      bool disabled = false;
 -      FILE *r, *w;
 -
 -      r = fopen(filename, "r");
 -      if(!r)
 -              return;
 -
 -      snprintf(tmpfile, sizeof tmpfile, "%s.tmp", filename);
 -
 -      w = fopen(tmpfile, "w");
 -
 -      while(fgets(buf, sizeof buf, r)) {
 -              if(!strncmp(buf, "-----BEGIN RSA", 14)) {       
 -                      buf[11] = 'O';
 -                      buf[12] = 'L';
 -                      buf[13] = 'D';
 -                      disabled = true;
 -              }
 -              else if(!strncmp(buf, "-----END RSA", 12)) {    
 -                      buf[ 9] = 'O';
 -                      buf[10] = 'L';
 -                      buf[11] = 'D';
 -                      disabled = true;
 -              }
 -              if(w && fputs(buf, w) < 0) {
 -                      disabled = false;
 -                      break;
 -              }
 -      }
 -
 -      if(w)
 -              fclose(w);
 -      fclose(r);
 -
 -      if(!w && disabled) {
 -              fprintf(stderr, "Warning: old key(s) found, remove them by hand!\n");
 -              return;
 -      }
 -
 -      if(disabled) {
 -#ifdef HAVE_MINGW
 -              // We cannot atomically replace files on Windows.
 -              char bakfile[PATH_MAX] = "";
 -              snprintf(bakfile, sizeof bakfile, "%s.bak", filename);
 -              if(rename(filename, bakfile) || rename(tmpfile, filename)) {
 -                      rename(bakfile, filename);
 -#else
 -              if(rename(tmpfile, filename)) {
 -#endif
 -                      fprintf(stderr, "Warning: old key(s) found, remove them by hand!\n");
 -              } else  {
 -#ifdef HAVE_MINGW
 -                      unlink(bakfile);
 -#endif
 -                      fprintf(stderr, "Warning: old key(s) found and disabled.\n");
 -              }
 -      }
 +bool append_config_file(const char *name, const char *key, const char *value) {
 +      char *fname;
 +      xasprintf(&fname, "%s/hosts/%s", confbase, name);
  
 -      unlink(tmpfile);
 -}
 +      FILE *fp = fopen(fname, "a");
  
 -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;
 +      if(!fp) {
 +              logger(DEBUG_ALWAYS, LOG_ERR, "Cannot open config file %s: %s", fname, strerror(errno));
        } 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;
 +              fprintf(fp, "\n# The following line was automatically added by tinc\n%s = %s\n", key, value);
 +              fclose(fp);
        }
  
 -      umask(0077);                            /* Disallow everything for group and other */
 -      disable_old_keys(fn);
 -
 -      /* Open it first to keep the inode busy */
 -
 -      r = fopen(fn, "a");
 -
 -      if(!r) {
 -              fprintf(stderr, "Error opening file `%s': %s\n",
 -                              fn, strerror(errno));
 -              return NULL;
 -      }
 +      free(fname);
  
 -      return r;
 +      return fp;
  }
- bool disable_old_keys(FILE *f) {
-       char buf[100];
-       long pos;
-       bool disabled = false;
-       rewind(f);
-       pos = ftell(f);
-       if(pos < 0)
-               return false;
--
-       while(fgets(buf, sizeof buf, f)) {
-               if(!strncmp(buf, "-----BEGIN RSA", 14)) {       
-                       buf[11] = 'O';
-                       buf[12] = 'L';
-                       buf[13] = 'D';
-                       if(fseek(f, pos, SEEK_SET))
-                               break;
-                       if(fputs(buf, f) <= 0)
-                               break;
-                       disabled = true;
-               }
-               else if(!strncmp(buf, "-----END RSA", 12)) {    
-                       buf[ 9] = 'O';
-                       buf[10] = 'L';
-                       buf[11] = 'D';
-                       if(fseek(f, pos, SEEK_SET))
-                               break;
-                       if(fputs(buf, f) <= 0)
-                               break;
-                       disabled = true;
-               }
-               pos = ftell(f);
-               if(pos < 0)
-                       break;
-       }
--
-       return disabled;
- }
diff --cc src/conf.h
@@@ -57,11 -57,11 +57,10 @@@ extern bool get_config_address(const co
  extern bool get_config_subnet(const config_t *, struct subnet_t **);
  
  extern config_t *parse_config_line(char *, const char *, int);
 -extern bool read_config_file(avl_tree_t *, const char *);
 -extern void read_config_options(avl_tree_t *, const char *);
 +extern bool read_config_file(splay_tree_t *, const char *);
 +extern void read_config_options(splay_tree_t *, const char *);
  extern bool read_server_config(void);
  extern bool read_connection_config(struct connection_t *);
 -extern FILE *ask_and_open(const char *, const char *);
 -extern bool is_safe_path(const char *);
 +extern bool append_config_file(const char *, const char *, const char *);
- extern bool disable_old_keys(FILE *);
  
  #endif                                                        /* __TINC_CONF_H__ */
@@@ -51,46 -48,66 +51,49 @@@ void exit_connections(void) 
  }
  
  connection_t *new_connection(void) {
 -      connection_t *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);
 -      c = xmalloc_and_zero(sizeof(connection_t));
++void free_connection_partially(connection_t *c) {
 +      cipher_close(&c->incipher);
 +      digest_close(&c->indigest);
 +      cipher_close(&c->outcipher);
 +      digest_close(&c->outdigest);
  
 -      if(!c)
 -              return NULL;
 +      sptps_stop(&c->sptps);
 +      ecdsa_free(&c->ecdsa);
 +      rsa_free(&c->rsa);
  
 -      gettimeofday(&c->start, NULL);
 +      if(c->hischallenge)
 +              free(c->hischallenge);
  
-       if(c->config_tree)
-               exit_configuration(&c->config_tree);
 -      return c;
 -}
 +      buffer_clear(&c->inbuf);
 +      buffer_clear(&c->outbuf);
 +      
 +      if(event_initialized(&c->inevent))
 +              event_del(&c->inevent);
  
 -void free_connection_partially(connection_t *c) {
 -      free(c->inkey);
 -      free(c->outkey);
 -      free(c->mychallenge);
 -      free(c->hischallenge);
 -      free(c->outbuf);
 -
 -      c->inkey = NULL;
 -      c->outkey = NULL;
 -      c->mychallenge = NULL;
 -      c->hischallenge = NULL;
 -      c->outbuf = NULL;
 -
 -      c->buflen = 0;
 -      c->reqlen = 0;
 -      c->tcplen = 0;
 -      c->allow_request = 0;
 -      c->outbuflen = 0;
 -      c->outbufsize = 0;
 -      c->outbufstart = 0;
 -
 -      if(c->inctx) {
 -              EVP_CIPHER_CTX_cleanup(c->inctx);
 -              free(c->inctx);
 -              c->inctx = NULL;
 -      }
 +      if(event_initialized(&c->outevent))
 +              event_del(&c->outevent);
  
 -      if(c->outctx) {
 -              EVP_CIPHER_CTX_cleanup(c->outctx);
 -              free(c->outctx);
 -              c->outctx = NULL;
 -      }
 +      if(c->socket > 0)
 +              closesocket(c->socket);
  
 -      if(c->rsa_key) {
 -              RSA_free(c->rsa_key);
 -              c->rsa_key = NULL;
 -      }
++      c->socket = -1;
+ }
+ void free_connection(connection_t *c) {
++      if(!c)
++              return;
++
+       free_connection_partially(c);
+       free(c->name);
+       free(c->hostname);
+       if(c->config_tree)
+               exit_configuration(&c->config_tree);
        free(c);
  }
  
@@@ -107,8 -107,9 +107,9 @@@ extern void init_connections(void)
  extern void exit_connections(void);
  extern connection_t *new_connection(void) __attribute__ ((__malloc__));
  extern void free_connection(connection_t *);
+ extern void free_connection_partially(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 --cc src/device.h
Simple merge
diff --cc src/graph.c
Simple merge
diff --cc src/ipv4.h
Simple merge
diff --cc src/ipv6.h
Simple merge
index 0000000,0b232db..e5e9a3f
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,228 +1,228 @@@
 -              logger(LOG_ERR, "Device variable required for %s", device_info);
+ /*
+     device.c -- multicast socket
+     Copyright (C) 2002-2005 Ivo Timmermans,
+                   2002-2012 Guus Sliepen <guus@tinc-vpn.org>
+     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
+     the Free Software Foundation; either version 2 of the License, or
+     (at your option) any later version.
+     This program is distributed in the hope that it will be useful,
+     but WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+     GNU General Public License for more details.
+     You should have received a copy of the GNU General Public License along
+     with this program; if not, write to the Free Software Foundation, Inc.,
+     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+ #include "system.h"
+ #include "conf.h"
+ #include "device.h"
+ #include "net.h"
+ #include "logger.h"
+ #include "netutl.h"
+ #include "utils.h"
+ #include "route.h"
+ #include "xalloc.h"
+ static char *device_info;
+ static uint64_t device_total_in = 0;
+ static uint64_t device_total_out = 0;
+ static struct addrinfo *ai = NULL;
+ static mac_t ignore_src = {{0}};
+ static bool setup_device(void) {
+       char *host;
+       char *port;
+       char *space;
+       int ttl = 1;
+       device_info = "multicast socket";
+       get_config_string(lookup_config(config_tree, "Interface"), &iface);
+       if(!get_config_string(lookup_config(config_tree, "Device"), &device)) {
 -              logger(LOG_ERR, "Port number required for %s", device_info);
++              logger(DEBUG_ALWAYS, LOG_ERR, "Device variable required for %s", device_info);
+               return false;
+       }
+       host = xstrdup(device);
+       space = strchr(host, ' ');
+       if(!space) {
 -              logger(LOG_ERR, "Creating socket failed: %s", sockstrerror(sockerrno));
++              logger(DEBUG_ALWAYS, LOG_ERR, "Port number required for %s", device_info);
+               return false;
+       }
+       *space++ = 0;
+       port = space;
+       space = strchr(port, ' ');
+       if(space) {
+               *space++ = 0;
+               ttl = atoi(space);
+       }
+       ai = str2addrinfo(host, port, SOCK_DGRAM);
+       if(!ai)
+               return false;
+       device_fd = socket(ai->ai_family, SOCK_DGRAM, IPPROTO_UDP);
+       if(device_fd < 0) {
 -              logger(LOG_ERR, "Can't bind to %s %s: %s", host, port, sockstrerror(sockerrno));
++              logger(DEBUG_ALWAYS, LOG_ERR, "Creating socket failed: %s", sockstrerror(sockerrno));
+               return false;
+       }
+ #ifdef FD_CLOEXEC
+       fcntl(device_fd, F_SETFD, FD_CLOEXEC);
+ #endif
+       static const int one = 1;
+       setsockopt(device_fd, SOL_SOCKET, SO_REUSEADDR, (void *)&one, sizeof one);
+       if(bind(device_fd, ai->ai_addr, ai->ai_addrlen)) {
+               closesocket(device_fd);
 -                              logger(LOG_ERR, "Cannot join multicast group %s %s: %s", host, port, sockstrerror(sockerrno));
++              logger(DEBUG_ALWAYS, LOG_ERR, "Can't bind to %s %s: %s", host, port, sockstrerror(sockerrno));
+               return false;
+       }
+       switch(ai->ai_family) {
+ #ifdef IP_ADD_MEMBERSHIP
+               case AF_INET: {
+                       struct ip_mreq mreq;
+                       struct sockaddr_in in;
+                       memcpy(&in, ai->ai_addr, sizeof in);
+                       mreq.imr_multiaddr.s_addr = in.sin_addr.s_addr;
+                       mreq.imr_interface.s_addr = htonl(INADDR_ANY);
+                       if(setsockopt(device_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void *)&mreq, sizeof mreq)) {
 -                              logger(LOG_ERR, "Cannot join multicast group %s %s: %s", host, port, sockstrerror(sockerrno));
++                              logger(DEBUG_ALWAYS, LOG_ERR, "Cannot join multicast group %s %s: %s", host, port, sockstrerror(sockerrno));
+                               closesocket(device_fd);
+                               return false;
+                       }
+ #ifdef IP_MULTICAST_LOOP
+                       setsockopt(device_fd, IPPROTO_IP, IP_MULTICAST_LOOP, (const void *)&one, sizeof one);
+ #endif
+ #ifdef IP_MULTICAST_TTL
+                       setsockopt(device_fd, IPPROTO_IP, IP_MULTICAST_TTL, (void *)&ttl, sizeof ttl);
+ #endif
+               } break;
+ #endif
+ #ifdef IPV6_JOIN_GROUP
+               case AF_INET6: {
+                       struct ipv6_mreq mreq;
+                       struct sockaddr_in6 in6;
+                       memcpy(&in6, ai->ai_addr, sizeof in6);
+                       memcpy(&mreq.ipv6mr_multiaddr, &in6.sin6_addr, sizeof mreq.ipv6mr_multiaddr);
+                       mreq.ipv6mr_interface = in6.sin6_scope_id;
+                       if(setsockopt(device_fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, (void *)&mreq, sizeof mreq)) {
 -                      logger(LOG_ERR, "Multicast for address family %hx unsupported", ai->ai_family);
++                              logger(DEBUG_ALWAYS, LOG_ERR, "Cannot join multicast group %s %s: %s", host, port, sockstrerror(sockerrno));
+                               closesocket(device_fd);
+                               return false;
+                       }
+ #ifdef IPV6_MULTICAST_LOOP
+                       setsockopt(device_fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (const void *)&one, sizeof one);
+ #endif
+ #ifdef IPV6_MULTICAST_HOPS
+                       setsockopt(device_fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (void *)&ttl, sizeof ttl);
+ #endif
+               } break;
+ #endif
+       
+               default:
 -      logger(LOG_INFO, "%s is a %s", device, device_info);
++                      logger(DEBUG_ALWAYS, LOG_ERR, "Multicast for address family %hx unsupported", ai->ai_family);
+                       closesocket(device_fd);
+                       return false;
+       }
 -              logger(LOG_ERR, "Error while reading from %s %s: %s", device_info,
++      logger(DEBUG_ALWAYS, LOG_INFO, "%s is a %s", device, device_info);
+       return true;
+ }
+ static void close_device(void) {
+       close(device_fd);
+       free(device);
+       free(iface);
+       if(ai)
+               freeaddrinfo(ai);
+ }
+ static bool read_packet(vpn_packet_t *packet) {
+       int lenin;
+       if((lenin = recv(device_fd, packet->data, MTU, 0)) <= 0) {
 -              ifdebug(SCARY_THINGS) logger(LOG_DEBUG, "Ignoring loopback packet of %d bytes from %s", lenin, device_info);
++              logger(DEBUG_ALWAYS, LOG_ERR, "Error while reading from %s %s: %s", device_info,
+                          device, strerror(errno));
+               return false;
+       }
+       if(!memcmp(&ignore_src, packet->data + 6, sizeof ignore_src)) {
 -      ifdebug(TRAFFIC) logger(LOG_DEBUG, "Read packet of %d bytes from %s", packet->len,
++              logger(DEBUG_SCARY_THINGS, LOG_DEBUG, "Ignoring loopback packet of %d bytes from %s", lenin, device_info);
+               packet->len = 0;
+               return true;
+       }
+       packet->len = lenin;
+       device_total_in += packet->len;
 -      ifdebug(TRAFFIC) logger(LOG_DEBUG, "Writing packet of %d bytes to %s",
++      logger(DEBUG_TRAFFIC, LOG_DEBUG, "Read packet of %d bytes from %s", packet->len,
+                          device_info);
+       return true;
+ }
+ static bool write_packet(vpn_packet_t *packet) {
 -              logger(LOG_ERR, "Can't write to %s %s: %s", device_info, device,
++      logger(DEBUG_TRAFFIC, LOG_DEBUG, "Writing packet of %d bytes to %s",
+                          packet->len, device_info);
+       if(sendto(device_fd, packet->data, packet->len, 0, ai->ai_addr, ai->ai_addrlen) < 0) {
 -      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(DEBUG_ALWAYS, LOG_ERR, "Can't write to %s %s: %s", device_info, device,
+                          strerror(errno));
+               return false;
+       }
+       device_total_out += packet->len;
+       memcpy(&ignore_src, packet->data + 6, sizeof ignore_src);
+       return true;
+ }
+ static void dump_device_stats(void) {
 -      logger(LOG_ERR, "Raw socket device not supported on this platform");
++      logger(DEBUG_ALWAYS, LOG_DEBUG, "Statistics for %s %s:", device_info, device);
++      logger(DEBUG_ALWAYS, LOG_DEBUG, " total bytes in:  %10"PRIu64, device_total_in);
++      logger(DEBUG_ALWAYS, LOG_DEBUG, " total bytes out: %10"PRIu64, device_total_out);
+ }
+ const devops_t multicast_devops = {
+       .setup = setup_device,
+       .close = close_device,
+       .read = read_packet,
+       .write = write_packet,
+       .dump_stats = dump_device_stats,
+ };
+ #if 0
+ static bool not_supported(void) {
++      logger(DEBUG_ALWAYS, LOG_ERR, "Raw socket device not supported on this platform");
+       return false;
+ }
+ const devops_t multicast_devops = {
+       .setup = not_supported,
+       .close = NULL,
+       .read = NULL,
+       .write = NULL,
+       .dump_stats = NULL,
+ };
+ #endif
diff --cc src/net.c
+++ b/src/net.c
@@@ -139,12 -204,14 +139,13 @@@ void terminate_connection(connection_t 
                }
        }
  
-       /* Check if this was our outgoing connection */
+       free_connection_partially(c);
  
-       if(c->outgoing)
-               retry_outgoing(c->outgoing);
+       /* Check if this was our outgoing connection */
  
-       connection_del(c);
+       if(c->outgoing) {
 -              c->status.remove = false;
+               do_outgoing_connection(c);      
+       }
  }
  
  /*
@@@ -170,10 -233,10 +171,10 @@@ static void timeout_handler(int fd, sho
                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",
 +                                      logger(DEBUG_CONNECTIONS, LOG_INFO, "%s (%s) didn't respond to PING in %ld seconds",
-                                                          c->name, c->hostname, now - c->last_ping_time);
+                                                          c->name, c->hostname, (long)now - c->last_ping_time);
 -                                      c->status.timeout = true;
                                        terminate_connection(c, true);
 +                                      continue;
                                } else if(c->last_ping_time + pinginterval <= now) {
                                        send_ping(c);
                                }
diff --cc src/net.h
Simple merge
Simple merge
diff --cc src/net_setup.c
Simple merge
diff --cc src/node.h
Simple merge
diff --cc src/protocol.c
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
diff --cc src/route.c
Simple merge
diff --cc src/route.h
Simple merge
diff --cc src/subnet.c
Simple merge
diff --cc src/tincd.c
@@@ -357,9 -516,9 +357,9 @@@ int main(int argc, char **argv) 
        make_names();
  
        if(show_version) {
 -              printf("%s version %s (built %s %s, protocol %d)\n", PACKAGE,
 -                         VERSION, __DATE__, __TIME__, PROT_CURRENT);
 +              printf("%s version %s (built %s %s, protocol %d.%d)\n", PACKAGE,
 +                         VERSION, __DATE__, __TIME__, PROT_MAJOR, PROT_MINOR);
-               printf("Copyright (C) 1998-2011 Ivo Timmermans, Guus Sliepen and others.\n"
+               printf("Copyright (C) 1998-2012 Ivo Timmermans, Guus Sliepen and others.\n"
                                "See the AUTHORS file for a complete list.\n\n"
                                "tinc comes with ABSOLUTELY NO WARRANTY.  This is free software,\n"
                                "and you are welcome to redistribute it under certain conditions;\n"
@@@ -99,9 -99,9 +99,9 @@@ static void close_device(void) 
  }
  
  static bool read_packet(vpn_packet_t *packet) {
-       int lenin = plug.vde_recv(conn, packet->data, MTU, 0);
+       int lenin = (ssize_t)plug.vde_recv(conn, packet->data, MTU, 0);
        if(lenin <= 0) {
 -              logger(LOG_ERR, "Error while reading from %s %s: %s", device_info, device, strerror(errno));
 +              logger(DEBUG_ALWAYS, LOG_ERR, "Error while reading from %s %s: %s", device_info, device, strerror(errno));
                running = false;
                return false;
        }
  }
  
  static bool write_packet(vpn_packet_t *packet) {
-       if(plug.vde_send(conn, packet->data, packet->len, 0) < 0) {
+       if((ssize_t)plug.vde_send(conn, packet->data, packet->len, 0) < 0) {
                if(errno != EINTR && errno != EAGAIN) {
 -                      logger(LOG_ERR, "Can't write to %s %s: %s", device_info, device, strerror(errno));
 +                      logger(DEBUG_ALWAYS, LOG_ERR, "Can't write to %s %s: %s", device_info, device, strerror(errno));
                        running = false;
                }