Merge branch 'master' of git://tinc-vpn.org/tinc into 1.1
authorGuus Sliepen <guus@tinc-vpn.org>
Thu, 8 Mar 2012 20:15:08 +0000 (21:15 +0100)
committerGuus Sliepen <guus@tinc-vpn.org>
Thu, 8 Mar 2012 20:15:08 +0000 (21:15 +0100)
Conflicts:
src/net_packet.c

1  2 
src/net_packet.c
src/net_setup.c

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 "logger.h"
  #include "net.h"
@@@ -55,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
@@@ -78,16 -77,16 +78,16 @@@ bool localdiscovery = false
     which will be broadcast to the local network.
  */
  
 -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);
 +              logger(DEBUG_TRAFFIC, LOG_INFO, "Trying to send MTU probe to unreachable or rekeying node %s (%s)", n->name, n->hostname);
                n->mtuprobes = 0;
                return;
        }
                        goto end;
                }
  
 -              ifdebug(TRAFFIC) logger(LOG_INFO, "%s (%s) did not respond to UDP ping, restarting PMTU discovery", n->name, n->hostname);
 +              logger(DEBUG_TRAFFIC, LOG_INFO, "%s (%s) did not respond to UDP ping, restarting PMTU discovery", n->name, n->hostname);
                n->mtuprobes = 1;
                n->minmtu = 0;
                n->maxmtu = MTU;
        }
  
        if(n->mtuprobes >= 10 && n->mtuprobes < 32 && !n->minmtu) {
 -              ifdebug(TRAFFIC) logger(LOG_INFO, "No response to MTU probes from %s (%s)", n->name, n->hostname);
 +              logger(DEBUG_TRAFFIC, LOG_INFO, "No response to MTU probes from %s (%s)", n->name, n->hostname);
                n->mtuprobes = 31;
        }
  
                else
                        n->maxmtu = n->minmtu;
                n->mtu = n->minmtu;
 -              ifdebug(TRAFFIC) logger(LOG_INFO, "Fixing MTU of %s (%s) to %d after %d probes", n->name, n->hostname, n->mtu, n->mtuprobes);
 +              logger(DEBUG_TRAFFIC, LOG_INFO, "Fixing MTU of %s (%s) to %d after %d probes", n->name, n->hostname, n->mtu, n->mtuprobes);
                n->mtuprobes = 31;
        }
  
                        len = 64;
                
                memset(packet.data, 0, 14);
 -              RAND_pseudo_bytes(packet.data + 14, len - 14);
 +              randomize(packet.data + 14, len - 14);
                packet.len = len;
                if(i >= 3 && n->mtuprobes <= 10)
                        packet.priority = -1;
                else
                        packet.priority = 0;
  
 -              ifdebug(TRAFFIC) logger(LOG_INFO, "Sending MTU probe length %d to %s (%s)", len, n->name, n->hostname);
 +              logger(DEBUG_TRAFFIC, LOG_INFO, "Sending MTU probe length %d to %s (%s)", len, n->name, n->hostname);
  
                send_udppacket(n, &packet);
        }
  
  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 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);
 +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);
 +}
 +
 +static void mtu_probe_h(node_t *n, vpn_packet_t *packet, length_t len) {
 +      logger(DEBUG_TRAFFIC, LOG_INFO, "Got MTU probe length %d from %s (%s)", packet->len, n->name, n->hostname);
  
        if(!packet->data[0]) {
                packet->data[0] = 1;
@@@ -242,20 -239,21 +242,20 @@@ static length_t uncompress_packet(uint8
  /* VPN packet I/O */
  
  static void receive_packet(node_t *n, vpn_packet_t *packet) {
 -      ifdebug(TRAFFIC) logger(LOG_DEBUG, "Received packet of %d bytes from %s (%s)",
 +      logger(DEBUG_TRAFFIC, 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) {
 -              ifdebug(TRAFFIC) logger(LOG_DEBUG, "Got packet from %s (%s) but he hasn't got our key yet",
 +      if(!cipher_active(&n->incipher)) {
 +              logger(DEBUG_TRAFFIC, 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) {
 -              ifdebug(TRAFFIC) logger(LOG_DEBUG, "Got too short packet from %s (%s)",
 +      if(inpkt->len < sizeof inpkt->seqno + digest_length(&n->indigest)) {
 +              logger(DEBUG_TRAFFIC, 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)) {
 +                      logger(DEBUG_TRAFFIC, 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)) {
 +                      logger(DEBUG_TRAFFIC, 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) {
                if(inpkt->seqno != n->received_seqno + 1) {
                        if(inpkt->seqno >= n->received_seqno + replaywin * 8) {
                                if(n->farfuture++ < replaywin >> 2) {
 -                                      logger(LOG_WARNING, "Packet from %s (%s) is %d seqs in the future, dropped (%u)",
 +                                      logger(DEBUG_ALWAYS, LOG_WARNING, "Packet from %s (%s) is %d seqs in the future, dropped (%u)",
                                                n->name, n->hostname, inpkt->seqno - n->received_seqno - 1, n->farfuture);
                                        return;
                                }
 -                              logger(LOG_WARNING, "Lost %d packets from %s (%s)",
 +                              logger(DEBUG_ALWAYS, LOG_WARNING, "Lost %d packets from %s (%s)",
                                                inpkt->seqno - n->received_seqno - 1, n->name, n->hostname);
                                memset(n->late, 0, replaywin);
                        } else if (inpkt->seqno <= n->received_seqno) {
                                if((n->received_seqno >= replaywin * 8 && inpkt->seqno <= n->received_seqno - replaywin * 8) || !(n->late[(inpkt->seqno / 8) % replaywin] & (1 << inpkt->seqno % 8))) {
 -                                      logger(LOG_WARNING, "Got late or replayed packet from %s (%s), seqno %d, last received %d",
 +                                      logger(DEBUG_ALWAYS, LOG_WARNING, "Got late or replayed packet from %s (%s), seqno %d, last received %d",
                                                n->name, n->hostname, inpkt->seqno, n->received_seqno);
                                        return;
                                }
                        } else {
 -                              for(i = n->received_seqno + 1; i < inpkt->seqno; i++)
 +                              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 */
  
                outpkt = pkt[nextpkt++];
  
                if((outpkt->len = uncompress_packet(outpkt->data, inpkt->data, inpkt->len, n->incompression)) < 0) {
 -                      ifdebug(TRAFFIC) logger(LOG_ERR, "Error while uncompressing packet from %s (%s)",
 +                      logger(DEBUG_TRAFFIC, LOG_ERR, "Error while uncompressing packet from %s (%s)",
                                                 n->name, n->hostname);
                        return;
                }
@@@ -386,24 -394,22 +386,24 @@@ 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;
  
        if(!n->status.reachable) {
 -              ifdebug(TRAFFIC) logger(LOG_INFO, "Trying to send UDP packet to unreachable node %s (%s)", n->name, n->hostname);
 +              logger(DEBUG_TRAFFIC, LOG_INFO, "Trying to send UDP packet to unreachable node %s (%s)", n->name, n->hostname);
                return;
        }
  
        /* Make sure we have a valid key */
  
        if(!n->status.validkey) {
 -              ifdebug(TRAFFIC) logger(LOG_INFO,
 +              time_t now = time(NULL);
 +
 +              logger(DEBUG_TRAFFIC, LOG_INFO,
                                   "No valid key known yet for %s (%s), forwarding via TCP",
                                   n->name, n->hostname);
  
        }
  
        if(n->options & OPTION_PMTU_DISCOVERY && inpkt->len > n->minmtu && (inpkt->data[12] | inpkt->data[13])) {
 -              ifdebug(TRAFFIC) logger(LOG_INFO,
 +              logger(DEBUG_TRAFFIC, LOG_INFO,
                                "Packet for %s (%s) larger than minimum MTU, forwarding via %s",
                                n->name, n->hostname, n != n->nexthop ? n->nexthop->name : "TCP");
  
                return;
        }
  
 -      origlen = inpkt->len;
 -      origpriority = inpkt->priority;
 -
        /* Compress the packet */
  
        if(n->outcompression) {
                outpkt = pkt[nextpkt++];
  
                if((outpkt->len = compress_packet(outpkt->data, inpkt->data, inpkt->len, n->outcompression)) < 0) {
 -                      ifdebug(TRAFFIC) logger(LOG_ERR, "Error while compressing packet to %s (%s)",
 +                      logger(DEBUG_TRAFFIC, LOG_ERR, "Error while compressing packet to %s (%s)",
                                   n->name, n->hostname);
                        return;
                }
        /* 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)) {
 +                      logger(DEBUG_TRAFFIC, 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 */
        if(priorityinheritance && origpriority != priority
           && listen_socket[n->sock].sa.sa.sa_family == AF_INET) {
                priority = origpriority;
 -              ifdebug(TRAFFIC) logger(LOG_DEBUG, "Setting outgoing packet priority to %d", priority);
 +              logger(DEBUG_TRAFFIC, LOG_DEBUG, "Setting outgoing packet priority to %d", priority);
                if(setsockopt(listen_socket[n->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));
 +                      logger(DEBUG_ALWAYS, LOG_ERR, "System call `%s' failed: %s", "setsockopt", strerror(errno));
        }
  #endif
  
                        if(n->mtu >= origlen)
                                n->mtu = origlen - 1;
                } else
-                       logger(DEBUG_ALWAYS, LOG_ERR, "Error sending packet to %s (%s): %s", n->name, n->hostname, sockstrerror(sockerrno));
 -                      ifdebug(TRAFFIC) logger(LOG_WARNING, "Error sending packet to %s (%s): %s", n->name, n->hostname, sockstrerror(sockerrno));
++                      logger(DEBUG_TRAFFIC, LOG_WARNING, "Error sending packet to %s (%s): %s", n->name, n->hostname, sockstrerror(sockerrno));
        }
  
  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;
                devops.write(packet);
                return;
        }
  
 -      ifdebug(TRAFFIC) logger(LOG_ERR, "Sending packet of %d bytes to %s (%s)",
 +      logger(DEBUG_TRAFFIC, LOG_ERR, "Sending packet of %d bytes to %s (%s)",
                           packet->len, n->name, n->hostname);
  
        if(!n->status.reachable) {
 -              ifdebug(TRAFFIC) logger(LOG_INFO, "Node %s (%s) is not reachable",
 +              logger(DEBUG_TRAFFIC, LOG_INFO, "Node %s (%s) is not reachable",
                                   n->name, n->hostname);
                return;
        }
  
 +      n->out_packets++;
 +      n->out_bytes += packet->len;
 +
        via = (packet->priority == -1 || n->via == myself) ? n->nexthop : n->via;
  
        if(via != n)
 -              ifdebug(TRAFFIC) logger(LOG_INFO, "Sending packet to %s via %s (%s)",
 +              logger(DEBUG_TRAFFIC, LOG_INFO, "Sending packet to %s via %s (%s)",
                           n->name, via->name, n->via->hostname);
  
        if(packet->priority == -1 || ((myself->options | via->options) & OPTION_TCPONLY)) {
  /* 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)",
 +      logger(DEBUG_TRAFFIC, LOG_INFO, "Broadcasting packet of %d bytes from %s (%s)",
                           packet->len, from->name, from->hostname);
  
        if(from != myself) {
  }
  
  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);
  
        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(listen_socket[sock].udp, (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));
 +                      logger(DEBUG_ALWAYS, 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);
                n = try_harder(&from, &pkt);
                if(n)
                        update_node_udp(n, &from);
 -              else ifdebug(PROTOCOL) {
 +              else if(debug_level >= DEBUG_PROTOCOL) {
                        hostname = sockaddr2hostname(&from);
 -                      logger(LOG_WARNING, "Received UDP packet from unknown source %s", hostname);
 +                      logger(DEBUG_PROTOCOL, LOG_WARNING, "Received UDP packet from unknown source %s", hostname);
                        free(hostname);
                        return;
                }
                        return;
        }
  
 -      n->sock = sock;
 +      n->sock = (intptr_t)data;
  
        receive_udppacket(n, &pkt);
  }
 +
 +void handle_device_data(int sock, short events, void *data) {
 +      vpn_packet_t packet;
 +
 +      packet.priority = 0;
 +
 +      if(devops.read(&packet)) {
 +              myself->in_packets++;
 +              myself->in_bytes += packet.len;
 +              route(myself, &packet);
 +      }
 +}
diff --combined src/net_setup.c
  
  #include "system.h"
  
 -#include <openssl/pem.h>
 -#include <openssl/rsa.h>
 -#include <openssl/rand.h>
 -#include <openssl/err.h>
 -#include <openssl/evp.h>
 -
 -#include "avl_tree.h"
 +#include "splay_tree.h"
 +#include "cipher.h"
  #include "conf.h"
  #include "connection.h"
 +#include "control.h"
  #include "device.h"
 -#include "event.h"
 +#include "digest.h"
 +#include "ecdsa.h"
  #include "graph.h"
  #include "logger.h"
  #include "net.h"
  #include "process.h"
  #include "protocol.h"
  #include "route.h"
 +#include "rsa.h"
  #include "subnet.h"
  #include "utils.h"
  #include "xalloc.h"
  
  char *myport;
 +static struct event device_ev;
  devops_t devops;
  
 -bool read_rsa_public_key(connection_t *c) {
 +bool node_read_ecdsa_public_key(node_t *n) {
 +      if(ecdsa_active(&n->ecdsa))
 +              return true;
 +
 +      splay_tree_t *config_tree;
        FILE *fp;
        char *fname;
 -      char *key;
 +      char *p;
 +      bool result = false;
  
 -      if(!c->rsa_key) {
 -              c->rsa_key = RSA_new();
 -//            RSA_blinding_on(c->rsa_key, NULL);
 -      }
 +      xasprintf(&fname, "%s/hosts/%s", confbase, n->name);
  
 -      /* First, check for simple PublicKey statement */
 +      init_configuration(&config_tree);
 +      if(!read_config_file(config_tree, fname))
 +              goto exit;
  
 -      if(get_config_string(lookup_config(c->config_tree, "PublicKey"), &key)) {
 -              BN_hex2bn(&c->rsa_key->n, key);
 -              BN_hex2bn(&c->rsa_key->e, "FFFF");
 -              free(key);
 -              return true;
 +      /* First, check for simple ECDSAPublicKey statement */
 +
 +      if(get_config_string(lookup_config(config_tree, "ECDSAPublicKey"), &p)) {
 +              result = ecdsa_set_base64_public_key(&n->ecdsa, p);
 +              free(p);
 +              goto exit;
        }
  
 -      /* Else, check for PublicKeyFile statement and read it */
 +      /* Else, check for ECDSAPublicKeyFile statement and read it */
  
 -      if(get_config_string(lookup_config(c->config_tree, "PublicKeyFile"), &fname)) {
 -              fp = fopen(fname, "r");
 +      free(fname);
  
 -              if(!fp) {
 -                      logger(LOG_ERR, "Error reading RSA public key file `%s': %s",
 -                                 fname, strerror(errno));
 -                      free(fname);
 -                      return false;
 -              }
 +      if(!get_config_string(lookup_config(config_tree, "ECDSAPublicKeyFile"), &fname))
 +              xasprintf(&fname, "%s/hosts/%s", confbase, n->name);
  
 -              free(fname);
 -              c->rsa_key = PEM_read_RSAPublicKey(fp, &c->rsa_key, NULL, NULL);
 -              fclose(fp);
 +      fp = fopen(fname, "r");
  
 -              if(c->rsa_key)
 -                      return true;            /* Woohoo. */
 +      if(!fp) {
 +              logger(DEBUG_ALWAYS, LOG_ERR, "Error reading ECDSA public key file `%s': %s", fname, strerror(errno));
 +              goto exit;
 +      }
  
 -              /* If it fails, try PEM_read_RSA_PUBKEY. */
 -              fp = fopen(fname, "r");
 +      result = ecdsa_read_pem_public_key(&n->ecdsa, fp);
 +      fclose(fp);
  
 -              if(!fp) {
 -                      logger(LOG_ERR, "Error reading RSA public key file `%s': %s",
 -                                 fname, strerror(errno));
 -                      free(fname);
 -                      return false;
 -              }
 +exit:
 +      exit_configuration(&config_tree);
 +      free(fname);
 +      return result;
 +}
  
 -              free(fname);
 -              c->rsa_key = PEM_read_RSA_PUBKEY(fp, &c->rsa_key, NULL, NULL);
 -              fclose(fp);
 +bool read_ecdsa_public_key(connection_t *c) {
 +      FILE *fp;
 +      char *fname;
 +      char *p;
 +      bool result;
  
 -              if(c->rsa_key) {
 -//                            RSA_blinding_on(c->rsa_key, NULL);
 -                      return true;
 -              }
 +      /* First, check for simple ECDSAPublicKey statement */
  
 -              logger(LOG_ERR, "Reading RSA public key file `%s' failed: %s",
 -                         fname, strerror(errno));
 -              return false;
 +      if(get_config_string(lookup_config(c->config_tree, "ECDSAPublicKey"), &p)) {
 +              result = ecdsa_set_base64_public_key(&c->ecdsa, p);
 +              free(p);
 +              return result;
        }
  
 -      /* Else, check if a harnessed public key is in the config file */
 +      /* Else, check for ECDSAPublicKeyFile statement and read it */
 +
 +      if(!get_config_string(lookup_config(c->config_tree, "ECDSAPublicKeyFile"), &fname))
 +              xasprintf(&fname, "%s/hosts/%s", confbase, c->name);
  
 -      xasprintf(&fname, "%s/hosts/%s", confbase, c->name);
        fp = fopen(fname, "r");
  
        if(!fp) {
 -              logger(LOG_ERR, "Error reading RSA public key file `%s': %s", fname, strerror(errno));
 +              logger(DEBUG_ALWAYS, LOG_ERR, "Error reading ECDSA public key file `%s': %s",
 +                         fname, strerror(errno));
                free(fname);
                return false;
        }
  
 -      c->rsa_key = PEM_read_RSAPublicKey(fp, &c->rsa_key, NULL, NULL);
 +      result = ecdsa_read_pem_public_key(&c->ecdsa, fp);
        fclose(fp);
 +
 +      if(!result) 
 +              logger(DEBUG_ALWAYS, LOG_ERR, "Reading ECDSA public key file `%s' failed: %s", fname, strerror(errno));
        free(fname);
 +      return result;
 +}
  
 -      if(c->rsa_key)
 -              return true;
 +bool read_rsa_public_key(connection_t *c) {
 +      FILE *fp;
 +      char *fname;
 +      char *n;
 +      bool result;
 +
 +      /* First, check for simple PublicKey statement */
 +
 +      if(get_config_string(lookup_config(c->config_tree, "PublicKey"), &n)) {
 +              result = rsa_set_hex_public_key(&c->rsa, n, "FFFF");
 +              free(n);
 +              return result;
 +      }
 +
 +      /* Else, check for PublicKeyFile statement and read it */
  
 -      /* Try again with PEM_read_RSA_PUBKEY. */
 +      if(!get_config_string(lookup_config(c->config_tree, "PublicKeyFile"), &fname))
 +              xasprintf(&fname, "%s/hosts/%s", confbase, c->name);
  
 -      xasprintf(&fname, "%s/hosts/%s", confbase, c->name);
        fp = fopen(fname, "r");
  
        if(!fp) {
 -              logger(LOG_ERR, "Error reading RSA public key file `%s': %s", fname, strerror(errno));
 +              logger(DEBUG_ALWAYS, LOG_ERR, "Error reading RSA public key file `%s': %s", fname, strerror(errno));
                free(fname);
                return false;
        }
  
 -      c->rsa_key = PEM_read_RSA_PUBKEY(fp, &c->rsa_key, NULL, NULL);
 -//    RSA_blinding_on(c->rsa_key, NULL);
 +      result = rsa_read_pem_public_key(&c->rsa, fp);
        fclose(fp);
 +
 +      if(!result) 
 +              logger(DEBUG_ALWAYS, LOG_ERR, "Reading RSA public key file `%s' failed: %s", fname, strerror(errno));
        free(fname);
 +      return result;
 +}
  
 -      if(c->rsa_key)
 -              return true;
 +static bool read_ecdsa_private_key(void) {
 +      FILE *fp;
 +      char *fname;
 +      bool result;
 +
 +      /* Check for PrivateKeyFile statement and read it */
 +
 +      if(!get_config_string(lookup_config(config_tree, "ECDSAPrivateKeyFile"), &fname))
 +              xasprintf(&fname, "%s/ecdsa_key.priv", confbase);
 +
 +      fp = fopen(fname, "r");
 +
 +      if(!fp) {
 +              logger(DEBUG_ALWAYS, LOG_ERR, "Error reading ECDSA private key file `%s': %s", fname, strerror(errno));
 +              free(fname);
 +              return false;
 +      }
 +
 +#if !defined(HAVE_MINGW) && !defined(HAVE_CYGWIN)
 +      struct stat s;
 +
 +      if(fstat(fileno(fp), &s)) {
 +              logger(DEBUG_ALWAYS, LOG_ERR, "Could not stat ECDSA private key file `%s': %s'", fname, strerror(errno));
 +              free(fname);
 +              return false;
 +      }
  
 -      logger(LOG_ERR, "No public key for %s specified!", c->name);
 +      if(s.st_mode & ~0100700)
 +              logger(DEBUG_ALWAYS, LOG_WARNING, "Warning: insecure file permissions for ECDSA private key file `%s'!", fname);
 +#endif
 +
 +      result = ecdsa_read_pem_private_key(&myself->connection->ecdsa, fp);
 +      fclose(fp);
  
 -      return false;
 +      if(!result) 
 +              logger(DEBUG_ALWAYS, LOG_ERR, "Reading ECDSA private key file `%s' failed: %s", fname, strerror(errno));
 +      free(fname);
 +      return result;
  }
  
  static bool read_rsa_private_key(void) {
        FILE *fp;
 -      char *fname, *key, *pubkey;
 -      struct stat s;
 +      char *fname;
 +      char *n, *d;
 +      bool result;
 +
 +      /* First, check for simple PrivateKey statement */
  
 -      if(get_config_string(lookup_config(config_tree, "PrivateKey"), &key)) {
 -              if(!get_config_string(lookup_config(config_tree, "PublicKey"), &pubkey)) {
 -                      logger(LOG_ERR, "PrivateKey used but no PublicKey found!");
 +      if(get_config_string(lookup_config(config_tree, "PrivateKey"), &d)) {
 +              if(!get_config_string(lookup_config(config_tree, "PublicKey"), &n)) {
 +                      logger(DEBUG_ALWAYS, LOG_ERR, "PrivateKey used but no PublicKey found!");
 +                      free(d);
                        return false;
                }
 -              myself->connection->rsa_key = RSA_new();
 -//            RSA_blinding_on(myself->connection->rsa_key, NULL);
 -              BN_hex2bn(&myself->connection->rsa_key->d, key);
 -              BN_hex2bn(&myself->connection->rsa_key->n, pubkey);
 -              BN_hex2bn(&myself->connection->rsa_key->e, "FFFF");
 -              free(key);
 -              free(pubkey);
 +              result = rsa_set_hex_private_key(&myself->connection->rsa, n, "FFFF", d);
 +              free(n);
 +              free(d);
                return true;
        }
  
 +      /* Else, check for PrivateKeyFile statement and read it */
 +
        if(!get_config_string(lookup_config(config_tree, "PrivateKeyFile"), &fname))
                xasprintf(&fname, "%s/rsa_key.priv", confbase);
  
        fp = fopen(fname, "r");
  
        if(!fp) {
 -              logger(LOG_ERR, "Error reading RSA private key file `%s': %s",
 +              logger(DEBUG_ALWAYS, LOG_ERR, "Error reading RSA private key file `%s': %s",
                           fname, strerror(errno));
                free(fname);
                return false;
        }
  
  #if !defined(HAVE_MINGW) && !defined(HAVE_CYGWIN)
 +      struct stat s;
 +
        if(fstat(fileno(fp), &s)) {
 -              logger(LOG_ERR, "Could not stat RSA private key file `%s': %s'",
 -                              fname, strerror(errno));
 +              logger(DEBUG_ALWAYS, LOG_ERR, "Could not stat RSA private key file `%s': %s'", fname, strerror(errno));
                free(fname);
                return false;
        }
  
        if(s.st_mode & ~0100700)
 -              logger(LOG_WARNING, "Warning: insecure file permissions for RSA private key file `%s'!", fname);
 +              logger(DEBUG_ALWAYS, LOG_WARNING, "Warning: insecure file permissions for RSA private key file `%s'!", fname);
  #endif
  
 -      myself->connection->rsa_key = PEM_read_RSAPrivateKey(fp, NULL, NULL, NULL);
 +      result = rsa_read_pem_private_key(&myself->connection->rsa, fp);
        fclose(fp);
  
 -      if(!myself->connection->rsa_key) {
 -              logger(LOG_ERR, "Reading RSA private key file `%s' failed: %s",
 -                         fname, strerror(errno));
 -              free(fname);
 -              return false;
 +      if(!result) 
 +              logger(DEBUG_ALWAYS, LOG_ERR, "Reading RSA private key file `%s' failed: %s", fname, strerror(errno));
 +      free(fname);
 +      return result;
 +}
 +
 +static struct event keyexpire_event;
 +
 +static void keyexpire_handler(int fd, short events, void *data) {
 +      regenerate_key();
 +}
 +
 +void regenerate_key(void) {
 +      if(timeout_initialized(&keyexpire_event)) {
 +              logger(DEBUG_STATUS, LOG_INFO, "Expiring symmetric keys");
 +              event_del(&keyexpire_event);
 +              send_key_changed();
 +      } else {
 +              timeout_set(&keyexpire_event, keyexpire_handler, NULL);
        }
  
 -      free(fname);
 -      return true;
 +      event_add(&keyexpire_event, &(struct timeval){keylifetime, 0});
  }
  
  /*
@@@ -288,16 -217,15 +288,15 @@@ void load_all_subnets(void) 
        struct dirent *ent;
        char *dname;
        char *fname;
 -      avl_tree_t *config_tree;
 +      splay_tree_t *config_tree;
        config_t *cfg;
        subnet_t *s, *s2;
        node_t *n;
-       bool result;
  
        xasprintf(&dname, "%s/hosts", confbase);
        dir = opendir(dname);
        if(!dir) {
 -              logger(LOG_ERR, "Could not open %s: %s", dname, strerror(errno));
 +              logger(DEBUG_ALWAYS, LOG_ERR, "Could not open %s: %s", dname, strerror(errno));
                free(dname);
                return;
        }
  
                xasprintf(&fname, "%s/hosts/%s", confbase, ent->d_name);
                init_configuration(&config_tree);
-               result = read_config_file(config_tree, fname);
+               read_config_options(config_tree, ent->d_name);
+               read_config_file(config_tree, fname);
                free(fname);
-               if(!result)
-                       continue;
  
                if(!n) {
                        n = new_node();
@@@ -364,16 -291,15 +362,16 @@@ static bool setup_myself(void) 
        myself->connection->hostname = xstrdup("MYSELF");
  
        myself->connection->options = 0;
 -      myself->connection->protocol_version = PROT_CURRENT;
 +      myself->connection->protocol_major = PROT_MAJOR;
 +      myself->connection->protocol_minor = PROT_MINOR;
  
        if(!get_config_string(lookup_config(config_tree, "Name"), &name)) {     /* Not acceptable */
 -              logger(LOG_ERR, "Name for tinc daemon required!");
 +              logger(DEBUG_ALWAYS, LOG_ERR, "Name for tinc daemon required!");
                return false;
        }
  
        if(!check_id(name)) {
 -              logger(LOG_ERR, "Invalid name for myself!");
 +              logger(DEBUG_ALWAYS, LOG_ERR, "Invalid name for myself!");
                free(name);
                return false;
        }
        read_config_file(config_tree, fname);
        free(fname);
  
 +      get_config_bool(lookup_config(config_tree, "ExperimentalProtocol"), &experimental);
 +
 +      if(experimental && !read_ecdsa_private_key())
 +              return false;
 +
        if(!read_rsa_private_key())
                return false;
  
                else if(!strcasecmp(mode, "hub"))
                        routing_mode = RMODE_HUB;
                else {
 -                      logger(LOG_ERR, "Invalid routing mode!");
 +                      logger(DEBUG_ALWAYS, LOG_ERR, "Invalid routing mode!");
                        return false;
                }
                free(mode);
                else if(!strcasecmp(mode, "kernel"))
                        forwarding_mode = FMODE_KERNEL;
                else {
 -                      logger(LOG_ERR, "Invalid forwarding mode!");
 +                      logger(DEBUG_ALWAYS, LOG_ERR, "Invalid forwarding mode!");
                        return false;
                }
                free(mode);
  
  #if !defined(SOL_IP) || !defined(IP_TOS)
        if(priorityinheritance)
 -              logger(LOG_WARNING, "%s not supported on this platform", "PriorityInheritance");
 +              logger(DEBUG_ALWAYS, LOG_WARNING, "%s not supported on this platform", "PriorityInheritance");
  #endif
  
        if(!get_config_int(lookup_config(config_tree, "MACExpire"), &macexpire))
  
        if(get_config_int(lookup_config(config_tree, "MaxTimeout"), &maxtimeout)) {
                if(maxtimeout <= 0) {
 -                      logger(LOG_ERR, "Bogus maximum timeout!");
 +                      logger(DEBUG_ALWAYS, LOG_ERR, "Bogus maximum timeout!");
                        return false;
                }
        } else
  
        if(get_config_int(lookup_config(config_tree, "UDPRcvBuf"), &udp_rcvbuf)) {
                if(udp_rcvbuf <= 0) {
 -                      logger(LOG_ERR, "UDPRcvBuf cannot be negative!");
 +                      logger(DEBUG_ALWAYS, LOG_ERR, "UDPRcvBuf cannot be negative!");
                        return false;
                }
        }
  
        if(get_config_int(lookup_config(config_tree, "UDPSndBuf"), &udp_sndbuf)) {
                if(udp_sndbuf <= 0) {
 -                      logger(LOG_ERR, "UDPSndBuf cannot be negative!");
 +                      logger(DEBUG_ALWAYS, LOG_ERR, "UDPSndBuf cannot be negative!");
                        return false;
                }
        }
  
        if(get_config_int(lookup_config(config_tree, "ReplayWindow"), &replaywin_int)) {
                if(replaywin_int < 0) {
 -                      logger(LOG_ERR, "ReplayWindow cannot be negative!");
 +                      logger(DEBUG_ALWAYS, LOG_ERR, "ReplayWindow cannot be negative!");
                        return false;
                }
                replaywin = (unsigned)replaywin_int;
                else if(!strcasecmp(afname, "any"))
                        addressfamily = AF_UNSPEC;
                else {
 -                      logger(LOG_ERR, "Invalid address family!");
 +                      logger(DEBUG_ALWAYS, LOG_ERR, "Invalid address family!");
                        return false;
                }
                free(afname);
  
        /* Generate packet encryption key */
  
 -      if(get_config_string
 -         (lookup_config(config_tree, "Cipher"), &cipher)) {
 -              if(!strcasecmp(cipher, "none")) {
 -                      myself->incipher = NULL;
 -              } else {
 -                      myself->incipher = EVP_get_cipherbyname(cipher);
 -
 -                      if(!myself->incipher) {
 -                              logger(LOG_ERR, "Unrecognized cipher type!");
 -                              return false;
 -                      }
 -              }
 -      } else
 -              myself->incipher = EVP_bf_cbc();
 -
 -      if(myself->incipher)
 -              myself->inkeylength = myself->incipher->key_len + myself->incipher->iv_len;
 -      else
 -              myself->inkeylength = 1;
 +      if(!get_config_string(lookup_config(config_tree, "Cipher"), &cipher))
 +              cipher = xstrdup("blowfish");
  
 -      myself->connection->outcipher = EVP_bf_ofb();
 +      if(!cipher_open_by_name(&myself->incipher, cipher)) {
 +              logger(DEBUG_ALWAYS, LOG_ERR, "Unrecognized cipher type!");
 +              return false;
 +      }
  
        if(!get_config_int(lookup_config(config_tree, "KeyExpire"), &keylifetime))
                keylifetime = 3600;
  
 -      keyexpires = now + keylifetime;
 -      
 -      /* Check if we want to use message authentication codes... */
 +      regenerate_key();
  
 -      if(get_config_string(lookup_config(config_tree, "Digest"), &digest)) {
 -              if(!strcasecmp(digest, "none")) {
 -                      myself->indigest = NULL;
 -              } else {
 -                      myself->indigest = EVP_get_digestbyname(digest);
 +      /* Check if we want to use message authentication codes... */
  
 -                      if(!myself->indigest) {
 -                              logger(LOG_ERR, "Unrecognized digest type!");
 -                              return false;
 -                      }
 -              }
 -      } else
 -              myself->indigest = EVP_sha1();
 +      int maclength = 4;
 +      get_config_int(lookup_config(config_tree, "MACLength"), &maclength);
  
 -      myself->connection->outdigest = EVP_sha1();
 +      if(maclength < 0) {
 +              logger(DEBUG_ALWAYS, LOG_ERR, "Bogus MAC length!");
 +              return false;
 +      }
  
 -      if(get_config_int(lookup_config(config_tree, "MACLength"), &myself->inmaclength)) {
 -              if(myself->indigest) {
 -                      if(myself->inmaclength > myself->indigest->md_size) {
 -                              logger(LOG_ERR, "MAC length exceeds size of digest!");
 -                              return false;
 -                      } else if(myself->inmaclength < 0) {
 -                              logger(LOG_ERR, "Bogus MAC length!");
 -                              return false;
 -                      }
 -              }
 -      } else
 -              myself->inmaclength = 4;
 +      if(!get_config_string(lookup_config(config_tree, "Digest"), &digest))
 +              digest = xstrdup("sha1");
  
 -      myself->connection->outmaclength = 0;
 +      if(!digest_open_by_name(&myself->indigest, digest, maclength)) {
 +              logger(DEBUG_ALWAYS, LOG_ERR, "Unrecognized digest type!");
 +              return false;
 +      }
  
        /* Compression */
  
        if(get_config_int(lookup_config(config_tree, "Compression"), &myself->incompression)) {
                if(myself->incompression < 0 || myself->incompression > 11) {
 -                      logger(LOG_ERR, "Bogus compression level!");
 +                      logger(DEBUG_ALWAYS, LOG_ERR, "Bogus compression level!");
                        return false;
                }
        } else
        if(!devops.setup())
                return false;
  
 +      if(device_fd >= 0) {
 +              event_set(&device_ev, device_fd, EV_READ|EV_PERSIST, handle_device_data, NULL);
 +
 +              if (event_add(&device_ev, NULL) < 0) {
 +                      logger(DEBUG_ALWAYS, LOG_ERR, "event_add failed: %s", strerror(errno));
 +                      devops.close();
 +                      return false;
 +              }
 +      }
 +
        /* Run tinc-up script to further initialize the tap interface */
        xasprintf(&envp[0], "NETNAME=%s", netname ? : "");
        xasprintf(&envp[1], "DEVICE=%s", device ? : "");
  
        execute_script("tinc-up", envp);
  
 -      for(i = 0; i < 5; i++)
 +      for(i = 0; i < 4; i++)
                free(envp[i]);
  
        /* Run subnet-up scripts for our own subnets */
                free(address);
  
                if(err || !ai) {
 -                      logger(LOG_ERR, "System call `%s' failed: %s", "getaddrinfo",
 +                      logger(DEBUG_ALWAYS, LOG_ERR, "System call `%s' failed: %s", "getaddrinfo",
                                   gai_strerror(err));
                        return false;
                }
  
                for(aip = ai; aip; aip = aip->ai_next) {
                        if(listen_sockets >= MAXSOCKETS) {
 -                              logger(LOG_ERR, "Too many listening sockets");
 +                              logger(DEBUG_ALWAYS, LOG_ERR, "Too many listening sockets");
                                return false;
                        }
  
                        listen_socket[listen_sockets].udp =
                                setup_vpn_in_socket((sockaddr_t *) aip->ai_addr);
  
 -                      if(listen_socket[listen_sockets].udp < 0)
 +                      if(listen_socket[listen_sockets].udp < 0) {
 +                              close(listen_socket[listen_sockets].tcp);
                                continue;
 +                      }
  
 -                      ifdebug(CONNECTIONS) {
 +                      event_set(&listen_socket[listen_sockets].ev_tcp,
 +                                        listen_socket[listen_sockets].tcp,
 +                                        EV_READ|EV_PERSIST,
 +                                        handle_new_meta_connection, NULL);
 +                      if(event_add(&listen_socket[listen_sockets].ev_tcp, NULL) < 0) {
 +                              logger(DEBUG_ALWAYS, LOG_ERR, "event_add failed: %s", strerror(errno));
 +                              abort();
 +                      }
 +
 +                      event_set(&listen_socket[listen_sockets].ev_udp,
 +                                        listen_socket[listen_sockets].udp,
 +                                        EV_READ|EV_PERSIST,
 +                                        handle_incoming_vpn_data, (void *)(intptr_t)listen_sockets);
 +                      if(event_add(&listen_socket[listen_sockets].ev_udp, NULL) < 0) {
 +                              logger(DEBUG_ALWAYS, LOG_ERR, "event_add failed: %s", strerror(errno));
 +                              abort();
 +                      }
 +
 +                      if(debug_level >= DEBUG_CONNECTIONS) {
                                hostname = sockaddr2hostname((sockaddr_t *) aip->ai_addr);
 -                              logger(LOG_NOTICE, "Listening on %s", hostname);
 +                              logger(DEBUG_CONNECTIONS, LOG_NOTICE, "Listening on %s", hostname);
                                free(hostname);
                        }
  
        } while(cfg);
  
        if(listen_sockets)
 -              logger(LOG_NOTICE, "Ready");
 +              logger(DEBUG_ALWAYS, LOG_NOTICE, "Ready");
        else {
 -              logger(LOG_ERR, "Unable to create any listening socket!");
 +              logger(DEBUG_ALWAYS, LOG_ERR, "Unable to create any listening socket!");
                return false;
        }
  
    initialize network
  */
  bool setup_network(void) {
 -      now = time(NULL);
 -
 -      init_events();
        init_connections();
        init_subnets();
        init_nodes();
    close all open network connections
  */
  void close_network_connections(void) {
 -      avl_node_t *node, *next;
 +      splay_node_t *node, *next;
        connection_t *c;
        char *envp[5];
        int i;
                terminate_connection(c, false);
        }
  
 -      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);
  
        if(myself && myself->connection) {
        }
  
        for(i = 0; i < listen_sockets; i++) {
 +              event_del(&listen_socket[i].ev_tcp);
 +              event_del(&listen_socket[i].ev_udp);
                close(listen_socket[i].tcp);
                close(listen_socket[i].udp);
        }
        exit_subnets();
        exit_nodes();
        exit_connections();
 -      exit_events();
  
        execute_script("tinc-down", envp);