From: Guus Sliepen Date: Wed, 22 Feb 2012 13:23:59 +0000 (+0100) Subject: Merge branch 'master' of git://tinc-vpn.org/tinc into 1.1 X-Git-Tag: release-1.1pre3~153 X-Git-Url: https://tinc-vpn.org/git/browse?p=tinc;a=commitdiff_plain;h=3fba80174dbe29bcfe0d121a2a1d2e61be5ee57b Merge branch 'master' of git://tinc-vpn.org/tinc into 1.1 Conflicts: NEWS README configure.in doc/tincd.8.in src/Makefile.am src/bsd/device.c src/connection.c src/connection.h src/cygwin/device.c src/device.h src/dropin.h src/linux/device.c src/mingw/device.c src/net.c src/net_packet.c src/net_setup.c src/net_socket.c src/process.c src/protocol.c src/protocol_key.c src/raw_socket_device.c src/route.c src/solaris/device.c src/tincd.c src/uml_device.c --- 3fba80174dbe29bcfe0d121a2a1d2e61be5ee57b diff --cc NEWS index 679040b5,ab36c237..36f50606 --- a/NEWS +++ b/NEWS @@@ -1,33 -1,10 +1,40 @@@ +Version 1.1pre2 Juli 17 2011 + + * .cookie files are renamed to .pid files, which are compatible with 1.0.x. + + * Experimental protocol enhancements that can be enabled with the option + ExperimentalProtocol = yes: + + * Ephemeral ECDH key exchange will be used for both the meta protocol and + UDP session keys. + * Key exchanges are signed with ECDSA. + * ECDSA public keys are automatically exchanged after RSA authentication if + nodes do not know each other's ECDSA public key yet. + +Version 1.1pre1 June 25 2011 + + * Control interface allows control of a running tinc daemon. Used by: + * tincctl, a commandline utility + * tinc-gui, a preliminary GUI implemented in Python/wxWidgets + + * Code cleanups and reorganization. + + * Repleacable cryptography backend, currently supports OpenSSL and libgcrypt. + + * Use libevent to handle I/O events and timeouts. + + * Use splay trees instead of AVL trees to manage internal datastructures. + + Thanks to Scott Lamb and Sven-Haegar Koch for their contributions to this + version of tinc. + + Version 1.0.16 July 23 2011 + + * Fixed a performance issue with TCP communication under Windows. + + * Fixed code that, during network outages, would cause tinc to exit when it + thought two nodes with identical Names were on the VPN. + Version 1.0.15 June 24 2011 * Improved logging to file. diff --cc configure.in index 2e519b03,4d6a9493..4d2bcbd6 --- a/configure.in +++ b/configure.in @@@ -100,8 -118,8 +117,8 @@@ dnl Checks for header files dnl We do this in multiple stages, because unlike Linux all the other operating systems really suck and don't include their own dependencies. AC_HEADER_STDC -AC_CHECK_HEADERS([stdbool.h syslog.h sys/file.h sys/ioctl.h sys/mman.h sys/param.h sys/resource.h sys/socket.h sys/time.h time.h sys/uio.h sys/wait.h netdb.h arpa/inet.h dirent.h]) -AC_CHECK_HEADERS([net/if.h net/if_types.h linux/if_tun.h net/if_tun.h net/tun/if_tun.h net/if_tap.h net/tap/if_tap.h net/ethernet.h net/if_arp.h netinet/in_systm.h netinet/in.h netinet/in6.h netpacket/packet.h], +AC_CHECK_HEADERS([stdbool.h syslog.h sys/file.h sys/ioctl.h sys/mman.h sys/param.h sys/resource.h sys/socket.h sys/time.h sys/uio.h sys/un.h sys/wait.h netdb.h arpa/inet.h dirent.h]) - AC_CHECK_HEADERS([net/if.h net/if_types.h linux/if_tun.h net/if_tun.h net/tun/if_tun.h net/if_tap.h net/tap/if_tap.h net/ethernet.h net/if_arp.h netinet/in_systm.h netinet/in.h netinet/in6.h time.h], ++AC_CHECK_HEADERS([net/if.h net/if_types.h linux/if_tun.h net/if_tun.h net/tun/if_tun.h net/if_tap.h net/tap/if_tap.h net/ethernet.h net/if_arp.h netinet/in_systm.h netinet/in.h netinet/in6.h time.h netpacket/packet.h], [], [], [#include "have.h"] ) AC_CHECK_HEADERS([netinet/if_ether.h netinet/ip.h netinet/ip6.h], diff --cc doc/tinc.texi index 69e5a2b2,b6d0c216..4d9f233a --- a/doc/tinc.texi +++ b/doc/tinc.texi @@@ -1595,11 -1615,18 +1639,17 @@@ This will let tinc read all configurati Specifying . for @var{netname} is the same as not specifying any @var{netname}. @xref{Multiple networks}. -@item -K, --generate-keys[=@var{bits}] -Generate public/private keypair of @var{bits} length. If @var{bits} is not specified, -2048 is the default. tinc will ask where you want to store the files, -but will default to the configuration directory (you can use the -c or -n option -in combination with -K). After that, tinc will quit. +@item --pidfile=@var{filename} +Store a cookie in @var{filename} which allows tincctl to authenticate. +If unspecified, the default is +@file{@value{localstatedir}/run/tinc.@var{netname}.pid}. + @item -o, --option=[@var{HOST}.]@var{KEY}=@var{VALUE} + Without specifying a @var{HOST}, this will set server configuration variable @var{KEY} to @var{VALUE}. + If specified as @var{HOST}.@var{KEY}=@var{VALUE}, + this will set the host configuration variable @var{KEY} of the host named @var{HOST} to @var{VALUE}. + This option can be used more than once to specify multiple configuration variables. + @item -L, --mlock Lock tinc into main memory. This will prevent sensitive data like shared private keys to be written to the system swap files/partitions. diff --cc doc/tincd.8.in index bb4aa48c,b117e8a9..24b4da46 --- a/doc/tincd.8.in +++ b/doc/tincd.8.in @@@ -8,13 -8,17 +8,14 @@@ .Nd tinc VPN daemon .Sh SYNOPSIS .Nm - .Op Fl cdDKnLRU -.Op Fl cdDkKnoLRU ++.Op Fl cdDKnoLRU .Op Fl -config Ns = Ns Ar DIR .Op Fl -no-detach .Op Fl -debug Ns Op = Ns Ar LEVEL -.Op Fl -kill Ns Op = Ns Ar SIGNAL .Op Fl -net Ns = Ns Ar NETNAME -.Op Fl -generate-keys Ns Op = Ns Ar BITS + .Op Fl -option Ns = Ns Ar [HOST.]KEY=VALUE .Op Fl -mlock .Op Fl -logfile Ns Op = Ns Ar FILE -.Op Fl -pidfile Ns = Ns Ar FILE .Op Fl -bypass-security .Op Fl -chroot .Op Fl -user Ns = Ns Ar USER @@@ -61,6 -73,29 +62,22 @@@ fo .Ar NETNAME is the same as not specifying any .Ar NETNAME . -.It Fl K, -generate-keys Ns Op = Ns Ar BITS -Generate public/private RSA keypair and exit. -If -.Ar BITS -is omitted, the default length will be 2048 bits. -When saving keys to existing files, tinc will not delete the old keys, -you have to remove them manually. + .It Fl o, -option Ns = Ns Ar [HOST.]KEY=VALUE + Without specifying a + .Ar HOST , + this will set server configuration variable + .Ar KEY + to + .Ar VALUE . + If specified as + .Ar HOST.KEY=VALUE , + this will set the host configuration variable + .Ar KEY + of the host named + .Ar HOST + to + .Ar VALUE . + This option can be used more than once to specify multiple configuration variables. .It Fl L, -mlock Lock tinc into main memory. This will prevent sensitive data like shared private keys to be written to the system swap files/partitions. diff --cc src/Makefile.am index 61f9a936,aca0e2dc..ba8b3479 --- a/src/Makefile.am +++ b/src/Makefile.am @@@ -1,29 -1,22 +1,38 @@@ ## Produce this file with automake to get Makefile.in -sbin_PROGRAMS = tincd +sbin_PROGRAMS = tincd tincctl sptps_test - EXTRA_DIST = linux bsd solaris cygwin mingw raw_socket uml_socket openssl gcrypt -EXTRA_DIST = linux/device.c bsd/device.c solaris/device.c cygwin/device.c mingw/device.c mingw/common.h ++EXTRA_DIST = linux bsd solaris cygwin mingw openssl gcrypt -tincd_SOURCES = conf.c connection.c edge.c event.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 \ +tincd_SOURCES = \ + utils.c getopt.c getopt1.c list.c splay_tree.c dropin.c fake-getaddrinfo.c fake-getnameinfo.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 subnet.c tincd.c + protocol_key.c protocol_subnet.c route.c subnet.c tincd.c \ + dummy_device.c raw_socket_device.c + + if UML + tincd_SOURCES += uml_device.c + endif + + if VDE + tincd_SOURCES += vde_device.c + endif +nodist_tincd_SOURCES = \ + device.c cipher.c crypto.c ecdh.c ecdsa.c digest.c prf.c rsa.c + +tincctl_SOURCES = \ + utils.c getopt.c getopt1.c dropin.c \ + list.c tincctl.c top.c + +nodist_tincctl_SOURCES = \ + ecdsagen.c rsagen.c + +sptps_test_SOURCES = \ + logger.c cipher.c crypto.c ecdh.c ecdsa.c digest.c prf.c \ + sptps.c sptps_test.c + if TUNEMU tincd_SOURCES += bsd/tunemu.c endif diff --cc src/bsd/device.c index 9c3009d5,45c2d5f6..9a1688a8 --- a/src/bsd/device.c +++ b/src/bsd/device.c @@@ -190,8 -194,8 +194,8 @@@ static void close_device(void) free(iface); } - bool read_packet(vpn_packet_t *packet) { + static bool read_packet(vpn_packet_t *packet) { - int lenin; + int inlen; switch(device_type) { case DEVICE_TYPE_TUN: diff --cc src/connection.c index bae86b90,0211845b..9587819d --- a/src/connection.c +++ b/src/connection.c @@@ -31,23 -28,23 +31,23 @@@ #include "utils.h" #include "xalloc.h" -avl_tree_t *connection_tree; /* Meta connections */ +splay_tree_t *connection_tree; /* Meta connections */ - connection_t *broadcast; + connection_t *everyone; static int connection_compare(const connection_t *a, const connection_t *b) { return a < b ? -1 : a == b ? 0 : 1; } void init_connections(void) { - connection_tree = avl_alloc_tree((avl_compare_t) connection_compare, (avl_action_t) free_connection); + connection_tree = splay_alloc_tree((splay_compare_t) connection_compare, (splay_action_t) free_connection); - broadcast = new_connection(); - broadcast->name = xstrdup("everyone"); - broadcast->hostname = xstrdup("BROADCAST"); + everyone = new_connection(); + everyone->name = xstrdup("everyone"); + everyone->hostname = xstrdup("BROADCAST"); } void exit_connections(void) { - avl_delete_tree(connection_tree); + splay_delete_tree(connection_tree); - free_connection(broadcast); + free_connection(everyone); } connection_t *new_connection(void) { diff --cc src/connection.h index 26aa3f0c,ff2c9ccd..20e00763 --- a/src/connection.h +++ b/src/connection.h @@@ -93,13 -89,19 +93,13 @@@ typedef struct connection_t int tcplen; /* length of incoming TCPpacket */ int allow_request; /* defined if there's only one request possible */ - char *outbuf; /* metadata output buffer */ - int outbufstart; /* index of first meaningful byte in output buffer */ - int outbuflen; /* number of meaningful bytes in output buffer */ - int outbufsize; /* number of bytes allocated to output buffer */ - time_t last_ping_time; /* last time we saw some activity from the other end or pinged them */ - time_t last_flushed_time; /* last time buffer was empty. Only meaningful if outbuflen > 0 */ - avl_tree_t *config_tree; /* Pointer to configuration tree belonging to him */ + splay_tree_t *config_tree; /* Pointer to configuration tree belonging to him */ } connection_t; -extern avl_tree_t *connection_tree; +extern splay_tree_t *connection_tree; - extern connection_t *broadcast; + extern connection_t *everyone; extern void init_connections(void); extern void exit_connections(void); diff --cc src/cygwin/device.c index a4ab938c,4365399d..cf9f1b57 --- a/src/cygwin/device.c +++ b/src/cygwin/device.c @@@ -225,10 -225,10 +225,10 @@@ static void close_device(void) free(iface); } - bool read_packet(vpn_packet_t *packet) { + static bool read_packet(vpn_packet_t *packet) { - int lenin; + int inlen; - if((lenin = read(sp[0], packet->data, MTU)) <= 0) { + if((inlen = read(sp[0], packet->data, MTU)) <= 0) { logger(LOG_ERR, "Error while reading from %s %s: %s", device_info, device, strerror(errno)); return false; @@@ -244,8 -244,8 +244,8 @@@ return true; } - bool write_packet(vpn_packet_t *packet) { + static bool write_packet(vpn_packet_t *packet) { - long lenout; + long outlen; ifdebug(TRAFFIC) logger(LOG_DEBUG, "Writing packet of %d bytes to %s", packet->len, device_info); diff --cc src/device.h index 16a24868,eaeca188..993d4ce8 --- a/src/device.h +++ b/src/device.h @@@ -25,17 -25,22 +25,26 @@@ extern int device_fd; extern char *device; - extern char *iface; +extern uint64_t device_in_packets; +extern uint64_t device_in_bytes; +extern uint64_t device_out_packets; +extern uint64_t device_out_bytes; + - extern bool setup_device(void); - extern void close_device(void); - extern bool read_packet(struct vpn_packet_t *); - extern bool write_packet(struct vpn_packet_t *); - extern void dump_device_stats(void); + typedef struct devops_t { + bool (*setup)(void); + void (*close)(void); + bool (*read)(struct vpn_packet_t *); + bool (*write)(struct vpn_packet_t *); + void (*dump_stats)(void); + } devops_t; + + extern const devops_t os_devops; + extern const devops_t dummy_devops; + extern const devops_t raw_socket_devops; + extern const devops_t uml_devops; + extern const devops_t vde_devops; + extern devops_t devops; #endif /* __TINC_DEVICE_H__ */ diff --cc src/linux/device.c index d36f3f67,cbf21529..ccb9a3f0 --- a/src/linux/device.c +++ b/src/linux/device.c @@@ -44,12 -49,13 +45,12 @@@ static char *type = NULL static char ifrname[IFNAMSIZ]; static char *device_info; -static uint64_t device_total_in = 0; -static uint64_t device_total_out = 0; +uint64_t device_in_packets = 0; +uint64_t device_in_bytes = 0; +uint64_t device_out_packets = 0; +uint64_t device_out_bytes = 0; - bool setup_device(void) { + static bool setup_device(void) { - struct ifreq ifr; - bool t1q = false; - if(!get_config_string(lookup_config(config_tree, "Device"), &device)) device = xstrdup(DEFAULT_DEVICE); @@@ -67,9 -73,23 +68,20 @@@ return false; } + #ifdef FD_CLOEXEC + fcntl(device_fd, F_SETFD, FD_CLOEXEC); + #endif + -#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)); + struct ifreq ifr = {{{0}}}; - if(routing_mode == RMODE_ROUTER) { + get_config_string(lookup_config(config_tree, "DeviceType"), &type); + + if(type && strcasecmp(type, "tun") && strcasecmp(type, "tap")) { + logger(LOG_ERR, "Unknown device type %s!", type); + return false; + } + + if((type && !strcasecmp(type, "tun")) || (!type && routing_mode == RMODE_ROUTER)) { ifr.ifr_flags = IFF_TUN; device_type = DEVICE_TYPE_TUN; device_info = "Linux tun/tap device (tun mode)"; @@@ -113,8 -142,8 +126,8 @@@ static void close_device(void) free(iface); } - bool read_packet(vpn_packet_t *packet) { + static bool read_packet(vpn_packet_t *packet) { - int lenin; + int inlen; switch(device_type) { case DEVICE_TYPE_TUN: @@@ -182,8 -225,16 +195,16 @@@ static bool write_packet(vpn_packet_t * return true; } - void dump_device_stats(void) { + static void dump_device_stats(void) { logger(LOG_DEBUG, "Statistics for %s %s:", device_info, device); - logger(LOG_DEBUG, " total bytes in: %10"PRIu64, device_total_in); - logger(LOG_DEBUG, " total bytes out: %10"PRIu64, device_total_out); + logger(LOG_DEBUG, " total bytes in: %10"PRIu64, device_in_bytes); + logger(LOG_DEBUG, " total bytes out: %10"PRIu64, device_out_bytes); } + + const devops_t os_devops = { + .setup = setup_device, + .close = close_device, + .read = read_packet, + .write = write_packet, + .dump_stats = dump_device_stats, + }; diff --cc src/mingw/device.c index bdca8424,b9588318..e4683386 --- a/src/mingw/device.c +++ b/src/mingw/device.c @@@ -221,8 -221,8 +221,8 @@@ static bool read_packet(vpn_packet_t *p return false; } - bool write_packet(vpn_packet_t *packet) { + static bool write_packet(vpn_packet_t *packet) { - long lenout; + long outlen; OVERLAPPED overlapped = {0}; ifdebug(TRAFFIC) logger(LOG_DEBUG, "Writing packet of %d bytes to %s", diff --cc src/net.c index f9020b3b,fc9ec574..336bf9ee --- a/src/net.c +++ b/src/net.c @@@ -117,9 -178,12 +117,9 @@@ void terminate_connection(connection_t if(c->node) c->node->connection = NULL; - if(c->socket) - closesocket(c->socket); - if(c->edge) { if(report && !tunnelserver) - send_del_edge(broadcast, c->edge); + send_del_edge(everyone, c->edge); edge_del(c->edge); @@@ -237,156 -350,263 +237,156 @@@ void handle_meta_connection_data(int fd } } -/* - this is where it all happens... -*/ -int main_loop(void) { - fd_set readset, writeset; -#ifdef HAVE_PSELECT - struct timespec tv; - sigset_t omask, block_mask; - time_t next_event; -#else - struct timeval tv; -#endif - int r, maxfd; - time_t last_ping_check, last_config_check, last_graph_dump; - event_t *event; +static void sigterm_handler(int signal, short events, void *data) { + logger(LOG_NOTICE, "Got %s signal", strsignal(signal)); + event_loopexit(NULL); +} - last_ping_check = now; - last_config_check = now; - last_graph_dump = now; - - srand(now); - -#ifdef HAVE_PSELECT - if(lookup_config(config_tree, "GraphDumpFile")) - graph_dump = true; - /* Block SIGHUP & SIGALRM */ - sigemptyset(&block_mask); - sigaddset(&block_mask, SIGHUP); - sigaddset(&block_mask, SIGALRM); - sigprocmask(SIG_BLOCK, &block_mask, &omask); -#endif +static void sighup_handler(int signal, short events, void *data) { + logger(LOG_NOTICE, "Got %s signal", strsignal(signal)); + reopenlogger(); + reload_configuration(); +} - running = true; - - while(running) { -#ifdef HAVE_PSELECT - next_event = last_ping_check + pingtimeout; - if(graph_dump && next_event > last_graph_dump + 60) - next_event = last_graph_dump + 60; - - if((event = peek_next_event()) && next_event > event->time) - next_event = event->time; - - if(next_event <= now) - tv.tv_sec = 0; - else - tv.tv_sec = next_event - now; - tv.tv_nsec = 0; -#else - tv.tv_sec = 1; - tv.tv_usec = 0; -#endif +static void sigalrm_handler(int signal, short events, void *data) { + logger(LOG_NOTICE, "Got %s signal", strsignal(signal)); + retry(); +} - maxfd = build_fdset(&readset, &writeset); +int reload_configuration(void) { + connection_t *c; + splay_node_t *node, *next; + char *fname; + struct stat s; + static time_t last_config_check = 0; -#ifdef HAVE_MINGW - LeaveCriticalSection(&mutex); -#endif -#ifdef HAVE_PSELECT - r = pselect(maxfd + 1, &readset, &writeset, NULL, &tv, &omask); -#else - r = select(maxfd + 1, &readset, &writeset, NULL, &tv); -#endif - now = time(NULL); -#ifdef HAVE_MINGW - EnterCriticalSection(&mutex); -#endif + /* Reread our own configuration file */ - if(r < 0) { - if(!sockwouldblock(sockerrno)) { - logger(LOG_ERR, "Error while waiting for input: %s", sockstrerror(sockerrno)); - dump_connections(); - return 1; - } - } + exit_configuration(&config_tree); + init_configuration(&config_tree); - if(r > 0) - check_network_activity(&readset, &writeset); + if(!read_server_config()) { + logger(LOG_ERR, "Unable to reread configuration file, exitting."); + event_loopexit(NULL); + return EINVAL; + } - if(do_purge) { - purge(); - do_purge = false; + /* Close connections to hosts that have a changed or deleted host config file */ + + for(node = connection_tree->head; node; node = next) { + c = node->data; + next = node->next; + + if(c->outgoing) { + free(c->outgoing->name); + if(c->outgoing->ai) + freeaddrinfo(c->outgoing->ai); + free(c->outgoing); + c->outgoing = NULL; } + + xasprintf(&fname, "%s/hosts/%s", confbase, c->name); + if(stat(fname, &s) || s.st_mtime > last_config_check) + terminate_connection(c, c->status.active); + free(fname); + } - /* Let's check if everybody is still alive */ - - if(last_ping_check + pingtimeout <= now) { - check_dead_connections(); - last_ping_check = now; - - if(routing_mode == RMODE_SWITCH) - age_subnets(); - - age_past_requests(); - - /* Should we regenerate our key? */ - - if(keyexpires <= now) { - avl_node_t *node; - node_t *n; - - ifdebug(STATUS) logger(LOG_INFO, "Expiring symmetric keys"); + last_config_check = time(NULL); - for(node = node_tree->head; node; node = node->next) { - n = node->data; - if(n->inkey) { - free(n->inkey); - n->inkey = NULL; - } - } + /* If StrictSubnet is set, expire deleted Subnets and read new ones in */ - send_key_changed(); - keyexpires = now + keylifetime; - } + if(strictsubnets) { + subnet_t *subnet; - /* Detect ADD_EDGE/DEL_EDGE storms that are caused when - * two tinc daemons with the same name are on the VPN. - * If so, sleep a while. If this happens multiple times - * in a row, sleep longer. */ - - if(contradicting_del_edge > 100 && contradicting_add_edge > 100) { - logger(LOG_WARNING, "Possible node with same Name as us! Sleeping %d seconds.", sleeptime); - usleep(sleeptime * 1000000LL); - sleeptime *= 2; - if(sleeptime < 0) - sleeptime = 3600; - } else { - sleeptime /= 2; - if(sleeptime < 10) - sleeptime = 10; - } - contradicting_add_edge = 0; - contradicting_del_edge = 0; + for(node = subnet_tree->head; node; node = node->next) { + subnet = node->data; + subnet->expires = 1; } - if(sigalrm) { - avl_node_t *node; - logger(LOG_INFO, "Flushing event queue"); - expire_events(); - for(node = connection_tree->head; node; node = node->next) { - connection_t *c = node->data; - send_ping(c); + load_all_subnets(); + + for(node = subnet_tree->head; node; node = next) { + next = node->next; + subnet = node->data; + if(subnet->expires == 1) { - send_del_subnet(broadcast, subnet); ++ send_del_subnet(everyone, subnet); + if(subnet->owner->status.reachable) + subnet_update(subnet->owner, subnet, false); + subnet_del(subnet->owner, subnet); + } else if(subnet->expires == -1) { + subnet->expires = 0; + } else { - send_add_subnet(broadcast, subnet); ++ send_add_subnet(everyone, subnet); + if(subnet->owner->status.reachable) + subnet_update(subnet->owner, subnet, true); } - sigalrm = false; } + } - while((event = get_expired_event())) { - event->handler(event->data); - free_event(event); - } - - if(sighup) { - connection_t *c; - avl_node_t *node, *next; - char *fname; - struct stat s; - - sighup = false; - - reopenlogger(); - - /* Reread our own configuration file */ - - exit_configuration(&config_tree); - init_configuration(&config_tree); - - if(!read_server_config()) { - logger(LOG_ERR, "Unable to reread configuration file, exitting."); - return 1; - } - - /* Cancel non-active outgoing connections */ - - for(node = connection_tree->head; node; node = next) { - next = node->next; - c = node->data; - - c->outgoing = NULL; - - if(c->status.connecting) { - terminate_connection(c, false); - connection_del(c); - } - } - - /* Wipe list of outgoing connections */ - - 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); - - /* Close connections to hosts that have a changed or deleted host config file */ - - for(node = connection_tree->head; node; node = node->next) { - c = node->data; - - xasprintf(&fname, "%s/hosts/%s", confbase, c->name); - if(stat(fname, &s) || s.st_mtime > last_config_check) - terminate_connection(c, c->status.active); - free(fname); - } - - last_config_check = now; - - /* If StrictSubnet is set, expire deleted Subnets and read new ones in */ - - if(strictsubnets) { - subnet_t *subnet; + /* Try to make outgoing connections */ + + try_outgoing_connections(); - for(node = subnet_tree->head; node; node = node->next) { - subnet = node->data; - subnet->expires = 1; - } + return 0; +} - load_all_subnets(); - - for(node = subnet_tree->head; node; node = next) { - next = node->next; - subnet = node->data; - if(subnet->expires == 1) { - send_del_subnet(everyone, subnet); - if(subnet->owner->status.reachable) - subnet_update(subnet->owner, subnet, false); - subnet_del(subnet->owner, subnet); - } else if(subnet->expires == -1) { - subnet->expires = 0; - } else { - send_add_subnet(everyone, subnet); - if(subnet->owner->status.reachable) - subnet_update(subnet->owner, subnet, true); - } - } - } +void retry(void) { + connection_t *c; + splay_node_t *node; - /* Try to make outgoing connections */ - - try_outgoing_connections(); - } + for(node = connection_tree->head; node; node = node->next) { + c = node->data; - /* Dump graph if wanted every 60 seconds*/ - - if(last_graph_dump + 60 <= now) { - dump_graph(); - last_graph_dump = now; + if(c->outgoing && !c->node) { + if(timeout_initialized(&c->outgoing->ev)) + event_del(&c->outgoing->ev); + if(c->status.connecting) + close(c->socket); + c->outgoing->timeout = 0; + do_outgoing_connection(c); } } +} + +/* + this is where it all happens... +*/ +int main_loop(void) { + struct event timeout_event; + + timeout_set(&timeout_event, timeout_handler, &timeout_event); + event_add(&timeout_event, &(struct timeval){pingtimeout, 0}); + +#ifndef HAVE_MINGW + struct event sighup_event; + struct event sigterm_event; + struct event sigquit_event; + struct event sigalrm_event; + + 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); +#endif + + if(event_loop(0) < 0) { + logger(LOG_ERR, "Error while waiting for input: %s", strerror(errno)); + return 1; + } -#ifdef HAVE_PSELECT - /* Restore SIGHUP & SIGALARM mask */ - sigprocmask(SIG_SETMASK, &omask, NULL); +#ifndef HAVE_MINGW + signal_del(&sighup_event); + signal_del(&sigterm_event); + signal_del(&sigquit_event); + signal_del(&sigalrm_event); #endif + event_del(&timeout_event); + return 0; } diff --cc src/net_packet.c index 875a0832,360f318e..f7d86403 --- a/src/net_packet.c +++ b/src/net_packet.c @@@ -375,13 -383,12 +375,12 @@@ static void send_udppacket(node_t *n, v vpn_packet_t *inpkt = origpkt; int nextpkt = 0; vpn_packet_t *outpkt; - int origlen; - int outlen, outpad; + int origlen = origpkt->len; + size_t outlen; #if defined(SOL_IP) && defined(IP_TOS) static int priority = 0; + int origpriority = origpkt->priority; #endif - int sock; - int origpriority; if(!n->status.reachable) { ifdebug(TRAFFIC) logger(LOG_INFO, "Trying to send UDP packet to unreachable node %s (%s)", n->name, n->hostname); @@@ -505,9 -519,7 +506,9 @@@ void send_packet(node_t *n, vpn_packet_ if(n == myself) { if(overwrite_mac) memcpy(packet->data, mymac.x, ETH_ALEN); + n->out_packets++; + n->out_bytes += packet->len; - write_packet(packet); + devops.write(packet); return; } @@@ -631,17 -636,7 +632,19 @@@ void handle_incoming_vpn_data(int sock return; } - n->sock = sock; ++ n->sock = (intptr_t)data; + receive_udppacket(n, &pkt); } + +void handle_device_data(int sock, short events, void *data) { + vpn_packet_t packet; + + packet.priority = 0; + - if(read_packet(&packet)) { ++ if(devops.read(&packet)) { + myself->in_packets++; + myself->in_bytes += packet.len; + route(myself, &packet); + } +} diff --cc src/net_setup.c index bacbeac9,299e3729..207ce425 --- a/src/net_setup.c +++ b/src/net_setup.c @@@ -43,74 -45,73 +43,75 @@@ #include "xalloc.h" char *myport; +static struct event device_ev; + devops_t devops; -bool read_rsa_public_key(connection_t *c) { +bool node_read_ecdsa_public_key(node_t *n) { + if(ecdsa_active(&n->ecdsa)) + return true; + + splay_tree_t *config_tree; FILE *fp; char *fname; - char *key; + char *p; + bool result = false; - if(!c->rsa_key) { - c->rsa_key = RSA_new(); -// RSA_blinding_on(c->rsa_key, NULL); - } + xasprintf(&fname, "%s/hosts/%s", confbase, n->name); - /* First, check for simple PublicKey statement */ + init_configuration(&config_tree); + if(!read_config_file(config_tree, fname)) + goto exit; - 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; + /* First, check for simple ECDSAPublicKey statement */ + + if(get_config_string(lookup_config(config_tree, "ECDSAPublicKey"), &p)) { + result = ecdsa_set_base64_public_key(&n->ecdsa, p); + free(p); + goto exit; } - /* Else, check for PublicKeyFile statement and read it */ + /* Else, check for ECDSAPublicKeyFile statement and read it */ - if(get_config_string(lookup_config(c->config_tree, "PublicKeyFile"), &fname)) { - fp = fopen(fname, "r"); + free(fname); - if(!fp) { - logger(LOG_ERR, "Error reading RSA public key file `%s': %s", - fname, strerror(errno)); - free(fname); - return false; - } + if(!get_config_string(lookup_config(config_tree, "ECDSAPublicKeyFile"), &fname)) + xasprintf(&fname, "%s/hosts/%s", confbase, n->name); - free(fname); - c->rsa_key = PEM_read_RSAPublicKey(fp, &c->rsa_key, NULL, NULL); - fclose(fp); + fp = fopen(fname, "r"); - if(c->rsa_key) - return true; /* Woohoo. */ + if(!fp) { + logger(LOG_ERR, "Error reading ECDSA public key file `%s': %s", fname, strerror(errno)); + goto exit; + } - /* If it fails, try PEM_read_RSA_PUBKEY. */ - fp = fopen(fname, "r"); + result = ecdsa_read_pem_public_key(&n->ecdsa, fp); + fclose(fp); - if(!fp) { - logger(LOG_ERR, "Error reading RSA public key file `%s': %s", - fname, strerror(errno)); - free(fname); - return false; - } +exit: + exit_configuration(&config_tree); + free(fname); + return result; +} - free(fname); - c->rsa_key = PEM_read_RSA_PUBKEY(fp, &c->rsa_key, NULL, NULL); - fclose(fp); +bool read_ecdsa_public_key(connection_t *c) { + FILE *fp; + char *fname; + char *p; + bool result; - if(c->rsa_key) { -// RSA_blinding_on(c->rsa_key, NULL); - return true; - } + /* First, check for simple ECDSAPublicKey statement */ - logger(LOG_ERR, "Reading RSA public key file `%s' failed: %s", - fname, strerror(errno)); - return false; + if(get_config_string(lookup_config(c->config_tree, "ECDSAPublicKey"), &p)) { + result = ecdsa_set_base64_public_key(&c->ecdsa, p); + free(p); + return result; } - /* Else, check if a harnessed public key is in the config file */ + /* Else, check for ECDSAPublicKeyFile statement and read it */ + + if(!get_config_string(lookup_config(c->config_tree, "ECDSAPublicKeyFile"), &fname)) + xasprintf(&fname, "%s/hosts/%s", confbase, c->name); - xasprintf(&fname, "%s/hosts/%s", confbase, c->name); fp = fopen(fname, "r"); if(!fp) { @@@ -157,54 -138,17 +157,53 @@@ bool read_rsa_public_key(connection_t * return false; } - c->rsa_key = PEM_read_RSA_PUBKEY(fp, &c->rsa_key, NULL, NULL); -// RSA_blinding_on(c->rsa_key, NULL); + 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); + return result; +} - if(c->rsa_key) - return true; +static bool read_ecdsa_private_key(void) { + FILE *fp; + char *fname; + bool result; - logger(LOG_ERR, "No public key for %s specified!", c->name); + /* Check for PrivateKeyFile statement and read it */ + + if(!get_config_string(lookup_config(config_tree, "ECDSAPrivateKeyFile"), &fname)) + xasprintf(&fname, "%s/ecdsa_key.priv", confbase); + + fp = fopen(fname, "r"); + + if(!fp) { - logger(LOG_ERR, "Error reading ECDSA private key file `%s': %s", - fname, strerror(errno)); ++ logger(LOG_ERR, "Error reading ECDSA private key file `%s': %s", fname, strerror(errno)); + free(fname); + return false; + } + +#if !defined(HAVE_MINGW) && !defined(HAVE_CYGWIN) + struct stat s; + + if(fstat(fileno(fp), &s)) { + logger(LOG_ERR, "Could not stat ECDSA private key file `%s': %s'", fname, strerror(errno)); + free(fname); + return false; + } - return false; + if(s.st_mode & ~0100700) + logger(LOG_WARNING, "Warning: insecure file permissions for ECDSA private key file `%s'!", fname); +#endif + + result = ecdsa_read_pem_private_key(&myself->connection->ecdsa, fp); + fclose(fp); + + if(!result) + logger(LOG_ERR, "Reading ECDSA private key file `%s' failed: %s", fname, strerror(errno)); + free(fname); + return result; } static bool read_rsa_private_key(void) { @@@ -589,19 -542,26 +590,36 @@@ static bool setup_myself(void) /* Open device */ - if(!setup_device()) + devops = os_devops; + + if(get_config_string(lookup_config(config_tree, "DeviceType"), &type)) { + if(!strcasecmp(type, "dummy")) + devops = dummy_devops; + else if(!strcasecmp(type, "raw_socket")) + devops = raw_socket_devops; + #ifdef ENABLE_UML + else if(!strcasecmp(type, "uml")) + devops = uml_devops; + #endif + #ifdef ENABLE_VDE + else if(!strcasecmp(type, "vde")) + devops = vde_devops; + #endif + } + + 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(LOG_ERR, "event_add failed: %s", strerror(errno)); - close_device(); ++ devops.close(); + return false; + } + } + /* Run tinc-up script to further initialize the tap interface */ xasprintf(&envp[0], "NETNAME=%s", netname ? : ""); xasprintf(&envp[1], "DEVICE=%s", device ? : ""); @@@ -620,72 -580,58 +638,78 @@@ /* Open sockets */ - get_config_string(lookup_config(config_tree, "BindToAddress"), &address); + listen_sockets = 0; + cfg = lookup_config(config_tree, "BindToAddress"); - hint.ai_family = addressfamily; - hint.ai_socktype = SOCK_STREAM; - hint.ai_protocol = IPPROTO_TCP; - hint.ai_flags = AI_PASSIVE; + do { + get_config_string(cfg, &address); + if(cfg) + cfg = lookup_config_next(config_tree, cfg); - err = getaddrinfo(address, myport, &hint, &ai); + hint.ai_family = addressfamily; + hint.ai_socktype = SOCK_STREAM; + hint.ai_protocol = IPPROTO_TCP; + hint.ai_flags = AI_PASSIVE; - if(err || !ai) { - logger(LOG_ERR, "System call `%s' failed: %s", "getaddrinfo", - gai_strerror(err)); - return false; - } + err = getaddrinfo(address, myport, &hint, &ai); + free(address); - listen_sockets = 0; + if(err || !ai) { + logger(LOG_ERR, "System call `%s' failed: %s", "getaddrinfo", + gai_strerror(err)); + return false; + } - for(aip = ai; aip; aip = aip->ai_next) { - listen_socket[listen_sockets].tcp = - setup_listen_socket((sockaddr_t *) aip->ai_addr); + for(aip = ai; aip; aip = aip->ai_next) { + if(listen_sockets >= MAXSOCKETS) { + logger(LOG_ERR, "Too many listening sockets"); + return false; + } - if(listen_socket[listen_sockets].tcp < 0) - continue; + listen_socket[listen_sockets].tcp = + setup_listen_socket((sockaddr_t *) aip->ai_addr); - listen_socket[listen_sockets].udp = - setup_vpn_in_socket((sockaddr_t *) aip->ai_addr); + if(listen_socket[listen_sockets].tcp < 0) + continue; - if(listen_socket[listen_sockets].udp < 0) { - close(listen_socket[listen_sockets].tcp); - continue; - } + listen_socket[listen_sockets].udp = + setup_vpn_in_socket((sockaddr_t *) aip->ai_addr); - 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(); - } - 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_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(); - } ++ 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(); ++ } + - ifdebug(CONNECTIONS) { - hostname = sockaddr2hostname((sockaddr_t *) aip->ai_addr); - logger(LOG_NOTICE, "Listening on %s", hostname); - free(hostname); - } ++ 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(LOG_ERR, "event_add failed: %s", strerror(errno)); ++ abort(); ++ } - memcpy(&listen_socket[listen_sockets].sa, aip->ai_addr, aip->ai_addrlen); - listen_sockets++; + ifdebug(CONNECTIONS) { + hostname = sockaddr2hostname((sockaddr_t *) aip->ai_addr); + logger(LOG_NOTICE, "Listening on %s", hostname); + free(hostname); + } - if(listen_sockets >= MAXSOCKETS) { - logger(LOG_WARNING, "Maximum of %d listening sockets reached", MAXSOCKETS); - break; + memcpy(&listen_socket[listen_sockets].sa, aip->ai_addr, aip->ai_addrlen); + listen_sockets++; } - } - freeaddrinfo(ai); + freeaddrinfo(ai); + } while(cfg); if(listen_sockets) logger(LOG_NOTICE, "Ready"); diff --cc src/openssl/prf.c index 13841c4d,00000000..1c432c7d mode 100644,000000..100644 --- a/src/openssl/prf.c +++ b/src/openssl/prf.c @@@ -1,73 -1,0 +1,75 @@@ +/* + prf.c -- Pseudo-Random Function for key material generation + Copyright (C) 2011 Guus Sliepen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "system.h" + ++#include ++ +#include "digest.h" +#include "prf.h" + +/* Generate key material from a master secret and a seed, based on RFC 4346 section 5. + We use SHA512 instead of MD5 and SHA1. + */ + +static bool prf_xor(int nid, const char *secret, size_t secretlen, char *seed, size_t seedlen, char *out, ssize_t outlen) { + digest_t digest; + + if(!digest_open_by_nid(&digest, nid, -1)) + return false; + + if(!digest_set_key(&digest, secret, secretlen)) + return false; + + size_t len = digest_length(&digest); + + /* Data is what the "inner" HMAC function processes. + It consists of the previous HMAC result plus the seed. + */ + + char data[len + seedlen]; + memset(data, 0, len); + memcpy(data + len, seed, seedlen); + + char hash[len]; + + while(outlen > 0) { + /* Inner HMAC */ + digest_create(&digest, data, len + seedlen, data); + + /* Outer HMAC */ + digest_create(&digest, data, len + seedlen, hash); + + /* XOR the results of the outer HMAC into the out buffer */ + for(int i = 0; i < len && i < outlen; i++) + *out++ ^= hash[i]; + + outlen -= len; + } + + digest_close(&digest); + return true; +} + +bool prf(const char *secret, size_t secretlen, char *seed, size_t seedlen, char *out, size_t outlen) { + /* This construction allows us to easily switch back to a scheme where the PRF is calculated using two different digest algorithms. */ + memset(out, 0, outlen); + + return prf_xor(NID_sha512, secret, secretlen, seed, seedlen, out, outlen); +} diff --cc src/protocol.c index 63163a0f,7dbee717..116e139e --- a/src/protocol.c +++ b/src/protocol.c @@@ -94,39 -94,42 +94,39 @@@ bool send_request(connection_t *c, cons c->name, c->hostname); } - buffer[len++] = '\n'; + request[len++] = '\n'; - if(c == broadcast) { + if(c == everyone) { - broadcast_meta(NULL, buffer, len); + broadcast_meta(NULL, request, len); return true; } else - return send_meta(c, buffer, len); + return send_meta(c, request, len); } -void forward_request(connection_t *from) { - int request; - +void forward_request(connection_t *from, char *request) { + /* Note: request is not zero terminated anymore after a call to this function! */ ifdebug(PROTOCOL) { - sscanf(from->buffer, "%d", &request); ifdebug(META) logger(LOG_DEBUG, "Forwarding %s from %s (%s): %s", - request_name[request], from->name, from->hostname, - from->buffer); + request_name[atoi(request)], from->name, from->hostname, request); else logger(LOG_DEBUG, "Forwarding %s from %s (%s)", - request_name[request], from->name, from->hostname); + request_name[atoi(request)], from->name, from->hostname); } - from->buffer[from->reqlen - 1] = '\n'; - - broadcast_meta(from, from->buffer, from->reqlen); + int len = strlen(request); + request[len++] = '\n'; + broadcast_meta(from, request, len); } -bool receive_request(connection_t *c) { - int request; +bool receive_request(connection_t *c, char *request) { + int reqno = atoi(request); - if(sscanf(c->buffer, "%d", &request) == 1) { - if((request < 0) || (request >= LAST) || !request_handlers[request]) { + if(reqno || *request == '0') { + if((reqno < 0) || (reqno >= LAST) || !request_handlers[reqno]) { ifdebug(META) logger(LOG_DEBUG, "Unknown request from %s (%s): %s", - c->name, c->hostname, c->buffer); + c->name, c->hostname, request); else logger(LOG_ERR, "Unknown request from %s (%s)", c->name, c->hostname); diff --cc src/protocol_key.c index 5246e7d2,1d471c80..c392a469 --- a/src/protocol_key.c +++ b/src/protocol_key.c @@@ -37,10 -37,10 +37,10 @@@ static bool mykeyused = false; void send_key_changed(void) { - avl_node_t *node; + splay_node_t *node; connection_t *c; - send_request(broadcast, "%d %x %s", KEY_CHANGED, rand(), myself->name); + send_request(everyone, "%d %x %s", KEY_CHANGED, rand(), myself->name); /* Immediately send new keys to directly connected nodes to keep UDP mappings alive */ diff --cc src/raw_socket_device.c index 410e46e4,1dd726f8..3785dcf6 --- a/src/raw_socket_device.c +++ b/src/raw_socket_device.c @@@ -57,7 -56,11 +56,12 @@@ static bool setup_device(void) return false; } + memset(&ifr, 0, sizeof ifr); ++ + #ifdef FD_CLOEXEC + fcntl(device_fd, F_SETFD, FD_CLOEXEC); + #endif + - memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_ifrn.ifrn_name, iface, IFNAMSIZ); if(ioctl(device_fd, SIOCGIFINDEX, &ifr)) { close(device_fd); @@@ -88,10 -91,10 +92,10 @@@ static void close_device(void) free(iface); } - bool read_packet(vpn_packet_t *packet) { + static bool read_packet(vpn_packet_t *packet) { - int lenin; + int inlen; - if((lenin = read(device_fd, packet->data, MTU)) <= 0) { + if((inlen = read(device_fd, packet->data, MTU)) <= 0) { logger(LOG_ERR, "Error while reading from %s %s: %s", device_info, device, strerror(errno)); return false; diff --cc src/route.c index 0b2d22eb,0b77bd4a..32be4dec --- a/src/route.c +++ b/src/route.c @@@ -40,8 -39,8 +41,9 @@@ bool directonly = false bool priorityinheritance = false; int macexpire = 600; bool overwrite_mac = false; + bool broadcast = true; mac_t mymac = {{0xFE, 0xFD, 0, 0, 0, 0}}; +bool pcap = false; /* Sizes of various headers */ @@@ -863,23 -849,51 +866,67 @@@ static void route_mac(node_t *source, v send_packet(subnet->owner, packet); } +static void send_pcap(vpn_packet_t *packet) { + pcap = false; + for(splay_node_t *node = connection_tree->head; node; node = node->next) { + connection_t *c = node->data; + if(!c->status.pcap) + continue; + else + pcap = true; + if(send_request(c, "%d %d %d", CONTROL, REQ_PCAP, packet->len)) + send_meta(c, (char *)packet->data, packet->len); + } +} + + static bool do_decrement_ttl(node_t *source, vpn_packet_t *packet) { + uint16_t type = packet->data[12] << 8 | packet->data[13]; + + switch (type) { + case ETH_P_IP: + if(!checklength(source, packet, 14 + 32)) + return false; + + if(packet->data[22] < 1) { + route_ipv4_unreachable(source, packet, 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]; + + uint32_t checksum = packet->data[24] << 8 | packet->data[25]; + checksum += old + (~new & 0xFFFF); + while(checksum >> 16) + checksum = (checksum & 0xFFFF) + (checksum >> 16); + packet->data[24] = checksum >> 8; + packet->data[25] = checksum & 0xff; + + return true; + + case ETH_P_IPV6: + if(!checklength(source, packet, 14 + 40)) + return false; + + if(packet->data[21] < 1) { + route_ipv6_unreachable(source, packet, ICMP6_TIME_EXCEEDED, ICMP6_TIME_EXCEED_TRANSIT); + return false; + } + + packet->data[21]--; + + return true; + + default: + return true; + } + } + void route(node_t *source, vpn_packet_t *packet) { + if(pcap) + send_pcap(packet); + if(forwarding_mode == FMODE_KERNEL && source != myself) { send_packet(myself, packet); return; diff --cc src/route.h index 5af2a094,c1481fa3..44023fdb --- a/src/route.h +++ b/src/route.h @@@ -38,11 -38,12 +38,13 @@@ typedef enum fmode_t extern rmode_t routing_mode; extern fmode_t forwarding_mode; + extern bool decrement_ttl; extern bool directonly; extern bool overwrite_mac; + extern bool broadcast; extern bool priorityinheritance; extern int macexpire; +extern bool pcap; extern mac_t mymac; diff --cc src/solaris/device.c index eac267ad,969b514a..365561fd --- a/src/solaris/device.c +++ b/src/solaris/device.c @@@ -114,10 -126,10 +126,10 @@@ static void close_device(void) free(iface); } - bool read_packet(vpn_packet_t *packet) { + static bool read_packet(vpn_packet_t *packet) { - int lenin; + int inlen; - if((lenin = read(device_fd, packet->data + 14, MTU - 14)) <= 0) { + if((inlen = read(device_fd, packet->data + 14, MTU - 14)) <= 0) { logger(LOG_ERR, "Error while reading from %s %s: %s", device_info, device, strerror(errno)); return false; diff --cc src/tincd.c index 19088465,a1be971a..7970bcc9 --- a/src/tincd.c +++ b/src/tincd.c @@@ -122,18 -130,20 +123,18 @@@ static void usage(bool status) program_name); else { printf("Usage: %s [option]...\n\n", program_name); - printf( " -c, --config=DIR Read configuration options from DIR.\n" - " -D, --no-detach Don't fork and detach.\n" - " -d, --debug[=LEVEL] Increase debug level or set it to LEVEL.\n" - " -n, --net=NETNAME Connect to net NETNAME.\n" - " -L, --mlock Lock tinc into main memory.\n" - " --logfile[=FILENAME] Write log entries to a logfile.\n" - " --pidfile=FILENAME Write PID and control socket cookie to FILENAME.\n" - " --bypass-security Disables meta protocol security, for debugging.\n" - " -o [HOST.]KEY=VALUE Set global/host configuration value.\n" - " -R, --chroot chroot to NET dir at startup.\n" - " -U, --user=USER setuid to given USER at startup.\n" " --help Display this help and exit.\n" - " --version Output version information and exit.\n\n"); - printf(" -c, --config=DIR Read configuration options from DIR.\n" - " -D, --no-detach Don't fork and detach.\n" - " -d, --debug[=LEVEL] Increase debug level or set it to LEVEL.\n" - " -k, --kill[=SIGNAL] Attempt to kill a running tincd and exit.\n" - " -n, --net=NETNAME Connect to net NETNAME.\n" - " -K, --generate-keys[=BITS] Generate public/private RSA keypair.\n" - " -L, --mlock Lock tinc into main memory.\n" - " --logfile[=FILENAME] Write log entries to a logfile.\n" - " --pidfile=FILENAME Write PID to FILENAME.\n" - " -o, --option=[HOST.]KEY=VALUE Set global/host configuration value.\n" - " -R, --chroot chroot to NET dir at startup.\n" - " -U, --user=USER setuid to given USER at startup.\n" - " --help Display this help and exit.\n" - " --version Output version information and exit.\n\n"); ++ printf( " -c, --config=DIR Read configuration options from DIR.\n" ++ " -D, --no-detach Don't fork and detach.\n" ++ " -d, --debug[=LEVEL] Increase debug level or set it to LEVEL.\n" ++ " -n, --net=NETNAME Connect to net NETNAME.\n" ++ " -L, --mlock Lock tinc into main memory.\n" ++ " --logfile[=FILENAME] Write log entries to a logfile.\n" ++ " --pidfile=FILENAME Write PID and control socket cookie to FILENAME.\n" ++ " --bypass-security Disables meta protocol security, for debugging.\n" ++ " -o, --option[HOST.]KEY=VALUE Set global/host configuration value.\n" ++ " -R, --chroot chroot to NET dir at startup.\n" ++ " -U, --user=USER setuid to given USER at startup.\n" " --help Display this help and exit.\n" ++ " --version Output version information and exit.\n\n"); printf("Report bugs to tinc@tinc-vpn.org.\n"); } } diff --cc src/uml_device.c index d8f13a55,a0b87f93..f0a9869b --- a/src/uml_device.c +++ b/src/uml_device.c @@@ -169,8 -179,8 +179,8 @@@ void close_device(void) if(iface) free(iface); } - bool read_packet(vpn_packet_t *packet) { + static bool read_packet(vpn_packet_t *packet) { - int lenin; + int inlen; switch(state) { case 0: {