- Version 1.0.10 not yet released
+Version 1.1-cvs Work in progress
+
+ * Use libevent to handle I/O events and timeouts.
+
+ * Use splay trees instead of AVL trees.
+
+ Version 1.0.11 Nov 1 2009
+
+ * Fixed potential crash when the HUP signal is sent.
+
+ * Fixes handling of weighted Subnets in switch and hub modes, preventing
+ unnecessary broadcasts.
+
+ * Works around a MinGW bug that caused packets to Windows nodes to always be
+ sent via TCP.
+
+ * Improvements to the PMTU discovery code, especially on Windows.
+
+ * Use UDP again in certain cases where 1.0.10 was too conservative and fell
+ back to TCP unnecessarily.
+
+ * Allow fast roaming of hosts to other nodes in a switched VPN.
+
+ Version 1.0.10 Oct 18 2009
* Fixed potential crashes during shutdown and (in rare conditions) when other
nodes disconnected from the VPN.
* Improved NAT handling: tinc now copes with mangled port numbers, and will
automatically fall back to TCP if direct UDP connection between nodes is not
- possible.
+ possible. The TCPOnly option should not have to be used anymore.
* Allow configuration files with CRLF line endings to be read on UNIX.
- * Disable old RSA keys when generating new ones.
+ * Disable old RSA keys when generating new ones, and raise the default size of
+ new RSA keys to 2048 bits.
- * Many fixes in the path MTU discovery code.
+ * Many fixes in the path MTU discovery code, especially when Compression is
+ being used.
* Tinc can now drop privileges and/or chroot itself.
-This is the README file for tinc version 1.0.11. Installation
+This is the README file for tinc version 1.1-cvs. Installation
instructions may be found in the INSTALL file.
tinc is Copyright (C) 1998-2009 by:
Compatibility
-------------
-Version 1.0.11 is compatible with 1.0pre8, 1.0 and later, but not with older
+Version 1.1-cvs is compatible with 1.0pre8, 1.0 and later, but not with older
versions of tinc.
library whether or not you plan to enable compression. You can find it at
http://www.oberhumer.com/opensource/lzo/.
+Since 1.1, the libevent library is used for the main event loop. You can find
+it at http://monkey.org/~provos/libevent/.
+
In order to compile tinc, you will need a GNU C compiler environment.
IPv6 addresses (without using :: abbreviations) and use ifconfig or ip (from
the iproute package) to give the virtual network interface corresponding IPv6
addresses. tinc does not provide autoconfiguration for IPv6 hosts, if you need
- it use radvd or zebra. Tunneling IPv6 packets only works on Linux, FreeBSD,
- Windows and possibly OpenBSD.
+ it use radvd or zebra.
It is also possible to make tunnels to other tinc daemons over IPv6 networks,
if the operating system supports IPv6. tinc will automatically use both IPv6
## Produce this file with automake to get Makefile.in
-sbin_PROGRAMS = tincd
+sbin_PROGRAMS = tincd tincctl
EXTRA_DIST = linux/device.c bsd/device.c solaris/device.c cygwin/device.c mingw/device.c mingw/common.h raw_socket/device.c uml_socket/device.c
-tincd_SOURCES = conf.c connection.c edge.c event.c graph.c logger.c meta.c net.c net_packet.c net_setup.c \
+tincd_SOURCES = cipher.c conf.c connection.c control.c crypto.c digest.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 subnet.c tincd.c
+ protocol_key.c protocol_subnet.c route.c rsa.c subnet.c tincd.c
+
+tincctl_SOURCES = tincctl.c rsagen.c
if TUNEMU
tincd_SOURCES += bsd/tunemu.c
INCLUDES = @INCLUDES@ -I$(top_builddir) -I$(top_srcdir)/lib
-noinst_HEADERS = conf.h connection.h device.h edge.h event.h graph.h logger.h meta.h net.h netutl.h node.h process.h \
- protocol.h route.h subnet.h bsd/tunemu.h
+noinst_HEADERS = cipher.h conf.h connection.h control.h crypto.h device.h digest.h edge.h graph.h logger.h meta.h net.h netutl.h node.h process.h \
+ protocol.h route.h rsa.h rsagen.h subnet.h bsd/tunemu.h
-LIBS = @LIBS@
+LIBS = @LIBS@ @LIBGCRYPT_LIBS@
if TUNEMU
LIBS += -lpcap
tincd_LDADD = \
$(top_builddir)/lib/libvpn.a
-AM_CFLAGS = @CFLAGS@ -DCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\"
+tincctl_LDADD = \
+ $(top_builddir)/lib/libvpn.a
+
- localedir = $(datadir)/locale
-
- AM_CFLAGS = @CFLAGS@ -DCONFDIR=\"$(sysconfdir)\" -DLOCALEDIR=\"$(localedir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DSBINDIR=\"$(sbindir)\"
++AM_CFLAGS = @CFLAGS@ -DCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DSBINDIR=\"$(sbindir)\"
dist-hook:
rm -f `find . -type l`
if(routing_mode == RMODE_ROUTER)
overwrite_mac = true;
device_info = "Generic BSD tap device";
+ #ifdef TAPGIFNAME
+ {
+ struct ifreq ifr;
+ if(ioctl(device_fd, TAPGIFNAME, (void*)&ifr) == 0) {
+ if(iface)
+ free(iface);
+ iface = xstrdup(ifr.ifr_name);
+ }
+ }
+
+ #endif
break;
#ifdef HAVE_TUNEMU
case DEVICE_TYPE_TUNEMU:
}
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
#else
- lenin = read(device_fd, packet->data + 14, MTU - 14);
+ inlen = read(device_fd, packet->data + 14, MTU - 14);
#endif
- if(lenin <= 0) {
+ if(inlen <= 0) {
logger(LOG_ERR, "Error while reading from %s %s: %s", device_info,
device, strerror(errno));
return false;
break;
default:
ifdebug(TRAFFIC) logger(LOG_ERR,
- _ ("Unknown IP version %d while reading packet from %s %s"),
+ "Unknown IP version %d while reading packet from %s %s",
packet->data[14] >> 4, device_info, device);
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;
default:
ifdebug(TRAFFIC) logger(LOG_ERR,
- _ ("Unknown address family %x while reading packet from %s %s"),
+ "Unknown address family %x while reading packet from %s %s",
ntohl(type), device_info, device);
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:
ifdebug(TRAFFIC) logger(LOG_DEBUG, "Read packet of %d bytes from %s",
packet->len, device_info);
- logger(LOG_INFO, "E:fd_read");
return true;
}
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];
#ifndef __TINC_CONNECTION_H__
#define __TINC_CONNECTION_H__
-#include <openssl/rsa.h>
-#include <openssl/evp.h>
-
-#include "avl_tree.h"
+#include "cipher.h"
+#include "digest.h"
+#include "rsa.h"
+#include "splay_tree.h"
#define OPTION_INDIRECT 0x0001
#define OPTION_TCPONLY 0x0002
#define OPTION_PMTU_DISCOVERY 0x0004
typedef struct connection_status_t {
- int pinged:1; /* sent ping */
- int active:1; /* 1 if active.. */
- int connecting:1; /* 1 if we are waiting for a non-blocking connect() to finish */
- int termreq:1; /* the termination of this connection was requested */
- int remove:1; /* Set to 1 if you want this connection removed */
- int timeout:1; /* 1 if gotten timeout */
- int encryptout:1; /* 1 if we can encrypt outgoing traffic */
- int decryptin:1; /* 1 if we have to decrypt incoming traffic */
- int mst:1; /* 1 if this connection is part of a minimum spanning tree */
- int unused:23;
+ int pinged:1; /* sent ping */
+ int active:1; /* 1 if active.. */
+ int connecting:1; /* 1 if we are waiting for a non-blocking connect() to finish */
+ int termreq:1; /* the termination of this connection was requested */
+ int remove_unused:1; /* Set to 1 if you want this connection removed */
+ int timeout_unused:1; /* 1 if gotten timeout */
+ int encryptout:1; /* 1 if we can encrypt outgoing traffic */
+ int decryptin:1; /* 1 if we have to decrypt incoming traffic */
+ int mst:1; /* 1 if this connection is part of a minimum spanning tree */
+ int unused:23;
} connection_status_t;
#include "edge.h"
int protocol_version; /* used protocol */
int socket; /* socket used for this connection */
- long int options; /* options for this connection */
+ uint32_t options; /* options for this connection */
connection_status_t status; /* status info */
int estimated_weight; /* estimation for the weight of the edge for this connection */
struct timeval start; /* time this connection was started, used for above estimation */
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 bufferevent *buffer; /* buffer events on this metadata connection */
+ struct event inevent; /* input 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);
extern void free_connection(connection_t *);
extern void connection_add(connection_t *);
extern void connection_del(connection_t *);
-extern void dump_connections(void);
+extern int dump_connections(struct evbuffer *);
extern bool read_connection_config(connection_t *);
#endif /* __TINC_CONNECTION_H__ */
#ifndef __TINC_EDGE_H__
#define __TINC_EDGE_H__
-#include "avl_tree.h"
+#include "splay_tree.h"
#include "connection.h"
#include "net.h"
#include "node.h"
struct node_t *to;
sockaddr_t address;
- long int options; /* options turned on for this edge */
+ uint32_t options; /* options turned on for this edge */
int weight; /* weight of this edge */
struct connection_t *connection; /* connection associated with this edge, if available */
struct edge_t *reverse; /* edge in the opposite direction, if available */
} edge_t;
-extern avl_tree_t *edge_weight_tree; /* Tree with all known edges sorted on weight */
+extern splay_tree_t *edge_weight_tree; /* Tree with all known edges sorted on weight */
extern void init_edges(void);
extern void exit_edges(void);
extern edge_t *new_edge(void) __attribute__ ((__malloc__));
extern void free_edge(edge_t *);
-extern avl_tree_t *new_edge_tree(void) __attribute__ ((__malloc__));
-extern void free_edge_tree(avl_tree_t *);
+extern splay_tree_t *new_edge_tree(void) __attribute__ ((__malloc__));
+extern void free_edge_tree(splay_tree_t *);
extern void edge_add(edge_t *);
extern void edge_del(edge_t *);
extern edge_t *lookup_edge(struct node_t *, struct node_t *);
-extern void dump_edges(void);
+extern int dump_edges(struct evbuffer *);
#endif /* __TINC_EDGE_H__ */
#include "system.h"
-#include <openssl/err.h>
-#include <openssl/evp.h>
-
-#include "avl_tree.h"
+#include "splay_tree.h"
+#include "cipher.h"
#include "connection.h"
#include "logger.h"
#include "meta.h"
#include "xalloc.h"
bool send_meta(connection_t *c, const char *buffer, int length) {
- int outlen;
- int result;
-
if(!c) {
logger(LOG_ERR, "send_meta() called with NULL pointer!");
abort();
ifdebug(META) logger(LOG_DEBUG, "Sending %d bytes of metadata to %s (%s)", length,
c->name, c->hostname);
- if(!c->outbuflen)
- c->last_flushed_time = now;
-
- /* Find room in connection's buffer */
- if(length + c->outbuflen > c->outbufsize) {
- c->outbufsize = length + c->outbuflen;
- c->outbuf = xrealloc(c->outbuf, c->outbufsize);
- }
-
- if(length + c->outbuflen + c->outbufstart > c->outbufsize) {
- memmove(c->outbuf, c->outbuf + c->outbufstart, c->outbuflen);
- c->outbufstart = 0;
- }
-
/* Add our data to buffer */
if(c->status.encryptout) {
- result = EVP_EncryptUpdate(c->outctx, (unsigned char *)c->outbuf + c->outbufstart + c->outbuflen,
- &outlen, (unsigned char *)buffer, length);
- if(!result || outlen < length) {
- logger(LOG_ERR, "Error while encrypting metadata to %s (%s): %s",
- c->name, c->hostname, ERR_error_string(ERR_get_error(), NULL));
- return false;
- } else if(outlen > length) {
- logger(LOG_EMERG, "Encrypted data too long! Heap corrupted!");
- abort();
- }
- c->outbuflen += outlen;
- } else {
- memcpy(c->outbuf + c->outbufstart + c->outbuflen, buffer, length);
- c->outbuflen += length;
- }
-
- return true;
-}
-
-bool flush_meta(connection_t *c) {
- int result;
-
- ifdebug(META) logger(LOG_DEBUG, "Flushing %d bytes to %s (%s)",
- c->outbuflen, c->name, c->hostname);
-
- while(c->outbuflen) {
- result = send(c->socket, c->outbuf + c->outbufstart, c->outbuflen, 0);
- if(result <= 0) {
- if(!errno || errno == EPIPE) {
- ifdebug(CONNECTIONS) logger(LOG_NOTICE, "Connection closed by %s (%s)",
- c->name, c->hostname);
- } else if(errno == EINTR) {
- continue;
- } else if(sockwouldblock(sockerrno)) {
- ifdebug(CONNECTIONS) logger(LOG_DEBUG, "Flushing %d bytes to %s (%s) would block",
- c->outbuflen, c->name, c->hostname);
- return true;
- } else {
- logger(LOG_ERR, "Flushing meta data to %s (%s) failed: %s", c->name,
- c->hostname, sockstrerror(sockerrno));
- }
+ char outbuf[length];
+ size_t outlen = length;
+ if(!cipher_encrypt(&c->outcipher, buffer, length, outbuf, &outlen, false) || outlen != length) {
+ logger(LOG_ERR, "Error while encrypting metadata to %s (%s)",
+ c->name, c->hostname);
return false;
}
-
- c->outbufstart += result;
- c->outbuflen -= result;
+
+ ifdebug(META) logger(LOG_DEBUG, "Encrypted write %p %p %p %d", c, c->buffer, outbuf, length);
+ bufferevent_write(c->buffer, (void *)outbuf, length);
+ ifdebug(META) logger(LOG_DEBUG, "Done.");
+ } else {
+ ifdebug(META) logger(LOG_DEBUG, "Unencrypted write %p %p %p %d", c, c->buffer, buffer, length);
+ bufferevent_write(c->buffer, (void *)buffer, length);
+ ifdebug(META) logger(LOG_DEBUG, "Done.");
}
- c->outbufstart = 0; /* avoid unnecessary memmoves */
return true;
}
void broadcast_meta(connection_t *from, const char *buffer, int length) {
- avl_node_t *node;
+ splay_node_t *node;
connection_t *c;
for(node = connection_tree->head; node; node = node->next) {
}
bool receive_meta(connection_t *c) {
- int oldlen, i, result;
- int lenin, lenout, reqlen;
- bool decrypted = false;
+ size_t inlen;
char inbuf[MAXBUFSIZE];
+ char *bufp = inbuf, *endp;
/* Strategy:
- Read as much as possible from the TCP socket in one go.
- If not, keep stuff in buffer and exit.
*/
- lenin = recv(c->socket, c->buffer + c->buflen, MAXBUFSIZE - c->buflen, 0);
+ inlen = recv(c->socket, inbuf, sizeof inbuf, 0);
- if(lenin <= 0) {
- if(!lenin || !errno) {
+ if(inlen <= 0) {
- logger(LOG_ERR, "Receive callback called for %s (%s) but no data to receive: %s", c->name, c->hostname, strerror(errno));
++ if(!inlen || !errno) {
+ ifdebug(CONNECTIONS) logger(LOG_NOTICE, "Connection closed by %s (%s)",
+ c->name, c->hostname);
+ } else if(sockwouldblock(sockerrno))
+ return true;
+ else
+ logger(LOG_ERR, "Metadata socket read error for %s (%s): %s",
+ c->name, c->hostname, sockstrerror(sockerrno));
-
return false;
}
- oldlen = c->buflen;
- c->buflen += lenin;
+ do {
+ if(!c->status.decryptin) {
+ endp = memchr(bufp, '\n', inlen);
+ if(endp)
+ endp++;
+ else
+ endp = bufp + inlen;
- while(lenin > 0) {
- /* Decrypt */
+ evbuffer_add(c->buffer->input, bufp, endp - bufp);
- if(c->status.decryptin && !decrypted) {
- result = EVP_DecryptUpdate(c->inctx, (unsigned char *)inbuf, &lenout, (unsigned char *)c->buffer + oldlen, lenin);
- if(!result || lenout != lenin) {
- logger(LOG_ERR, "Error while decrypting metadata from %s (%s): %s",
- c->name, c->hostname, ERR_error_string(ERR_get_error(), NULL));
+ inlen -= endp - bufp;
+ bufp = endp;
+ } else {
+ size_t outlen = inlen;
+ ifdebug(META) logger(LOG_DEBUG, "Received encrypted %zu bytes", inlen);
+ evbuffer_expand(c->buffer->input, c->buffer->input->off + inlen);
+
+ if(!cipher_decrypt(&c->incipher, bufp, inlen, c->buffer->input->buffer + c->buffer->input->off, &outlen, false) || inlen != outlen) {
+ logger(LOG_ERR, "Error while decrypting metadata from %s (%s)",
+ c->name, c->hostname);
return false;
}
- memcpy(c->buffer + oldlen, inbuf, lenin);
- decrypted = true;
+ c->buffer->input->off += inlen;
+
+ inlen = 0;
}
- /* Are we receiving a TCPpacket? */
+ while(c->buffer->input->off) {
+ /* Are we receiving a TCPpacket? */
+
+ if(c->tcplen) {
+ if(c->tcplen <= c->buffer->input->off) {
+ receive_tcppacket(c, (char *)c->buffer->input->buffer, c->tcplen);
+ evbuffer_drain(c->buffer->input, c->tcplen);
+ c->tcplen = 0;
+ continue;
+ } else {
+ break;
+ }
+ }
- if(c->tcplen) {
- if(c->tcplen <= c->buflen) {
- receive_tcppacket(c, c->buffer, c->tcplen);
+ /* Otherwise we are waiting for a request */
- c->buflen -= c->tcplen;
- lenin -= c->tcplen - oldlen;
- memmove(c->buffer, c->buffer + c->tcplen, c->buflen);
- oldlen = 0;
- c->tcplen = 0;
+ char *request = evbuffer_readline(c->buffer->input);
+ if(request) {
+ bool result = receive_request(c, request);
+ free(request);
+ if(!result)
+ return false;
continue;
} else {
break;
}
}
-
- /* Otherwise we are waiting for a request */
-
- reqlen = 0;
-
- for(i = oldlen; i < c->buflen; i++) {
- if(c->buffer[i] == '\n') {
- c->buffer[i] = '\0'; /* replace end-of-line by end-of-string so we can use sscanf */
- reqlen = i + 1;
- break;
- }
- }
-
- if(reqlen) {
- c->reqlen = reqlen;
- if(!receive_request(c))
- return false;
-
- c->buflen -= reqlen;
- lenin -= reqlen - oldlen;
- memmove(c->buffer, c->buffer + reqlen, c->buflen);
- oldlen = 0;
- continue;
- } else {
- break;
- }
- }
-
- if(c->buflen >= MAXBUFSIZE) {
- logger(LOG_ERR, "Metadata read buffer overflow for %s (%s)",
- c->name, c->hostname);
- return false;
- }
+ } while(inlen);
- c->last_ping_time = time(NULL);
-
return true;
}
#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;
-
-time_t now = 0;
-
/* Purge edges and subnets of unreachable nodes. Use carefully. */
-static void purge(void) {
- avl_node_t *nnode, *nnext, *enode, *enext, *snode, *snext;
+void purge(void) {
+ splay_node_t *nnode, *nnext, *enode, *enext, *snode, *snext;
node_t *n;
edge_t *e;
subnet_t *s;
}
}
-/*
- 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;
if(c->status.pinged) {
ifdebug(CONNECTIONS) logger(LOG_INFO, "%s (%s) didn't respond to PING in %ld seconds",
c->name, c->hostname, now - c->last_ping_time);
- c->status.timeout = true;
terminate_connection(c, true);
+ continue;
} else if(c->last_ping_time + pinginterval < now) {
send_ping(c);
}
} else {
- if(c->status.remove) {
- logger(LOG_WARNING, "Old connection_t for %s (%s) status %04x still lingering, deleting...",
- c->name, c->hostname, 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;
-
- /* check input from kernel */
- if(device_fd >= 0 && FD_ISSET(device_fd, readset)) {
- if(read_packet(&packet)) {
- packet.priority = 0;
- route(myself, &packet);
- }
- }
-
- /* check meta connections */
- for(node = connection_tree->head; node; node = node->next) {
- c = node->data;
-
- if(c->status.remove)
- continue;
-
- if(FD_ISSET(c->socket, readset)) {
- 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);
- continue;
- }
- }
- 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, strerror(result));
++ 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;
- struct timeval tv;
- int r, maxfd;
- time_t last_ping_check, last_config_check, last_graph_dump;
- event_t *event;
-
- last_ping_check = now;
- last_config_check = now;
- last_graph_dump = now;
-
- srand(now);
-
- running = true;
-
- while(running) {
- now = time(NULL);
-
- // tv.tv_sec = 1 + (rand() & 7); /* Approx. 5 seconds, randomized to prevent global synchronisation effects */
- tv.tv_sec = 1;
- tv.tv_usec = 0;
-
- maxfd = build_fdset(&readset, &writeset);
-
-#ifdef HAVE_MINGW
- LeaveCriticalSection(&mutex);
-#endif
- r = select(maxfd + 1, &readset, &writeset, NULL, &tv);
-#ifdef HAVE_MINGW
- EnterCriticalSection(&mutex);
-#endif
-
- if(r < 0) {
- if(!sockwouldblock(sockerrno)) {
- logger(LOG_ERR, "Error while waiting for input: %s", sockstrerror(sockerrno));
- dump_connections();
- return 1;
- }
-
- continue;
- }
-
- check_network_activity(&readset, &writeset);
-
- if(do_purge) {
- purge();
- do_purge = false;
- }
-
- /* Let's check if everybody is still alive */
-
- if(last_ping_check + pingtimeout < now) {
- check_dead_connections();
- last_ping_check = now;
-
- if(routing_mode == RMODE_SWITCH)
- age_subnets();
-
- age_past_requests();
-
- /* Should we regenerate our key? */
+static void sigterm_handler(int signal, short events, void *data) {
+ logger(LOG_NOTICE, "Got %s signal", strsignal(signal));
+ event_loopexit(NULL);
+}
- if(keyexpires < now) {
- avl_node_t *node;
- node_t *n;
+static void sighup_handler(int signal, short events, void *data) {
+ logger(LOG_NOTICE, "Got %s signal", strsignal(signal));
+ reload_configuration();
+}
- ifdebug(STATUS) logger(LOG_INFO, "Expiring symmetric keys");
+int reload_configuration(void) {
+ connection_t *c;
+ splay_node_t *node, *next;
+ char *fname;
+ struct stat s;
+ static time_t last_config_check = 0;
- for(node = node_tree->head; node; node = node->next) {
- n = node->data;
- if(n->inkey) {
- free(n->inkey);
- n->inkey = NULL;
- }
- }
+ /* Reread our own configuration file */
- send_key_changed(broadcast, myself);
- keyexpires = now + keylifetime;
- }
- }
+ exit_configuration(&config_tree);
+ init_configuration(&config_tree);
- if(sigalrm) {
- logger(LOG_INFO, "Flushing event queue");
- expire_events();
- sigalrm = false;
- }
+ if(!read_server_config()) {
+ logger(LOG_ERR, "Unable to reread configuration file, exitting.");
+ event_loopexit(NULL);
+ return EINVAL;
+ }
- while((event = get_expired_event())) {
- event->handler(event->data);
- free_event(event);
+ /* 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);
+ }
- if(sighup) {
- connection_t *c;
- avl_node_t *node, *next;
- char *fname;
- struct stat s;
-
- sighup = false;
-
- /* Reread our own configuration file */
-
- exit_configuration(&config_tree);
- init_configuration(&config_tree);
-
- if(!read_server_config()) {
- logger(LOG_ERR, "Unable to reread configuration file, exitting.");
- return 1;
- }
-
- /* 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;
+ last_config_check = time(NULL);
- if(outgoing->event)
- event_del(outgoing->event);
- }
+ /* Try to make outgoing connections */
+
+ try_outgoing_connections();
- 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);
- }
+ return 0;
+}
- last_config_check = now;
+void retry(void) {
+ connection_t *c;
+ splay_node_t *node;
- /* Try to make outgoing connections */
-
- try_outgoing_connections();
- }
+ for(node = connection_tree->head; node; node = node->next) {
+ c = node->data;
- /* Dump graph if wanted every 60 seconds*/
-
- if(last_graph_dump + 60 < now) {
- dump_graph();
- last_graph_dump = now;
+ 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);
}
}
+}
+
+/*
+ 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;
+
+ timeout_set(&timeout_event, timeout_handler, &timeout_event);
+ event_add(&timeout_event, &(struct timeval){pingtimeout, 0});
+ signal_set(&sighup_event, SIGHUP, sighup_handler, NULL);
+ signal_add(&sighup_event, NULL);
+ signal_set(&sigterm_event, SIGTERM, sigterm_handler, NULL);
+ signal_add(&sigterm_event, NULL);
+ signal_set(&sigquit_event, SIGQUIT, sigterm_handler, NULL);
+ signal_add(&sigquit_event, NULL);
+
+ if(event_loop(0) < 0) {
+ logger(LOG_ERR, "Error while waiting for input: %s", strerror(errno));
+ return 1;
+ }
+
+ signal_del(&sighup_event);
+ signal_del(&sigterm_event);
+ signal_del(&sigquit_event);
+ event_del(&timeout_event);
return 0;
}
#include "system.h"
-#include <openssl/rand.h>
-#include <openssl/err.h>
-#include <openssl/evp.h>
-#include <openssl/pem.h>
-#include <openssl/hmac.h>
-
#include <zlib.h>
#include LZO1X_H
-#include "avl_tree.h"
+#include "splay_tree.h"
+#include "cipher.h"
#include "conf.h"
#include "connection.h"
+#include "crypto.h"
+#include "digest.h"
#include "device.h"
#include "ethernet.h"
-#include "event.h"
#include "graph.h"
#include "list.h"
#include "logger.h"
#include "utils.h"
#include "xalloc.h"
- #ifdef WSAEMSGSIZE
- #define EMSGSIZE WSAEMSGSIZE
- #endif
-
int keylifetime = 0;
int keyexpires = 0;
static char lzo_wrkmem[LZO1X_999_MEM_COMPRESS > LZO1X_1_MEM_COMPRESS ? LZO1X_999_MEM_COMPRESS : LZO1X_1_MEM_COMPRESS];
#define MAX_SEQNO 1073741824
-void send_mtu_probe(node_t *n) {
+ // mtuprobes == 1..30: initial discovery, send bursts with 1 second interval
+ // mtuprobes == 31: sleep pinginterval seconds
+ // mtuprobes == 32: send 1 burst, sleep pingtimeout second
+ // mtuprobes == 33: no response from other side, restart PMTU discovery process
+
+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) {
- logger(LOG_DEBUG, "Trying to send MTU probe to unreachable node %s (%s)", n->name, n->hostname);
+ 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);
+ n->mtuprobes = 0;
return;
}
+ if(n->mtuprobes > 32) {
+ ifdebug(TRAFFIC) logger(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->minmtu) {
ifdebug(TRAFFIC) logger(LOG_INFO, "No response to MTU probes from %s (%s)", n->name, n->hostname);
+ n->mtuprobes = 0;
return;
}
+ if(n->mtuprobes == 30 || (n->mtuprobes < 30 && n->minmtu >= n->maxmtu)) {
+ if(n->minmtu > n->maxmtu)
+ n->minmtu = n->maxmtu;
+ 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);
+ n->mtuprobes = 31;
+ }
+
+ if(n->mtuprobes == 31) {
+ timeout = pinginterval;
+ goto end;
+ } else if(n->mtuprobes == 32) {
+ timeout = pingtimeout;
+ }
+
for(i = 0; i < 3; i++) {
- if(n->mtuprobes >= 30 || n->minmtu >= n->maxmtu) {
- 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);
- return;
- }
+ if(n->maxmtu <= n->minmtu)
+ len = n->maxmtu;
+ else
+ len = n->minmtu + 1 + rand() % (n->maxmtu - n->minmtu);
- len = n->minmtu + 1 + rand() % (n->maxmtu - n->minmtu);
if(len < 64)
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;
send_udppacket(n, &packet);
}
- event_add(&n->mtuevent, &(struct timeval){1, 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) {
if(!packet->data[0]) {
packet->data[0] = 1;
- send_packet(n, packet);
+ send_udppacket(n, packet);
} else {
+ if(len > n->maxmtu)
+ len = n->maxmtu;
if(n->minmtu < len)
n->minmtu = len;
+ if(n->mtuprobes > 30)
+ n->mtuprobes = 30;
}
}
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, (const char *)&inpkt->seqno + inpkt->len);
}
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];
+ size_t outlen;
int i;
- 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);
- return;
- }
+ if(digest_active(&n->indigest) && !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(inpkt->seqno != n->received_seqno + 1) {
- if(inpkt->seqno >= n->received_seqno + sizeof(n->late) * 8) {
+ if(inpkt->seqno >= n->received_seqno + sizeof n->late * 8) {
logger(LOG_WARNING, "Lost %d packets from %s (%s)",
inpkt->seqno - n->received_seqno - 1, n->name, n->hostname);
- memset(n->late, 0, sizeof(n->late));
+ memset(n->late, 0, sizeof n->late);
} else if (inpkt->seqno <= n->received_seqno) {
- if((n->received_seqno >= sizeof(n->late) * 8 && inpkt->seqno <= n->received_seqno - sizeof(n->late) * 8) || !(n->late[(inpkt->seqno / 8) % sizeof(n->late)] & (1 << inpkt->seqno % 8))) {
+ if((n->received_seqno >= sizeof n->late * 8 && inpkt->seqno <= n->received_seqno - sizeof n->late * 8) || !(n->late[(inpkt->seqno / 8) % sizeof n->late] & (1 << inpkt->seqno % 8))) {
logger(LOG_WARNING, "Got late or replayed packet from %s (%s), seqno %d, last received %d",
n->name, n->hostname, inpkt->seqno, n->received_seqno);
return;
}
} else {
for(i = n->received_seqno + 1; i < inpkt->seqno; i++)
- n->late[(i / 8) % sizeof(n->late)] |= 1 << i % 8;
+ n->late[(i / 8) % sizeof n->late] |= 1 << i % 8;
}
}
- n->late[(inpkt->seqno / 8) % sizeof(n->late)] &= ~(1 << inpkt->seqno % 8);
+ n->late[(inpkt->seqno / 8) % sizeof n->late] &= ~(1 << inpkt->seqno % 8);
if(inpkt->seqno > n->received_seqno)
n->received_seqno = inpkt->seqno;
if(n->received_seqno > MAX_SEQNO)
- keyexpires = 0;
+ regenerate_key();
/* Decompress the packet */
int nextpkt = 0;
vpn_packet_t *outpkt;
int origlen;
- int outlen, outpad;
+ size_t outlen;
static int priority = 0;
int origpriority;
int sock;
if(n->options & OPTION_PMTU_DISCOVERY && inpkt->len > n->minmtu && (inpkt->data[12] | inpkt->data[13])) {
ifdebug(TRAFFIC) logger(LOG_INFO,
- "Packet for %s (%s) larger than minimum MTU, forwarding via TCP",
- n->name, n->hostname);
+ "Packet for %s (%s) larger than minimum MTU, forwarding via %s",
+ n->name, n->hostname, n != n->nexthop ? n->nexthop->name : "TCP");
- send_tcppacket(n->nexthop->connection, origpkt);
+ if(n != n->nexthop)
+ send_packet(n->nexthop, origpkt);
+ else
+ send_tcppacket(n->nexthop->connection, origpkt);
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)) {
+ 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
- if((sendto(listen_socket[sock].udp, (char *) &inpkt->seqno, inpkt->len, 0, &(n->address.sa), SALEN(n->address.sa))) < 0) {
- if(errno == EMSGSIZE) {
+ if(sendto(listen_socket[sock].udp, (char *) &inpkt->seqno, inpkt->len, 0, &(n->address.sa), SALEN(n->address.sa)) < 0 && !sockwouldblock(sockerrno)) {
+ if(sockmsgsize(sockerrno)) {
if(n->maxmtu >= origlen)
n->maxmtu = origlen - 1;
if(n->mtu >= origlen)
n->mtu = origlen - 1;
} else
- logger(LOG_ERR, "Error sending packet to %s (%s): %s", n->name, n->hostname, strerror(errno));
+ logger(LOG_ERR, "Error sending packet to %s (%s): %s", n->name, n->hostname, sockstrerror(sockerrno));
}
end:
/* 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;
+ 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;
- if(sockaddrcmp_noport(from, &e->address))
- continue;
+ if(sockaddrcmp_noport(from, &e->address)) {
+ if(last_hard_try == now)
+ continue;
+ last_hard_try = now;
+ }
if(!n)
n = e->to;
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;
pkt.len = recvfrom(sock, (char *) &pkt.seqno, MAXSIZE, 0, &from.sa, &fromlen);
if(pkt.len < 0) {
- if(errno != EAGAIN && errno != EINTR)
- logger(LOG_ERR, "Receiving packet failed: %s", strerror(errno));
+ if(!sockwouldblock(sockerrno))
+ logger(LOG_ERR, "Receiving packet failed: %s", sockstrerror(sockerrno));
return;
}
receive_udppacket(n, &pkt);
}
+
+void handle_device_data(int sock, short events, void *data) {
+ vpn_packet_t packet;
+
+ if(read_packet(&packet))
+ route(myself, &packet);
+}
#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 "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;
bool read_rsa_public_key(connection_t *c) {
FILE *fp;
char *fname;
- char *key;
-
- if(!c->rsa_key) {
- c->rsa_key = RSA_new();
-// RSA_blinding_on(c->rsa_key, NULL);
- }
+ char *n;
+ bool result;
/* First, check for simple PublicKey statement */
- if(get_config_string(lookup_config(c->config_tree, "PublicKey"), &key)) {
- BN_hex2bn(&c->rsa_key->n, key);
- BN_hex2bn(&c->rsa_key->e, "FFFF");
- free(key);
- return true;
+ if(get_config_string(lookup_config(c->config_tree, "PublicKey"), &n)) {
+ result = rsa_set_hex_public_key(&c->rsa, n, "FFFF");
+ free(n);
+ return result;
}
/* Else, check for PublicKeyFile statement and read it */
- if(get_config_string(lookup_config(c->config_tree, "PublicKeyFile"), &fname)) {
- fp = fopen(fname, "r");
-
- if(!fp) {
- logger(LOG_ERR, "Error reading RSA public key file `%s': %s",
- fname, strerror(errno));
- free(fname);
- return false;
- }
-
- free(fname);
- c->rsa_key = PEM_read_RSAPublicKey(fp, &c->rsa_key, NULL, NULL);
- fclose(fp);
-
- if(c->rsa_key)
- return true; /* Woohoo. */
-
- /* If it fails, try PEM_read_RSA_PUBKEY. */
- fp = fopen(fname, "r");
+ if(!get_config_string(lookup_config(c->config_tree, "PublicKeyFile"), &fname))
+ xasprintf(&fname, "%s/hosts/%s", confbase, c->name);
- if(!fp) {
- logger(LOG_ERR, "Error reading RSA public key file `%s': %s",
- fname, strerror(errno));
- free(fname);
- return false;
- }
-
- free(fname);
- c->rsa_key = PEM_read_RSA_PUBKEY(fp, &c->rsa_key, NULL, NULL);
- fclose(fp);
-
- if(c->rsa_key) {
-// RSA_blinding_on(c->rsa_key, NULL);
- return true;
- }
+ fp = fopen(fname, "r");
- logger(LOG_ERR, "Reading RSA public key file `%s' failed: %s",
+ if(!fp) {
+ logger(LOG_ERR, "Error reading RSA public key file `%s': %s",
fname, strerror(errno));
+ free(fname);
return false;
}
- /* Else, check if a harnessed public key is in the config file */
-
- xasprintf(&fname, "%s/hosts/%s", confbase, c->name);
- fp = fopen(fname, "r");
-
- if(fp) {
- c->rsa_key = PEM_read_RSAPublicKey(fp, &c->rsa_key, NULL, NULL);
- fclose(fp);
- }
-
- free(fname);
-
- if(c->rsa_key)
- return true;
-
- /* Try again with PEM_read_RSA_PUBKEY. */
-
- xasprintf(&fname, "%s/hosts/%s", confbase, c->name);
- fp = fopen(fname, "r");
-
- if(fp) {
- c->rsa_key = PEM_read_RSA_PUBKEY(fp, &c->rsa_key, NULL, NULL);
-// RSA_blinding_on(c->rsa_key, NULL);
- fclose(fp);
- }
+ result = rsa_read_pem_public_key(&c->rsa, fp);
+ fclose(fp);
+ if(!result)
+ logger(LOG_ERR, "Reading RSA public key file `%s' failed: %s", fname, strerror(errno));
free(fname);
-
- if(c->rsa_key)
- return true;
-
- logger(LOG_ERR, "No public key for %s specified!", c->name);
-
- return false;
+ return result;
}
-bool read_rsa_private_key(void) {
+bool read_rsa_private_key() {
FILE *fp;
- char *fname, *key, *pubkey;
- struct stat s;
+ char *fname;
+ char *n, *d;
+ bool result;
+
+ /* First, check for simple PrivateKey statement */
- if(get_config_string(lookup_config(config_tree, "PrivateKey"), &key)) {
- if(!get_config_string(lookup_config(myself->connection->config_tree, "PublicKey"), &pubkey)) {
+ if(get_config_string(lookup_config(config_tree, "PrivateKey"), &d)) {
+ if(!get_config_string(lookup_config(myself->connection->config_tree, "PublicKey"), &n)) {
logger(LOG_ERR, "PrivateKey used but no PublicKey found!");
+ free(d);
return false;
}
- myself->connection->rsa_key = RSA_new();
-// RSA_blinding_on(myself->connection->rsa_key, NULL);
- BN_hex2bn(&myself->connection->rsa_key->d, key);
- BN_hex2bn(&myself->connection->rsa_key->n, pubkey);
- BN_hex2bn(&myself->connection->rsa_key->e, "FFFF");
- free(key);
- free(pubkey);
+ result = rsa_set_hex_private_key(&myself->connection->rsa, n, "FFFF", d);
+ free(n);
+ free(d);
return true;
}
+ /* Else, check for PrivateKeyFile statement and read it */
+
if(!get_config_string(lookup_config(config_tree, "PrivateKeyFile"), &fname))
xasprintf(&fname, "%s/rsa_key.priv", confbase);
}
#if !defined(HAVE_MINGW) && !defined(HAVE_CYGWIN)
+ struct stat s;
+
if(fstat(fileno(fp), &s)) {
- logger(LOG_ERR, "Could not stat RSA private key file `%s': %s'",
- fname, strerror(errno));
+ logger(LOG_ERR, "Could not stat RSA private key file `%s': %s'", fname, strerror(errno));
free(fname);
return false;
}
logger(LOG_WARNING, "Warning: insecure file permissions for RSA private key file `%s'!", fname);
#endif
- myself->connection->rsa_key = PEM_read_RSAPrivateKey(fp, NULL, NULL, NULL);
+ result = rsa_read_pem_private_key(&myself->connection->rsa, fp);
fclose(fp);
- if(!myself->connection->rsa_key) {
- logger(LOG_ERR, "Reading RSA private key file `%s' failed: %s",
- fname, strerror(errno));
- free(fname);
- return false;
+ if(!result)
+ logger(LOG_ERR, "Reading RSA private key file `%s' failed: %s", fname, strerror(errno));
+ free(fname);
+ return result;
+}
+
+static struct event keyexpire_event;
+
+static void keyexpire_handler(int fd, short events, void *data) {
+ regenerate_key();
+}
+
+void regenerate_key() {
+ if(timeout_initialized(&keyexpire_event)) {
+ ifdebug(STATUS) logger(LOG_INFO, "Expiring symmetric keys");
+ event_del(&keyexpire_event);
+ send_key_changed(broadcast, myself);
+ } else {
+ timeout_set(&keyexpire_event, keyexpire_handler, NULL);
}
- free(fname);
- return true;
+ event_add(&keyexpire_event, &(struct timeval){keylifetime, 0});
}
/*
/* Generate packet encryption key */
- if(get_config_string
- (lookup_config(myself->connection->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(!get_config_string(lookup_config(myself->connection->config_tree, "Cipher"), &cipher))
- cipher = xstrdup("aes256");
++ cipher = xstrdup("blowfish");
- if(myself->incipher)
- myself->inkeylength = myself->incipher->key_len + myself->incipher->iv_len;
- else
- myself->inkeylength = 1;
-
- myself->connection->outcipher = EVP_bf_ofb();
+ if(!cipher_open_by_name(&myself->incipher, cipher)) {
+ logger(LOG_ERR, "Unrecognized cipher type!");
+ return false;
+ }
if(!get_config_int(lookup_config(config_tree, "KeyExpire"), &keylifetime))
keylifetime = 3600;
- keyexpires = now + keylifetime;
-
+ regenerate_key();
+
/* Check if we want to use message authentication codes... */
- if(get_config_string(lookup_config(myself->connection->config_tree, "Digest"), &digest)) {
- if(!strcasecmp(digest, "none")) {
- myself->indigest = NULL;
- } else {
- myself->indigest = EVP_get_digestbyname(digest);
+ if(!get_config_string(lookup_config(myself->connection->config_tree, "Digest"), &digest))
- digest = xstrdup("sha256");
++ digest = xstrdup("sha1");
- if(!myself->indigest) {
- logger(LOG_ERR, "Unrecognized digest type!");
- return false;
- }
- }
- } else
- myself->indigest = EVP_sha1();
-
- myself->connection->outdigest = EVP_sha1();
-
- if(get_config_int(lookup_config(myself->connection->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;
+ int maclength = 4;
+ get_config_int(lookup_config(myself->connection->config_tree, "MACLength"), &maclength);
+
+ if(maclength < 0) {
+ logger(LOG_ERR, "Bogus MAC length!");
+ return false;
+ }
- myself->connection->outmaclength = 0;
+ if(!digest_open_by_name(&myself->indigest, digest, maclength)) {
+ logger(LOG_ERR, "Unrecognized digest type!");
+ return false;
+ }
/* Compression */
if(!setup_device())
return false;
+ event_set(&device_ev, device_fd, EV_READ|EV_PERSIST, handle_device_data, NULL);
+
+ if (event_add(&device_ev, NULL) < 0) {
+ logger(LOG_ERR, "event_add failed: %s", strerror(errno));
+ close_device();
+ return false;
+ }
+
/* Run tinc-up script to further initialize the tap interface */
xasprintf(&envp[0], "NETNAME=%s", netname ? : "");
xasprintf(&envp[1], "DEVICE=%s", device ? : "");
listen_socket[listen_sockets].udp =
setup_vpn_in_socket((sockaddr_t *) aip->ai_addr);
- if(listen_socket[listen_sockets].udp < 0)
+ if(listen_socket[listen_sockets].udp < 0) {
+ close(listen_socket[listen_sockets].tcp);
continue;
+ }
+
+ event_set(&listen_socket[listen_sockets].ev_tcp,
+ listen_socket[listen_sockets].tcp,
+ EV_READ|EV_PERSIST,
+ handle_new_meta_connection, NULL);
+ if(event_add(&listen_socket[listen_sockets].ev_tcp, NULL) < 0) {
+ logger(LOG_EMERG, "event_add failed: %s", strerror(errno));
+ abort();
+ }
+
+ event_set(&listen_socket[listen_sockets].ev_udp,
+ listen_socket[listen_sockets].udp,
+ EV_READ|EV_PERSIST,
+ handle_incoming_vpn_data, NULL);
+ if(event_add(&listen_socket[listen_sockets].ev_udp, NULL) < 0) {
+ logger(LOG_EMERG, "event_add failed: %s", strerror(errno));
+ abort();
+ }
ifdebug(CONNECTIONS) {
hostname = sockaddr2hostname((sockaddr_t *) aip->ai_addr);
memcpy(&listen_socket[listen_sockets].sa, aip->ai_addr, aip->ai_addrlen);
listen_sockets++;
+
+ if(listen_sockets >= MAXSOCKETS) {
+ logger(LOG_WARNING, "Maximum of %d listening sockets reached", MAXSOCKETS);
+ break;
+ }
}
freeaddrinfo(ai);
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;
for(node = connection_tree->head; node; node = next) {
next = node->next;
c = node->data;
- c->outgoing = NULL;
+ c->outgoing = false;
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);
#include "system.h"
-#include "avl_tree.h"
+#include "splay_tree.h"
#include "conf.h"
#include "connection.h"
-#include "event.h"
#include "logger.h"
#include "meta.h"
#include "net.h"
#include <assert.h>
- #ifdef WSAEINPROGRESS
- #define EINPROGRESS WSAEINPROGRESS
- #endif
-
/* Needed on Mac OS/X */
#ifndef SOL_TCP
#define SOL_TCP IPPROTO_TCP
unsigned long arg = 1;
if(ioctlsocket(c->socket, FIONBIO, &arg) != 0) {
- logger(LOG_ERR, "ioctlsocket for %s: WSA error %d", c->hostname, WSAGetLastError());
+ logger(LOG_ERR, "ioctlsocket for %s: %d", c->hostname, sockstrerror(sockerrno));
}
#endif
#if defined(SOL_TCP) && defined(TCP_NODELAY)
option = 1;
- setsockopt(c->socket, SOL_TCP, TCP_NODELAY, &option, sizeof(option));
+ setsockopt(c->socket, SOL_TCP, TCP_NODELAY, &option, sizeof option);
#endif
#if defined(SOL_IP) && defined(IP_TOS) && defined(IPTOS_LOWDELAY)
option = IPTOS_LOWDELAY;
- setsockopt(c->socket, SOL_IP, IP_TOS, &option, sizeof(option));
+ setsockopt(c->socket, SOL_IP, IP_TOS, &option, sizeof option);
#endif
}
if(status) {
- logger(LOG_ERR, "Can't bind to %s/tcp: %s", node,
- strerror(errno));
+ logger(LOG_ERR, "Can't bind to %s/tcp: %s", node, sockstrerror(sockerrno));
} else ifdebug(CONNECTIONS) {
logger(LOG_DEBUG, "Successfully bound outgoing "
"TCP socket to %s", node);
nfd = socket(sa->sa.sa_family, SOCK_STREAM, IPPROTO_TCP);
if(nfd < 0) {
- ifdebug(STATUS) logger(LOG_ERR, "Creating metasocket failed: %s", strerror(errno));
+ ifdebug(STATUS) logger(LOG_ERR, "Creating metasocket failed: %s", sockstrerror(sockerrno));
return -1;
}
/* Optimize TCP settings */
option = 1;
- setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option));
+ setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof option);
#if defined(SOL_IPV6) && defined(IPV6_V6ONLY)
if(sa->sa.sa_family == AF_INET6)
#if defined(SOL_SOCKET) && defined(SO_BINDTODEVICE)
struct ifreq ifr;
- memset(&ifr, 0, sizeof(ifr));
+ memset(&ifr, 0, sizeof ifr);
strncpy(ifr.ifr_ifrn.ifrn_name, iface, IFNAMSIZ);
- if(setsockopt(nfd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr))) {
+ if(setsockopt(nfd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof ifr)) {
closesocket(nfd);
logger(LOG_ERR, "Can't bind to interface %s: %s", iface,
- strerror(errno));
+ strerror(sockerrno));
return -1;
}
#else
if(bind(nfd, &sa->sa, SALEN(sa->sa))) {
closesocket(nfd);
addrstr = sockaddr2hostname(sa);
- logger(LOG_ERR, "Can't bind to %s/tcp: %s", addrstr,
- strerror(errno));
+ logger(LOG_ERR, "Can't bind to %s/tcp: %s", addrstr, sockstrerror(sockerrno));
free(addrstr);
return -1;
}
if(listen(nfd, 3)) {
closesocket(nfd);
- logger(LOG_ERR, "System call `%s' failed: %s", "listen",
- strerror(errno));
+ logger(LOG_ERR, "System call `%s' failed: %s", "listen", sockstrerror(sockerrno));
return -1;
}
nfd = socket(sa->sa.sa_family, SOCK_DGRAM, IPPROTO_UDP);
if(nfd < 0) {
- logger(LOG_ERR, "Creating UDP socket failed: %s", strerror(errno));
+ logger(LOG_ERR, "Creating UDP socket failed: %s", sockstrerror(sockerrno));
return -1;
}
unsigned long arg = 1;
if(ioctlsocket(nfd, FIONBIO, &arg) != 0) {
closesocket(nfd);
- logger(LOG_ERR, "Call to `%s' failed: WSA error %d", "ioctlsocket",
- WSAGetLastError());
+ logger(LOG_ERR, "Call to `%s' failed: %s", "ioctlsocket", sockstrerror(sockerrno));
return -1;
}
}
#endif
option = 1;
- setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option));
+ setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof option);
#if defined(SOL_IPV6) && defined(IPV6_V6ONLY)
if(sa->sa.sa_family == AF_INET6)
option = IP_PMTUDISC_DO;
setsockopt(nfd, SOL_IP, IP_MTU_DISCOVER, &option, sizeof(option));
}
+ #elif defined(IPPROTO_IP) && defined(IP_DONTFRAGMENT)
+ if(myself->options & OPTION_PMTU_DISCOVERY) {
+ option = 1;
+ setsockopt(nfd, IPPROTO_IP, IP_DONTFRAGMENT, &option, sizeof(option));
+ }
#endif
#if defined(SOL_IPV6) && defined(IPV6_MTU_DISCOVER) && defined(IPV6_PMTUDISC_DO)
if(bind(nfd, &sa->sa, SALEN(sa->sa))) {
closesocket(nfd);
addrstr = sockaddr2hostname(sa);
- logger(LOG_ERR, "Can't bind to %s/udp: %s", addrstr,
- strerror(errno));
+ logger(LOG_ERR, "Can't bind to %s/udp: %s", addrstr, sockstrerror(sockerrno));
free(addrstr);
return -1;
}
return nfd;
} /* int setup_vpn_in_socket */
+static void retry_outgoing_handler(int fd, short events, void *data) {
+ setup_outgoing_connection(data);
+}
+
void retry_outgoing(outgoing_t *outgoing) {
outgoing->timeout += 5;
if(outgoing->timeout > maxtimeout)
outgoing->timeout = maxtimeout;
- if(outgoing->event)
- event_del(outgoing->event);
- outgoing->event = new_event();
- outgoing->event->handler = (event_handler_t) setup_outgoing_connection;
- outgoing->event->time = now + outgoing->timeout;
- outgoing->event->data = outgoing;
- event_add(outgoing->event);
+ timeout_set(&outgoing->ev, retry_outgoing_handler, outgoing);
+ event_add(&outgoing->ev, &(struct timeval){outgoing->timeout, 0});
ifdebug(CONNECTIONS) logger(LOG_NOTICE,
"Trying to re-establish outgoing connection in %d seconds",
configure_tcp(c);
- c->last_ping_time = now;
+ c->last_ping_time = time(NULL);
+ c->status.connecting = false;
send_id(c);
}
char *address, *port;
int result;
+ if(!c->outgoing) {
+ logger(LOG_ERR, "do_outgoing_connection() for %s called without c->outgoing", c->name);
+ abort();
+ }
+
begin:
if(!c->outgoing->ai) {
if(!c->outgoing->cfg) {
ifdebug(CONNECTIONS) logger(LOG_ERR, "Could not set up a meta connection to %s",
c->name);
- c->status.remove = true;
retry_outgoing(c->outgoing);
c->outgoing = NULL;
+ connection_del(c);
return;
}
c->socket = socket(c->address.sa.sa_family, SOCK_STREAM, IPPROTO_TCP);
if(c->socket == -1) {
- ifdebug(CONNECTIONS) logger(LOG_ERR, "Creating socket for %s failed: %s", c->hostname,
- strerror(errno));
-
+ ifdebug(CONNECTIONS) logger(LOG_ERR, "Creating socket for %s failed: %s", c->hostname, sockstrerror(sockerrno));
goto begin;
}
result = connect(c->socket, &c->address.sa, SALEN(c->address.sa));
if(result == -1) {
- if(errno == EINPROGRESS
- #if defined(WIN32) && !defined(O_NONBLOCK)
- || WSAGetLastError() == WSAEWOULDBLOCK
- #endif
- ) {
+ if(sockinprogress(sockerrno)) {
c->status.connecting = true;
return;
}
closesocket(c->socket);
- ifdebug(CONNECTIONS) logger(LOG_ERR, "%s: %s", c->hostname, strerror(errno));
+ ifdebug(CONNECTIONS) logger(LOG_ERR, "%s: %s", c->hostname, sockstrerror(sockerrno));
goto begin;
}
return;
}
+void handle_meta_read(struct bufferevent *event, void *data) {
+ logger(LOG_EMERG, "handle_meta_read() called");
+ abort();
+}
+
+void handle_meta_write(struct bufferevent *event, void *data) {
+ ifdebug(META) logger(LOG_DEBUG, "handle_meta_write() called");
+}
+
+void handle_meta_connection_error(struct bufferevent *event, short what, void *data) {
+ connection_t *c = data;
+ logger(LOG_EMERG, "handle_meta_connection_error() called: %d: %s", what, strerror(errno));
+ terminate_connection(c, c->status.active);
+}
+
void setup_outgoing_connection(outgoing_t *outgoing) {
connection_t *c;
node_t *n;
- outgoing->event = NULL;
++ event_del(&outgoing->ev);
+
n = lookup_node(outgoing->name);
if(n)
}
c->outgoing = outgoing;
- c->last_ping_time = now;
+ c->last_ping_time = time(NULL);
connection_add(c);
do_outgoing_connection(c);
+
+ event_set(&c->inevent, c->socket, EV_READ | EV_PERSIST, handle_meta_connection_data, c);
+ event_add(&c->inevent, NULL);
+ c->buffer = bufferevent_new(c->socket, handle_meta_read, handle_meta_write, handle_meta_connection_error, c);
+ if(!c->buffer) {
+ logger(LOG_EMERG, "bufferevent_new() failed: %s", strerror(errno));
+ abort();
+ }
+ bufferevent_disable(c->buffer, EV_READ);
}
/*
accept a new tcp connect and create a
new connection
*/
-bool handle_new_meta_connection(int sock) {
+void handle_new_meta_connection(int sock, short events, void *data) {
connection_t *c;
sockaddr_t sa;
int fd;
- socklen_t len = sizeof(sa);
+ socklen_t len = sizeof sa;
fd = accept(sock, &sa.sa, &len);
if(fd < 0) {
- logger(LOG_ERR, "Accepting a new connection failed: %s", strerror(errno));
+ logger(LOG_ERR, "Accepting a new connection failed: %s", sockstrerror(sockerrno));
- return false;
+ return;
}
sockaddrunmap(&sa);
c->address = sa;
c->hostname = sockaddr2hostname(&sa);
c->socket = fd;
- c->last_ping_time = now;
+ c->last_ping_time = time(NULL);
ifdebug(CONNECTIONS) logger(LOG_NOTICE, "Connection from %s", c->hostname);
+ event_set(&c->inevent, c->socket, EV_READ | EV_PERSIST, handle_meta_connection_data, c);
+ event_add(&c->inevent, NULL);
+ c->buffer = bufferevent_new(c->socket, NULL, handle_meta_write, handle_meta_connection_error, c);
+ if(!c->buffer) {
+ logger(LOG_EMERG, "bufferevent_new() failed: %s", strerror(errno));
+ abort();
+ }
+ bufferevent_disable(c->buffer, EV_READ);
+
configure_tcp(c);
connection_add(c);
c->allow_request = ID;
send_id(c);
-
- return true;
}
void free_outgoing(outgoing_t *outgoing) {
static config_t *cfg = NULL;
char *name;
outgoing_t *outgoing;
- connection_t *c;
- splay_node_t *node;
- if(outgoing_list) {
- for(node = connection_tree->head; node; node = node->next) {
- c = node->data;
- c->outgoing = NULL;
- }
-
- list_delete_list(outgoing_list);
- }
-
outgoing_list = list_alloc((list_action_t)free_outgoing);
for(cfg = lookup_config(config_tree, "ConnectTo"); cfg; cfg = lookup_config_next(config_tree, cfg)) {
continue;
}
- outgoing = xmalloc_and_zero(sizeof(*outgoing));
+ outgoing = xmalloc_and_zero(sizeof *outgoing);
outgoing->name = name;
list_insert_tail(outgoing_list, outgoing);
setup_outgoing_connection(outgoing);
#ifndef __TINC_NODE_H__
#define __TINC_NODE_H__
-#include "avl_tree.h"
+#include <event.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_t {
char *name; /* name of this node */
- long int options; /* options turned on for this node */
+ uint32_t options; /* options turned on for this node */
sockaddr_t address; /* his real (internet) ip to send UDP packets to */
char *hostname; /* the hostname of its real ip */
node_status_t status;
- 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 */
} 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);
extern void node_del(node_t *);
extern node_t *lookup_node(char *);
extern node_t *lookup_node_udp(const sockaddr_t *);
+extern int dump_nodes(struct evbuffer *);
extern void update_node_udp(node_t *, const sockaddr_t *);
-extern void dump_nodes(void);
#endif /* __TINC_NODE_H__ */
#include "conf.h"
#include "connection.h"
+#include "control.h"
#include "device.h"
#include "edge.h"
#include "logger.h"
#include "node.h"
-#include "pidfile.h"
#include "process.h"
#include "subnet.h"
#include "utils.h"
/* If zero, don't detach from the terminal. */
bool do_detach = true;
-bool sighup = false;
bool sigalrm = false;
extern char *identname;
-extern char *pidfilename;
extern char **g_argv;
extern bool use_logfile;
-extern volatile bool running;
sigset_t emptysigset;
-static int saved_debug_level = -1;
-
static void memory_full(int size) {
logger(LOG_ERR, "Memory exhausted (couldn't allocate %d bytes), exitting.", size);
exit(1);
command, NULL, NULL, NULL, NULL, NULL);
if(!service) {
- logger(LOG_ERR, "Could not create %s service: %s", identname, winerror(GetLastError()));
- return false;
+ DWORD lasterror = GetLastError();
+ logger(LOG_ERR, "Could not create %s service: %s", identname, winerror(lasterror));
+ if(lasterror != ERROR_SERVICE_EXISTS)
+ return false;
}
- ChangeServiceConfig2(service, SERVICE_CONFIG_DESCRIPTION, &description);
-
- logger(LOG_INFO, "%s service installed", identname);
+ if(service) {
+ ChangeServiceConfig2(service, SERVICE_CONFIG_DESCRIPTION, &description);
+ logger(LOG_INFO, "%s service installed", identname);
+ } else {
+ service = OpenService(manager, identname, SERVICE_ALL_ACCESS);
+ }
if(!StartService(service, 0, NULL))
logger(LOG_WARNING, "Could not start %s service: %s", identname, winerror(GetLastError()));
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) {
}
#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 */
-
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());
*/
#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));
close_network_connections();
sleep(5);
- remove_pid(pidfilename);
+ exit_control();
execvp(g_argv[0], g_argv);
} else {
logger(LOG_NOTICE, "Not restarting.");
}
}
-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));
}
int signal;
void (*handler)(int);
} sighandlers[] = {
- {SIGHUP, sighup_handler},
- {SIGTERM, sigterm_handler},
- {SIGQUIT, sigquit_handler},
{SIGSEGV, fatal_signal_handler},
{SIGBUS, fatal_signal_handler},
{SIGILL, fatal_signal_handler},
{SIGPIPE, ignore_signal_handler},
- {SIGINT, sigint_handler},
- {SIGUSR1, sigusr1_handler},
- {SIGUSR2, sigusr2_handler},
{SIGCHLD, ignore_signal_handler},
- {SIGALRM, sigalrm_handler},
- {SIGWINCH, sigwinch_handler},
{0, NULL}
};
#endif
/* If we didn't detach, allow coredumps */
if(!do_detach)
- sighandlers[3].handler = SIG_DFL;
+ sighandlers[0].handler = SIG_DFL;
/* Then, for each known signal that we want to catch, assign a
handler to the signal, with error checking this time. */
#include "system.h"
-#include <openssl/sha.h>
-#include <openssl/rand.h>
-#include <openssl/err.h>
-#include <openssl/evp.h>
-
-#include "avl_tree.h"
+#include "splay_tree.h"
#include "conf.h"
#include "connection.h"
+#include "crypto.h"
#include "edge.h"
#include "graph.h"
#include "logger.h"
#include "netutl.h"
#include "node.h"
#include "protocol.h"
+#include "rsa.h"
#include "utils.h"
#include "xalloc.h"
bool send_id(connection_t *c) {
+ gettimeofday(&c->start, NULL);
+
return send_request(c, "%d %s %d", ID, myself->connection->name,
myself->connection->protocol_version);
}
-bool id_h(connection_t *c) {
+bool id_h(connection_t *c, char *request) {
char name[MAX_STRING_SIZE];
- if(sscanf(c->buffer, "%*d " MAX_STRING " %d", name, &c->protocol_version) != 2) {
+ if(sscanf(request, "%*d " MAX_STRING " %d", name, &c->protocol_version) != 2) {
logger(LOG_ERR, "Got bad %s from %s (%s)", "ID", c->name,
c->hostname);
return false;
}
bool send_metakey(connection_t *c) {
- char *buffer;
- int len;
- bool x;
-
- len = RSA_size(c->rsa_key);
+ size_t len = rsa_size(&c->rsa);
+ char key[len];
+ char enckey[len];
+ char hexkey[2 * len + 1];
- /* Allocate buffers for the meta key */
-
- buffer = alloca(2 * len + 1);
+ if(!cipher_open_blowfish_ofb(&c->outcipher))
+ return false;
- c->outkey = xrealloc(c->outkey, len);
-
- if(!c->outctx)
- c->outctx = xmalloc_and_zero(sizeof(*c->outctx));
+ if(!digest_open_sha1(&c->outdigest, -1))
+ return false;
- /* Copy random data to the buffer */
+ /* Create a random key */
- RAND_pseudo_bytes((unsigned char *)c->outkey, len);
+ randomize(key, len);
/* The message we send must be smaller than the modulus of the RSA key.
By definition, for a key of k bits, the following formula holds:
This can be done by setting the most significant bit to zero.
*/
- c->outkey[0] &= 0x7F;
+ key[0] &= 0x7F;
+
+ cipher_set_key_from_rsa(&c->outcipher, key, len, true);
ifdebug(SCARY_THINGS) {
- bin2hex(c->outkey, buffer, len);
- buffer[len * 2] = '\0';
- logger(LOG_DEBUG, "Generated random meta key (unencrypted): %s",
- buffer);
+ bin2hex(key, hexkey, len);
+ hexkey[len * 2] = '\0';
+ logger(LOG_DEBUG, "Generated random meta key (unencrypted): %s", hexkey);
}
/* Encrypt the random data
with a length equal to that of the modulus of the RSA key.
*/
- if(RSA_public_encrypt(len, (unsigned char *)c->outkey, (unsigned char *)buffer, c->rsa_key, RSA_NO_PADDING) != len) {
- logger(LOG_ERR, "Error during encryption of meta key for %s (%s)",
- c->name, c->hostname);
+ if(!rsa_public_encrypt(&c->rsa, key, len, enckey)) {
+ logger(LOG_ERR, "Error during encryption of meta key for %s (%s)", c->name, c->hostname);
return false;
}
/* Convert the encrypted random data to a hexadecimal formatted string */
- bin2hex(buffer, buffer, len);
- buffer[len * 2] = '\0';
+ bin2hex(enckey, hexkey, len);
+ hexkey[len * 2] = '\0';
/* Send the meta key */
- x = send_request(c, "%d %d %d %d %d %s", METAKEY,
- c->outcipher ? c->outcipher->nid : 0,
- c->outdigest ? c->outdigest->type : 0, c->outmaclength,
- c->outcompression, buffer);
-
- /* Further outgoing requests are encrypted with the key we just generated */
-
- if(c->outcipher) {
- if(!EVP_EncryptInit(c->outctx, c->outcipher,
- (unsigned char *)c->outkey + len - c->outcipher->key_len,
- (unsigned char *)c->outkey + len - c->outcipher->key_len -
- c->outcipher->iv_len)) {
- logger(LOG_ERR, "Error during initialisation of cipher for %s (%s): %s",
- c->name, c->hostname, ERR_error_string(ERR_get_error(), NULL));
- return false;
- }
-
- c->status.encryptout = true;
- }
-
- return x;
+ bool result = send_request(c, "%d %d %d %d %d %s", METAKEY,
+ cipher_get_nid(&c->outcipher),
+ digest_get_nid(&c->outdigest), c->outmaclength,
+ c->outcompression, hexkey);
+
+ c->status.encryptout = true;
+ return result;
}
-bool metakey_h(connection_t *c) {
- char buffer[MAX_STRING_SIZE];
+bool metakey_h(connection_t *c, char *request) {
+ char hexkey[MAX_STRING_SIZE];
int cipher, digest, maclength, compression;
- int len;
+ size_t len = rsa_size(&myself->connection->rsa);
+ char enckey[len];
+ char key[len];
- if(sscanf(c->buffer, "%*d %d %d %d %d " MAX_STRING, &cipher, &digest, &maclength, &compression, buffer) != 5) {
- logger(LOG_ERR, "Got bad %s from %s (%s)", "METAKEY", c->name,
- c->hostname);
+ if(sscanf(request, "%*d %d %d %d %d " MAX_STRING, &cipher, &digest, &maclength, &compression, hexkey) != 5) {
+ logger(LOG_ERR, "Got bad %s from %s (%s)", "METAKEY", c->name, c->hostname);
return false;
}
- len = RSA_size(myself->connection->rsa_key);
-
/* Check if the length of the meta key is all right */
- if(strlen(buffer) != len * 2) {
+ if(strlen(hexkey) != len * 2) {
logger(LOG_ERR, "Possible intruder %s (%s): %s", c->name, c->hostname, "wrong keylength");
return false;
}
- /* Allocate buffers for the meta key */
-
- c->inkey = xrealloc(c->inkey, len);
-
- if(!c->inctx)
- c->inctx = xmalloc_and_zero(sizeof(*c->inctx));
-
/* Convert the challenge from hexadecimal back to binary */
- hex2bin(buffer, buffer, len);
+ hex2bin(hexkey, enckey, len);
/* Decrypt the meta key */
- if(RSA_private_decrypt(len, (unsigned char *)buffer, (unsigned char *)c->inkey, myself->connection->rsa_key, RSA_NO_PADDING) != len) { /* See challenge() */
- logger(LOG_ERR, "Error during decryption of meta key for %s (%s)",
- c->name, c->hostname);
+ if(!rsa_private_decrypt(&myself->connection->rsa, enckey, len, key)) {
+ logger(LOG_ERR, "Error during decryption of meta key for %s (%s)", c->name, c->hostname);
return false;
}
ifdebug(SCARY_THINGS) {
- bin2hex(c->inkey, buffer, len);
- buffer[len * 2] = '\0';
- logger(LOG_DEBUG, "Received random meta key (unencrypted): %s", buffer);
+ bin2hex(key, hexkey, len);
+ hexkey[len * 2] = '\0';
+ logger(LOG_DEBUG, "Received random meta key (unencrypted): %s", hexkey);
}
- /* All incoming requests will now be encrypted. */
-
/* Check and lookup cipher and digest algorithms */
- if(cipher) {
- c->incipher = EVP_get_cipherbynid(cipher);
-
- if(!c->incipher) {
- logger(LOG_ERR, "%s (%s) uses unknown cipher!", c->name, c->hostname);
- return false;
- }
-
- if(!EVP_DecryptInit(c->inctx, c->incipher,
- (unsigned char *)c->inkey + len - c->incipher->key_len,
- (unsigned char *)c->inkey + len - c->incipher->key_len -
- c->incipher->iv_len)) {
- logger(LOG_ERR, "Error during initialisation of cipher from %s (%s): %s",
- c->name, c->hostname, ERR_error_string(ERR_get_error(), NULL));
- return false;
- }
-
- c->status.decryptin = true;
- } else {
- c->incipher = NULL;
+ if(!cipher_open_by_nid(&c->incipher, cipher) || !cipher_set_key_from_rsa(&c->incipher, key, len, false)) {
+ logger(LOG_ERR, "Error during initialisation of cipher from %s (%s)", c->name, c->hostname);
+ return false;
}
- c->inmaclength = maclength;
-
- if(digest) {
- c->indigest = EVP_get_digestbynid(digest);
-
- if(!c->indigest) {
- logger(LOG_ERR, "Node %s (%s) uses unknown digest!", c->name, c->hostname);
- return false;
- }
-
- if(c->inmaclength > c->indigest->md_size || c->inmaclength < 0) {
- logger(LOG_ERR, "%s (%s) uses bogus MAC length!", c->name, c->hostname);
- return false;
- }
- } else {
- c->indigest = NULL;
+ if(!digest_open_by_nid(&c->indigest, digest, -1)) {
+ logger(LOG_ERR, "Error during initialisation of digest from %s (%s)", c->name, c->hostname);
+ return false;
}
- c->incompression = compression;
+ c->status.decryptin = true;
c->allow_request = CHALLENGE;
}
bool send_challenge(connection_t *c) {
- char *buffer;
- int len;
+ size_t len = rsa_size(&c->rsa);
+ char buffer[len * 2 + 1];
- /* CHECKME: what is most reasonable value for len? */
-
- len = RSA_size(c->rsa_key);
-
- /* Allocate buffers for the challenge */
-
- buffer = alloca(2 * len + 1);
-
- c->hischallenge = xrealloc(c->hischallenge, len);
+ if(!c->hischallenge)
+ c->hischallenge = xrealloc(c->hischallenge, len);
/* Copy random data to the buffer */
- RAND_pseudo_bytes((unsigned char *)c->hischallenge, len);
+ randomize(c->hischallenge, len);
/* Convert to hex */
return send_request(c, "%d %s", CHALLENGE, buffer);
}
-bool challenge_h(connection_t *c) {
+bool challenge_h(connection_t *c, char *request) {
char buffer[MAX_STRING_SIZE];
- int len;
+ size_t len = rsa_size(&myself->connection->rsa);
+ size_t digestlen = digest_length(&c->outdigest);
+ char digest[digestlen];
- if(sscanf(c->buffer, "%*d " MAX_STRING, buffer) != 1) {
- logger(LOG_ERR, "Got bad %s from %s (%s)", "CHALLENGE", c->name,
- c->hostname);
+ if(sscanf(request, "%*d " MAX_STRING, buffer) != 1) {
+ logger(LOG_ERR, "Got bad %s from %s (%s)", "CHALLENGE", c->name, c->hostname);
return false;
}
- len = RSA_size(myself->connection->rsa_key);
-
/* Check if the length of the challenge is all right */
if(strlen(buffer) != len * 2) {
- logger(LOG_ERR, "Possible intruder %s (%s): %s", c->name,
- c->hostname, "wrong challenge length");
+ logger(LOG_ERR, "Possible intruder %s (%s): %s", c->name, c->hostname, "wrong challenge length");
return false;
}
- /* Allocate buffers for the challenge */
-
- c->mychallenge = xrealloc(c->mychallenge, len);
-
/* Convert the challenge from hexadecimal back to binary */
- hex2bin(buffer, c->mychallenge, len);
+ hex2bin(buffer, buffer, len);
c->allow_request = CHAL_REPLY;
- /* Rest is done by send_chal_reply() */
-
- return send_chal_reply(c);
-}
-
-bool send_chal_reply(connection_t *c) {
- char hash[EVP_MAX_MD_SIZE * 2 + 1];
- EVP_MD_CTX ctx;
-
/* Calculate the hash from the challenge we received */
- if(!EVP_DigestInit(&ctx, c->indigest)
- || !EVP_DigestUpdate(&ctx, c->mychallenge, RSA_size(myself->connection->rsa_key))
- || !EVP_DigestFinal(&ctx, (unsigned char *)hash, NULL)) {
- logger(LOG_ERR, "Error during calculation of response for %s (%s): %s",
- c->name, c->hostname, ERR_error_string(ERR_get_error(), NULL));
- return false;
- }
+ digest_create(&c->indigest, buffer, len, digest);
/* Convert the hash to a hexadecimal formatted string */
- bin2hex(hash, hash, c->indigest->md_size);
- hash[c->indigest->md_size * 2] = '\0';
+ bin2hex(digest, buffer, digestlen);
+ buffer[digestlen * 2] = '\0';
/* Send the reply */
- return send_request(c, "%d %s", CHAL_REPLY, hash);
+ return send_request(c, "%d %s", CHAL_REPLY, buffer);
}
-bool chal_reply_h(connection_t *c) {
+bool chal_reply_h(connection_t *c, char *request) {
char hishash[MAX_STRING_SIZE];
- char myhash[EVP_MAX_MD_SIZE];
- EVP_MD_CTX ctx;
- if(sscanf(c->buffer, "%*d " MAX_STRING, hishash) != 1) {
+ if(sscanf(request, "%*d " MAX_STRING, hishash) != 1) {
logger(LOG_ERR, "Got bad %s from %s (%s)", "CHAL_REPLY", c->name,
c->hostname);
return false;
/* Check if the length of the hash is all right */
- if(strlen(hishash) != c->outdigest->md_size * 2) {
- logger(LOG_ERR, "Possible intruder %s (%s): %s", c->name,
- c->hostname, "wrong challenge reply length");
+ if(strlen(hishash) != digest_length(&c->outdigest) * 2) {
+ logger(LOG_ERR, "Possible intruder %s (%s): %s", c->name, c->hostname, "wrong challenge reply length");
return false;
}
/* Convert the hash to binary format */
- hex2bin(hishash, hishash, c->outdigest->md_size);
+ hex2bin(hishash, hishash, digest_length(&c->outdigest));
- /* Calculate the hash from the challenge we sent */
-
- if(!EVP_DigestInit(&ctx, c->outdigest)
- || !EVP_DigestUpdate(&ctx, c->hischallenge, RSA_size(c->rsa_key))
- || !EVP_DigestFinal(&ctx, (unsigned char *)myhash, NULL)) {
- logger(LOG_ERR, "Error during calculation of response from %s (%s): %s",
- c->name, c->hostname, ERR_error_string(ERR_get_error(), NULL));
- return false;
- }
-
- /* Verify the incoming hash with the calculated hash */
-
- if(memcmp(hishash, myhash, c->outdigest->md_size)) {
- logger(LOG_ERR, "Possible intruder %s (%s): %s", c->name,
- c->hostname, "wrong challenge reply");
-
- ifdebug(SCARY_THINGS) {
- bin2hex(myhash, hishash, SHA_DIGEST_LENGTH);
- hishash[SHA_DIGEST_LENGTH * 2] = '\0';
- logger(LOG_DEBUG, "Expected challenge reply: %s", hishash);
- }
+ /* Verify the hash */
+ if(!digest_verify(&c->outdigest, c->hischallenge, rsa_size(&c->rsa), hishash)) {
+ logger(LOG_ERR, "Possible intruder %s (%s): %s", c->name, c->hostname, "wrong challenge reply");
return false;
}
Send an acknowledgement with the rest of the information needed.
*/
+ free(c->hischallenge);
+ c->hischallenge = NULL;
c->allow_request = ACK;
return send_ack(c);
get_config_int(lookup_config(c->config_tree, "Weight"), &c->estimated_weight);
- return send_request(c, "%d %s %d %lx", ACK, myport, c->estimated_weight, c->options);
+ return send_request(c, "%d %s %d %x", ACK, myport, c->estimated_weight, c->options);
}
static void send_everything(connection_t *c) {
- avl_node_t *node, *node2;
+ splay_node_t *node, *node2;
node_t *n;
subnet_t *s;
edge_t *e;
}
}
-bool ack_h(connection_t *c) {
+bool ack_h(connection_t *c, char *request) {
char hisport[MAX_STRING_SIZE];
char *hisaddress, *dummy;
int weight, mtu;
- long int options;
+ uint32_t options;
node_t *n;
- if(sscanf(request, "%*d " MAX_STRING " %d %lx", hisport, &weight, &options) != 3) {
- if(sscanf(c->buffer, "%*d " MAX_STRING " %d %x", hisport, &weight, &options) != 3) {
++ if(sscanf(request, "%*d " MAX_STRING " %d %x", hisport, &weight, &options) != 3) {
logger(LOG_ERR, "Got bad %s from %s (%s)", "ACK", c->name,
c->hostname);
return false;
} else {
if(n->connection) {
/* Oh dear, we already have a connection to this node. */
- ifdebug(CONNECTIONS) logger(LOG_DEBUG, "Established a second connection with %s (%s), closing old connection",
- n->name, n->hostname);
+ ifdebug(CONNECTIONS) logger(LOG_DEBUG, "Established a second connection with %s (%s), closing old connection", n->connection->name, n->connection->hostname);
+
+ if(n->connection->outgoing) {
+ if(c->outgoing)
+ logger(LOG_WARNING, "Two outgoing connections to the same node!");
+ else
+ c->outgoing = n->connection->outgoing;
+
+ n->connection->outgoing = NULL;
+ }
+
terminate_connection(n->connection, false);
/* Run graph algorithm to purge key and make sure up/down scripts are rerun with new IP addresses and stuff */
graph();
#include "system.h"
-#include "avl_tree.h"
+#include "splay_tree.h"
#include "conf.h"
#include "connection.h"
#include "edge.h"
sockaddr2str(&e->address, &address, &port);
- x = send_request(c, "%d %x %s %s %s %s %lx %d", ADD_EDGE, rand(),
+ x = send_request(c, "%d %x %s %s %s %s %x %d", ADD_EDGE, rand(),
e->from->name, e->to->name, address, port,
e->options, e->weight);
free(address);
return x;
}
-bool add_edge_h(connection_t *c) {
+bool add_edge_h(connection_t *c, char *request) {
edge_t *e;
node_t *from, *to;
char from_name[MAX_STRING_SIZE];
char to_address[MAX_STRING_SIZE];
char to_port[MAX_STRING_SIZE];
sockaddr_t address;
- long int options;
+ uint32_t options;
int weight;
- if(sscanf(request, "%*d %*x "MAX_STRING" "MAX_STRING" "MAX_STRING" "MAX_STRING" %lx %d",
- if(sscanf(c->buffer, "%*d %*x "MAX_STRING" "MAX_STRING" "MAX_STRING" "MAX_STRING" %x %d",
++ if(sscanf(request, "%*d %*x "MAX_STRING" "MAX_STRING" "MAX_STRING" "MAX_STRING" %x %d",
from_name, to_name, to_address, to_port, &options, &weight) != 6) {
logger(LOG_ERR, "Got bad %s from %s (%s)", "ADD_EDGE", c->name,
c->hostname);
/* Check if names are valid */
- if(!check_id(from_name)) {
- logger(LOG_ERR, "Got bad %s from %s (%s): %s", "ADD_EDGE", c->name,
- c->hostname, "invalid name");
- return false;
- }
-
- if(!check_id(to_name)) {
+ if(!check_id(from_name) || !check_id(to_name)) {
logger(LOG_ERR, "Got bad %s from %s (%s): %s", "ADD_EDGE", c->name,
c->hostname, "invalid name");
return false;
}
- if(seen_request(c->buffer))
+ if(seen_request(request))
return true;
/* Lookup nodes */
/* Tell the rest about the new edge */
if(!tunnelserver)
- forward_request(c);
+ forward_request(c, request);
/* Run MST before or after we tell the rest? */
e->from->name, e->to->name);
}
-bool del_edge_h(connection_t *c) {
+bool del_edge_h(connection_t *c, char *request) {
edge_t *e;
char from_name[MAX_STRING_SIZE];
char to_name[MAX_STRING_SIZE];
node_t *from, *to;
- if(sscanf(c->buffer, "%*d %*x "MAX_STRING" "MAX_STRING, from_name, to_name) != 2) {
+ if(sscanf(request, "%*d %*x "MAX_STRING" "MAX_STRING, from_name, to_name) != 2) {
logger(LOG_ERR, "Got bad %s from %s (%s)", "DEL_EDGE", c->name,
c->hostname);
return false;
/* Check if names are valid */
- if(!check_id(from_name)) {
- logger(LOG_ERR, "Got bad %s from %s (%s): %s", "DEL_EDGE", c->name,
- c->hostname, "invalid name");
- return false;
- }
-
- if(!check_id(to_name)) {
+ if(!check_id(from_name) || !check_id(to_name)) {
logger(LOG_ERR, "Got bad %s from %s (%s): %s", "DEL_EDGE", c->name,
c->hostname, "invalid name");
return false;
}
- if(seen_request(c->buffer))
+ if(seen_request(request))
return true;
/* Lookup nodes */
/* Tell the rest about the deleted edge */
if(!tunnelserver)
- forward_request(c);
+ forward_request(c, request);
/* Delete the edge */
return send_request(c, "%d %x %s %s", ADD_SUBNET, rand(), subnet->owner->name, netstr);
}
-bool add_subnet_h(connection_t *c) {
+bool add_subnet_h(connection_t *c, char *request) {
char subnetstr[MAX_STRING_SIZE];
char name[MAX_STRING_SIZE];
node_t *owner;
- subnet_t s = {0}, *new;
+ subnet_t s = {0}, *new, *old;
- if(sscanf(c->buffer, "%*d %*x " MAX_STRING " " MAX_STRING, name, subnetstr) != 2) {
+ if(sscanf(request, "%*d %*x " MAX_STRING " " MAX_STRING, name, subnetstr) != 2) {
logger(LOG_ERR, "Got bad %s from %s (%s)", "ADD_SUBNET", c->name,
c->hostname);
return false;
return false;
}
- if(seen_request(c->buffer))
+ if(seen_request(request))
return true;
/* Check if the owner of the new subnet is in the connection list */
for(cfg = lookup_config(c->config_tree, "Subnet"); cfg; cfg = lookup_config_next(c->config_tree, cfg)) {
if(!get_config_subnet(cfg, &allowed))
- return false;
+ continue;
if(!subnet_compare(&s, allowed))
break;
}
if(!cfg) {
- logger(LOG_WARNING, "Unauthorized %s from %s (%s) for %s",
- "ADD_SUBNET", c->name, c->hostname, subnetstr);
- return false;
+ logger(LOG_WARNING, "Ignoring unauthorized %s from %s (%s): %s",
+ "ADD_SUBNET", c->name, c->hostname, subnetstr);
+ return true;
}
free_subnet(allowed);
/* Tell the rest */
if(!tunnelserver)
- forward_request(c);
+ forward_request(c, request);
- old->expires = now;
+ /* Fast handoff of roaming MAC addresses */
+
+ if(s.type == SUBNET_MAC && owner != myself && (old = lookup_subnet(myself, &s)) && old->expires)
++ old->expires = 1;
+
return true;
}
return send_request(c, "%d %x %s %s", DEL_SUBNET, rand(), s->owner->name, netstr);
}
-bool del_subnet_h(connection_t *c) {
+bool del_subnet_h(connection_t *c, char *request) {
char subnetstr[MAX_STRING_SIZE];
char name[MAX_STRING_SIZE];
node_t *owner;
subnet_t s = {0}, *find;
- if(sscanf(c->buffer, "%*d %*x " MAX_STRING " " MAX_STRING, name, subnetstr) != 2) {
+ if(sscanf(request, "%*d %*x " MAX_STRING " " MAX_STRING, name, subnetstr) != 2) {
logger(LOG_ERR, "Got bad %s from %s (%s)", "DEL_SUBNET", c->name,
c->hostname);
return false;
return false;
}
- if(seen_request(c->buffer))
+ if(seen_request(request))
return true;
/* Check if the owner of the subnet being deleted is in the connection list */
/* Tell the rest */
if(!tunnelserver)
- forward_request(c);
+ forward_request(c, request);
/* Finally, delete it. */
#include "system.h"
-#include "avl_tree.h"
+#include "splay_tree.h"
#include "connection.h"
#include "ethernet.h"
#include "ipv4.h"
static const size_t ns_size = sizeof(struct nd_neighbor_solicit);
static const size_t opt_size = sizeof(struct nd_opt_hdr);
+static struct event age_subnets_event;
+
/* RFC 1071 */
static uint16_t inet_checksum(void *data, int len, uint16_t prevsum) {
static bool ratelimit(int frequency) {
static time_t lasttime = 0;
static int count = 0;
+ time_t now = time(NULL);
if(lasttime == now) {
if(++count > frequency)
memcpy(&packet->data[6], &tmp, sizeof tmp);
}
+static void age_subnets(int fd, short events, void *data) {
+ subnet_t *s;
+ connection_t *c;
+ splay_node_t *node, *next, *node2;
+ bool left = false;
+ time_t now = time(NULL);
+
+ for(node = myself->subnet_tree->head; node; node = next) {
+ next = node->next;
+ s = node->data;
+ if(s->expires && s->expires < now) {
+ ifdebug(TRAFFIC) {
+ char netstr[MAXNETSTR];
+ if(net2str(netstr, sizeof netstr, s))
+ logger(LOG_INFO, "Subnet %s expired", netstr);
+ }
+
+ for(node2 = connection_tree->head; node2; node2 = node2->next) {
+ c = node2->data;
+ if(c->status.active)
+ send_del_subnet(c, s);
+ }
+
+ subnet_del(myself, s);
+ } else {
+ if(s->expires)
+ left = true;
+ }
+ }
+
+ if(left)
+ event_add(&age_subnets_event, &(struct timeval){10, 0});
+}
+
static void learn_mac(mac_t *address) {
subnet_t *subnet;
- avl_node_t *node;
+ splay_node_t *node;
connection_t *c;
subnet = lookup_subnet_mac(address);
subnet = new_subnet();
subnet->type = SUBNET_MAC;
- subnet->expires = now + macexpire;
+ subnet->expires = time(NULL) + macexpire;
subnet->net.mac.address = *address;
+ subnet->weight = 10;
subnet_add(myself, subnet);
/* And tell all other tinc daemons it's our MAC */
if(c->status.active)
send_add_subnet(c, subnet);
}
- }
-
- if(subnet->expires)
- subnet->expires = now + macexpire;
-}
-
-void age_subnets(void) {
- subnet_t *s;
- connection_t *c;
- avl_node_t *node, *next, *node2;
-
- for(node = myself->subnet_tree->head; node; node = next) {
- next = node->next;
- s = node->data;
- if(s->expires && s->expires < now) {
- ifdebug(TRAFFIC) {
- char netstr[MAXNETSTR];
- if(net2str(netstr, sizeof netstr, s))
- logger(LOG_INFO, "Subnet %s expired", netstr);
- }
- for(node2 = connection_tree->head; node2; node2 = node2->next) {
- c = node2->data;
- if(c->status.active)
- send_del_subnet(c, s);
- }
-
- subnet_del(myself, s);
- }
+ if(!timeout_initialized(&age_subnets_event))
+ timeout_set(&age_subnets_event, age_subnets, NULL);
+ event_add(&age_subnets_event, &(struct timeval){10, 0});
+ } else {
+ if(subnet->expires)
+ subnet->expires = time(NULL) + macexpire;
}
}
/* Generate checksum */
- checksum = inet_checksum(&pseudo, sizeof(pseudo), ~0);
+ checksum = inet_checksum(&pseudo, sizeof pseudo, ~0);
checksum = inet_checksum(&icmp6, icmp6_size, checksum);
checksum = inet_checksum(packet->data + ether_size + ip6_size + icmp6_size, ntohl(pseudo.length) - icmp6_size, checksum);
/* Generate checksum */
- checksum = inet_checksum(&pseudo, sizeof(pseudo), ~0);
+ checksum = inet_checksum(&pseudo, sizeof pseudo, ~0);
checksum = inet_checksum(&ns, ns_size, checksum);
if(has_opt) {
checksum = inet_checksum(&opt, opt_size, checksum);
/* Generate checksum */
- checksum = inet_checksum(&pseudo, sizeof(pseudo), ~0);
+ checksum = inet_checksum(&pseudo, sizeof pseudo, ~0);
checksum = inet_checksum(&ns, ns_size, checksum);
if(has_opt) {
checksum = inet_checksum(&opt, opt_size, checksum);
/* Check if this is a valid ARP request */
if(ntohs(arp.arp_hrd) != ARPHRD_ETHER || ntohs(arp.arp_pro) != ETH_P_IP ||
- arp.arp_hln != ETH_ALEN || arp.arp_pln != sizeof(addr) || ntohs(arp.arp_op) != ARPOP_REQUEST) {
+ arp.arp_hln != ETH_ALEN || arp.arp_pln != sizeof addr || ntohs(arp.arp_op) != ARPOP_REQUEST) {
ifdebug(TRAFFIC) logger(LOG_WARNING, "Cannot route packet: received unknown type ARP request");
return;
}
memcpy(packet->data, packet->data + ETH_ALEN, ETH_ALEN); /* copy destination address */
packet->data[ETH_ALEN * 2 - 1] ^= 0xFF; /* mangle source address so it looks like it's not from us */
- memcpy(&addr, arp.arp_tpa, sizeof(addr)); /* save protocol addr */
- memcpy(arp.arp_tpa, arp.arp_spa, sizeof(addr)); /* swap destination and source protocol address */
- memcpy(arp.arp_spa, &addr, sizeof(addr)); /* ... */
+ memcpy(&addr, arp.arp_tpa, sizeof addr); /* save protocol addr */
+ memcpy(arp.arp_tpa, arp.arp_spa, sizeof addr); /* swap destination and source protocol address */
+ memcpy(arp.arp_spa, &addr, sizeof addr); /* ... */
memcpy(arp.arp_tha, arp.arp_sha, ETH_ALEN); /* set target hard/proto addr */
memcpy(arp.arp_sha, packet->data + ETH_ALEN, ETH_ALEN); /* add fake source hard addr */
}
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;
break;
default:
ifdebug(TRAFFIC) logger(LOG_ERR,
- _ ("Unknown IP version %d while reading packet from %s %s"),
+ "Unknown IP version %d while reading packet from %s %s",
packet->data[14] >> 4, device_info, device);
return false;
}
- packet->len = lenin + 14;
+ packet->len = inlen + 14;
device_total_in += packet->len;
#include "system.h"
-#include "avl_tree.h"
+#include "splay_tree.h"
#include "device.h"
#include "logger.h"
#include "net.h"
/* lists type of subnet */
-avl_tree_t *subnet_tree;
+splay_tree_t *subnet_tree;
/* Subnet lookup cache */
static bool cache_ipv6_valid[2];
static int cache_ipv6_slot;
+ static mac_t cache_mac_address[2];
+ static subnet_t *cache_mac_subnet[2];
+ static bool cache_mac_valid[2];
+ static int cache_mac_slot;
+
void subnet_cache_flush() {
cache_ipv4_valid[0] = cache_ipv4_valid[1] = false;
cache_ipv6_valid[0] = cache_ipv6_valid[1] = false;
+ cache_mac_valid[0] = cache_mac_valid[1] = false;
}
/* Subnet comparison */
static int subnet_compare_mac(const subnet_t *a, const subnet_t *b) {
int result;
- result = memcmp(&a->net.mac.address, &b->net.mac.address, sizeof(mac_t));
+ result = memcmp(&a->net.mac.address, &b->net.mac.address, sizeof a->net.mac.address);
if(result)
return result;
/* Initialising trees */
void init_subnets(void) {
- subnet_tree = avl_alloc_tree((avl_compare_t) subnet_compare, (avl_action_t) free_subnet);
+ subnet_tree = splay_alloc_tree((splay_compare_t) subnet_compare, (splay_action_t) free_subnet);
subnet_cache_flush();
}
void exit_subnets(void) {
- avl_delete_tree(subnet_tree);
+ splay_delete_tree(subnet_tree);
}
-avl_tree_t *new_subnet_tree(void) {
- return avl_alloc_tree((avl_compare_t) subnet_compare, NULL);
+splay_tree_t *new_subnet_tree(void) {
+ return splay_alloc_tree((splay_compare_t) subnet_compare, NULL);
}
-void free_subnet_tree(avl_tree_t *subnet_tree) {
- avl_delete_tree(subnet_tree);
+void free_subnet_tree(splay_tree_t *subnet_tree) {
+ splay_delete_tree(subnet_tree);
}
/* Allocating and freeing space for subnets */
void subnet_add(node_t *n, subnet_t *subnet) {
subnet->owner = n;
- avl_insert(subnet_tree, subnet);
- avl_insert(n->subnet_tree, subnet);
+ splay_insert(subnet_tree, subnet);
+ splay_insert(n->subnet_tree, subnet);
subnet_cache_flush();
}
void subnet_del(node_t *n, subnet_t *subnet) {
- avl_delete(n->subnet_tree, subnet);
- avl_delete(subnet_tree, subnet);
+ splay_delete(n->subnet_tree, subnet);
+ splay_delete(subnet_tree, subnet);
subnet_cache_flush();
}
/* Subnet lookup routines */
subnet_t *lookup_subnet(const node_t *owner, const subnet_t *subnet) {
- return avl_search(owner->subnet_tree, subnet);
+ return splay_search(owner->subnet_tree, subnet);
}
subnet_t *lookup_subnet_mac(const mac_t *address) {
- subnet_t *p, subnet = {0};
+ subnet_t *p, *r = NULL, subnet = {0};
- avl_node_t *n;
++ splay_node_t *n;
+ int i;
+
+ // Check if this address is cached
+
+ for(i = 0; i < 2; i++) {
+ if(!cache_mac_valid[i])
+ continue;
+ if(!memcmp(address, &cache_mac_address[i], sizeof *address))
+ return cache_mac_subnet[i];
+ }
+
+ // Search all subnets for a matching one
subnet.type = SUBNET_MAC;
subnet.net.mac.address = *address;
subnet.owner = NULL;
- p = splay_search(subnet_tree, &subnet);
+ for(n = subnet_tree->head; n; n = n->next) {
+ p = n->data;
+
+ if(!p || p->type != subnet.type)
+ continue;
+
+ if(!memcmp(address, &p->net.mac.address, sizeof *address)) {
+ r = p;
+ if(p->owner->status.reachable)
+ break;
+ }
+ }
+
+ // Cache the result
+
+ cache_mac_slot = !cache_mac_slot;
+ memcpy(&cache_mac_address[cache_mac_slot], address, sizeof *address);
+ cache_mac_subnet[cache_mac_slot] = r;
+ cache_mac_valid[cache_mac_slot] = true;
- return p;
+ return r;
}
subnet_t *lookup_subnet_ipv4(const ipv4_t *address) {
subnet_t *p, *r = NULL, subnet = {0};
- avl_node_t *n;
+ splay_node_t *n;
int i;
// Check if this address is cached
subnet_t *lookup_subnet_ipv6(const ipv6_t *address) {
subnet_t *p, *r = NULL, subnet = {0};
- avl_node_t *n;
+ splay_node_t *n;
int i;
// Check if this address is cached
}
void subnet_update(node_t *owner, subnet_t *subnet, bool up) {
- avl_node_t *node;
+ splay_node_t *node;
int i;
char *envp[9] = {0};
char netstr[MAXNETSTR];
free(envp[i]);
}
-void dump_subnets(void) {
+int dump_subnets(struct evbuffer *out) {
char netstr[MAXNETSTR];
subnet_t *subnet;
- avl_node_t *node;
-
- logger(LOG_DEBUG, "Subnet list:");
+ splay_node_t *node;
for(node = subnet_tree->head; node; node = node->next) {
subnet = node->data;
if(!net2str(netstr, sizeof netstr, subnet))
continue;
- logger(LOG_DEBUG, " %s owner %s", netstr, subnet->owner->name);
+ if(evbuffer_add_printf(out, " %s owner %s\n",
+ netstr, subnet->owner->name) == -1)
+ return errno;
}
- logger(LOG_DEBUG, "End of subnet list.");
+ return 0;
}