Import Upstream version 1.1~pre4

This commit is contained in:
Guus Sliepen 2019-08-26 13:44:50 +02:00
parent 34d5939212
commit ff64081061
48 changed files with 1739 additions and 1176 deletions

View file

@ -8,7 +8,7 @@ tincd_SOURCES = \
utils.c getopt.c getopt1.c list.c splay_tree.c dropin.c fake-getaddrinfo.c fake-getnameinfo.c hash.c \
buffer.c conf.c connection.c control.c edge.c graph.c logger.c meta.c net.c net_packet.c net_setup.c \
net_socket.c netutl.c node.c process.c protocol.c protocol_auth.c protocol_edge.c protocol_misc.c \
protocol_key.c protocol_subnet.c route.c sptps.c subnet.c subnet_parse.c tincd.c \
protocol_key.c protocol_subnet.c route.c sptps.c subnet.c subnet_parse.c event.c tincd.c \
dummy_device.c raw_socket_device.c multicast_device.c
if UML
@ -46,7 +46,7 @@ INCLUDES = @INCLUDES@ -I$(top_builddir)
noinst_HEADERS = \
xalloc.h utils.h getopt.h list.h splay_tree.h dropin.h fake-getaddrinfo.h fake-getnameinfo.h fake-gai-errnos.h ipv6.h ipv4.h ethernet.h \
buffer.h conf.h connection.h control.h control_common.h device.h edge.h graph.h info.h logger.h meta.h net.h netutl.h node.h process.h \
protocol.h route.h subnet.h sptps.h tincctl.h top.h bsd/tunemu.h hash.h
protocol.h route.h subnet.h sptps.h tincctl.h top.h bsd/tunemu.h hash.h event.h
nodist_noinst_HEADERS = \
cipher.h crypto.h ecdh.h ecdsa.h digest.h prf.h rsa.h ecdsagen.h rsagen.h

View file

@ -62,10 +62,9 @@ DIST_COMMON = $(noinst_HEADERS) $(srcdir)/Makefile.am \
$(srcdir)/Makefile.in
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/m4/attribute.m4 \
$(top_srcdir)/m4/curses.m4 $(top_srcdir)/m4/libevent.m4 \
$(top_srcdir)/m4/lzo.m4 $(top_srcdir)/m4/openssl.m4 \
$(top_srcdir)/m4/readline.m4 $(top_srcdir)/m4/zlib.m4 \
$(top_srcdir)/configure.in
$(top_srcdir)/m4/curses.m4 $(top_srcdir)/m4/lzo.m4 \
$(top_srcdir)/m4/openssl.m4 $(top_srcdir)/m4/readline.m4 \
$(top_srcdir)/m4/zlib.m4 $(top_srcdir)/configure.in
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
mkinstalldirs = $(install_sh) -d
@ -95,8 +94,8 @@ am__tincd_SOURCES_DIST = utils.c getopt.c getopt1.c list.c \
netutl.c node.c process.c protocol.c protocol_auth.c \
protocol_edge.c protocol_misc.c protocol_key.c \
protocol_subnet.c route.c sptps.c subnet.c subnet_parse.c \
tincd.c dummy_device.c raw_socket_device.c multicast_device.c \
uml_device.c vde_device.c bsd/tunemu.c
event.c tincd.c dummy_device.c raw_socket_device.c \
multicast_device.c uml_device.c vde_device.c bsd/tunemu.c
@UML_TRUE@am__objects_1 = uml_device.$(OBJEXT)
@VDE_TRUE@am__objects_2 = vde_device.$(OBJEXT)
@TUNEMU_TRUE@am__objects_3 = tunemu.$(OBJEXT)
@ -112,9 +111,10 @@ am_tincd_OBJECTS = utils.$(OBJEXT) getopt.$(OBJEXT) getopt1.$(OBJEXT) \
protocol_edge.$(OBJEXT) protocol_misc.$(OBJEXT) \
protocol_key.$(OBJEXT) protocol_subnet.$(OBJEXT) \
route.$(OBJEXT) sptps.$(OBJEXT) subnet.$(OBJEXT) \
subnet_parse.$(OBJEXT) tincd.$(OBJEXT) dummy_device.$(OBJEXT) \
raw_socket_device.$(OBJEXT) multicast_device.$(OBJEXT) \
$(am__objects_1) $(am__objects_2) $(am__objects_3)
subnet_parse.$(OBJEXT) event.$(OBJEXT) tincd.$(OBJEXT) \
dummy_device.$(OBJEXT) raw_socket_device.$(OBJEXT) \
multicast_device.$(OBJEXT) $(am__objects_1) $(am__objects_2) \
$(am__objects_3)
nodist_tincd_OBJECTS = device.$(OBJEXT) cipher.$(OBJEXT) \
crypto.$(OBJEXT) ecdh.$(OBJEXT) ecdsa.$(OBJEXT) \
digest.$(OBJEXT) prf.$(OBJEXT) rsa.$(OBJEXT)
@ -251,7 +251,7 @@ tincd_SOURCES = utils.c getopt.c getopt1.c list.c splay_tree.c \
net.c net_packet.c net_setup.c net_socket.c netutl.c node.c \
process.c protocol.c protocol_auth.c protocol_edge.c \
protocol_misc.c protocol_key.c protocol_subnet.c route.c \
sptps.c subnet.c subnet_parse.c tincd.c dummy_device.c \
sptps.c subnet.c subnet_parse.c event.c tincd.c dummy_device.c \
raw_socket_device.c multicast_device.c $(am__append_1) \
$(am__append_2) $(am__append_3)
nodist_tincd_SOURCES = \
@ -273,7 +273,7 @@ DEFAULT_INCLUDES =
noinst_HEADERS = \
xalloc.h utils.h getopt.h list.h splay_tree.h dropin.h fake-getaddrinfo.h fake-getnameinfo.h fake-gai-errnos.h ipv6.h ipv4.h ethernet.h \
buffer.h conf.h connection.h control.h control_common.h device.h edge.h graph.h info.h logger.h meta.h net.h netutl.h node.h process.h \
protocol.h route.h subnet.h sptps.h tincctl.h top.h bsd/tunemu.h hash.h
protocol.h route.h subnet.h sptps.h tincctl.h top.h bsd/tunemu.h hash.h event.h
nodist_noinst_HEADERS = \
cipher.h crypto.h ecdh.h ecdsa.h digest.h prf.h rsa.h ecdsagen.h rsagen.h
@ -383,6 +383,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ecdsa.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ecdsagen.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/edge.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/event.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fake-getaddrinfo.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fake-getnameinfo.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getopt.Po@am__quote@

View file

@ -68,11 +68,7 @@ void free_connection(connection_t *c) {
buffer_clear(&c->inbuf);
buffer_clear(&c->outbuf);
if(event_initialized(&c->inevent))
event_del(&c->inevent);
if(event_initialized(&c->outevent))
event_del(&c->outevent);
io_del(&c->io);
if(c->socket > 0)
closesocket(c->socket);

View file

@ -90,8 +90,7 @@ typedef struct connection_t {
struct buffer_t inbuf;
struct buffer_t outbuf;
struct event inevent; /* input event on this metadata connection */
struct event outevent; /* output event on this metadata connection */
io_t io; /* input/output event on this metadata connection */
int tcplen; /* length of incoming TCPpacket */
int allow_request; /* defined if there's only one request possible */

View file

@ -58,7 +58,7 @@ bool control_h(connection_t *c, const char *request) {
switch (type) {
case REQ_STOP:
event_loopexit(NULL);
event_exit();
return control_ok(c, REQ_STOP);
case REQ_DUMP_NODES:
@ -156,7 +156,7 @@ bool init_control(void) {
// Make sure we have a valid address, and map 0.0.0.0 and :: to 127.0.0.1 and ::1.
if(getsockname(listen_socket[0].tcp, (struct sockaddr *)&sa, &len)) {
if(getsockname(listen_socket[0].tcp.fd, (struct sockaddr *)&sa, &len)) {
xasprintf(&localhost, "127.0.0.1 port %d", myport);
} else {
if(sa.sa.sa_family == AF_INET) {

View file

@ -45,4 +45,22 @@ extern int gettimeofday(struct timeval *, void *);
extern int usleep(long long usec);
#endif
#ifndef timeradd
#define timeradd(a, b, r) do {\
(r)->tv_sec = (a)->tv_sec + (b)->tv_sec;\
(r)->tv_usec = (a)->tv_usec + (b)->tv_usec;\
if((r)->tv_usec >= 1000000)\
(r)->tv_sec++, (r)->tv_usec -= 1000000;\
} while (0)
#endif
#ifndef timersub
#define timersub(a, b, r) do {\
(r)->tv_sec = (a)->tv_sec - (b)->tv_sec;\
(r)->tv_usec = (a)->tv_usec - (b)->tv_usec;\
if((r)->tv_usec < 1000000)\
(r)->tv_sec--, (r)->tv_usec += 1000000;\
} while (0)
#endif
#endif /* __DROPIN_H__ */

View file

@ -41,6 +41,10 @@
#define ETH_P_IPV6 0x86DD
#endif
#ifndef ETH_P_8021Q
#define ETH_P_8021Q 0x8100
#endif
#ifndef HAVE_STRUCT_ETHER_HEADER
struct ether_header {
uint8_t ether_dhost[ETH_ALEN];

250
src/event.c Normal file
View file

@ -0,0 +1,250 @@
/*
event.c -- I/O, timeout and signal event handling
Copyright (C) 2012 Guus Sliepen <guus@tinc-vpn.org>
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 "dropin.h"
#include "event.h"
#include "net.h"
#include "utils.h"
struct timeval now;
static fd_set readfds;
static fd_set writefds;
static volatile bool running;
static int io_compare(const io_t *a, const io_t *b) {
return a->fd - b->fd;
}
static int timeout_compare(const timeout_t *a, const timeout_t *b) {
struct timeval diff;
timersub(&a->tv, &b->tv, &diff);
if(diff.tv_sec < 0)
return -1;
if(diff.tv_sec > 0)
return 1;
if(diff.tv_usec < 0)
return -1;
if(diff.tv_usec > 0)
return 1;
if(a < b)
return -1;
if(a > b)
return 1;
return 0;
}
static int signal_compare(const signal_t *a, const signal_t *b) {
return a->signum - b->signum;
}
static splay_tree_t io_tree = {.compare = (splay_compare_t)io_compare};
static splay_tree_t timeout_tree = {.compare = (splay_compare_t)timeout_compare};
static splay_tree_t signal_tree = {.compare = (splay_compare_t)signal_compare};
void io_add(io_t *io, io_cb_t cb, void *data, int fd, int flags) {
if(io->cb)
return;
io->fd = fd;
io->cb = cb;
io->data = data;
io->node.data = io;
io_set(io, flags);
if(!splay_insert_node(&io_tree, &io->node))
abort();
}
void io_set(io_t *io, int flags) {
io->flags = flags;
if(flags & IO_READ)
FD_SET(io->fd, &readfds);
else
FD_CLR(io->fd, &readfds);
if(flags & IO_WRITE)
FD_SET(io->fd, &writefds);
else
FD_CLR(io->fd, &writefds);
}
void io_del(io_t *io) {
if(!io->cb)
return;
io_set(io, 0);
splay_unlink_node(&io_tree, &io->node);
io->cb = NULL;
}
void timeout_add(timeout_t *timeout, timeout_cb_t cb, void *data, struct timeval *tv) {
timeout->cb = cb;
timeout->data = data;
timeout->node.data = timeout;
timeout_set(timeout, tv);
}
void timeout_set(timeout_t *timeout, struct timeval *tv) {
if(timerisset(&timeout->tv))
splay_unlink_node(&timeout_tree, &timeout->node);
if(!now.tv_sec)
gettimeofday(&now, NULL);
timeradd(&now, tv, &timeout->tv);
if(!splay_insert_node(&timeout_tree, &timeout->node))
abort();
}
void timeout_del(timeout_t *timeout) {
if(!timeout->cb)
return;
splay_unlink_node(&timeout_tree, &timeout->node);
timeout->cb = 0;
timeout->tv = (struct timeval){0, 0};
}
#ifndef HAVE_MINGW
static io_t signalio;
static int pipefd[2] = {-1, -1};
static void signal_handler(int signum) {
unsigned char num = signum;
write(pipefd[1], &num, 1);
}
static void signalio_handler(void *data, int flags) {
unsigned char signum;
if(read(pipefd[0], &signum, 1) != 1)
return;
signal_t *sig = splay_search(&signal_tree, &((signal_t){.signum = signum}));
if(sig)
sig->cb(sig->data);
}
static void pipe_init(void) {
if(!pipe(pipefd))
io_add(&signalio, signalio_handler, NULL, pipefd[0], IO_READ);
}
void signal_add(signal_t *sig, signal_cb_t cb, void *data, int signum) {
if(sig->cb)
return;
sig->cb = cb;
sig->data = data;
sig->signum = signum;
sig->node.data = sig;
if(pipefd[0] == -1)
pipe_init();
signal(sig->signum, signal_handler);
if(!splay_insert_node(&signal_tree, &sig->node))
abort();
}
void signal_del(signal_t *sig) {
if(!sig->cb)
return;
signal(sig->signum, SIG_DFL);
splay_unlink_node(&signal_tree, &sig->node);
sig->cb = NULL;
}
#endif
bool event_loop(void) {
running = true;
fd_set readable;
fd_set writable;
while(running) {
gettimeofday(&now, NULL);
struct timeval diff, *tv = NULL;
while(timeout_tree.head) {
timeout_t *timeout = timeout_tree.head->data;
timersub(&timeout->tv, &now, &diff);
if(diff.tv_sec < 0) {
timeout->cb(timeout->data);
if(timercmp(&timeout->tv, &now, <))
timeout_del(timeout);
} else {
tv = &diff;
break;
}
}
memcpy(&readable, &readfds, sizeof readable);
memcpy(&writable, &writefds, sizeof writable);
int fds = 0;
if(io_tree.tail) {
io_t *last = io_tree.tail->data;
fds = last->fd + 1;
}
#ifdef HAVE_MINGW
LeaveCriticalSection(&mutex);
#endif
int n = select(fds, &readable, &writable, NULL, tv);
#ifdef HAVE_MINGW
EnterCriticalSection(&mutex);
#endif
if(n < 0) {
if(sockwouldblock(errno))
continue;
else
return false;
}
if(!n)
continue;
for splay_each(io_t, io, &io_tree) {
if(FD_ISSET(io->fd, &writable))
io->cb(io->data, IO_WRITE);
else if(FD_ISSET(io->fd, &readable))
io->cb(io->data, IO_READ);
}
}
return true;
}
void event_exit(void) {
running = false;
}

70
src/event.h Normal file
View file

@ -0,0 +1,70 @@
/*
event.h -- I/O, timeout and signal event handling
Copyright (C) 2012 Guus Sliepen <guus@tinc-vpn.org>
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.
*/
#ifndef __TINC_EVENT_H__
#define __TINC_EVENT_H__
#include "splay_tree.h"
#define IO_READ 1
#define IO_WRITE 2
typedef void (*io_cb_t)(void *data, int flags);
typedef void (*timeout_cb_t)(void *data);
typedef void (*signal_cb_t)(void *data);
typedef struct io_t {
int fd;
int flags;
io_cb_t cb;
void *data;
splay_node_t node;
} io_t;
typedef struct timeout_t {
struct timeval tv;
timeout_cb_t cb;
void *data;
splay_node_t node;
} timeout_t;
typedef struct signal_t {
int signum;
signal_cb_t cb;
void *data;
splay_node_t node;
} signal_t;
extern struct timeval now;
extern void io_add(io_t *io, io_cb_t cb, void *data, int fd, int flags);
extern void io_del(io_t *io);
extern void io_set(io_t *io, int flags);
extern void timeout_add(timeout_t *timeout, timeout_cb_t cb, void *data, struct timeval *tv);
extern void timeout_del(timeout_t *timeout);
extern void timeout_set(timeout_t *timeout, struct timeval *tv);
extern void signal_add(signal_t *sig, signal_cb_t cb, void *data, int signum);
extern void signal_del(signal_t *sig);
extern bool event_loop(void);
extern void event_exit(void);
#endif

View file

@ -213,8 +213,7 @@ static void check_reachability(void) {
n->minmtu = 0;
n->mtuprobes = 0;
if(timeout_initialized(&n->mtuevent))
event_del(&n->mtuevent);
timeout_del(&n->mtutimeout);
char *name;
char *address;

View file

@ -55,7 +55,7 @@ hash_t *hash_alloc(size_t n, size_t size) {
hash_t *hash = xmalloc_and_zero(sizeof *hash);
hash->n = n;
hash->size = size;
hash->keys = xmalloc(hash->n * hash->size);
hash->keys = xmalloc_and_zero(hash->n * hash->size);
hash->values = xmalloc_and_zero(hash->n * sizeof *hash->values);
return hash;
}
@ -100,6 +100,8 @@ void hash_clear(hash_t *hash) {
void hash_resize(hash_t *hash, size_t n) {
hash->keys = xrealloc(hash->keys, n * hash->size);
hash->values = xrealloc(hash->values, n * sizeof *hash->values);
if(n > hash->n)
if(n > hash->n) {
memset(hash->keys + hash->n * hash->size, 0, (n - hash->n) * hash->size);
memset(hash->values + hash->n, 0, (n - hash->n) * sizeof *hash->values);
}
}

View file

@ -41,7 +41,6 @@ bool logcontrol = false;
static void real_logger(int level, int priority, const char *message) {
char timestr[32] = "";
time_t now;
static bool suppress = false;
// Bail out early if there is nothing to do.
@ -58,8 +57,10 @@ static void real_logger(int level, int priority, const char *message) {
fflush(stderr);
break;
case LOGMODE_FILE:
now = time(NULL);
strftime(timestr, sizeof timestr, "%Y-%m-%d %H:%M:%S", localtime(&now));
if(!now.tv_sec)
gettimeofday(&now, NULL);
time_t now_sec = now.tv_sec;
strftime(timestr, sizeof timestr, "%Y-%m-%d %H:%M:%S", localtime(&now_sec));
fprintf(logfile, "%s %s[%ld]: %s\n", timestr, logident, (long)logpid, message);
fflush(logfile);
break;

View file

@ -39,7 +39,7 @@ bool send_meta_sptps(void *handle, uint8_t type, const char *buffer, size_t leng
}
buffer_add(&c->outbuf, buffer, length);
event_add(&c->outevent, NULL);
io_set(&c->io, IO_READ | IO_WRITE);
return true;
}
@ -65,12 +65,11 @@ bool send_meta(connection_t *c, const char *buffer, int length) {
c->name, c->hostname);
return false;
}
} else {
buffer_add(&c->outbuf, buffer, length);
}
event_add(&c->outevent, NULL);
io_set(&c->io, IO_READ | IO_WRITE);
return true;
}

175
src/net.c
View file

@ -74,7 +74,7 @@ void purge(void) {
if(e->to == n)
return;
if(!strictsubnets || !n->subnet_tree->head)
if(!autoconnect && (!strictsubnets || !n->subnet_tree->head))
/* in strictsubnets mode do not delete nodes with subnets */
node_del(n);
}
@ -137,18 +137,16 @@ void terminate_connection(connection_t *c, bool report) {
end does not reply in time, we consider them dead
and close the connection.
*/
static void timeout_handler(int fd, short events, void *event) {
time_t now = time(NULL);
static void timeout_handler(void *data) {
for list_each(connection_t, c, connection_list) {
if(c->status.control)
continue;
if(c->last_ping_time + pingtimeout <= now) {
if(c->last_ping_time + pingtimeout <= now.tv_sec) {
if(c->status.active) {
if(c->status.pinged) {
logger(DEBUG_CONNECTIONS, LOG_INFO, "%s (%s) didn't respond to PING in %ld seconds", c->name, c->hostname, (long)now - c->last_ping_time);
} else if(c->last_ping_time + pinginterval <= now) {
logger(DEBUG_CONNECTIONS, LOG_INFO, "%s (%s) didn't respond to PING in %ld seconds", c->name, c->hostname, (long)now.tv_sec - c->last_ping_time);
} else if(c->last_ping_time + pinginterval <= now.tv_sec) {
send_ping(c);
continue;
} else {
@ -164,6 +162,15 @@ static void timeout_handler(int fd, short events, void *event) {
}
}
timeout_set(data, &(struct timeval){pingtimeout, rand() % 100000});
}
static void periodic_handler(void *data) {
/* Check if there are too many contradicting ADD_EDGE and DEL_EDGE messages.
This usually only happens when another node has the same Name as this node.
If so, sleep for a short while to prevent a storm of contradicting messages.
*/
if(contradicting_del_edge > 100 && contradicting_add_edge > 100) {
logger(DEBUG_ALWAYS, LOG_WARNING, "Possible node with same Name as us! Sleeping %d seconds.", sleeptime);
usleep(sleeptime * 1000000LL);
@ -179,11 +186,100 @@ static void timeout_handler(int fd, short events, void *event) {
contradicting_add_edge = 0;
contradicting_del_edge = 0;
event_add(event, &(struct timeval){pingtimeout, 0});
/* If AutoConnect is set, check if we need to make or break connections. */
if(autoconnect && node_tree->count > 1) {
/* Count number of active connections */
int nc = 0;
for list_each(connection_t, c, connection_list) {
if(c->status.active && !c->status.control)
nc++;
}
if(nc < autoconnect) {
/* Not enough active connections, try to add one.
Choose a random node, if we don't have a connection to it,
and we are not already trying to make one, create an
outgoing connection to this node.
*/
int r = rand() % node_tree->count;
int i = 0;
for splay_each(node_t, n, node_tree) {
if(i++ != r)
continue;
if(n->connection)
break;
bool found = false;
for list_each(outgoing_t, outgoing, outgoing_list) {
if(!strcmp(outgoing->name, n->name)) {
found = true;
break;
}
}
if(!found) {
logger(DEBUG_CONNECTIONS, LOG_INFO, "Autoconnecting to %s", n->name);
outgoing_t *outgoing = xmalloc_and_zero(sizeof *outgoing);
outgoing->name = xstrdup(n->name);
list_insert_tail(outgoing_list, outgoing);
setup_outgoing_connection(outgoing);
}
break;
}
} else if(nc > autoconnect) {
/* Too many active connections, try to remove one.
Choose a random outgoing connection to a node
that has at least one other connection.
*/
int r = rand() % nc;
int i = 0;
for list_each(connection_t, c, connection_list) {
if(!c->status.active || c->status.control)
continue;
if(i++ != r)
continue;
if(!c->outgoing || !c->node || c->node->edge_tree->count < 2)
break;
logger(DEBUG_CONNECTIONS, LOG_INFO, "Autodisconnecting from %s", c->name);
list_delete(outgoing_list, c->outgoing);
c->outgoing = NULL;
terminate_connection(c, c->status.active);
break;
}
}
if(nc >= autoconnect) {
/* If we have enough active connections,
remove any pending outgoing connections.
*/
for list_each(outgoing_t, o, outgoing_list) {
bool found = false;
for list_each(connection_t, c, connection_list) {
if(c->outgoing == o) {
found = true;
break;
}
}
if(!found) {
logger(DEBUG_CONNECTIONS, LOG_INFO, "Cancelled outgoing connection to %s", o->name);
list_delete_node(outgoing_list, node);
}
}
}
}
timeout_set(data, &(struct timeval){5, rand() % 100000});
}
void handle_meta_connection_data(int fd, short events, void *data) {
connection_t *c = data;
void handle_meta_connection_data(connection_t *c) {
int result;
socklen_t len = sizeof result;
@ -207,19 +303,19 @@ void handle_meta_connection_data(int fd, short events, void *data) {
}
}
static void sigterm_handler(int signal, short events, void *data) {
logger(DEBUG_ALWAYS, LOG_NOTICE, "Got %s signal", strsignal(signal));
event_loopexit(NULL);
static void sigterm_handler(void *data) {
logger(DEBUG_ALWAYS, LOG_NOTICE, "Got %s signal", strsignal(((signal_t *)data)->signum));
event_exit();
}
static void sighup_handler(int signal, short events, void *data) {
logger(DEBUG_ALWAYS, LOG_NOTICE, "Got %s signal", strsignal(signal));
static void sighup_handler(void *data) {
logger(DEBUG_ALWAYS, LOG_NOTICE, "Got %s signal", strsignal(((signal_t *)data)->signum));
reopenlogger();
reload_configuration();
}
static void sigalrm_handler(int signal, short events, void *data) {
logger(DEBUG_ALWAYS, LOG_NOTICE, "Got %s signal", strsignal(signal));
static void sigalrm_handler(void *data) {
logger(DEBUG_ALWAYS, LOG_NOTICE, "Got %s signal", strsignal(((signal_t *)data)->signum));
retry();
}
@ -233,7 +329,7 @@ int reload_configuration(void) {
if(!read_server_config()) {
logger(DEBUG_ALWAYS, LOG_ERR, "Unable to reread configuration file, exitting.");
event_loopexit(NULL);
event_exit();
return EINVAL;
}
@ -332,8 +428,7 @@ int reload_configuration(void) {
void retry(void) {
for list_each(connection_t, c, connection_list) {
if(c->outgoing && !c->node) {
if(timeout_initialized(&c->outgoing->ev))
event_del(&c->outgoing->ev);
timeout_del(&c->outgoing->ev);
if(c->status.connecting)
close(c->socket);
c->outgoing->timeout = 0;
@ -346,40 +441,38 @@ void retry(void) {
this is where it all happens...
*/
int main_loop(void) {
struct event timeout_event;
timeout_t pingtimer = {{0}};
timeout_t periodictimer = {{0}};
timeout_set(&timeout_event, timeout_handler, &timeout_event);
event_add(&timeout_event, &(struct timeval){pingtimeout, 0});
timeout_add(&pingtimer, timeout_handler, &pingtimer, &(struct timeval){pingtimeout, rand() % 100000});
timeout_add(&periodictimer, periodic_handler, &periodictimer, &(struct timeval){pingtimeout, rand() % 100000});
#ifndef HAVE_MINGW
struct event sighup_event;
struct event sigterm_event;
struct event sigquit_event;
struct event sigalrm_event;
signal_t sighup = {0};
signal_t sigterm = {0};
signal_t sigquit = {0};
signal_t sigalrm = {0};
signal_set(&sighup_event, SIGHUP, sighup_handler, NULL);
signal_add(&sighup_event, NULL);
signal_set(&sigterm_event, SIGTERM, sigterm_handler, NULL);
signal_add(&sigterm_event, NULL);
signal_set(&sigquit_event, SIGQUIT, sigterm_handler, NULL);
signal_add(&sigquit_event, NULL);
signal_set(&sigalrm_event, SIGALRM, sigalrm_handler, NULL);
signal_add(&sigalrm_event, NULL);
signal_add(&sighup, sighup_handler, &sighup, SIGHUP);
signal_add(&sigterm, sigterm_handler, &sigterm, SIGTERM);
signal_add(&sigquit, sigterm_handler, &sigquit, SIGQUIT);
signal_add(&sigalrm, sigalrm_handler, &sigalrm, SIGALRM);
#endif
if(event_loop(0) < 0) {
if(!event_loop()) {
logger(DEBUG_ALWAYS, LOG_ERR, "Error while waiting for input: %s", strerror(errno));
return 1;
}
#ifndef HAVE_MINGW
signal_del(&sighup_event);
signal_del(&sigterm_event);
signal_del(&sigquit_event);
signal_del(&sigalrm_event);
signal_del(&sighup);
signal_del(&sigalrm);
signal_del(&sigquit);
signal_del(&sigterm);
#endif
event_del(&timeout_event);
timeout_del(&periodictimer);
timeout_del(&pingtimer);
return 0;
}

View file

@ -24,6 +24,7 @@
#include "ipv6.h"
#include "cipher.h"
#include "digest.h"
#include "event.h"
#ifdef ENABLE_JUMBOGRAMS
#define MTU 9018 /* 9000 bytes payload + 14 bytes ethernet header + 4 bytes VLAN tag */
@ -99,10 +100,8 @@ typedef enum packet_type_t {
} packet_type_t;
typedef struct listen_socket_t {
struct event ev_tcp;
struct event ev_udp;
int tcp;
int udp;
io_t tcp;
io_t udp;
sockaddr_t sa;
} listen_socket_t;
@ -116,7 +115,7 @@ typedef struct outgoing_t {
struct config_t *cfg;
struct addrinfo *ai;
struct addrinfo *aip;
struct event ev;
timeout_t ev;
} outgoing_t;
extern list_t *outgoing_list;
@ -134,6 +133,7 @@ extern int udp_rcvbuf;
extern int udp_sndbuf;
extern bool do_prune;
extern char *myport;
extern int autoconnect;
extern int contradicting_add_edge;
extern int contradicting_del_edge;
extern time_t last_config_check;
@ -160,10 +160,10 @@ extern char *scriptextension;
#include "node.h"
extern void retry_outgoing(outgoing_t *);
extern void handle_incoming_vpn_data(int, short, void *);
extern void handle_incoming_vpn_data(void *, int);
extern void finish_connecting(struct connection_t *);
extern bool do_outgoing_connection(struct outgoing_t *);
extern void handle_new_meta_connection(int, short, void *);
extern void handle_new_meta_connection(void *, int);
extern int setup_listen_socket(const sockaddr_t *);
extern int setup_vpn_in_socket(const sockaddr_t *);
extern bool send_sptps_data(void *handle, uint8_t type, const char *data, size_t len);
@ -183,13 +183,14 @@ extern bool node_read_ecdsa_public_key(struct node_t *);
extern bool read_ecdsa_public_key(struct connection_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 handle_device_data(void *, int);
extern void handle_meta_connection_data(struct connection_t *);
extern void regenerate_key(void);
extern void purge(void);
extern void retry(void);
extern int reload_configuration(void);
extern void load_all_subnets(void);
extern void load_all_nodes(void);
#ifndef HAVE_MINGW
#define closesocket(s) close(s)

View file

@ -77,7 +77,7 @@ bool localdiscovery = false;
which will be broadcast to the local network.
*/
static void send_mtu_probe_handler(int fd, short events, void *data) {
static void send_mtu_probe_handler(void *data) {
node_t *n = data;
int timeout = 1;
@ -151,23 +151,37 @@ static void send_mtu_probe_handler(int fd, short events, void *data) {
}
end:
event_add(&n->mtuevent, &(struct timeval){timeout, 0});
timeout_set(&n->mtutimeout, &(struct timeval){timeout, rand() % 100000});
}
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);
timeout_add(&n->mtutimeout, send_mtu_probe_handler, n, &(struct timeval){1, 0});
send_mtu_probe_handler(n);
}
static void mtu_probe_h(node_t *n, vpn_packet_t *packet, length_t len) {
logger(DEBUG_TRAFFIC, LOG_INFO, "Got MTU probe length %d from %s (%s)", packet->len, n->name, n->hostname);
if(!packet->data[0]) {
/* It's a probe request, send back a reply */
packet->data[0] = 1;
send_udppacket(n, packet);
} else {
/* Temporarily set udp_confirmed, so that the reply is sent
back exactly the way it came in. */
bool udp_confirmed = n->status.udp_confirmed;
n->status.udp_confirmed = true;
send_udppacket(n, packet);
n->status.udp_confirmed = udp_confirmed;
} else {
/* It's a valid reply: now we know bidirectional communication
is possible using the address and socket that the reply
packet used. */
n->status.udp_confirmed = true;
/* If we haven't established the PMTU yet, restart the discovery process. */
if(n->mtuprobes > 30) {
if(n->minmtu)
@ -176,6 +190,8 @@ static void mtu_probe_h(node_t *n, vpn_packet_t *packet, length_t len) {
n->mtuprobes = 1;
}
/* If applicable, raise the minimum supported MTU */
if(len > n->maxmtu)
len = n->maxmtu;
if(n->minmtu < len)
@ -438,6 +454,84 @@ static void send_sptps_packet(node_t *n, vpn_packet_t *origpkt) {
return;
}
static void choose_udp_address(const node_t *n, const sockaddr_t **sa, int *sock) {
/* Latest guess */
*sa = &n->address;
*sock = n->sock;
/* If the UDP address is confirmed, use it. */
if(n->status.udp_confirmed)
return;
/* Send every third packet to n->address; that could be set
to the node's reflexive UDP address discovered during key
exchange. */
static int x = 0;
if(++x >= 3) {
x = 0;
return;
}
/* Otherwise, address are found in edges to this node.
So we pick a random edge and a random socket. */
int i = 0;
int j = rand() % n->edge_tree->count;
edge_t *candidate = NULL;
for splay_each(edge_t, e, n->edge_tree) {
if(i++ == j) {
candidate = e->reverse;
break;
}
}
if(candidate) {
*sa = &candidate->address;
*sock = rand() % listen_sockets;
}
/* Make sure we have a suitable socket for the chosen address */
if(listen_socket[*sock].sa.sa.sa_family != (*sa)->sa.sa_family) {
for(int i = 0; i < listen_sockets; i++) {
if(listen_socket[i].sa.sa.sa_family == (*sa)->sa.sa_family) {
*sock = i;
break;
}
}
}
}
static void choose_broadcast_address(const node_t *n, const sockaddr_t **sa, int *sock) {
static sockaddr_t broadcast_ipv4 = {
.in = {
.sin_family = AF_INET,
.sin_addr.s_addr = -1,
}
};
static sockaddr_t broadcast_ipv6 = {
.in6 = {
.sin6_family = AF_INET6,
.sin6_addr.s6_addr[0x0] = 0xff,
.sin6_addr.s6_addr[0x1] = 0x02,
.sin6_addr.s6_addr[0xf] = 0x01,
}
};
*sock = rand() % listen_sockets;
if(listen_socket[*sock].sa.sa.sa_family == AF_INET6) {
broadcast_ipv6.in6.sin6_port = n->prevedge->address.in.sin_port;
broadcast_ipv6.in6.sin6_scope_id = listen_socket[*sock].sa.in6.sin6_scope_id;
*sa = &broadcast_ipv6;
} else {
broadcast_ipv4.in.sin_port = n->prevedge->address.in.sin_port;
*sa = &broadcast_ipv4;
}
}
static void send_udppacket(node_t *n, vpn_packet_t *origpkt) {
vpn_packet_t pkt1, pkt2;
vpn_packet_t *pkt[] = { &pkt1, &pkt2, &pkt1, &pkt2 };
@ -462,15 +556,13 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt) {
/* Make sure we have a valid key */
if(!n->status.validkey) {
time_t now = time(NULL);
logger(DEBUG_TRAFFIC, LOG_INFO,
"No valid key known yet for %s (%s), forwarding via TCP",
n->name, n->hostname);
if(n->last_req_key + 10 <= now) {
if(n->last_req_key + 10 <= now.tv_sec) {
send_req_key(n);
n->last_req_key = now;
n->last_req_key = now.tv_sec;
}
send_tcppacket(n->nexthop->connection, origpkt);
@ -534,88 +626,27 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt) {
/* Send the packet */
sockaddr_t *sa;
const sockaddr_t *sa;
int sock;
/* Overloaded use of priority field: -1 means local broadcast */
if(origpriority == -1 && n->prevedge) {
sockaddr_t broadcast;
broadcast.in.sin_family = AF_INET;
broadcast.in.sin_addr.s_addr = -1;
broadcast.in.sin_port = n->prevedge->address.in.sin_port;
sa = &broadcast;
sock = 0;
} else {
if(origpriority == -1)
origpriority = 0;
if(n->status.udp_confirmed) {
/* Address of this node is confirmed, so use it. */
sa = &n->address;
sock = n->sock;
} else {
/* Otherwise, go through the list of known addresses of
this node. The first address we try is always the
one in n->address; that could be set to the node's
reflexive UDP address discovered during key
exchange. The other known addresses are those found
in edges to this node. */
static unsigned int i;
int j = 0;
edge_t *candidate = NULL;
if(i) {
for splay_each(edge_t, e, edge_weight_tree) {
if(e->to != n)
continue;
j++;
if(!candidate || j == i)
candidate = e;
}
}
if(!candidate) {
sa = &n->address;
sock = n->sock;
} else {
sa = &candidate->address;
sock = rand() % listen_sockets;
}
if(i++)
if(i > j)
i = 0;
}
}
/* Determine which socket we have to use */
if(sa->sa.sa_family != listen_socket[sock].sa.sa.sa_family)
for(sock = 0; sock < listen_sockets; sock++)
if(sa->sa.sa_family == listen_socket[sock].sa.sa.sa_family)
break;
if(sock >= listen_sockets)
sock = 0;
if(!n->status.udp_confirmed)
n->sock = sock;
if(origpriority == -1 && n->prevedge)
choose_broadcast_address(n, &sa, &sock);
else
choose_udp_address(n, &sa, &sock);
#if defined(SOL_IP) && defined(IP_TOS)
if(priorityinheritance && origpriority != priority
&& listen_socket[n->sock].sa.sa.sa_family == AF_INET) {
priority = origpriority;
logger(DEBUG_TRAFFIC, LOG_DEBUG, "Setting outgoing packet priority to %d", priority);
if(setsockopt(listen_socket[n->sock].udp, SOL_IP, IP_TOS, &priority, sizeof(priority))) /* SO_PRIORITY doesn't seem to work */
if(setsockopt(listen_socket[n->sock].udp.fd, SOL_IP, IP_TOS, &priority, sizeof(priority))) /* SO_PRIORITY doesn't seem to work */
logger(DEBUG_ALWAYS, LOG_ERR, "System call `%s' failed: %s", "setsockopt", strerror(errno));
}
#endif
socklen_t sl = SALEN(n->address.sa);
if(sendto(listen_socket[sock].udp, (char *) &inpkt->seqno, inpkt->len, 0, &sa->sa, sl) < 0 && !sockwouldblock(sockerrno)) {
if(sendto(listen_socket[sock].udp.fd, (char *) &inpkt->seqno, inpkt->len, 0, &sa->sa, SALEN(sa->sa)) < 0 && !sockwouldblock(sockerrno)) {
if(sockmsgsize(sockerrno)) {
if(n->maxmtu >= origlen)
n->maxmtu = origlen - 1;
@ -647,15 +678,12 @@ bool send_sptps_data(void *handle, uint8_t type, const char *data, size_t len) {
/* Otherwise, send the packet via UDP */
struct sockaddr *sa;
socklen_t sl;
const sockaddr_t *sa;
int sock;
sa = &(to->address.sa);
sl = SALEN(to->address.sa);
sock = to->sock;
choose_udp_address(to, &sa, &sock);
if(sendto(listen_socket[sock].udp, data, len, 0, sa, sl) < 0 && !sockwouldblock(sockerrno)) {
if(sendto(listen_socket[sock].udp.fd, data, len, 0, &sa->sa, SALEN(sa->sa)) < 0 && !sockwouldblock(sockerrno)) {
if(sockmsgsize(sockerrno)) {
if(to->maxmtu >= len)
to->maxmtu = len - 1;
@ -711,11 +739,11 @@ bool receive_sptps_record(void *handle, uint8_t type, const char *data, uint16_t
int offset = (type & PKT_MAC) ? 0 : 14;
if(type & PKT_COMPRESSED) {
len = uncompress_packet(inpkt.data + offset, (const uint8_t *)data, len, from->incompression);
if(len < 0) {
length_t ulen = uncompress_packet(inpkt.data + offset, (const uint8_t *)data, len, from->incompression);
if(ulen < 0) {
return false;
} else {
inpkt.len = len + offset;
inpkt.len = ulen + offset;
}
if(inpkt.len > MAXSIZE)
abort();
@ -838,14 +866,13 @@ static node_t *try_harder(const sockaddr_t *from, const vpn_packet_t *pkt) {
node_t *n = NULL;
bool hard = false;
static time_t last_hard_try = 0;
time_t now = time(NULL);
for splay_each(edge_t, e, edge_weight_tree) {
if(!e->to->status.reachable || e->to == myself)
continue;
if(sockaddrcmp_noport(from, &e->address)) {
if(last_hard_try == now)
if(last_hard_try == now.tv_sec)
continue;
hard = true;
}
@ -858,13 +885,14 @@ static node_t *try_harder(const sockaddr_t *from, const vpn_packet_t *pkt) {
}
if(hard)
last_hard_try = now;
last_hard_try = now.tv_sec;
last_hard_try = now;
last_hard_try = now.tv_sec;
return n;
}
void handle_incoming_vpn_data(int sock, short events, void *data) {
void handle_incoming_vpn_data(void *data, int flags) {
listen_socket_t *ls = data;
vpn_packet_t pkt;
char *hostname;
sockaddr_t from = {{0}};
@ -872,7 +900,7 @@ void handle_incoming_vpn_data(int sock, short events, void *data) {
node_t *n;
int len;
len = recvfrom(sock, (char *) &pkt.seqno, MAXSIZE, 0, &from.sa, &fromlen);
len = recvfrom(ls->udp.fd, (char *) &pkt.seqno, MAXSIZE, 0, &from.sa, &fromlen);
if(len <= 0 || len > MAXSIZE) {
if(!sockwouldblock(sockerrno))
@ -900,12 +928,12 @@ void handle_incoming_vpn_data(int sock, short events, void *data) {
return;
}
n->sock = (intptr_t)data;
n->sock = ls - listen_socket;
receive_udppacket(n, &pkt);
}
void handle_device_data(int sock, short events, void *data) {
void handle_device_data(void *data, int flags) {
vpn_packet_t packet;
packet.priority = 0;

View file

@ -42,7 +42,7 @@
#include "xalloc.h"
char *myport;
static struct event device_ev;
static io_t device_io;
devops_t devops;
char *proxyhost;
@ -50,6 +50,7 @@ char *proxyport;
char *proxyuser;
char *proxypass;
proxytype_t proxytype;
int autoconnect;
char *scriptinterpreter;
char *scriptextension;
@ -269,22 +270,16 @@ static bool read_rsa_private_key(void) {
return result;
}
static struct event keyexpire_event;
static timeout_t keyexpire_timeout;
static void keyexpire_handler(int fd, short events, void *data) {
static void keyexpire_handler(void *data) {
regenerate_key();
timeout_set(data, &(struct timeval){keylifetime, rand() % 100000});
}
void regenerate_key(void) {
if(timeout_initialized(&keyexpire_event)) {
logger(DEBUG_STATUS, LOG_INFO, "Expiring symmetric keys");
event_del(&keyexpire_event);
send_key_changed();
} else {
timeout_set(&keyexpire_event, keyexpire_handler, NULL);
}
event_add(&keyexpire_event, &(struct timeval){keylifetime, 0});
logger(DEBUG_STATUS, LOG_INFO, "Expiring symmetric keys");
send_key_changed();
}
/*
@ -347,6 +342,36 @@ void load_all_subnets(void) {
closedir(dir);
}
void load_all_nodes(void) {
DIR *dir;
struct dirent *ent;
char *dname;
xasprintf(&dname, "%s" SLASH "hosts", confbase);
dir = opendir(dname);
if(!dir) {
logger(DEBUG_ALWAYS, LOG_ERR, "Could not open %s: %s", dname, strerror(errno));
free(dname);
return;
}
while((ent = readdir(dir))) {
if(!check_id(ent->d_name))
continue;
node_t *n = lookup_node(ent->d_name);
if(n)
continue;
n = new_node();
n->name = xstrdup(ent->d_name);
node_add(n);
}
closedir(dir);
}
char *get_name(void) {
char *name = NULL;
@ -362,7 +387,7 @@ char *get_name(void) {
logger(DEBUG_ALWAYS, LOG_ERR, "Invalid Name: environment variable %s does not exist\n", name + 1);
return false;
}
envname = alloca(32);
char envname[32];
if(gethostname(envname, 32)) {
logger(DEBUG_ALWAYS, LOG_ERR, "Could not get hostname: %s\n", strerror(errno));
return false;
@ -570,6 +595,8 @@ bool setup_myself_reloadable(void) {
if(!get_config_int(lookup_config(config_tree, "KeyExpire"), &keylifetime))
keylifetime = 3600;
get_config_int(lookup_config(config_tree, "AutoConnect"), &autoconnect);
return true;
}
@ -683,7 +710,8 @@ static bool setup_myself(void) {
free(cipher);
regenerate_key();
send_key_changed();
timeout_add(&keyexpire_timeout, keyexpire_handler, &keyexpire_timeout, &(struct timeval){keylifetime, rand() % 100000});
/* Check if we want to use message authentication codes... */
@ -730,6 +758,8 @@ static bool setup_myself(void) {
if(strictsubnets)
load_all_subnets();
else if(autoconnect)
load_all_nodes();
/* Open device */
@ -755,15 +785,8 @@ static bool setup_myself(void) {
if(!devops.setup())
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(DEBUG_ALWAYS, LOG_ERR, "event_add failed: %s", strerror(errno));
devops.close();
return false;
}
}
if(device_fd >= 0)
io_add(&device_io, handle_device_data, NULL, device_fd, IO_READ);
/* Run tinc-up script to further initialize the tap interface */
char *envp[5];
@ -805,27 +828,16 @@ static bool setup_myself(void) {
return false;
}
listen_socket[i].tcp = i + 3;
#ifdef FD_CLOEXEC
fcntl(i + 3, F_SETFD, FD_CLOEXEC);
#endif
listen_socket[i].udp = setup_vpn_in_socket(&sa);
if(listen_socket[i].udp < 0)
int udp_fd = setup_vpn_in_socket(&sa);
if(udp_fd < 0)
return false;
event_set(&listen_socket[i].ev_tcp, listen_socket[i].tcp, EV_READ|EV_PERSIST, handle_new_meta_connection, NULL);
if(event_add(&listen_socket[i].ev_tcp, NULL) < 0) {
logger(DEBUG_ALWAYS, LOG_ERR, "event_add failed: %s", strerror(errno));
abort();
}
event_set(&listen_socket[i].ev_udp, listen_socket[i].udp, EV_READ|EV_PERSIST, handle_incoming_vpn_data, (void *)(intptr_t)listen_sockets);
if(event_add(&listen_socket[listen_sockets].ev_udp, NULL) < 0) {
logger(DEBUG_ALWAYS, LOG_ERR, "event_add failed: %s", strerror(errno));
abort();
}
io_add(&listen_socket[i].tcp, (io_cb_t)handle_new_meta_connection, &listen_socket[i], i + 3, IO_READ);
io_add(&listen_socket[i].udp, (io_cb_t)handle_incoming_vpn_data, &listen_socket[i], udp_fd, IO_READ);
if(debug_level >= DEBUG_CONNECTIONS) {
hostname = sockaddr2hostname(&sa);
@ -878,37 +890,20 @@ static bool setup_myself(void) {
return false;
}
listen_socket[listen_sockets].tcp =
setup_listen_socket((sockaddr_t *) aip->ai_addr);
int tcp_fd = setup_listen_socket((sockaddr_t *) aip->ai_addr);
if(listen_socket[listen_sockets].tcp < 0)
if(tcp_fd < 0)
continue;
listen_socket[listen_sockets].udp =
setup_vpn_in_socket((sockaddr_t *) aip->ai_addr);
int udp_fd = setup_vpn_in_socket((sockaddr_t *) aip->ai_addr);
if(listen_socket[listen_sockets].udp < 0) {
close(listen_socket[listen_sockets].tcp);
if(tcp_fd < 0) {
close(tcp_fd);
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(DEBUG_ALWAYS, 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, (void *)(intptr_t)listen_sockets);
if(event_add(&listen_socket[listen_sockets].ev_udp, NULL) < 0) {
logger(DEBUG_ALWAYS, LOG_ERR, "event_add failed: %s", strerror(errno));
abort();
}
io_add(&listen_socket[listen_sockets].tcp, handle_new_meta_connection, &listen_socket[listen_sockets], tcp_fd, IO_READ);
io_add(&listen_socket[listen_sockets].udp, handle_incoming_vpn_data, &listen_socket[listen_sockets], udp_fd, IO_READ);
if(debug_level >= DEBUG_CONNECTIONS) {
hostname = sockaddr2hostname((sockaddr_t *) aip->ai_addr);
@ -990,10 +985,10 @@ void close_network_connections(void) {
}
for(int 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);
io_del(&listen_socket[i].tcp);
io_del(&listen_socket[i].udp);
close(listen_socket[i].tcp.fd);
close(listen_socket[i].udp.fd);
}
char *envp[5];

View file

@ -271,7 +271,7 @@ int setup_vpn_in_socket(const sockaddr_t *sa) {
return nfd;
} /* int setup_vpn_in_socket */
static void retry_outgoing_handler(int fd, short events, void *data) {
static void retry_outgoing_handler(void *data) {
setup_outgoing_connection(data);
}
@ -281,12 +281,9 @@ void retry_outgoing(outgoing_t *outgoing) {
if(outgoing->timeout > maxtimeout)
outgoing->timeout = maxtimeout;
timeout_set(&outgoing->ev, retry_outgoing_handler, outgoing);
event_add(&outgoing->ev, &(struct timeval){outgoing->timeout, 0});
timeout_add(&outgoing->ev, retry_outgoing_handler, outgoing, &(struct timeval){outgoing->timeout, rand() % 100000});
logger(DEBUG_CONNECTIONS, LOG_NOTICE,
"Trying to re-establish outgoing connection in %d seconds",
outgoing->timeout);
logger(DEBUG_CONNECTIONS, LOG_NOTICE, "Trying to re-establish outgoing connection in %d seconds", outgoing->timeout);
}
void finish_connecting(connection_t *c) {
@ -349,9 +346,7 @@ static void do_outgoing_pipe(connection_t *c, char *command) {
#endif
}
static void handle_meta_write(int sock, short events, void *data) {
connection_t *c = data;
static void handle_meta_write(connection_t *c) {
ssize_t outlen = send(c->socket, c->outbuf.data + c->outbuf.offset, c->outbuf.len - c->outbuf.offset, 0);
if(outlen <= 0) {
if(!errno || errno == EPIPE) {
@ -368,10 +363,16 @@ static void handle_meta_write(int sock, short events, void *data) {
}
buffer_read(&c->outbuf, outlen);
if(!c->outbuf.len && event_initialized(&c->outevent))
event_del(&c->outevent);
if(!c->outbuf.len)
io_set(&c->io, IO_READ);
}
static void handle_meta_io(void *data, int flags) {
if(flags & IO_WRITE)
handle_meta_write(data);
else
handle_meta_connection_data(data);
}
bool do_outgoing_connection(outgoing_t *outgoing) {
char *address, *port, *space;
@ -487,16 +488,13 @@ begin:
connection_add(c);
event_set(&c->inevent, c->socket, EV_READ | EV_PERSIST, handle_meta_connection_data, c);
event_set(&c->outevent, c->socket, EV_WRITE | EV_PERSIST, handle_meta_write, c);
event_add(&c->inevent, NULL);
io_add(&c->io, handle_meta_io, c, c->socket, IO_READ);
return true;
}
void setup_outgoing_connection(outgoing_t *outgoing) {
if(event_initialized(&outgoing->ev))
event_del(&outgoing->ev);
timeout_del(&outgoing->ev);
node_t *n = lookup_node(outgoing->name);
@ -523,13 +521,14 @@ void setup_outgoing_connection(outgoing_t *outgoing) {
accept a new tcp connect and create a
new connection
*/
void handle_new_meta_connection(int sock, short events, void *data) {
void handle_new_meta_connection(void *data, int flags) {
listen_socket_t *l = data;
connection_t *c;
sockaddr_t sa;
int fd;
socklen_t len = sizeof sa;
fd = accept(sock, &sa.sa, &len);
fd = accept(l->tcp.fd, &sa.sa, &len);
if(fd < 0) {
logger(DEBUG_ALWAYS, LOG_ERR, "Accepting a new connection failed: %s", sockstrerror(sockerrno));
@ -552,9 +551,7 @@ void handle_new_meta_connection(int sock, short events, void *data) {
logger(DEBUG_CONNECTIONS, LOG_NOTICE, "Connection from %s", c->hostname);
event_set(&c->inevent, c->socket, EV_READ | EV_PERSIST, handle_meta_connection_data, c);
event_set(&c->outevent, c->socket, EV_WRITE | EV_PERSIST, handle_meta_write, c);
event_add(&c->inevent, NULL);
io_add(&c->io, handle_meta_io, c, c->socket, IO_READ);
configure_tcp(c);
@ -565,8 +562,7 @@ void handle_new_meta_connection(int sock, short events, void *data) {
}
static void free_outgoing(outgoing_t *outgoing) {
if(event_initialized(&outgoing->ev))
event_del(&outgoing->ev);
timeout_del(&outgoing->ev);
if(outgoing->ai)
freeaddrinfo(outgoing->ai);

View file

@ -52,7 +52,7 @@ struct addrinfo *str2addrinfo(const char *address, const char *service, int sock
sockaddr_t str2sockaddr(const char *address, const char *port) {
struct addrinfo *ai, hint = {0};
sockaddr_t result;
sockaddr_t result = {{0}};
int err;
hint.ai_family = AF_UNSPEC;

View file

@ -78,8 +78,7 @@ void free_node(node_t *n) {
ecdsa_free(&n->ecdsa);
sptps_stop(&n->sptps);
if(timeout_initialized(&n->mtuevent))
event_del(&n->mtuevent);
timeout_del(&n->mtutimeout);
if(n->hostname)
free(n->hostname);
@ -129,6 +128,13 @@ void update_node_udp(node_t *n, const sockaddr_t *sa) {
if(sa) {
n->address = *sa;
n->sock = 0;
for(int i = 0; i < listen_sockets; i++) {
if(listen_socket[i].sa.sa.sa_family == sa->sa.sa_family) {
n->sock = i;
break;
}
}
hash_insert(node_udp_cache, sa, n);
free(n->hostname);
n->hostname = sockaddr2hostname(&n->address);

View file

@ -25,6 +25,7 @@
#include "cipher.h"
#include "connection.h"
#include "digest.h"
#include "event.h"
#include "subnet.h"
typedef struct node_status_t {
@ -83,7 +84,7 @@ typedef struct node_t {
length_t minmtu; /* Probed minimum MTU */
length_t maxmtu; /* Probed maximum MTU */
int mtuprobes; /* Number of probes */
struct event mtuevent; /* Probe event */
timeout_t mtutimeout; /* Probe event */
uint64_t in_packets;
uint64_t in_bytes;

View file

@ -25,6 +25,7 @@
#include "control.h"
#include "device.h"
#include "edge.h"
#include "event.h"
#include "logger.h"
#include "net.h"
#include "node.h"
@ -127,7 +128,7 @@ DWORD WINAPI controlhandler(DWORD request, DWORD type, LPVOID boe, LPVOID bah) {
return ERROR_CALL_NOT_IMPLEMENTED;
}
event_loopexit(NULL);
event_exit();
status.dwWaitHint = 30000;
status.dwCurrentState = SERVICE_STOP_PENDING;
SetServiceStatus(statushandle, &status);

View file

@ -165,7 +165,24 @@ static void free_past_request(past_request_t *r) {
free(r);
}
static struct event past_request_event;
static timeout_t past_request_timeout;
static void age_past_requests(void *data) {
int left = 0, deleted = 0;
for splay_each(past_request_t, p, past_request_tree) {
if(p->firstseen + pinginterval <= now.tv_sec)
splay_delete_node(past_request_tree, node), deleted++;
else
left++;
}
if(left || deleted)
logger(DEBUG_SCARY_THINGS, LOG_DEBUG, "Aging past requests: deleted %d, left %d", deleted, left);
if(left)
timeout_set(&past_request_timeout, &(struct timeval){10, rand() % 100000});
}
bool seen_request(const char *request) {
past_request_t *new, p = {NULL};
@ -180,39 +197,17 @@ bool seen_request(const char *request) {
new->request = xstrdup(request);
new->firstseen = time(NULL);
splay_insert(past_request_tree, new);
event_add(&past_request_event, &(struct timeval){10, 0});
timeout_add(&past_request_timeout, age_past_requests, NULL, &(struct timeval){10, rand() % 100000});
return false;
}
}
static void age_past_requests(int fd, short events, void *data) {
int left = 0, deleted = 0;
time_t now = time(NULL);
for splay_each(past_request_t, p, past_request_tree) {
if(p->firstseen + pinginterval <= now)
splay_delete_node(past_request_tree, node), deleted++;
else
left++;
}
if(left || deleted)
logger(DEBUG_SCARY_THINGS, LOG_DEBUG, "Aging past requests: deleted %d, left %d",
deleted, left);
if(left)
event_add(&past_request_event, &(struct timeval){10, 0});
}
void init_requests(void) {
past_request_tree = splay_alloc_tree((splay_compare_t) past_request_compare, (splay_action_t) free_past_request);
timeout_set(&past_request_event, age_past_requests, NULL);
}
void exit_requests(void) {
splay_delete_tree(past_request_tree);
if(timeout_initialized(&past_request_event))
event_del(&past_request_event);
timeout_del(&past_request_timeout);
}

View file

@ -59,7 +59,7 @@ static const size_t opt_size = sizeof(struct nd_opt_hdr);
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#endif
static struct event age_subnets_event;
static timeout_t age_subnets_timeout;
/* RFC 1071 */
@ -84,13 +84,12 @@ static uint16_t inet_checksum(void *data, int len, uint16_t prevsum) {
static bool ratelimit(int frequency) {
static time_t lasttime = 0;
static int count = 0;
time_t now = time(NULL);
if(lasttime == now) {
if(lasttime == now.tv_sec) {
if(count >= frequency)
return true;
} else {
lasttime = now;
lasttime = now.tv_sec;
count = 0;
}
@ -115,15 +114,22 @@ static void clamp_mss(const node_t *source, const node_t *via, vpn_packet_t *pac
mtu = via->mtu;
/* Find TCP header */
int start = 0;
int start = ether_size;
uint16_t type = packet->data[12] << 8 | packet->data[13];
if(type == ETH_P_IP && packet->data[23] == 6)
start = 14 + (packet->data[14] & 0xf) * 4;
else if(type == ETH_P_IPV6 && packet->data[20] == 6)
start = 14 + 40;
if(type == ETH_P_8021Q) {
start += 4;
type = packet->data[16] << 8 | packet->data[17];
}
if(!start || packet->len <= start + 20)
if(type == ETH_P_IP && packet->data[start + 9] == 6)
start += (packet->data[start] & 0xf) * 4;
else if(type == ETH_P_IPV6 && packet->data[start + 6] == 6)
start += 40;
else
return;
if(packet->len <= start + 20)
return;
/* Use data offset field to calculate length of options field */
@ -185,12 +191,11 @@ static void swap_mac_addresses(vpn_packet_t *packet) {
memcpy(&packet->data[6], &tmp, sizeof tmp);
}
static void age_subnets(int fd, short events, void *data) {
static void age_subnets(void *data) {
bool left = false;
time_t now = time(NULL);
for splay_each(subnet_t, s, myself->subnet_tree) {
if(s->expires && s->expires < now) {
if(s->expires && s->expires < now.tv_sec) {
if(debug_level >= DEBUG_TRAFFIC) {
char netstr[MAXNETSTR];
if(net2str(netstr, sizeof netstr, s))
@ -209,7 +214,7 @@ static void age_subnets(int fd, short events, void *data) {
}
if(left)
event_add(&age_subnets_event, &(struct timeval){10, 0});
timeout_set(&age_subnets_timeout, &(struct timeval){10, rand() % 100000});
}
static void learn_mac(mac_t *address) {
@ -236,9 +241,7 @@ static void learn_mac(mac_t *address) {
if(c->status.active)
send_add_subnet(c, subnet);
if(!timeout_initialized(&age_subnets_event))
timeout_set(&age_subnets_event, age_subnets, NULL);
event_add(&age_subnets_event, &(struct timeval){10, 0});
timeout_add(&age_subnets_timeout, age_subnets, NULL, &(struct timeval){10, rand() % 100000});
} else {
if(subnet->expires)
subnet->expires = time(NULL) + macexpire;
@ -247,7 +250,7 @@ static void learn_mac(mac_t *address) {
/* RFC 792 */
static void route_ipv4_unreachable(node_t *source, vpn_packet_t *packet, uint8_t type, uint8_t code) {
static void route_ipv4_unreachable(node_t *source, vpn_packet_t *packet, length_t ether_size, uint8_t type, uint8_t code) {
struct ip ip = {0};
struct icmp icmp = {0};
@ -320,7 +323,7 @@ static void route_ipv4_unreachable(node_t *source, vpn_packet_t *packet, uint8_t
/* RFC 791 */
static void fragment_ipv4_packet(node_t *dest, vpn_packet_t *packet) {
static void fragment_ipv4_packet(node_t *dest, vpn_packet_t *packet, length_t ether_size) {
struct ip ip;
vpn_packet_t fragment;
int len, maxlen, todo;
@ -384,7 +387,7 @@ static void route_ipv4_unicast(node_t *source, vpn_packet_t *packet) {
dest.x[2],
dest.x[3]);
route_ipv4_unreachable(source, packet, ICMP_DEST_UNREACH, ICMP_NET_UNKNOWN);
route_ipv4_unreachable(source, packet, ether_size, ICMP_DEST_UNREACH, ICMP_NET_UNKNOWN);
return;
}
@ -394,10 +397,10 @@ static void route_ipv4_unicast(node_t *source, vpn_packet_t *packet) {
}
if(!subnet->owner->status.reachable)
return route_ipv4_unreachable(source, packet, ICMP_DEST_UNREACH, ICMP_NET_UNREACH);
return route_ipv4_unreachable(source, packet, ether_size, ICMP_DEST_UNREACH, ICMP_NET_UNREACH);
if(forwarding_mode == FMODE_OFF && source != myself && subnet->owner != myself)
return route_ipv4_unreachable(source, packet, ICMP_DEST_UNREACH, ICMP_NET_ANO);
return route_ipv4_unreachable(source, packet, ether_size, ICMP_DEST_UNREACH, ICMP_NET_ANO);
if(priorityinheritance)
packet->priority = packet->data[15];
@ -410,15 +413,15 @@ static void route_ipv4_unicast(node_t *source, vpn_packet_t *packet) {
}
if(directonly && subnet->owner != via)
return route_ipv4_unreachable(source, packet, ICMP_DEST_UNREACH, ICMP_NET_ANO);
return route_ipv4_unreachable(source, packet, ether_size, ICMP_DEST_UNREACH, ICMP_NET_ANO);
if(via && packet->len > MAX(via->mtu, 590) && via != myself) {
logger(DEBUG_TRAFFIC, LOG_INFO, "Packet for %s (%s) length %d larger than MTU %d", subnet->owner->name, subnet->owner->hostname, packet->len, via->mtu);
if(packet->data[20] & 0x40) {
packet->len = MAX(via->mtu, 590);
route_ipv4_unreachable(source, packet, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED);
route_ipv4_unreachable(source, packet, ether_size, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED);
} else {
fragment_ipv4_packet(via, packet);
fragment_ipv4_packet(via, packet, ether_size);
}
return;
@ -445,7 +448,7 @@ static void route_ipv4(node_t *source, vpn_packet_t *packet) {
/* RFC 2463 */
static void route_ipv6_unreachable(node_t *source, vpn_packet_t *packet, uint8_t type, uint8_t code) {
static void route_ipv6_unreachable(node_t *source, vpn_packet_t *packet, length_t ether_size, uint8_t type, uint8_t code) {
struct ip6_hdr ip6;
struct icmp6_hdr icmp6 = {0};
uint16_t checksum;
@ -543,7 +546,7 @@ static void route_ipv6_unicast(node_t *source, vpn_packet_t *packet) {
ntohs(dest.x[6]),
ntohs(dest.x[7]));
route_ipv6_unreachable(source, packet, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_ADDR);
route_ipv6_unreachable(source, packet, ether_size, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_ADDR);
return;
}
@ -553,10 +556,10 @@ static void route_ipv6_unicast(node_t *source, vpn_packet_t *packet) {
}
if(!subnet->owner->status.reachable)
return route_ipv6_unreachable(source, packet, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_NOROUTE);
return route_ipv6_unreachable(source, packet, ether_size, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_NOROUTE);
if(forwarding_mode == FMODE_OFF && source != myself && subnet->owner != myself)
return route_ipv6_unreachable(source, packet, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_ADMIN);
return route_ipv6_unreachable(source, packet, ether_size, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_ADMIN);
via = (subnet->owner->via == myself) ? subnet->owner->nexthop : subnet->owner->via;
@ -566,12 +569,12 @@ static void route_ipv6_unicast(node_t *source, vpn_packet_t *packet) {
}
if(directonly && subnet->owner != via)
return route_ipv6_unreachable(source, packet, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_ADMIN);
return route_ipv6_unreachable(source, packet, ether_size, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_ADMIN);
if(via && packet->len > MAX(via->mtu, 1294) && via != myself) {
logger(DEBUG_TRAFFIC, LOG_INFO, "Packet for %s (%s) length %d larger than MTU %d", subnet->owner->name, subnet->owner->hostname, packet->len, via->mtu);
packet->len = MAX(via->mtu, 1294);
route_ipv6_unreachable(source, packet, ICMP6_PACKET_TOO_BIG, 0);
route_ipv6_unreachable(source, packet, ether_size, ICMP6_PACKET_TOO_BIG, 0);
return;
}
@ -842,17 +845,24 @@ static void route_mac(node_t *source, vpn_packet_t *packet) {
if(via && packet->len > via->mtu && via != myself) {
logger(DEBUG_TRAFFIC, LOG_INFO, "Packet for %s (%s) length %d larger than MTU %d", subnet->owner->name, subnet->owner->hostname, packet->len, via->mtu);
uint16_t type = packet->data[12] << 8 | packet->data[13];
if(type == ETH_P_IP && packet->len > 590) {
if(packet->data[20] & 0x40) {
length_t ethlen = 14;
if(type == ETH_P_8021Q) {
type = packet->data[16] << 8 | packet->data[17];
ethlen += 4;
}
if(type == ETH_P_IP && packet->len > 576 + ethlen) {
if(packet->data[6 + ethlen] & 0x40) {
packet->len = via->mtu;
route_ipv4_unreachable(source, packet, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED);
route_ipv4_unreachable(source, packet, ethlen, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED);
} else {
fragment_ipv4_packet(via, packet);
fragment_ipv4_packet(via, packet, ethlen);
}
return;
} else if(type == ETH_P_IPV6 && packet->len > 1294) {
} else if(type == ETH_P_IPV6 && packet->len > 1280 + ethlen) {
packet->len = via->mtu;
route_ipv6_unreachable(source, packet, ICMP6_PACKET_TOO_BIG, 0);
route_ipv6_unreachable(source, packet, ethlen, ICMP6_PACKET_TOO_BIG, 0);
return;
}
}
@ -881,42 +891,48 @@ static void send_pcap(vpn_packet_t *packet) {
static bool do_decrement_ttl(node_t *source, vpn_packet_t *packet) {
uint16_t type = packet->data[12] << 8 | packet->data[13];
length_t ethlen = ether_size;
if(type == ETH_P_8021Q) {
type = packet->data[16] << 8 | packet->data[17];
ethlen += 4;
}
switch (type) {
case ETH_P_IP:
if(!checklength(source, packet, 14 + 32))
if(!checklength(source, packet, ethlen + ip_size))
return false;
if(packet->data[22] < 1) {
if(packet->data[25] != IPPROTO_ICMP || packet->data[46] != ICMP_TIME_EXCEEDED)
route_ipv4_unreachable(source, packet, ICMP_TIME_EXCEEDED, ICMP_EXC_TTL);
if(packet->data[ethlen + 8] < 1) {
if(packet->data[ethlen + 11] != IPPROTO_ICMP || packet->data[ethlen + 32] != ICMP_TIME_EXCEEDED)
route_ipv4_unreachable(source, packet, ethlen, ICMP_TIME_EXCEEDED, ICMP_EXC_TTL);
return false;
}
uint16_t old = packet->data[22] << 8 | packet->data[23];
packet->data[22]--;
uint16_t new = packet->data[22] << 8 | packet->data[23];
uint16_t old = packet->data[ethlen + 8] << 8 | packet->data[ethlen + 9];
packet->data[ethlen + 8]--;
uint16_t new = packet->data[ethlen + 8] << 8 | packet->data[ethlen + 9];
uint32_t checksum = packet->data[24] << 8 | packet->data[25];
uint32_t checksum = packet->data[ethlen + 10] << 8 | packet->data[ethlen + 11];
checksum += old + (~new & 0xFFFF);
while(checksum >> 16)
checksum = (checksum & 0xFFFF) + (checksum >> 16);
packet->data[24] = checksum >> 8;
packet->data[25] = checksum & 0xff;
packet->data[ethlen + 10] = checksum >> 8;
packet->data[ethlen + 11] = checksum & 0xff;
return true;
case ETH_P_IPV6:
if(!checklength(source, packet, 14 + 40))
if(!checklength(source, packet, ethlen + ip6_size))
return false;
if(packet->data[21] < 1) {
if(packet->data[20] != IPPROTO_ICMPV6 || packet->data[54] != ICMP6_TIME_EXCEEDED)
route_ipv6_unreachable(source, packet, ICMP6_TIME_EXCEEDED, ICMP6_TIME_EXCEED_TRANSIT);
if(packet->data[ethlen + 7] < 1) {
if(packet->data[ethlen + 6] != IPPROTO_ICMPV6 || packet->data[ethlen + 40] != ICMP6_TIME_EXCEEDED)
route_ipv6_unreachable(source, packet, ethlen, ICMP6_TIME_EXCEEDED, ICMP6_TIME_EXCEED_TRANSIT);
return false;
}
packet->data[21]--;
packet->data[ethlen + 7]--;
return true;

View file

@ -398,6 +398,8 @@ splay_node_t *splay_insert_node(splay_tree_t *tree, splay_node_t *node) {
splay_node_t *closest;
int result;
node->left = node->right = node->parent = node->next = node->prev = NULL;
if(!tree->root)
splay_insert_top(tree, node);
else {
@ -418,6 +420,7 @@ splay_node_t *splay_insert_node(splay_tree_t *tree, splay_node_t *node) {
void splay_insert_top(splay_tree_t *tree, splay_node_t *node) {
node->prev = node->next = node->left = node->right = node->parent = NULL;
tree->head = tree->tail = tree->root = node;
tree->count++;
}
void splay_insert_before(splay_tree_t *tree, splay_node_t *before, splay_node_t *node) {
@ -446,6 +449,7 @@ void splay_insert_before(splay_tree_t *tree, splay_node_t *before, splay_node_t
node->parent = NULL;
tree->root = node;
tree->count++;
}
void splay_insert_after(splay_tree_t *tree, splay_node_t *after, splay_node_t *node) {
@ -474,6 +478,7 @@ void splay_insert_after(splay_tree_t *tree, splay_node_t *after, splay_node_t *n
node->parent = NULL;
tree->root = node;
tree->count++;
}
splay_node_t *splay_unlink(splay_tree_t *tree, void *data) {
@ -511,6 +516,8 @@ void splay_unlink_node(splay_tree_t *tree, splay_node_t *node) {
} else {
tree->root = NULL;
}
tree->count--;
}
void splay_delete_node(splay_tree_t *tree, splay_node_t *node) {

View file

@ -58,6 +58,8 @@ typedef struct splay_tree_t {
splay_compare_t compare;
splay_action_t delete;
int count;
} splay_tree_t;
/* (De)constructors */

View file

@ -29,6 +29,7 @@ bool send_request(void *c, const char *msg, ...) { return false; }
struct list_t *connection_list = NULL;
bool send_meta(void *c, const char *msg , int len) { return false; }
char *logfilename = NULL;
struct timeval now;
ecdsa_t mykey, hiskey;

View file

@ -57,11 +57,12 @@ static char *name = NULL;
static char *identname = NULL; /* program name for syslog */
static char *pidfilename = NULL; /* pid file location */
static char *confdir = NULL;
static char controlcookie[1024];
static char controlcookie[1025];
char *netname = NULL;
char *confbase = NULL;
static char *tinc_conf = NULL;
static char *hosts_dir = NULL;
struct timeval now;
// Horrible global variables...
static int pid = 0;
@ -134,7 +135,7 @@ static void usage(bool status) {
" generate-rsa-keys [bits] Generate a new RSA public/private keypair.\n"
" generate-ecdsa-keys Generate a new ECDSA public/private keypair.\n"
" dump Dump a list of one of the following things:\n"
" nodes - all known nodes in the VPN\n"
" [reachable] nodes - all known nodes in the VPN\n"
" edges - all known connections in the VPN\n"
" subnets - all known subnets in the VPN\n"
" connections - all meta connections with ourself\n"
@ -708,8 +709,8 @@ static bool connect_tincd(bool verbose) {
return false;
}
char host[128];
char port[128];
char host[129];
char port[129];
if(fscanf(f, "%20d %1024s %128s port %128s", &pid, controlcookie, host, port) != 4) {
if(verbose)
@ -911,6 +912,19 @@ static int cmd_reload(int argc, char *argv[]) {
}
static int cmd_dump(int argc, char *argv[]) {
bool only_reachable = false;
if(argc > 2 && !strcasecmp(argv[1], "reachable")) {
if(strcasecmp(argv[2], "nodes")) {
fprintf(stderr, "`reachable' only supported for nodes.\n");
usage(true);
return 1;
}
only_reachable = true;
argv++;
argc--;
}
if(argc != 2) {
fprintf(stderr, "Invalid number of arguments.\n");
usage(true);
@ -985,8 +999,10 @@ static int cmd_dump(int argc, char *argv[]) {
fprintf(stderr, "Unable to parse node dump from tincd: %s\n", line);
return 1;
}
memcpy(&status, &status_int, sizeof status);
if(do_graph) {
memcpy(&status, &status_int, sizeof status);
const char *color = "black";
if(!strcmp(host, "MYSELF"))
color = "green";
@ -1000,6 +1016,8 @@ static int cmd_dump(int argc, char *argv[]) {
color = "green";
printf(" %s [label = \"%s\", color = \"%s\"%s];\n", node, node, color, strcmp(host, "MYSELF") ? "" : ", style = \"filled\"");
} else {
if(only_reachable && !status.reachable)
continue;
printf("%s at %s port %s cipher %d digest %d maclength %d compression %d options %x status %04x nexthop %s via %s distance %d pmtu %hd (min %hd max %hd)\n",
node, host, port, cipher, digest, maclength, compression, options, status_int, nexthop, via, distance, pmtu, minmtu, maxmtu);
}
@ -1233,6 +1251,7 @@ static struct {
} const variables[] = {
/* Server configuration */
{"AddressFamily", VAR_SERVER},
{"AutoConnect", VAR_SERVER},
{"BindToAddress", VAR_SERVER | VAR_MULTIPLE},
{"BindToInterface", VAR_SERVER},
{"Broadcast", VAR_SERVER},
@ -1945,7 +1964,7 @@ static char *complete_command(const char *text, int state) {
}
static char *complete_dump(const char *text, int state) {
const char *matches[] = {"nodes", "edges", "subnets", "connections", "graph", NULL};
const char *matches[] = {"reachable", "nodes", "edges", "subnets", "connections", "graph", NULL};
static int i;
if(!state)

View file

@ -450,11 +450,6 @@ int main2(int argc, char **argv) {
}
#endif
if(!event_init()) {
logger(DEBUG_ALWAYS, LOG_ERR, "Error initializing libevent!");
return 1;
}
/* Setup sockets and open device. */
if(!setup_network())

198
src/top.c
View file

@ -57,11 +57,13 @@ static int sortmode = 0;
static bool cumulative = false;
static list_t node_list;
static struct timeval now, prev, diff;
static struct timeval cur, prev, diff;
static int delay = 1000;
static bool changed = true;
static const char *unit = "bytes";
static float scale = 1;
static const char *bunit = "bytes";
static float bscale = 1;
static const char *punit = "pkts";
static float pscale = 1;
#ifndef timersub
#define timersub(a, b, c) do {(c)->tv_sec = (a)->tv_sec - (b)->tv_sec; (c)->tv_usec = (a)->tv_usec = (b)->tv_usec;} while(0)
@ -69,10 +71,10 @@ static float scale = 1;
static void update(int fd) {
sendline(fd, "%d %d", CONTROL, REQ_DUMP_TRAFFIC);
gettimeofday(&now, NULL);
gettimeofday(&cur, NULL);
timersub(&now, &prev, &diff);
prev = now;
timersub(&cur, &prev, &diff);
prev = cur;
float interval = diff.tv_sec + diff.tv_usec * 1e-6;
char line[4096];
@ -136,12 +138,69 @@ static void update(int fd) {
}
}
static int cmpfloat(float a, float b) {
if(a < b)
return -1;
else if(a > b)
return 1;
else
return 0;
}
static int cmpu64(uint64_t a, uint64_t b) {
if(a < b)
return -1;
else if(a > b)
return 1;
else
return 0;
}
static int sortfunc(const void *a, const void *b) {
const nodestats_t *na = *(const nodestats_t **)a;
const nodestats_t *nb = *(const nodestats_t **)b;
switch(sortmode) {
case 1:
if(cumulative)
return -cmpu64(na->in_packets, nb->in_packets) ?: na->i - nb->i;
else
return -cmpfloat(na->in_packets_rate, nb->in_packets_rate) ?: na->i - nb->i;
case 2:
if(cumulative)
return -cmpu64(na->in_bytes, nb->in_bytes) ?: na->i - nb->i;
else
return -cmpfloat(na->in_bytes_rate, nb->in_bytes_rate) ?: na->i - nb->i;
case 3:
if(cumulative)
return -cmpu64(na->out_packets, nb->out_packets) ?: na->i - nb->i;
else
return -cmpfloat(na->out_packets_rate, nb->out_packets_rate) ?: na->i - nb->i;
case 4:
if(cumulative)
return -cmpu64(na->out_bytes, nb->out_bytes) ?: na->i - nb->i;
else
return -cmpfloat(na->out_bytes_rate, nb->out_bytes_rate) ?: na->i - nb->i;
case 5:
if(cumulative)
return -cmpu64(na->in_packets + na->out_packets, nb->in_packets + nb->out_packets) ?: na->i - nb->i;
else
return -cmpfloat(na->in_packets_rate + na->out_packets_rate, nb->in_packets_rate + nb->out_packets_rate) ?: na->i - nb->i;
case 6:
if(cumulative)
return -cmpu64(na->in_bytes + na->out_bytes, nb->in_bytes + nb->out_bytes) ?: na->i - nb->i;
else
return -cmpfloat(na->in_bytes_rate + na->out_bytes_rate, nb->in_bytes_rate + nb->out_bytes_rate) ?: na->i - nb->i;
default:
return strcmp(na->name, nb->name) ?: na->i - nb->i;
}
}
static void redraw(void) {
erase();
mvprintw(0, 0, "Tinc %-16s Nodes: %4d Sort: %-10s %s", netname ?: "", node_list.count, sortname[sortmode], cumulative ? "Cumulative" : "Current");
attrset(A_REVERSE);
mvprintw(2, 0, "Node IN pkts IN %s OUT pkts OUT %s", unit, unit);
mvprintw(2, 0, "Node IN %s IN %s OUT %s OUT %s", punit, bunit, punit, bunit);
chgat(-1, A_REVERSE, 0, NULL);
static nodestats_t **sorted = 0;
@ -157,63 +216,6 @@ static void redraw(void) {
for(int i = 0; i < n; i++)
sorted[i]->i = i;
int cmpfloat(float a, float b) {
if(a < b)
return -1;
else if(a > b)
return 1;
else
return 0;
}
int cmpu64(uint64_t a, uint64_t b) {
if(a < b)
return -1;
else if(a > b)
return 1;
else
return 0;
}
int sortfunc(const void *a, const void *b) {
const nodestats_t *na = *(const nodestats_t **)a;
const nodestats_t *nb = *(const nodestats_t **)b;
switch(sortmode) {
case 1:
if(cumulative)
return -cmpu64(na->in_packets, nb->in_packets) ?: na->i - nb->i;
else
return -cmpfloat(na->in_packets_rate, nb->in_packets_rate) ?: na->i - nb->i;
case 2:
if(cumulative)
return -cmpu64(na->in_bytes, nb->in_bytes) ?: na->i - nb->i;
else
return -cmpfloat(na->in_bytes_rate, nb->in_bytes_rate) ?: na->i - nb->i;
case 3:
if(cumulative)
return -cmpu64(na->out_packets, nb->out_packets) ?: na->i - nb->i;
else
return -cmpfloat(na->out_packets_rate, nb->out_packets_rate) ?: na->i - nb->i;
case 4:
if(cumulative)
return -cmpu64(na->out_bytes, nb->out_bytes) ?: na->i - nb->i;
else
return -cmpfloat(na->out_bytes_rate, nb->out_bytes_rate) ?: na->i - nb->i;
case 5:
if(cumulative)
return -cmpu64(na->in_packets + na->out_packets, nb->in_packets + nb->out_packets) ?: na->i - nb->i;
else
return -cmpfloat(na->in_packets_rate + na->out_packets_rate, nb->in_packets_rate + nb->out_packets_rate) ?: na->i - nb->i;
case 6:
if(cumulative)
return -cmpu64(na->in_bytes + na->out_bytes, nb->in_bytes + nb->out_bytes) ?: na->i - nb->i;
else
return -cmpfloat(na->in_bytes_rate + na->out_bytes_rate, nb->in_bytes_rate + nb->out_bytes_rate) ?: na->i - nb->i;
default:
return strcmp(na->name, nb->name) ?: na->i - nb->i;
}
}
qsort(sorted, n, sizeof *sorted, sortfunc);
for(int i = 0, row = 3; i < n; i++, row++) {
@ -228,10 +230,10 @@ static void redraw(void) {
if(cumulative)
mvprintw(row, 0, "%-16s %10"PRIu64" %10.0f %10"PRIu64" %10.0f",
node->name, node->in_packets, node->in_bytes * scale, node->out_packets, node->out_bytes * scale);
node->name, node->in_packets * pscale, node->in_bytes * bscale, node->out_packets * pscale, node->out_bytes * bscale);
else
mvprintw(row, 0, "%-16s %10.0f %10.0f %10.0f %10.0f",
node->name, node->in_packets_rate, node->in_bytes_rate * scale, node->out_packets_rate, node->out_bytes_rate * scale);
node->name, node->in_packets_rate * pscale, node->in_bytes_rate * bscale, node->out_packets_rate * pscale, node->out_bytes_rate * bscale);
}
attrset(A_NORMAL);
@ -262,45 +264,53 @@ void top(int fd) {
break;
}
case 'c':
cumulative = !cumulative;
break;
cumulative = !cumulative;
break;
case 'n':
sortmode = 0;
break;
sortmode = 0;
break;
case 'i':
sortmode = 2;
break;
sortmode = 2;
break;
case 'I':
sortmode = 1;
break;
sortmode = 1;
break;
case 'o':
sortmode = 4;
break;
sortmode = 4;
break;
case 'O':
sortmode = 3;
break;
sortmode = 3;
break;
case 't':
sortmode = 6;
break;
sortmode = 6;
break;
case 'T':
sortmode = 5;
break;
sortmode = 5;
break;
case 'b':
unit = "bytes";
scale = 1;
break;
bunit = "bytes";
bscale = 1;
punit = "pkts";
pscale = 1;
break;
case 'k':
unit = "kbyte";
scale = 1e-3;
break;
bunit = "kbyte";
bscale = 1e-3;
punit = "pkts";
pscale = 1;
break;
case 'M':
unit = "Mbyte";
scale = 1e-6;
break;
bunit = "Mbyte";
bscale = 1e-6;
punit = "kpkt";
pscale = 1e-3;
break;
case 'G':
unit = "Gbyte";
scale = 1e-9;
break;
bunit = "Gbyte";
bscale = 1e-9;
punit = "Mpkt";
pscale = 1e-6;
break;
case 'q':
case KEY_BREAK:
running = false;