+Version 1.1-cvs Work in progress
+
+ * Use libevent to handle I/O events and timeouts.
+
+ * Use splay trees instead of AVL trees.
+
+ Version 1.0.14 not released yet
+
+ * Fixed reading configuration files that do not end with a newline. Again.
+
+ Version 1.0.13 Apr 11 2010
+
+ * Allow building tinc without LZO and/or Zlib.
+
+ * Clamp MSS of TCP packets in both directions.
+
+ * Experimental StrictSubnets, Forwarding and DirectOnly options,
+ giving more control over information and packets received from/sent to other
+ nodes.
+
+ * Ensure tinc never sends symbolic names for ports over the wire.
+
Version 1.0.12 Feb 3 2010
* Really allow fast roaming of hosts to other nodes in a switched VPN.
Parse a configuration file and put the results in the configuration tree
starting at *base.
*/
-bool read_config_file(avl_tree_t *config_tree, const char *fname) {
+int read_config_file(splay_tree_t *config_tree, const char *fname) {
- int err = -2; /* Parse error */
FILE *fp;
char buffer[MAX_STRING_SIZE];
char *line;
--- /dev/null
+/*
+ ipv4.h -- missing IPv4 related definitions
+ Copyright (C) 2005 Ivo Timmermans
+ 2006 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_IPV4_H__
+#define __TINC_IPV4_H__
+
+#ifndef AF_INET
+#define AF_INET 2
+#endif
+
+#ifndef IPPROTO_ICMP
+#define IPPROTO_ICMP 1
+#endif
+
+#ifndef ICMP_DEST_UNREACH
+#define ICMP_DEST_UNREACH 3
+#endif
+
+#ifndef ICMP_FRAG_NEEDED
+#define ICMP_FRAG_NEEDED 4
+#endif
+
+#ifndef ICMP_NET_UNKNOWN
+#define ICMP_NET_UNKNOWN 6
+#endif
+
+#ifndef ICMP_NET_UNREACH
+#define ICMP_NET_UNREACH 0
+#endif
+
++#ifndef ICMP_NET_ANO
++#define ICMP_NET_ANO 9
++#endif
++
+#ifndef IP_MSS
+#define IP_MSS 576
+#endif
+
+#ifndef HAVE_STRUCT_IP
+struct ip {
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ unsigned int ip_hl:4;
+ unsigned int ip_v:4;
+#else
+ unsigned int ip_v:4;
+ unsigned int ip_hl:4;
+#endif
+ uint8_t ip_tos;
+ uint16_t ip_len;
+ uint16_t ip_id;
+ uint16_t ip_off;
+#define IP_RF 0x8000
+#define IP_DF 0x4000
+#define IP_MF 0x2000
+ uint8_t ip_ttl;
+ uint8_t ip_p;
+ uint16_t ip_sum;
+ struct in_addr ip_src, ip_dst;
+} __attribute__ ((__packed__));
+#endif
+
+#ifndef IP_OFFMASK
+#define IP_OFFMASK 0x1fff
+#endif
+
+#ifndef HAVE_STRUCT_ICMP
+struct icmp {
+ uint8_t icmp_type;
+ uint8_t icmp_code;
+ uint16_t icmp_cksum;
+ union {
+ uint8_t ih_pptr;
+ struct in_addr ih_gwaddr;
+ struct ih_idseq {
+ uint16_t icd_id;
+ uint16_t icd_seq;
+ } ih_idseq;
+ uint32_t ih_void;
+
+
+ struct ih_pmtu {
+ uint16_t ipm_void;
+ uint16_t ipm_nextmtu;
+ } ih_pmtu;
+
+ struct ih_rtradv {
+ uint8_t irt_num_addrs;
+ uint8_t irt_wpa;
+ uint16_t irt_lifetime;
+ } ih_rtradv;
+ } icmp_hun;
+#define icmp_pptr icmp_hun.ih_pptr
+#define icmp_gwaddr icmp_hun.ih_gwaddr
+#define icmp_id icmp_hun.ih_idseq.icd_id
+#define icmp_seq icmp_hun.ih_idseq.icd_seq
+#define icmp_void icmp_hun.ih_void
+#define icmp_pmvoid icmp_hun.ih_pmtu.ipm_void
+#define icmp_nextmtu icmp_hun.ih_pmtu.ipm_nextmtu
+#define icmp_num_addrs icmp_hun.ih_rtradv.irt_num_addrs
+#define icmp_wpa icmp_hun.ih_rtradv.irt_wpa
+#define icmp_lifetime icmp_hun.ih_rtradv.irt_lifetime
+ union {
+ struct {
+ uint32_t its_otime;
+ uint32_t its_rtime;
+ uint32_t its_ttime;
+ } id_ts;
+ struct {
+ struct ip idi_ip;
+ } id_ip;
+ uint32_t id_mask;
+ uint8_t id_data[1];
+ } icmp_dun;
+#define icmp_otime icmp_dun.id_ts.its_otime
+#define icmp_rtime icmp_dun.id_ts.its_rtime
+#define icmp_ttime icmp_dun.id_ts.its_ttime
+#define icmp_ip icmp_dun.id_ip.idi_ip
+#define icmp_radv icmp_dun.id_radv
+#define icmp_mask icmp_dun.id_mask
+#define icmp_data icmp_dun.id_data
+} __attribute__ ((__packed__));
+#endif
+
+#endif /* __TINC_IPV4_H__ */
--- /dev/null
+/*
+ ipv6.h -- missing IPv6 related definitions
+ Copyright (C) 2005 Ivo Timmermans
+ 2006 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_IPV6_H__
+#define __TINC_IPV6_H__
+
+#ifndef AF_INET6
+#define AF_INET6 10
+#endif
+
+#ifndef IPPROTO_ICMPV6
+#define IPPROTO_ICMPV6 58
+#endif
+
+#ifndef HAVE_STRUCT_IN6_ADDR
+struct in6_addr {
+ union {
+ uint8_t u6_addr8[16];
+ uint16_t u6_addr16[8];
+ uint32_t u6_addr32[4];
+ } in6_u;
+} __attribute__ ((__packed__));
+#define s6_addr in6_u.u6_addr8
+#define s6_addr16 in6_u.u6_addr16
+#define s6_addr32 in6_u.u6_addr32
+#endif
+
+#ifndef HAVE_STRUCT_SOCKADDR_IN6
+struct sockaddr_in6 {
+ uint16_t sin6_family;
+ uint16_t sin6_port;
+ uint32_t sin6_flowinfo;
+ struct in6_addr sin6_addr;
+ uint32_t sin6_scope_id;
+} __attribute__ ((__packed__));
+#endif
+
+#ifndef IN6_IS_ADDR_V4MAPPED
+#define IN6_IS_ADDR_V4MAPPED(a) \
+ ((((__const uint32_t *) (a))[0] == 0) \
+ && (((__const uint32_t *) (a))[1] == 0) \
+ && (((__const uint32_t *) (a))[2] == htonl (0xffff)))
+#endif
+
+#ifndef HAVE_STRUCT_IP6_HDR
+struct ip6_hdr {
+ union {
+ struct ip6_hdrctl {
+ uint32_t ip6_un1_flow;
+ uint16_t ip6_un1_plen;
+ uint8_t ip6_un1_nxt;
+ uint8_t ip6_un1_hlim;
+ } ip6_un1;
+ uint8_t ip6_un2_vfc;
+ } ip6_ctlun;
+ struct in6_addr ip6_src;
+ struct in6_addr ip6_dst;
+} __attribute__ ((__packed__));
+#define ip6_vfc ip6_ctlun.ip6_un2_vfc
+#define ip6_flow ip6_ctlun.ip6_un1.ip6_un1_flow
+#define ip6_plen ip6_ctlun.ip6_un1.ip6_un1_plen
+#define ip6_nxt ip6_ctlun.ip6_un1.ip6_un1_nxt
+#define ip6_hlim ip6_ctlun.ip6_un1.ip6_un1_hlim
+#define ip6_hops ip6_ctlun.ip6_un1.ip6_un1_hlim
+#endif
+
+#ifndef HAVE_STRUCT_ICMP6_HDR
+struct icmp6_hdr {
+ uint8_t icmp6_type;
+ uint8_t icmp6_code;
+ uint16_t icmp6_cksum;
+ union {
+ uint32_t icmp6_un_data32[1];
+ uint16_t icmp6_un_data16[2];
+ uint8_t icmp6_un_data8[4];
+ } icmp6_dataun;
+} __attribute__ ((__packed__));
+#define ICMP6_DST_UNREACH_NOROUTE 0
+#define ICMP6_DST_UNREACH 1
+#define ICMP6_PACKET_TOO_BIG 2
++#define ICMP6_DST_UNREACH_ADMIN 1
+#define ICMP6_DST_UNREACH_ADDR 3
+#define ND_NEIGHBOR_SOLICIT 135
+#define ND_NEIGHBOR_ADVERT 136
+#define icmp6_data32 icmp6_dataun.icmp6_un_data32
+#define icmp6_data16 icmp6_dataun.icmp6_un_data16
+#define icmp6_data8 icmp6_dataun.icmp6_un_data8
+#define icmp6_mtu icmp6_data32[0]
+#endif
+
+#ifndef HAVE_STRUCT_ND_NEIGHBOR_SOLICIT
+struct nd_neighbor_solicit {
+ struct icmp6_hdr nd_ns_hdr;
+ struct in6_addr nd_ns_target;
+} __attribute__ ((__packed__));
+#define ND_OPT_SOURCE_LINKADDR 1
+#define ND_OPT_TARGET_LINKADDR 2
+#define nd_ns_type nd_ns_hdr.icmp6_type
+#define nd_ns_code nd_ns_hdr.icmp6_code
+#define nd_ns_cksum nd_ns_hdr.icmp6_cksum
+#define nd_ns_reserved nd_ns_hdr.icmp6_data32[0]
+#endif
+
+#ifndef HAVE_STRUCT_ND_OPT_HDR
+struct nd_opt_hdr {
+ uint8_t nd_opt_type;
+ uint8_t nd_opt_len;
+} __attribute__ ((__packed__));
+#endif
+
+#endif /* __TINC_IPV6_H__ */
}
}
-/*
- this is where it all happens...
-*/
-int main_loop(void) {
- fd_set readset, writeset;
- struct timeval tv;
- int r, maxfd;
- time_t last_ping_check, last_config_check, last_graph_dump;
- event_t *event;
-
- last_ping_check = now;
- last_config_check = now;
- last_graph_dump = now;
-
- srand(now);
-
- running = true;
-
- while(running) {
- now = time(NULL);
+static void sigterm_handler(int signal, short events, void *data) {
+ logger(LOG_NOTICE, "Got %s signal", strsignal(signal));
+ event_loopexit(NULL);
+}
- // tv.tv_sec = 1 + (rand() & 7); /* Approx. 5 seconds, randomized to prevent global synchronisation effects */
- tv.tv_sec = 1;
- tv.tv_usec = 0;
+static void sighup_handler(int signal, short events, void *data) {
+ logger(LOG_NOTICE, "Got %s signal", strsignal(signal));
+ reload_configuration();
+}
- 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
- r = select(maxfd + 1, &readset, &writeset, NULL, &tv);
-#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;
+ last_config_check = time(NULL);
- ifdebug(STATUS) logger(LOG_INFO, "Expiring symmetric keys");
++ /* If StrictSubnet is set, expire deleted Subnets and read new ones in */
+
- for(node = node_tree->head; node; node = node->next) {
- n = node->data;
- if(n->inkey) {
- free(n->inkey);
- n->inkey = NULL;
- }
- }
++ if(strictsubnets) {
++ subnet_t *subnet;
+
- send_key_changed(broadcast, myself);
- keyexpires = now + keylifetime;
- }
++ 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);
++ 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);
++ 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;
-
- /* 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);
- }
+ /* Try to make outgoing connections */
+
+ try_outgoing_connections();
- last_config_check = now;
+ return 0;
+}
- /* If StrictSubnet is set, expire deleted Subnets and read new ones in */
+void retry(void) {
+ connection_t *c;
+ splay_node_t *node;
- if(strictsubnets) {
- subnet_t *subnet;
+ for(node = connection_tree->head; node; node = node->next) {
+ c = node->data;
+
+ 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);
+ }
+ }
+}
- for(node = subnet_tree->head; node; node = node->next) {
- subnet = node->data;
- subnet->expires = 1;
- }
+/*
+ this is where it all happens...
+*/
+int main_loop(void) {
+ struct event timeout_event;
+ struct event sighup_event;
+ struct event sigterm_event;
+ struct event sigquit_event;
- 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);
- 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);
- if(subnet->owner->status.reachable)
- subnet_update(subnet->owner, subnet, true);
- }
- }
- }
+ timeout_set(&timeout_event, timeout_handler, &timeout_event);
+ event_add(&timeout_event, &(struct timeval){pingtimeout, 0});
- /* Try to make outgoing connections */
-
- try_outgoing_connections();
- }
-
- /* Dump graph if wanted every 60 seconds*/
+#ifdef SIGHUP
+ signal_set(&sighup_event, SIGHUP, sighup_handler, NULL);
+ signal_add(&sighup_event, NULL);
+#endif
+#ifdef SIGTERM
+ signal_set(&sigterm_event, SIGTERM, sigterm_handler, NULL);
+ signal_add(&sigterm_event, NULL);
+#endif
+#ifdef SIGQUIT
+ signal_set(&sigquit_event, SIGQUIT, sigterm_handler, NULL);
+ signal_add(&sigquit_event, NULL);
+#endif
- if(last_graph_dump + 60 < now) {
- dump_graph();
- last_graph_dump = now;
- }
+ if(event_loop(0) < 0) {
+ logger(LOG_ERR, "Error while waiting for input: %s", strerror(errno));
+ return 1;
}
+ signal_del(&sighup_event);
+ signal_del(&sigterm_event);
+ signal_del(&sigquit_event);
+ event_del(&timeout_event);
+
return 0;
}
extern void flush_queue(struct node_t *);
extern bool read_rsa_public_key(struct connection_t *);
extern void send_mtu_probe(struct node_t *);
+extern void handle_device_data(int, short, void *);
+extern void handle_meta_connection_data(int, short, void *);
+extern void regenerate_key();
+extern void purge(void);
+extern void retry(void);
+extern int reload_configuration(void);
+ extern void load_all_subnets();
#ifndef HAVE_MINGW
#define closesocket(s) close(s)
struct dirent *ent;
char *dname;
char *fname;
- avl_tree_t *config_tree;
+ splay_tree_t *config_tree;
config_t *cfg;
- subnet_t *s;
+ subnet_t *s, *s2;
node_t *n;
bool result;
}
}
-bool ack_h(connection_t *c) {
+bool ack_h(connection_t *c, char *request) {
char hisport[MAX_STRING_SIZE];
- char *hisaddress, *dummy;
+ char *hisaddress;
int weight, mtu;
uint32_t options;
node_t *n;
static const size_t icmp6_size = sizeof(struct icmp6_hdr);
static const size_t ns_size = sizeof(struct nd_neighbor_solicit);
static const size_t opt_size = sizeof(struct nd_opt_hdr);
- #define max(a, b) ((a) > (b) ? (a) : (b))
+
+ #ifndef MAX
+ #define MAX(a, b) ((a) > (b) ? (a) : (b))
+ #endif
+static struct event age_subnets_event;
+
/* RFC 1071 */
static uint16_t inet_checksum(void *data, int len, uint16_t prevsum) {
#define MAXNETSTR 64
-extern avl_tree_t *subnet_tree;
++extern splay_tree_t *subnet_tree;
+
extern int subnet_compare(const struct subnet_t *, const struct subnet_t *);
extern subnet_t *new_subnet(void) __attribute__ ((__malloc__));
extern void free_subnet(subnet_t *);