return 0;
}
#endif
+
+ #ifdef HAVE_MINGW
+ int usleep(long usec) {
+ Sleep(usec / 1000);
+ return 0;
+ }
+ #endif
bool setup_device(void) {
struct ifreq ifr;
+ bool t1q = false;
if(!get_config_string(lookup_config(config_tree, "Device"), &device))
device = xstrdup(DEFAULT_DEVICE);
#ifdef HAVE_LINUX_IF_TUN_H
/* Ok now check if this is an old ethertap or a new tun/tap thingie */
- memset(&ifr, 0, sizeof(ifr));
+ memset(&ifr, 0, sizeof ifr);
if(routing_mode == RMODE_ROUTER) {
ifr.ifr_flags = IFF_TUN;
device_type = DEVICE_TYPE_TUN;
device_info = "Linux tun/tap device (tap mode)";
}
+ #ifdef IFF_ONE_QUEUE
+ /* Set IFF_ONE_QUEUE flag... */
+ if(get_config_bool(lookup_config(config_tree, "IffOneQueue"), &t1q) && t1q)
+ ifr.ifr_flags |= IFF_ONE_QUEUE;
+ #endif
+
if(iface)
strncpy(ifr.ifr_name, iface, IFNAMSIZ);
}
bool read_packet(vpn_packet_t *packet) {
- int lenin;
+ int inlen;
switch(device_type) {
case DEVICE_TYPE_TUN:
- lenin = read(device_fd, packet->data + 10, MTU - 10);
+ inlen = read(device_fd, packet->data + 10, MTU - 10);
- if(lenin <= 0) {
+ if(inlen <= 0) {
logger(LOG_ERR, "Error while reading from %s %s: %s",
device_info, device, strerror(errno));
return false;
}
- packet->len = lenin + 10;
+ packet->len = inlen + 10;
break;
case DEVICE_TYPE_TAP:
- lenin = read(device_fd, packet->data, MTU);
+ inlen = read(device_fd, packet->data, MTU);
- if(lenin <= 0) {
+ if(inlen <= 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;
case DEVICE_TYPE_ETHERTAP:
- lenin = read(device_fd, packet->data - 2, MTU + 2);
+ inlen = read(device_fd, packet->data - 2, MTU + 2);
- if(lenin <= 0) {
+ if(inlen <= 0) {
logger(LOG_ERR, "Error while reading from %s %s: %s",
device_info, device, strerror(errno));
return false;
}
- packet->len = lenin - 2;
+ packet->len = inlen - 2;
break;
}
#ifndef __TINC_NET_H__
#define __TINC_NET_H__
-#include <openssl/evp.h>
-
#include "ipv6.h"
+#include "cipher.h"
+#include "digest.h"
#ifdef ENABLE_JUMBOGRAMS
#define MTU 9018 /* 9000 bytes payload + 14 bytes ethernet header + 4 bytes VLAN tag */
#define MTU 1518 /* 1500 bytes payload + 14 bytes ethernet header + 4 bytes VLAN tag */
#endif
-#define MAXSIZE (MTU + 4 + EVP_MAX_BLOCK_LENGTH + EVP_MAX_MD_SIZE + MTU/64 + 20) /* MTU + seqno + padding + HMAC + compressor overhead */
+#define MAXSIZE (MTU + 4 + CIPHER_MAX_BLOCK_SIZE + DIGEST_MAX_SIZE + MTU/64 + 20) /* MTU + seqno + padding + HMAC + compressor overhead */
#define MAXBUFSIZE ((MAXSIZE > 2048 ? MAXSIZE : 2048) + 128) /* Enough room for a request with a MAXSIZEd packet or a 8192 bits RSA key */
-#define MAXSOCKETS 128 /* Overkill... */
+#define MAXSOCKETS 8 /* Probably overkill... */
typedef struct mac_t {
uint8_t x[6];
} vpn_packet_t;
typedef struct listen_socket_t {
+ struct event ev_tcp;
+ struct event ev_udp;
int tcp;
int udp;
sockaddr_t sa;
struct config_t *cfg;
struct addrinfo *ai;
struct addrinfo *aip;
- struct event *event;
+ struct event ev;
} outgoing_t;
extern list_t *outgoing_list;
extern int maxoutbufsize;
extern int seconds_till_retry;
extern int addressfamily;
+ extern unsigned replaywin;
extern listen_socket_t listen_socket[MAXSOCKETS];
extern int listen_sockets;
-extern int keyexpires;
extern int keylifetime;
+ extern int udp_rcvbuf;
+ extern int udp_sndbuf;
extern bool do_prune;
-extern bool do_purge;
extern char *myport;
-extern time_t now;
extern int contradicting_add_edge;
extern int contradicting_del_edge;
#include "node.h"
extern void retry_outgoing(outgoing_t *);
-extern void handle_incoming_vpn_data(int);
+extern void handle_incoming_vpn_data(int, short, void *);
extern void finish_connecting(struct connection_t *);
extern void do_outgoing_connection(struct connection_t *);
-extern bool handle_new_meta_connection(int);
+extern void handle_new_meta_connection(int, short, void *);
extern int setup_listen_socket(const sockaddr_t *);
extern int setup_vpn_in_socket(const sockaddr_t *);
extern void send_packet(const struct node_t *, vpn_packet_t *);
extern void flush_queue(struct node_t *);
extern bool read_rsa_public_key(struct connection_t *);
extern void send_mtu_probe(struct node_t *);
+extern void handle_device_data(int, short, void *);
+extern void handle_meta_connection_data(int, short, void *);
+extern void regenerate_key();
+extern void purge(void);
+extern void retry(void);
+extern int reload_configuration(void);
extern void load_all_subnets();
#ifndef HAVE_MINGW
Copyright (C) 1998-2005 Ivo Timmermans,
2000-2010 Guus Sliepen <guus@tinc-vpn.org>
2010 Timothy Redaelli <timothy@redaelli.eu>
+ 2010 Brandon Black <blblack@gmail.com>
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
#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"
static void send_udppacket(node_t *, vpn_packet_t *);
+ unsigned replaywin = 16;
+
#define MAX_SEQNO 1073741824
// mtuprobes == 1..30: initial discovery, send bursts with 1 second interval
// mtuprobes == 32: send 1 burst, sleep pingtimeout second
// mtuprobes == 33: no response from other side, restart PMTU discovery process
-void send_mtu_probe(node_t *n) {
+static void send_mtu_probe_handler(int fd, short events, void *data) {
+ node_t *n = data;
vpn_packet_t packet;
int len, i;
int timeout = 1;
n->mtuprobes++;
- n->mtuevent = NULL;
if(!n->status.reachable || !n->status.validkey) {
ifdebug(TRAFFIC) logger(LOG_INFO, "Trying to send MTU probe to unreachable or rekeying node %s (%s)", n->name, n->hostname);
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;
}
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) {
route(n, packet);
}
-static bool try_mac(const node_t *n, const vpn_packet_t *inpkt) {
- unsigned char hmac[EVP_MAX_MD_SIZE];
-
- if(!n->indigest || !n->inmaclength || !n->inkey || inpkt->len < sizeof inpkt->seqno + n->inmaclength)
+static bool try_mac(node_t *n, const vpn_packet_t *inpkt) {
+ if(!digest_active(&n->indigest) || inpkt->len < sizeof inpkt->seqno + digest_length(&n->indigest))
return false;
- HMAC(n->indigest, n->inkey, n->inkeylength, (unsigned char *) &inpkt->seqno, inpkt->len - n->inmaclength, (unsigned char *)hmac, NULL);
-
- return !memcmp(hmac, (char *) &inpkt->seqno + inpkt->len - n->inmaclength, n->inmaclength);
+ return digest_verify(&n->indigest, &inpkt->seqno, inpkt->len - n->indigest.maclength, (const char *)&inpkt->seqno + inpkt->len - n->indigest.maclength);
}
static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) {
vpn_packet_t *pkt[] = { &pkt1, &pkt2, &pkt1, &pkt2 };
int nextpkt = 0;
vpn_packet_t *outpkt = pkt[0];
- int outlen, outpad;
- unsigned char hmac[EVP_MAX_MD_SIZE];
+ 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);
+ if(digest_active(&n->indigest)) {
+ inpkt->len -= n->indigest.maclength;
+ if(!digest_verify(&n->indigest, &inpkt->seqno, inpkt->len, (const char *)&inpkt->seqno + inpkt->len)) {
+ 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) {
- 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);
- } 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))) {
- 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;
+ if(replaywin) {
+ if(inpkt->seqno != n->received_seqno + 1) {
+ if(inpkt->seqno >= n->received_seqno + replaywin * 8) {
+ if(n->farfuture++ < replaywin >> 2) {
+ logger(LOG_WARNING, "Packet from %s (%s) is %d seqs in the future, dropped (%u)",
+ n->name, n->hostname, inpkt->seqno - n->received_seqno - 1, n->farfuture);
+ return;
+ }
+ logger(LOG_WARNING, "Lost %d packets from %s (%s)",
+ inpkt->seqno - n->received_seqno - 1, n->name, n->hostname);
+ memset(n->late, 0, replaywin);
+ } else if (inpkt->seqno <= n->received_seqno) {
+ if((n->received_seqno >= replaywin * 8 && inpkt->seqno <= n->received_seqno - replaywin * 8) || !(n->late[(inpkt->seqno / 8) % replaywin] & (1 << inpkt->seqno % 8))) {
+ logger(LOG_WARNING, "Got late or replayed packet from %s (%s), seqno %d, last received %d",
+ 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) % replaywin] |= 1 << i % 8;
}
- } else {
- for(i = n->received_seqno + 1; i < inpkt->seqno; i++)
- n->late[(i / 8) % sizeof n->late] |= 1 << i % 8;
}
+
+ n->farfuture = 0;
+ n->late[(inpkt->seqno / 8) % replaywin] &= ~(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;
#if defined(SOL_IP) && defined(IP_TOS)
static int priority = 0;
#endif
/* Make sure we have a valid key */
if(!n->status.validkey) {
+ time_t now = time(NULL);
+
ifdebug(TRAFFIC) logger(LOG_INFO,
"No valid key known yet for %s (%s), forwarding via TCP",
n->name, n->hostname);
/* 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
/* 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;
- edge_t *e;
- node_t *n = NULL;
+ splay_node_t *node;
+ node_t *n, *found = 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)) {
- if(last_hard_try == now)
- continue;
- last_hard_try = now;
- }
+ if(last_hard_try == now)
+ return NULL;
+ else
+ last_hard_try = now;
- if(!n)
- n = e->to;
+ for(node = node_tree->head; node; node = node->next) {
+ n = node->data;
- if(!try_mac(e->to, pkt))
+ if(n == myself || !n->status.reachable || !digest_active(&n->indigest))
continue;
- n = e->to;
- break;
+ if(try_mac(n, pkt)) {
+ found = n;
+ break;
+ }
}
- return n;
+ return found;
}
-void handle_incoming_vpn_data(int sock) {
+void handle_incoming_vpn_data(int sock, short events, void *data) {
vpn_packet_t pkt;
char *hostname;
sockaddr_t from;
- socklen_t fromlen = sizeof(from);
+ socklen_t fromlen = sizeof from;
node_t *n;
+ int len;
- pkt.len = recvfrom(sock, (char *) &pkt.seqno, MAXSIZE, 0, &from.sa, &fromlen);
+ len = recvfrom(sock, (char *) &pkt.seqno, MAXSIZE, 0, &from.sa, &fromlen);
- if(pkt.len < 0) {
+ if(len <= 0 || len > MAXSIZE) {
if(!sockwouldblock(sockerrno))
logger(LOG_ERR, "Receiving packet failed: %s", sockstrerror(sockerrno));
return;
}
+ pkt.len = len;
+
sockaddrunmap(&from); /* Some braindead IPv6 implementations do stupid things. */
n = lookup_node_udp(&from);
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);
+}
Copyright (C) 1998-2005 Ivo Timmermans,
2000-2010 Guus Sliepen <guus@tinc-vpn.org>
2006 Scott Lamb <slamb@slamb.org>
+ 2010 Brandon Black <blblack@gmail.com>
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
#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;
- if(get_config_string(lookup_config(config_tree, "PrivateKey"), &key)) {
- if(!get_config_string(lookup_config(config_tree, "PublicKey"), &pubkey)) {
+ /* First, check for simple PrivateKey statement */
+
+ if(get_config_string(lookup_config(config_tree, "PrivateKey"), &d)) {
+ if(!get_config_string(lookup_config(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});
}
/*
struct dirent *ent;
char *dname;
char *fname;
- avl_tree_t *config_tree;
+ splay_tree_t *config_tree;
config_t *cfg;
subnet_t *s, *s2;
node_t *n;
struct addrinfo *ai, *aip, hint = {0};
bool choice;
int i, err;
+ int replaywin_int;
myself = new_node();
myself->connection = new_connection();
} else
maxtimeout = 900;
+ if(get_config_int(lookup_config(config_tree, "UDPRcvBuf"), &udp_rcvbuf)) {
+ if(udp_rcvbuf <= 0) {
+ logger(LOG_ERR, "UDPRcvBuf cannot be negative!");
+ return false;
+ }
+ }
+
+ if(get_config_int(lookup_config(config_tree, "UDPSndBuf"), &udp_sndbuf)) {
+ if(udp_sndbuf <= 0) {
+ logger(LOG_ERR, "UDPSndBuf cannot be negative!");
+ return false;
+ }
+ }
+
+ if(get_config_int(lookup_config(config_tree, "ReplayWindow"), &replaywin_int)) {
+ if(replaywin_int < 0) {
+ logger(LOG_ERR, "ReplayWindow cannot be negative!");
+ return false;
+ }
+ replaywin = (unsigned)replaywin_int;
+ }
+
if(get_config_string(lookup_config(config_tree, "AddressFamily"), &afname)) {
if(!strcasecmp(afname, "IPv4"))
addressfamily = AF_INET;
/* Generate packet encryption key */
- if(get_config_string
- (lookup_config(config_tree, "Cipher"), &cipher)) {
- if(!strcasecmp(cipher, "none")) {
- myself->incipher = NULL;
- } else {
- myself->incipher = EVP_get_cipherbyname(cipher);
+ if(!get_config_string(lookup_config(config_tree, "Cipher"), &cipher))
+ cipher = xstrdup("blowfish");
- if(!myself->incipher) {
- logger(LOG_ERR, "Unrecognized cipher type!");
- return false;
- }
- }
- } else
- myself->incipher = EVP_bf_cbc();
-
- if(myself->incipher)
- myself->inkeylength = myself->incipher->key_len + myself->incipher->iv_len;
- else
- myself->inkeylength = 1;
-
- 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(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("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(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(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;
+ if(device_fd >= 0) {
+ event_set(&device_ev, device_fd, EV_READ|EV_PERSIST, handle_device_data, NULL);
+
+ if (event_add(&device_ev, NULL) < 0) {
+ logger(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 ? : "");
execute_script("tinc-up", envp);
- for(i = 0; i < 5; i++)
+ for(i = 0; i < 4; i++)
free(envp[i]);
/* Run subnet-up scripts for our own subnets */
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_ERR, "event_add failed: %s", strerror(errno));
+ abort();
+ }
+
+ event_set(&listen_socket[listen_sockets].ev_udp,
+ listen_socket[listen_sockets].udp,
+ EV_READ|EV_PERSIST,
+ handle_incoming_vpn_data, NULL);
+ if(event_add(&listen_socket[listen_sockets].ev_udp, NULL) < 0) {
+ logger(LOG_ERR, "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"
int addressfamily = AF_UNSPEC;
int maxtimeout = 900;
int seconds_till_retry = 5;
+ int udp_rcvbuf = 0;
+ int udp_sndbuf = 0;
listen_socket_t listen_socket[MAXSOCKETS];
int listen_sockets;
#if defined(SOL_TCP) && defined(TCP_NODELAY)
option = 1;
- setsockopt(c->socket, SOL_TCP, TCP_NODELAY, (void *)&option, sizeof(option));
+ setsockopt(c->socket, SOL_TCP, TCP_NODELAY, (void *)&option, sizeof option);
#endif
#if defined(SOL_IP) && defined(IP_TOS) && defined(IPTOS_LOWDELAY)
option = IPTOS_LOWDELAY;
- setsockopt(c->socket, SOL_IP, IP_TOS, (void *)&option, sizeof(option));
+ setsockopt(c->socket, SOL_IP, IP_TOS, (void *)&option, sizeof option);
#endif
}
/* Optimize TCP settings */
option = 1;
- setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, (void *)&option, sizeof(option));
+ setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, (void *)&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, (void *)&ifr, sizeof(ifr))) {
+ if(setsockopt(nfd, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof ifr)) {
closesocket(nfd);
logger(LOG_ERR, "Can't bind to interface %s: %s", iface,
strerror(sockerrno));
#endif
option = 1;
- setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, (void *)&option, sizeof(option));
+ setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, (void *)&option, sizeof option);
+ if(udp_rcvbuf && setsockopt(nfd, SOL_SOCKET, SO_RCVBUF, (void *)&udp_rcvbuf, sizeof(udp_rcvbuf)))
+ logger(LOG_WARNING, "Can't set UDP SO_RCVBUF to %i: %s", udp_rcvbuf, strerror(errno));
+
+ if(udp_sndbuf && setsockopt(nfd, SOL_SOCKET, SO_SNDBUF, (void *)&udp_sndbuf, sizeof(udp_sndbuf)))
+ logger(LOG_WARNING, "Can't set UDP SO_SNDBUF to %i: %s", udp_sndbuf, strerror(errno));
+
#if defined(IPPROTO_IPV6) && defined(IPV6_V6ONLY)
if(sa->sa.sa_family == AF_INET6)
setsockopt(nfd, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&option, sizeof option);
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);
}
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;
}
return;
}
+void handle_meta_read(struct bufferevent *event, void *data) {
+ logger(LOG_ERR, "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_ERR, "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);
}
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_ERR, "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", 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_ERR, "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) {
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);
#include "system.h"
-#include "avl_tree.h"
+#include "control_common.h"
+#include "splay_tree.h"
#include "logger.h"
#include "net.h"
#include "netutl.h"
#include "utils.h"
#include "xalloc.h"
-avl_tree_t *node_tree; /* Known nodes, sorted by name */
-avl_tree_t *node_udp_tree; /* Known nodes, sorted by address and port */
+splay_tree_t *node_tree; /* Known nodes, sorted by name */
+splay_tree_t *node_udp_tree; /* Known nodes, sorted by address and port */
node_t *myself;
}
static int node_udp_compare(const node_t *a, const node_t *b) {
- return sockaddrcmp(&a->address, &b->address);
+ int result;
+
+ result = sockaddrcmp(&a->address, &b->address);
+
+ if(result)
+ return result;
+
+ return (a->name && b->name) ? strcmp(a->name, b->name) : 0;
}
void init_nodes(void) {
- node_tree = avl_alloc_tree((avl_compare_t) node_compare, (avl_action_t) free_node);
- node_udp_tree = avl_alloc_tree((avl_compare_t) node_udp_compare, NULL);
+ node_tree = splay_alloc_tree((splay_compare_t) node_compare, (splay_action_t) free_node);
+ node_udp_tree = splay_alloc_tree((splay_compare_t) node_udp_compare, NULL);
}
void exit_nodes(void) {
- avl_delete_tree(node_udp_tree);
- avl_delete_tree(node_tree);
+ splay_delete_tree(node_udp_tree);
+ splay_delete_tree(node_tree);
}
node_t *new_node(void) {
- node_t *n = xmalloc_and_zero(sizeof(*n));
+ node_t *n = xmalloc_and_zero(sizeof *n);
+ if(replaywin) n->late = xmalloc_and_zero(replaywin);
n->subnet_tree = new_subnet_tree();
n->edge_tree = new_edge_tree();
- EVP_CIPHER_CTX_init(&n->inctx);
- EVP_CIPHER_CTX_init(&n->outctx);
n->mtu = MTU;
n->maxmtu = MTU;
}
void free_node(node_t *n) {
- if(n->inkey)
- free(n->inkey);
-
- if(n->outkey)
- free(n->outkey);
-
if(n->subnet_tree)
free_subnet_tree(n->subnet_tree);
sockaddrfree(&n->address);
- EVP_CIPHER_CTX_cleanup(&n->inctx);
- EVP_CIPHER_CTX_cleanup(&n->outctx);
+ cipher_close(&n->incipher);
+ digest_close(&n->indigest);
+ cipher_close(&n->outcipher);
+ digest_close(&n->outdigest);
- if(n->mtuevent)
- event_del(n->mtuevent);
+ event_del(&n->mtuevent);
if(n->hostname)
free(n->hostname);
if(n->name)
free(n->name);
+ if(n->late)
+ free(n->late);
+
free(n);
}
void node_add(node_t *n) {
- avl_insert(node_tree, n);
+ splay_insert(node_tree, n);
}
void node_del(node_t *n) {
- avl_node_t *node, *next;
+ splay_node_t *node, *next;
edge_t *e;
subnet_t *s;
edge_del(e);
}
- avl_delete(node_udp_tree, n);
- avl_delete(node_tree, n);
+ splay_delete(node_udp_tree, n);
+ splay_delete(node_tree, n);
}
node_t *lookup_node(char *name) {
n.name = name;
- return avl_search(node_tree, &n);
+ return splay_search(node_tree, &n);
}
node_t *lookup_node_udp(const sockaddr_t *sa) {
n.address = *sa;
n.name = NULL;
- return avl_search(node_udp_tree, &n);
+ return splay_search(node_udp_tree, &n);
}
void update_node_udp(node_t *n, const sockaddr_t *sa) {
- avl_delete(node_udp_tree, n);
+ splay_delete(node_udp_tree, n);
if(n->hostname)
free(n->hostname);
if(sa) {
n->address = *sa;
n->hostname = sockaddr2hostname(&n->address);
- avl_insert(node_udp_tree, n);
- ifdebug(PROTOCOL) logger(LOG_DEBUG, "UDP address of %s set to %s", n->name, n->hostname);
+ splay_insert(node_udp_tree, n);
+ logger(LOG_DEBUG, "UDP address of %s set to %s", n->name, n->hostname);
} else {
memset(&n->address, 0, sizeof n->address);
n->hostname = 0;
}
}
-void dump_nodes(void) {
- avl_node_t *node;
+bool dump_nodes(connection_t *c) {
+ splay_node_t *node;
node_t *n;
- logger(LOG_DEBUG, "Nodes:");
-
for(node = node_tree->head; node; node = node->next) {
n = node->data;
- logger(LOG_DEBUG, " %s at %s cipher %d digest %d maclength %d compression %d options %x status %04x nexthop %s via %s pmtu %d (min %d max %d)",
- n->name, n->hostname, n->outcipher ? n->outcipher->nid : 0,
- n->outdigest ? n->outdigest->type : 0, n->outmaclength, n->outcompression,
+ send_request(c, "%d %d %s at %s cipher %d digest %d maclength %zd compression %d options %x status %04x nexthop %s via %s distance %d pmtu %d (min %d max %d)", CONTROL, REQ_DUMP_NODES,
+ n->name, n->hostname, cipher_get_nid(&n->outcipher),
+ digest_get_nid(&n->outdigest), digest_length(&n->outdigest), n->outcompression,
n->options, bitfield_to_int(&n->status, sizeof n->status), n->nexthop ? n->nexthop->name : "-",
- n->via ? n->via->name : "-", n->mtu, n->minmtu, n->maxmtu);
+ n->via ? n->via->name : "-", n->distance, n->mtu, n->minmtu, n->maxmtu);
}
- logger(LOG_DEBUG, "End of nodes.");
+ return send_request(c, "%d %d", CONTROL, REQ_DUMP_NODES);
}
#ifndef __TINC_NODE_H__
#define __TINC_NODE_H__
-#include "avl_tree.h"
+#include "splay_tree.h"
+#include "cipher.h"
#include "connection.h"
-#include "event.h"
+#include "digest.h"
#include "list.h"
#include "subnet.h"
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 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) */
uint32_t sent_seqno; /* Sequence number last sent to this node */
uint32_t received_seqno; /* Sequence number last received from this node */
- unsigned char late[16]; /* Bitfield marking late packets */
+ uint32_t farfuture; /* Packets in a row that have arrived from the far future */
+ unsigned char* late; /* Bitfield marking late packets */
length_t mtu; /* Maximum size of packets to send to this node */
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 bool dump_nodes(struct connection_t *);
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;
#ifndef HAVE_MINGW
sigset_t emptysigset;
#endif
-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);
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());
bool execute_script(const char *name, char **envp) {
#ifdef HAVE_SYSTEM
int status, len;
- char *scriptname, *p;
+ char *scriptname;
int i;
#ifndef HAVE_MINGW
for(i = 0; envp[i]; i++) {
char *e = strchr(envp[i], '=');
if(e) {
- p = alloca(e - envp[i] + 1);
+ char p[e - envp[i] + 1];
strncpy(p, envp[i], e - envp[i]);
p[e - envp[i]] = '\0';
putenv(p);
*/
#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/evp.h>
-#include <openssl/err.h>
-#include <openssl/rand.h>
-
-#include "avl_tree.h"
+#include "splay_tree.h"
+#include "cipher.h"
#include "connection.h"
+#include "crypto.h"
#include "logger.h"
#include "net.h"
#include "netutl.h"
#include "utils.h"
#include "xalloc.h"
-bool mykeyused = false;
+static bool mykeyused = false;
void send_key_changed() {
- avl_node_t *node;
+ splay_node_t *node;
connection_t *c;
send_request(broadcast, "%d %x %s", KEY_CHANGED, rand(), myself->name);
}
}
-bool key_changed_h(connection_t *c) {
+bool key_changed_h(connection_t *c, char *request) {
char name[MAX_STRING_SIZE];
node_t *n;
- if(sscanf(c->buffer, "%*d %*x " MAX_STRING, name) != 1) {
+ if(sscanf(request, "%*d %*x " MAX_STRING, name) != 1) {
logger(LOG_ERR, "Got bad %s from %s (%s)", "KEY_CHANGED",
c->name, c->hostname);
return false;
}
- if(!check_id(name)) {
- logger(LOG_ERR, "Got bad %s from %s (%s): %s", "KEY_CHANGED", c->name, c->hostname, "invalid name");
- return false;
- }
-
- if(seen_request(c->buffer))
+ if(seen_request(request))
return true;
n = lookup_node(name);
/* Tell the others */
if(!tunnelserver)
- forward_request(c);
+ forward_request(c, request);
return true;
}
return send_request(to->nexthop->connection, "%d %s %s", REQ_KEY, myself->name, to->name);
}
-bool req_key_h(connection_t *c) {
+bool req_key_h(connection_t *c, char *request) {
char from_name[MAX_STRING_SIZE];
char to_name[MAX_STRING_SIZE];
node_t *from, *to;
- if(sscanf(c->buffer, "%*d " MAX_STRING " " MAX_STRING, from_name, to_name) != 2) {
+ if(sscanf(request, "%*d " MAX_STRING " " MAX_STRING, from_name, to_name) != 2) {
logger(LOG_ERR, "Got bad %s from %s (%s)", "REQ_KEY", c->name,
c->hostname);
return false;
/* Check if this key request is for us */
if(to == myself) { /* Yes, send our own key back */
+
send_ans_key(from);
} else {
if(tunnelserver)
return true;
}
- send_request(to->nexthop->connection, "%s", c->buffer);
+ send_request(to->nexthop->connection, "%s", request);
}
return true;
}
bool send_ans_key(node_t *to) {
- // Set key parameters
- to->incipher = myself->incipher;
- to->inkeylength = myself->inkeylength;
- to->indigest = myself->indigest;
- to->inmaclength = myself->inmaclength;
+ size_t keylen = cipher_keylength(&myself->incipher);
+ char key[keylen * 2 + 1];
+
+ cipher_open_by_nid(&to->incipher, cipher_get_nid(&myself->incipher));
+ digest_open_by_nid(&to->indigest, digest_get_nid(&myself->indigest), digest_length(&myself->indigest));
to->incompression = myself->incompression;
- // Allocate memory for key
- to->inkey = xrealloc(to->inkey, to->inkeylength);
+ randomize(key, keylen);
+ cipher_set_key(&to->incipher, key, true);
+ digest_set_key(&to->indigest, key, keylen);
- // Create a new key
- RAND_pseudo_bytes((unsigned char *)to->inkey, to->inkeylength);
- if(to->incipher)
- EVP_DecryptInit_ex(&to->inctx, to->incipher, NULL, (unsigned char *)to->inkey, (unsigned char *)to->inkey + to->incipher->key_len);
+ bin2hex(key, key, keylen);
+ key[keylen * 2] = '\0';
// Reset sequence number and late packet window
mykeyused = true;
to->received_seqno = 0;
- memset(to->late, 0, sizeof(to->late));
+ if(replaywin) memset(to->late, 0, replaywin);
- // Convert to hexadecimal and send
- char key[2 * to->inkeylength + 1];
- bin2hex(to->inkey, key, to->inkeylength);
- key[to->inkeylength * 2] = '\0';
-
- return send_request(to->nexthop->connection, "%d %s %s %s %d %d %d %d", ANS_KEY,
- myself->name, to->name, key,
- to->incipher ? to->incipher->nid : 0,
- to->indigest ? to->indigest->type : 0, to->inmaclength,
- to->incompression);
+ return send_request(to->nexthop->connection, "%d %s %s %s %d %d %zu %d", ANS_KEY,
+ myself->name, to->name, key,
+ cipher_get_nid(&to->incipher),
+ digest_get_nid(&to->indigest),
+ digest_length(&to->indigest),
+ to->incompression);
}
-bool ans_key_h(connection_t *c) {
+bool ans_key_h(connection_t *c, char *request) {
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 "MAX_STRING" "MAX_STRING,
from_name, to_name, key, &cipher, &digest, &maclength,
&compression, address, port) < 7) {
logger(LOG_ERR, "Got bad %s from %s (%s)", "ANS_KEY", c->name,
if(!to->status.reachable) {
logger(LOG_WARNING, "Got %s from %s (%s) destination %s which is not reachable",
- "ANS_KEY", c->name, c->hostname, to_name);
+ "ANS_KEY", c->name, c->hostname, to_name);
return true;
}
char *address, *port;
ifdebug(PROTOCOL) logger(LOG_DEBUG, "Appending reflexive UDP address to ANS_KEY from %s to %s", from->name, to->name);
sockaddr2str(&from->address, &address, &port);
- send_request(to->nexthop->connection, "%s %s %s", c->buffer, address, port);
+ send_request(to->nexthop->connection, "%s %s %s", request, address, port);
free(address);
free(port);
return true;
}
- return send_request(to->nexthop->connection, "%s", c->buffer);
+ return send_request(to->nexthop->connection, "%s", request);
}
- /* Update our copy of the origin's packet key */
- from->outkey = xrealloc(from->outkey, strlen(key) / 2);
-
- from->outkey = xstrdup(key);
- from->outkeylength = strlen(key) / 2;
- hex2bin(key, from->outkey, from->outkeylength);
-
/* Check and lookup cipher and digest algorithms */
- if(cipher) {
- from->outcipher = EVP_get_cipherbynid(cipher);
-
- if(!from->outcipher) {
- logger(LOG_ERR, "Node %s (%s) uses unknown cipher!", from->name,
- from->hostname);
- return true;
- }
-
- if(from->outkeylength != from->outcipher->key_len + from->outcipher->iv_len) {
- logger(LOG_ERR, "Node %s (%s) uses wrong keylength!", from->name,
- from->hostname);
- return true;
- }
- } else {
- from->outcipher = NULL;
+ if(!cipher_open_by_nid(&from->outcipher, cipher)) {
+ logger(LOG_ERR, "Node %s (%s) uses unknown cipher!", from->name, from->hostname);
+ return false;
}
- from->outmaclength = maclength;
+ keylen = strlen(key) / 2;
- if(digest) {
- from->outdigest = EVP_get_digestbynid(digest);
+ if(keylen != cipher_keylength(&from->outcipher)) {
+ logger(LOG_ERR, "Node %s (%s) uses wrong keylength!", from->name, from->hostname);
+ return false;
+ }
- if(!from->outdigest) {
- logger(LOG_ERR, "Node %s (%s) uses unknown digest!", from->name,
- from->hostname);
- return true;
- }
+ if(!digest_open_by_nid(&from->outdigest, digest, maclength)) {
+ logger(LOG_ERR, "Node %s (%s) uses unknown digest!", from->name, from->hostname);
+ return false;
+ }
- if(from->outmaclength > from->outdigest->md_size || from->outmaclength < 0) {
- logger(LOG_ERR, "Node %s (%s) uses bogus MAC length!",
- from->name, from->hostname);
- return true;
- }
- } else {
- from->outdigest = NULL;
+ if(maclength != digest_length(&from->outdigest)) {
+ logger(LOG_ERR, "Node %s (%s) uses bogus MAC length!", from->name, from->hostname);
+ return false;
}
if(compression < 0 || compression > 11) {
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->sent_seqno = 0;