+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.12 Feb 3 2010
+
+ * Really allow fast roaming of hosts to other nodes in a switched VPN.
+
+ * Fixes missing or incorrect environment variables when calling host-up/down
+ and subnet-up/down scripts in some cases.
+
+ * Allow port to be specified in Address statements.
+
+ * Clamp MSS of TCP packets to the discovered path MTU.
+
+ * Let two nodes behind NAT learn each others current UDP address and port via
+ a third node, potentially allowing direct communications in a similar way to
+ STUN.
+
Version 1.0.11 Nov 1 2009
* Fixed potential crash when the HUP signal is sent.
-This is the README file for tinc version 1.0.12. 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:
+ tinc is Copyright (C) 1998-2010 by:
Ivo Timmermans,
Guus Sliepen <guus@tinc-vpn.org>,
@page
@vskip 0pt plus 1filll
+@cindex copyright
This is the info manual for @value{PACKAGE} version @value{VERSION}, a Virtual Private Network daemon.
- Copyright @copyright{} 1998-2009 Ivo Timmermans,
+ Copyright @copyright{} 1998-2010 Ivo Timmermans,
Guus Sliepen <guus@@tinc-vpn.org> and
Wessel Dankers <wsl@@tinc-vpn.org>.
#include <sys/uio.h>
#endif
+#ifdef HAVE_SYS_UN_H
+#include <sys/un.h>
+#endif
+
+ #ifdef HAVE_DIRENT_H
+ #include <dirent.h>
+ #endif
+
/* SunOS really wants sys/socket.h BEFORE net/if.h,
and FreeBSD wants these lines below the rest. */
Parse a configuration file and put the results in the configuration tree
starting at *base.
*/
-bool read_config_file(avl_tree_t *config_tree, const char *fname) {
+int read_config_file(splay_tree_t *config_tree, const char *fname) {
+ int err = -2; /* Parse error */
FILE *fp;
- char *buffer, *line;
+ char buffer[MAX_STRING_SIZE];
+ char *line;
char *variable, *value, *eol;
int lineno = 0;
int len;
#define OPTION_INDIRECT 0x0001
#define OPTION_TCPONLY 0x0002
#define OPTION_PMTU_DISCOVERY 0x0004
+ #define OPTION_CLAMP_MSS 0x0008
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 control:1;
+ int unused:22;
} connection_status_t;
#include "edge.h"
#include "system.h"
+ #include <openssl/rand.h>
+ #include <openssl/err.h>
+ #include <openssl/evp.h>
+ #include <openssl/pem.h>
+ #include <openssl/hmac.h>
+
+ #ifdef HAVE_ZLIB
#include <zlib.h>
+ #endif
+
+ #ifdef HAVE_LZO
#include LZO1X_H
+ #endif
-#include "avl_tree.h"
+#include "splay_tree.h"
+#include "cipher.h"
#include "conf.h"
#include "connection.h"
+#include "crypto.h"
+#include "digest.h"
#include "device.h"
#include "ethernet.h"
-#include "event.h"
#include "graph.h"
#include "list.h"
#include "logger.h"
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});
}
+ /*
+ Read Subnets from all host config files
+ */
+ static void load_all_subnets(void) {
+ DIR *dir;
+ struct dirent *ent;
+ char *dname;
+ char *fname;
+ avl_tree_t *config_tree;
+ config_t *cfg;
+ subnet_t *s;
+ node_t *n;
+ bool result;
+
+ xasprintf(&dname, "%s/hosts", confbase);
+ dir = opendir(dname);
+ if(!dir) {
+ logger(LOG_ERR, "Could not open %s: %s", dname, strerror(errno));
+ free(dname);
+ return;
+ }
+
+ while((ent = readdir(dir))) {
+ if(!check_id(ent->d_name))
+ continue;
+
+ n = lookup_node(ent->d_name);
+ if(n)
+ continue;
+
+ #ifdef _DIRENT_HAVE_D_TYPE
+ //if(ent->d_type != DT_REG)
+ // continue;
+ #endif
+
+ xasprintf(&fname, "%s/hosts/%s", confbase, ent->d_name);
+ init_configuration(&config_tree);
+ result = read_config_file(config_tree, fname);
+ free(fname);
+ if(!result)
+ continue;
+
+ n = new_node();
+ n->name = xstrdup(ent->d_name);
+ node_add(n);
+
+ for(cfg = lookup_config(config_tree, "Subnet"); cfg; cfg = lookup_config_next(config_tree, cfg)) {
+ if(!get_config_subnet(cfg, &s))
+ continue;
+
+ subnet_add(n, s);
+ }
+
+ exit_configuration(&config_tree);
+ }
+
+ closedir(dir);
+ }
+
/*
Configure node_t myself and set up the local sockets (listen only)
*/
#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 defined(IPPROTO_IPV6) && defined(IPV6_V6ONLY)
if(sa->sa.sa_family == AF_INET6)
- setsockopt(nfd, SOL_IPV6, IPV6_V6ONLY, &option, sizeof option);
+ setsockopt(nfd, IPPROTO_IPV6, IPV6_V6ONLY, &option, sizeof option);
+ #endif
+
+ #if defined(IP_DONTFRAG) && !defined(IP_DONTFRAGMENT)
+ #define IP_DONTFRAGMENT IP_DONTFRAG
#endif
#if defined(SOL_IP) && defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DO)
char *hostname; /* the hostname of its real ip */
node_status_t status;
+ time_t last_req_key;
- 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 weight, mtu;
uint32_t options;
node_t *n;
+ bool choice;
- 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;
#include "utils.h"
#include "xalloc.h"
-bool mykeyused = false;
+static bool mykeyused = false;
- bool send_key_changed() {
- /* Only send this message if some other daemon requested our key previously.
- This reduces unnecessary key_changed broadcasts.
- */
+ void send_key_changed() {
+ avl_node_t *node;
+ connection_t *c;
- if(!mykeyused)
- return true;
+ send_request(broadcast, "%d %x %s", KEY_CHANGED, rand(), myself->name);
+
+ /* Immediately send new keys to directly connected nodes to keep UDP mappings alive */
- return send_request(broadcast, "%d %x %s", KEY_CHANGED, rand(), myself->name);
+ for(node = connection_tree->head; node; node = node->next) {
+ c = node->data;
+ if(c->status.active && c->node && c->node->status.reachable)
+ send_ans_key(c->node);
+ }
}
-bool key_changed_h(connection_t *c) {
+bool key_changed_h(connection_t *c, char *request) {
char name[MAX_STRING_SIZE];
node_t *n;
char from_name[MAX_STRING_SIZE];
char to_name[MAX_STRING_SIZE];
char key[MAX_STRING_SIZE];
- char address[MAX_STRING_SIZE] = "";
- char port[MAX_STRING_SIZE] = "";
- int cipher, digest, maclength, compression;
++ char address[MAX_STRING_SIZE] = "";
++ char port[MAX_STRING_SIZE] = "";
+ int cipher, digest, maclength, compression, keylen;
node_t *from, *to;
- if(sscanf(c->buffer, "%*d "MAX_STRING" "MAX_STRING" "MAX_STRING" %d %d %d %d "MAX_STRING" "MAX_STRING,
+ if(sscanf(request, "%*d "MAX_STRING" "MAX_STRING" "MAX_STRING" %d %d %d %d",
from_name, to_name, key, &cipher, &digest, &maclength,
- &compression) != 7) {
+ &compression, address, port) < 7) {
logger(LOG_ERR, "Got bad %s from %s (%s)", "ANS_KEY", c->name,
c->hostname);
return false;
from->outcompression = compression;
- if(from->outcipher)
- if(!EVP_EncryptInit_ex(&from->outctx, from->outcipher, NULL, (unsigned char *)from->outkey, (unsigned char *)from->outkey + from->outcipher->key_len)) {
- logger(LOG_ERR, "Error during initialisation of key from %s (%s): %s",
- from->name, from->hostname, ERR_error_string(ERR_get_error(), NULL));
- return true;
- }
+ /* Update our copy of the origin's packet key */
+
+ hex2bin(key, key, keylen);
+ cipher_set_key(&from->outcipher, key, false);
+ digest_set_key(&from->outdigest, key, keylen);
from->status.validkey = true;
+ from->status.waitingforkey = false;
from->sent_seqno = 0;
+ if(*address && *port) {
+ ifdebug(PROTOCOL) logger(LOG_DEBUG, "Using reflexive UDP address from %s: %s port %s", from->name, address, port);
+ sockaddr_t sa = str2sockaddr(address, port);
+ update_node_udp(from, &sa);
+ }
+
if(from->options & OPTION_PMTU_DISCOVERY && !from->mtuprobes)
send_mtu_probe(from);
return true;
}
+ if(tunnelserver)
+ return true;
+
/* Tell the rest */
- forward_request(c);
+ if(!tunnelserver)
+ forward_request(c, request);
+ if(strictsubnets)
+ return true;
/* Finally, delete it. */
static const size_t icmp6_size = sizeof(struct icmp6_hdr);
static const size_t ns_size = sizeof(struct nd_neighbor_solicit);
static const size_t opt_size = sizeof(struct nd_opt_hdr);
+ #define max(a, b) ((a) > (b) ? (a) : (b))
+static struct event age_subnets_event;
+
/* RFC 1071 */
static uint16_t inet_checksum(void *data, int len, uint16_t prevsum) {
logger(LOG_ERR, "Error initializing LZO compressor!");
return 1;
}
+ #endif
#ifdef HAVE_MINGW
- if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
- logger(LOG_ERR, "System call `%s' failed: %s", "WSAStartup", winerror(GetLastError()));
- return 1;
- }
-
if(!do_detach || !init_service())
return main2(argc, argv);
else