From: Guus Sliepen Date: Mon, 2 Nov 2009 13:24:27 +0000 (+0100) Subject: Merge branch 'master' into 1.1 X-Git-Tag: import-tinc-1.1~584 X-Git-Url: http://git.meshlink.io/?p=meshlink;a=commitdiff_plain;h=108b238915c5f58b3d94ab433dc5d04e064c2b11;hp=761517c21c37a808a19b487aa116c3c19439feca Merge branch 'master' into 1.1 Conflicts: NEWS README configure.in doc/tinc.texi doc/tincd.8.in src/Makefile.am src/connection.c src/edge.c src/meta.c src/net.c src/net.h src/net_packet.c src/net_setup.c src/net_socket.c src/node.c src/openssl/rsagen.h src/protocol_auth.c src/protocol_edge.c src/subnet.c --- diff --git a/Makefile.am b/Makefile.am index 3a8e51fe..b13689aa 100644 --- a/Makefile.am +++ b/Makefile.am @@ -6,7 +6,7 @@ SUBDIRS = m4 lib src doc ACLOCAL_AMFLAGS = -I m4 -EXTRA_DIST = config.rpath mkinstalldirs have.h system.h COPYING.README depcomp +EXTRA_DIST = have.h system.h COPYING.README ChangeLog: git log > ChangeLog diff --git a/NEWS b/NEWS index 09b02b8e..51f11be3 100644 --- a/NEWS +++ b/NEWS @@ -4,20 +4,39 @@ Version 1.1-cvs Work in progress * Use splay trees instead of AVL trees. -Version 1.0.10 not yet released +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. diff --git a/README b/README index c324e2b7..01ea9507 100644 --- a/README +++ b/README @@ -118,8 +118,7 @@ Support for routing IPv6 packets has been added. Just add Subnet lines with 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 diff --git a/THANKS b/THANKS index ed57da59..e0d33d3f 100644 --- a/THANKS +++ b/THANKS @@ -4,8 +4,9 @@ We would like to thank the following people for their contributions to tinc: * Allesandro Gatti * Andreas van Cranenburgh * Armijn Hemel -* dnk * Cris van Pelt +* Delf Eldkraft +* dnk * Enrique Zanardi * Flynn Marquardt * Grzegorz Dymarek diff --git a/doc/tinc.conf.5.in b/doc/tinc.conf.5.in index 223005fb..1cb2f0c8 100644 --- a/doc/tinc.conf.5.in +++ b/doc/tinc.conf.5.in @@ -427,13 +427,17 @@ higher priority. Packets will be sent to the node with the highest priority, unless that node is not reachable, in which case the node with the next highest priority will be tried, and so on. -.It Va TCPOnly Li = yes | no Pq no +.It Va TCPOnly Li = yes | no Pq no Bq obsolete If this variable is set to yes, then the packets are tunnelled over the TCP connection instead of a UDP connection. This is especially useful for those who want to run a tinc daemon from behind a masquerading firewall, or if UDP packet routing is disabled somehow. Setting this options also implicitly sets IndirectData. + +.Pp +Since version 1.0.10, tinc will automatically detect whether communication via +UDP is possible or not. .El .Sh SCRIPTS @@ -515,6 +519,9 @@ When a host becomes (un)reachable, this is set to the port number it uses for co .It Ev SUBNET When a subnet becomes (un)reachable, this is set to the subnet. + +.It Ev WEIGHT +When a subnet becomes (un)reachable, this is set to the subnet weight. .El .Sh FILES diff --git a/lib/getopt.c b/lib/getopt.c index fce2f0c7..b2f88b42 100644 --- a/lib/getopt.c +++ b/lib/getopt.c @@ -85,8 +85,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #define getpid() GetCurrentProcessId() #endif -#include "gettext.h" - /* This version of `getopt' appears to the caller like standard Unix `getopt' but it behaves differently for the user, since it allows the user to intersperse the options with the other arguments. diff --git a/lib/utils.h b/lib/utils.h index c6fb1807..4456616d 100644 --- a/lib/utils.h +++ b/lib/utils.h @@ -27,6 +27,17 @@ extern void bin2hex(char *src, char *dst, int length); #ifdef HAVE_MINGW extern const char *winerror(int); #define strerror(x) ((x)>0?strerror(x):winerror(GetLastError())) +#define sockerrno WSAGetLastError() +#define sockstrerror(x) winerror(x) +#define sockwouldblock(x) ((x) == WSAEWOULDBLOCK || (x) == WSAEINTR) +#define sockmsgsize(x) ((x) == WSAEMSGSIZE) +#define sockinprogress(x) ((x) == WSAEINPROGRESS || (x) == WSAEWOULDBLOCK) +#else +#define sockerrno errno +#define sockstrerror(x) strerror(x) +#define sockwouldblock(x) ((x) == EWOULDBLOCK || (x) == EINTR) +#define sockmsgsize(x) ((x) == EMSGSIZE) +#define sockinprogress(x) ((x) == EINPROGRESS) #endif extern unsigned int bitfield_to_int(void *bitfield, size_t size); diff --git a/lib/xmalloc.c b/lib/xmalloc.c index 9563391e..4e79aff9 100644 --- a/lib/xmalloc.c +++ b/lib/xmalloc.c @@ -34,6 +34,7 @@ void *realloc (); void free (); #endif +#include "dropin.h" #include "xalloc.h" #ifndef EXIT_FAILURE diff --git a/src/Makefile.am b/src/Makefile.am index 3af74ca1..bb78d4dd 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -35,9 +35,7 @@ tincd_LDADD = \ 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` diff --git a/src/bsd/device.c b/src/bsd/device.c index 1ffc960a..a9e39d4a 100644 --- a/src/bsd/device.c +++ b/src/bsd/device.c @@ -150,6 +150,17 @@ bool setup_device(void) { 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: @@ -209,7 +220,7 @@ bool read_packet(vpn_packet_t *packet) { 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; } @@ -240,7 +251,7 @@ bool read_packet(vpn_packet_t *packet) { 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; } @@ -268,7 +279,6 @@ bool read_packet(vpn_packet_t *packet) { ifdebug(TRAFFIC) logger(LOG_DEBUG, "Read packet of %d bytes from %s", packet->len, device_info); - logger(LOG_INFO, "E:fd_read"); return true; } diff --git a/src/connection.h b/src/connection.h index 93191df9..9476996f 100644 --- a/src/connection.h +++ b/src/connection.h @@ -56,7 +56,7 @@ typedef struct connection_t { 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 */ diff --git a/src/dummy/device.c b/src/dummy/device.c new file mode 100644 index 00000000..a9600b28 --- /dev/null +++ b/src/dummy/device.c @@ -0,0 +1,54 @@ +/* + device.c -- Dummy device + Copyright (C) 2009 Guus Sliepen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "system.h" + +#include "logger.h" +#include "net.h" + +int device_fd = -1; +char *device = "dummy"; +char *iface = "dummy"; +static char *device_info = "dummy device"; + +static int device_total_in = 0; +static int device_total_out = 0; + +bool setup_device(void) { + logger(LOG_INFO, "%s (%s) is a %s", device, iface, device_info); + return true; +} + +void close_device(void) { +} + +bool read_packet(vpn_packet_t *packet) { + return false; +} + +bool write_packet(vpn_packet_t *packet) { + device_total_out += packet->len; + return true; +} + +void dump_device_stats(void) { + logger(LOG_DEBUG, "Statistics for %s %s:", device_info, device); + logger(LOG_DEBUG, " total bytes in: %10d", device_total_in); + logger(LOG_DEBUG, " total bytes out: %10d", device_total_out); +} diff --git a/src/edge.h b/src/edge.h index 442ec410..cf62b71b 100644 --- a/src/edge.h +++ b/src/edge.h @@ -31,7 +31,7 @@ typedef struct edge_t { 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 */ diff --git a/src/meta.c b/src/meta.c index 787ccbd0..1bb634fd 100644 --- a/src/meta.c +++ b/src/meta.c @@ -92,7 +92,14 @@ bool receive_meta(connection_t *c) { inlen = recv(c->socket, inbuf, sizeof inbuf, 0); 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; } @@ -152,7 +159,5 @@ bool receive_meta(connection_t *c) { } } while(inlen); - c->last_ping_time = time(NULL); - return true; } diff --git a/src/net.c b/src/net.c index 1c267f64..9445b68a 100644 --- a/src/net.c +++ b/src/net.c @@ -208,7 +208,7 @@ void handle_meta_connection_data(int fd, short events, void *data) { 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; diff --git a/src/net_packet.c b/src/net_packet.c index e430b6c9..aaa0c720 100644 --- a/src/net_packet.c +++ b/src/net_packet.c @@ -42,10 +42,6 @@ #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]; @@ -54,31 +50,61 @@ static void send_udppacket(node_t *, vpn_packet_t *); #define MAX_SEQNO 1073741824 +// 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++; - 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; @@ -92,7 +118,8 @@ static void send_mtu_probe_handler(int fd, short events, void *data) { send_udppacket(n, &packet); } - event_add(&n->mtuevent, &(struct timeval){1, 0}); +end: + event_add(&n->mtuevent, &(struct timeval){timeout, 0}); } void send_mtu_probe(node_t *n) { @@ -106,10 +133,14 @@ 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; } } @@ -317,10 +348,13 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt) { 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; } @@ -390,14 +424,14 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt) { } #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: @@ -469,12 +503,17 @@ static node_t *try_harder(const sockaddr_t *from, const vpn_packet_t *pkt) { 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; @@ -499,8 +538,8 @@ void handle_incoming_vpn_data(int sock, short events, void *data) { 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; } diff --git a/src/net_setup.c b/src/net_setup.c index 44c8d8dc..de2d0fea 100644 --- a/src/net_setup.c +++ b/src/net_setup.c @@ -293,7 +293,7 @@ bool setup_myself(void) { /* Generate packet encryption key */ if(!get_config_string(lookup_config(myself->connection->config_tree, "Cipher"), &cipher)) - cipher = xstrdup("aes256"); + cipher = xstrdup("blowfish"); if(!cipher_open_by_name(&myself->incipher, cipher)) { logger(LOG_ERR, "Unrecognized cipher type!"); @@ -308,7 +308,7 @@ bool setup_myself(void) { /* Check if we want to use message authentication codes... */ if(!get_config_string(lookup_config(myself->connection->config_tree, "Digest"), &digest)) - digest = xstrdup("sha256"); + digest = xstrdup("sha1"); int maclength = 4; get_config_int(lookup_config(myself->connection->config_tree, "MACLength"), &maclength); diff --git a/src/net_socket.c b/src/net_socket.c index be44a1ce..d05dfd5c 100644 --- a/src/net_socket.c +++ b/src/net_socket.c @@ -35,10 +35,6 @@ #include -#ifdef WSAEINPROGRESS -#define EINPROGRESS WSAEINPROGRESS -#endif - /* Needed on Mac OS/X */ #ifndef SOL_TCP #define SOL_TCP IPPROTO_TCP @@ -67,7 +63,7 @@ static void configure_tcp(connection_t *c) { 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 @@ -156,8 +152,7 @@ static bool bind_to_address(connection_t *c) { 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); @@ -178,7 +173,7 @@ int setup_listen_socket(const sockaddr_t *sa) { 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; } @@ -203,7 +198,7 @@ int setup_listen_socket(const sockaddr_t *sa) { 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 @@ -214,16 +209,14 @@ int setup_listen_socket(const sockaddr_t *sa) { 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; } @@ -238,7 +231,7 @@ int setup_vpn_in_socket(const sockaddr_t *sa) { 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; } @@ -258,8 +251,7 @@ int setup_vpn_in_socket(const sockaddr_t *sa) { 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; } } @@ -278,6 +270,11 @@ int setup_vpn_in_socket(const sockaddr_t *sa) { 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) @@ -295,8 +292,7 @@ int setup_vpn_in_socket(const sockaddr_t *sa) { 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; } @@ -337,6 +333,11 @@ void do_outgoing_connection(connection_t *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) { @@ -382,9 +383,7 @@ begin: 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; } @@ -406,18 +405,14 @@ 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; } @@ -446,6 +441,8 @@ void setup_outgoing_connection(outgoing_t *outgoing) { connection_t *c; node_t *n; + event_del(&outgoing->ev); + n = lookup_node(outgoing->name); if(n) @@ -504,7 +501,7 @@ void handle_new_meta_connection(int sock, short events, void *data) { 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; } @@ -555,18 +552,7 @@ void try_outgoing_connections(void) { 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)) { diff --git a/src/node.h b/src/node.h index 830dcac6..1e08c7ec 100644 --- a/src/node.h +++ b/src/node.h @@ -42,7 +42,7 @@ typedef struct node_status_t { 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 */ diff --git a/src/process.c b/src/process.c index f0e5dd8b..09fd63e1 100644 --- a/src/process.c +++ b/src/process.c @@ -98,13 +98,18 @@ bool install_service(void) { 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())); diff --git a/src/protocol_auth.c b/src/protocol_auth.c index c783189b..a38b9adf 100644 --- a/src/protocol_auth.c +++ b/src/protocol_auth.c @@ -348,7 +348,7 @@ bool send_ack(connection_t *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) { @@ -387,10 +387,10 @@ 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(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; diff --git a/src/protocol_edge.c b/src/protocol_edge.c index df7e9c25..4aad53f0 100644 --- a/src/protocol_edge.c +++ b/src/protocol_edge.c @@ -41,7 +41,7 @@ bool send_add_edge(connection_t *c, const edge_t *e) { 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); @@ -58,10 +58,10 @@ bool add_edge_h(connection_t *c, char *request) { 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(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); @@ -70,13 +70,7 @@ bool add_edge_h(connection_t *c, char *request) { /* 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; @@ -186,13 +180,7 @@ bool del_edge_h(connection_t *c, char *request) { /* 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; diff --git a/src/protocol_subnet.c b/src/protocol_subnet.c index 6ec50548..ea112b9d 100644 --- a/src/protocol_subnet.c +++ b/src/protocol_subnet.c @@ -45,7 +45,7 @@ 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(request, "%*d %*x " MAX_STRING " " MAX_STRING, name, subnetstr) != 2) { logger(LOG_ERR, "Got bad %s from %s (%s)", "ADD_SUBNET", c->name, @@ -112,7 +112,7 @@ bool add_subnet_h(connection_t *c, char *request) { 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; @@ -121,9 +121,9 @@ bool add_subnet_h(connection_t *c, char *request) { } 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); @@ -142,6 +142,11 @@ bool add_subnet_h(connection_t *c, char *request) { if(!tunnelserver) forward_request(c, request); + /* 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; } diff --git a/src/route.c b/src/route.c index 600c53dd..758801be 100644 --- a/src/route.c +++ b/src/route.c @@ -154,6 +154,7 @@ static void learn_mac(mac_t *address) { subnet->type = SUBNET_MAC; 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 */ diff --git a/src/solaris/device.c b/src/solaris/device.c index 6fbd0538..8221b9fd 100644 --- a/src/solaris/device.c +++ b/src/solaris/device.c @@ -131,7 +131,7 @@ bool read_packet(vpn_packet_t *packet) { 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; } diff --git a/src/subnet.c b/src/subnet.c index 1cb81639..29ea96d9 100644 --- a/src/subnet.c +++ b/src/subnet.c @@ -47,9 +47,15 @@ static subnet_t *cache_ipv6_subnet[2]; 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 */ @@ -324,15 +330,46 @@ subnet_t *lookup_subnet(const node_t *owner, const subnet_t *subnet) { } subnet_t *lookup_subnet_mac(const mac_t *address) { - subnet_t *p, subnet = {0}; + subnet_t *p, *r = NULL, subnet = {0}; + 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; - return p; + 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 r; } subnet_t *lookup_subnet_ipv4(const ipv4_t *address) {