- 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.
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 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;
}
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;
}
}
}
}
-
- 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;
}
}
#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;
}
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) {
}
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;
/* 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 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
connection_t *c;
node_t *n;
- outgoing->event = NULL;
++ event_del(&outgoing->ev);
+
n = lookup_node(outgoing->name);
if(n)
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);
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;
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);
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;
/* 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;
}
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 */
}
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;