chacha-poly1305/poly1305.c chacha-poly1305/poly1305.h
tincd_SOURCES = \
+ address_cache.c address_cache.h \
autoconnect.c autoconnect.h \
buffer.c buffer.h \
cipher.h \
--- /dev/null
+/*
+ address_cache.c -- Manage cache of recently seen addresses
+ Copyright (C) 2018 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 "address_cache.h"
+#include "conf.h"
+#include "names.h"
+#include "netutl.h"
+#include "xalloc.h"
+
+static const unsigned int NOT_CACHED = -1;
+
+// Find edges pointing to this node, and use them to build a list of unique, known addresses.
+static struct addrinfo *get_known_addresses(node_t *n) {
+ struct addrinfo *ai = NULL;
+ struct addrinfo *oai = NULL;
+
+ for splay_each(edge_t, e, n->edge_tree) {
+ if(!e->reverse) {
+ continue;
+ }
+
+ bool found = false;
+
+ for(struct addrinfo *aip = ai; aip; aip = aip->ai_next) {
+ if(!sockaddrcmp(&e->reverse->address, (sockaddr_t *)aip->ai_addr)) {
+ found = true;
+ break;
+ }
+ }
+
+ if(found) {
+ continue;
+ }
+
+ oai = ai;
+ ai = xzalloc(sizeof(*ai));
+ ai->ai_family = e->reverse->address.sa.sa_family;
+ ai->ai_socktype = SOCK_STREAM;
+ ai->ai_protocol = IPPROTO_TCP;
+ ai->ai_addrlen = SALEN(e->reverse->address.sa);
+ ai->ai_addr = xmalloc(ai->ai_addrlen);
+ memcpy(ai->ai_addr, &e->reverse->address, ai->ai_addrlen);
+ ai->ai_next = oai;
+ }
+
+ return ai;
+}
+
+static void free_known_addresses(struct addrinfo *ai) {
+ for(struct addrinfo *aip = ai, *next; aip; aip = next) {
+ next = aip->ai_next;
+ free(aip);
+ }
+}
+
+static unsigned int find_cached(address_cache_t *cache, const sockaddr_t *sa) {
+ for(unsigned int i = 0; i < cache->data.used; i++)
+ if(!sockaddrcmp(&cache->data.address[i], sa)) {
+ return i;
+ }
+
+ return NOT_CACHED;
+}
+
+void add_recent_address(address_cache_t *cache, const sockaddr_t *sa) {
+ // Check if it's already cached
+ unsigned int pos = find_cached(cache, sa);
+
+ // It's in the first spot, so nothing to do
+ if (pos == 0) {
+ return;
+ }
+
+ // Shift everything, move/add the address to the first slot
+ if(pos == NOT_CACHED) {
+ if(cache->data.used < MAX_CACHED_ADDRESSES)
+ cache->data.used++;
+ pos = cache->data.used - 1;
+ }
+
+ memmove(&cache->data.address[1], &cache->data.address[0], pos * sizeof(cache->data.address[0]));
+
+ cache->data.address[0] = *sa;
+
+ // Write the cache
+ char fname[PATH_MAX];
+ snprintf(fname, sizeof(fname), "%s" SLASH "cache" SLASH "%s", confbase, cache->node->name);
+ FILE *fp = fopen(fname, "wb");
+
+ if(fp) {
+ fprintf(stderr, "Writing cache to %s\n", fname);
+ fwrite(&cache->data, sizeof(cache->data), 1, fp);
+ fclose(fp);
+ }
+}
+
+const sockaddr_t *get_recent_address(address_cache_t *cache) {
+ // Check if there is an address in our cache of recently seen addresses
+ if(cache->tried < cache->data.used) {
+ return &cache->data.address[cache->tried++];
+ }
+
+ // Next, check any recently seen addresses not in our cache
+ while(cache->tried == cache->data.used) {
+ if(!cache->ai) {
+ cache->aip = cache->ai = get_known_addresses(cache->node);
+ }
+
+ if(cache->ai) {
+ if(cache->aip) {
+ sockaddr_t *sa = (sockaddr_t *)cache->aip;
+
+ if(find_cached(cache, sa) != NOT_CACHED) {
+ continue;
+ }
+
+ cache->aip = cache->aip->ai_next;
+ return sa;
+ } else {
+ free_known_addresses(cache->ai);
+ cache->ai = NULL;
+ }
+ }
+
+ cache->tried++;
+ }
+
+ // Otherwise, check if there are any known Address statements
+ if(!cache->config_tree) {
+ init_configuration(&cache->config_tree);
+ read_host_config(cache->config_tree, cache->node->name, false);
+ cache->cfg = lookup_config(cache->config_tree, "Address");
+ }
+
+ while(cache->cfg && !cache->ai) {
+ char *address, *port;
+
+ get_config_string(cache->cfg, &address);
+
+ char *space = strchr(address, ' ');
+
+ if(space) {
+ port = xstrdup(space + 1);
+ *space = 0;
+ } else {
+ if(!get_config_string(lookup_config(cache->config_tree, "Port"), &port)) {
+ port = xstrdup("655");
+ }
+ }
+
+ cache->aip = cache->ai = str2addrinfo(address, port, SOCK_STREAM);
+ free(address);
+ free(port);
+
+ cache->cfg = lookup_config_next(cache->config_tree, cache->cfg);
+ }
+
+ if(cache->aip) {
+ sockaddr_t *sa = (sockaddr_t *)cache->aip->ai_addr;
+ cache->aip = cache->aip->ai_next;
+
+ if(!cache->aip) {
+ freeaddrinfo(cache->aip);
+ cache->aip = NULL;
+ }
+
+ return sa;
+ }
+
+ // We're all out of addresses.
+ exit_configuration(&cache->config_tree);
+ return false;
+}
+
+address_cache_t *open_address_cache(node_t *node) {
+ address_cache_t *cache = xmalloc(sizeof(*cache));
+ cache->node = node;
+
+ // Try to open an existing address cache
+ char fname[PATH_MAX];
+ snprintf(fname, sizeof(fname), "%s" SLASH "cache" SLASH "%s", confbase, node->name);
+ FILE *fp = fopen(fname, "rb");
+
+ if(!fp || fread(&cache->data, sizeof(cache->data), 1, fp) != 1 || cache->data.version != ADDRESS_CACHE_VERSION) {
+ memset(&cache->data, 0, sizeof(cache->data));
+ }
+
+ if(fp) {
+ fclose(fp);
+ }
+
+ // Ensure we have a valid state
+ cache->config_tree = NULL;
+ cache->cfg = NULL;
+ cache->ai = NULL;
+ cache->aip = NULL;
+ cache->tried = 0;
+ cache->data.version = ADDRESS_CACHE_VERSION;
+
+ if(cache->data.used > MAX_CACHED_ADDRESSES) {
+ cache->data.used = 0;
+ }
+
+ return cache;
+}
+
+void reset_address_cache(address_cache_t *cache, const sockaddr_t *sa) {
+ if(sa) {
+ add_recent_address(cache, sa);
+ }
+
+ if(cache->config_tree) {
+ exit_configuration(&cache->config_tree);
+ }
+
+ if(cache->ai) {
+ if(cache->tried == cache->data.used) {
+ free_known_addresses(cache->ai);
+ } else {
+ freeaddrinfo(cache->ai);
+ }
+ }
+
+ cache->config_tree = NULL;
+ cache->cfg = NULL;
+ cache->ai = NULL;
+ cache->aip = NULL;
+ cache->tried = 0;
+}
+
+void close_address_cache(address_cache_t *cache) {
+ if(cache->config_tree) {
+ exit_configuration(&cache->config_tree);
+ }
+
+ if(cache->ai) {
+ if(cache->tried == cache->data.used) {
+ free_known_addresses(cache->ai);
+ } else {
+ freeaddrinfo(cache->ai);
+ }
+ }
+
+ free(cache);
+}
--- /dev/null
+#ifndef TINC_ADDRESS_CACHE_H
+#define TINC_ADDRESS_CACHE_H
+
+/*
+ address_cache.h -- header for address_cache.c
+ Copyright (C) 2018 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 "net.h"
+
+#define MAX_CACHED_ADDRESSES 8
+#define ADDRESS_CACHE_VERSION 1
+
+typedef struct address_cache_t {
+ struct node_t *node;
+ struct splay_tree_t *config_tree;
+ struct config_t *cfg;
+ struct addrinfo *ai;
+ struct addrinfo *aip;
+ unsigned int tried;
+
+ struct {
+ unsigned int version;
+ unsigned int used;
+ sockaddr_t address[MAX_CACHED_ADDRESSES];
+ } data;
+} address_cache_t;
+
+void add_recent_address(address_cache_t *cache, const sockaddr_t *sa);
+const sockaddr_t *get_recent_address(address_cache_t *cache);
+
+address_cache_t *open_address_cache(struct node_t *node);
+void reset_address_cache(address_cache_t *cache, const sockaddr_t *sa);
+void close_address_cache(address_cache_t *cache);
+
+#endif
bool found = false;
for list_each(outgoing_t, outgoing, outgoing_list) {
- if(!strcmp(outgoing->name, n->name)) {
+ if(outgoing->node == n) {
found = true;
break;
}
if(!found) {
logger(DEBUG_CONNECTIONS, LOG_INFO, "Autoconnecting to %s", n->name);
outgoing_t *outgoing = xzalloc(sizeof(*outgoing));
- outgoing->name = xstrdup(n->name);
+ outgoing->node = n;
list_insert_tail(outgoing_list, outgoing);
setup_outgoing_connection(outgoing, false);
}
return;
}
- /* Are we already trying to make an outgoing connection to it? If not, return. */
- for list_each(outgoing_t, outgoing, outgoing_list)
- if(!strcmp(outgoing->name, n->name)) {
+ /* Are we already trying to make an outgoing connection to it? If so, return. */
+ for list_each(outgoing_t, outgoing, outgoing_list) {
+ if(outgoing->node == n) {
return;
}
+ }
logger(DEBUG_CONNECTIONS, LOG_INFO, "Autoconnecting to %s", n->name);
outgoing_t *outgoing = xzalloc(sizeof(*outgoing));
- outgoing->name = xstrdup(n->name);
+ outgoing->node = n;
list_insert_tail(outgoing_list, outgoing);
setup_outgoing_connection(outgoing, false);
continue;
}
- logger(DEBUG_CONNECTIONS, LOG_INFO, "Cancelled outgoing connection to %s", o->name);
+ logger(DEBUG_CONNECTIONS, LOG_INFO, "Cancelled outgoing connection to %s", o->node->name);
list_delete_node(outgoing_list, node);
}
}
#include "list.h"
typedef struct outgoing_t {
- char *name;
+ struct node_t *node;
int timeout;
- splay_tree_t *config_tree;
- struct config_t *cfg;
- struct addrinfo *ai; // addresses from config files
- struct addrinfo *aip;
- struct addrinfo *kai; // addresses known via other online nodes (use free_known_addresses())
+ struct address_cache_t *address_cache;
timeout_t ev;
} outgoing_t;
#include LZO1X_H
#endif
+#include "address_cache.h"
#include "cipher.h"
#include "conf.h"
#include "connection.h"
/* 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(!n->status.udp_confirmed) {
+ n->status.udp_confirmed = true;
+ fprintf(stderr, "Updating address cache...\n");
+ if (!n->address_cache)
+ n->address_cache = open_address_cache(n);
+ reset_address_cache(n->address_cache, &n->address);
+ }
// Reset the UDP ping timer.
n->udp_ping_sent = now;
#include "system.h"
+#include "address_cache.h"
#include "conf.h"
#include "connection.h"
#include "control_common.h"
}
}
-static void free_known_addresses(struct addrinfo *ai) {
- for(struct addrinfo *aip = ai, *next; aip; aip = next) {
- next = aip->ai_next;
- free(aip);
- }
-}
-
bool do_outgoing_connection(outgoing_t *outgoing) {
- char *address, *port, *space;
+ const sockaddr_t *sa;
struct addrinfo *proxyai = NULL;
int result;
begin:
+ sa = get_recent_address(outgoing->address_cache);
- if(!outgoing->ai && !outgoing->kai) {
- if(!outgoing->cfg) {
- logger(DEBUG_CONNECTIONS, LOG_ERR, "Could not set up a meta connection to %s", outgoing->name);
- retry_outgoing(outgoing);
- return false;
- }
-
- get_config_string(outgoing->cfg, &address);
-
- space = strchr(address, ' ');
-
- if(space) {
- port = xstrdup(space + 1);
- *space = 0;
- } else {
- if(!get_config_string(lookup_config(outgoing->config_tree, "Port"), &port)) {
- port = xstrdup("655");
- }
- }
-
- outgoing->ai = str2addrinfo(address, port, SOCK_STREAM);
- free(address);
- free(port);
-
- outgoing->aip = outgoing->ai;
- outgoing->cfg = lookup_config_next(outgoing->config_tree, outgoing->cfg);
- }
-
- if(!outgoing->aip) {
- if(outgoing->ai) {
- freeaddrinfo(outgoing->ai);
- }
-
- outgoing->ai = NULL;
-
- if(outgoing->kai) {
- free_known_addresses(outgoing->kai);
- }
-
- outgoing->kai = NULL;
-
- goto begin;
+ if(!sa) {
+ logger(DEBUG_CONNECTIONS, LOG_ERR, "Could not set up a meta connection to %s", outgoing->node->name);
+ retry_outgoing(outgoing);
+ return false;
}
connection_t *c = new_connection();
c->outgoing = outgoing;
-
- memcpy(&c->address, outgoing->aip->ai_addr, outgoing->aip->ai_addrlen);
- outgoing->aip = outgoing->aip->ai_next;
-
+ c->address = *sa;
c->hostname = sockaddr2hostname(&c->address);
- logger(DEBUG_CONNECTIONS, LOG_INFO, "Trying to connect to %s (%s)", outgoing->name, c->hostname);
+ logger(DEBUG_CONNECTIONS, LOG_INFO, "Trying to connect to %s (%s)", outgoing->node->name, c->hostname);
if(!proxytype) {
c->socket = socket(c->address.sa.sa_family, SOCK_STREAM, IPPROTO_TCP);
}
if(result == -1 && !sockinprogress(sockerrno)) {
- logger(DEBUG_CONNECTIONS, LOG_ERR, "Could not connect to %s (%s): %s", outgoing->name, c->hostname, sockstrerror(sockerrno));
+ logger(DEBUG_CONNECTIONS, LOG_ERR, "Could not connect to %s (%s): %s", outgoing->node->name, c->hostname, sockstrerror(sockerrno));
free_connection(c);
goto begin;
c->last_ping_time = time(NULL);
c->status.connecting = true;
- c->name = xstrdup(outgoing->name);
+ c->name = xstrdup(outgoing->node->name);
#ifndef DISABLE_LEGACY
c->outcipher = myself->connection->outcipher;
c->outdigest = myself->connection->outdigest;
return true;
}
-// Find edges pointing to this node, and use them to build a list of unique, known addresses.
-static struct addrinfo *get_known_addresses(node_t *n) {
- struct addrinfo *ai = NULL;
- struct addrinfo *oai = NULL;
-
- for splay_each(edge_t, e, n->edge_tree) {
- if(!e->reverse) {
- continue;
- }
-
- bool found = false;
-
- for(struct addrinfo *aip = ai; aip; aip = aip->ai_next) {
- if(!sockaddrcmp(&e->reverse->address, (sockaddr_t *)aip->ai_addr)) {
- found = true;
- break;
- }
- }
-
- if(found) {
- continue;
- }
-
- oai = ai;
- ai = xzalloc(sizeof(*ai));
- ai->ai_family = e->reverse->address.sa.sa_family;
- ai->ai_socktype = SOCK_STREAM;
- ai->ai_protocol = IPPROTO_TCP;
- ai->ai_addrlen = SALEN(e->reverse->address.sa);
- ai->ai_addr = xmalloc(ai->ai_addrlen);
- memcpy(ai->ai_addr, &e->reverse->address, ai->ai_addrlen);
- ai->ai_next = oai;
- }
-
- return ai;
-}
-
void setup_outgoing_connection(outgoing_t *outgoing, bool verbose) {
timeout_del(&outgoing->ev);
- node_t *n = lookup_node(outgoing->name);
+ node_t *n = outgoing->node;
- if(n && n->connection) {
- logger(DEBUG_CONNECTIONS, LOG_INFO, "Already connected to %s", outgoing->name);
+ if(n->connection) {
+ logger(DEBUG_CONNECTIONS, LOG_INFO, "Already connected to %s", n->name);
if(!n->connection->outgoing) {
n->connection->outgoing = outgoing;
}
}
- init_configuration(&outgoing->config_tree);
- read_host_config(outgoing->config_tree, outgoing->name, verbose);
- outgoing->cfg = lookup_config(outgoing->config_tree, "Address");
-
- if(!outgoing->cfg) {
- if(n) {
- outgoing->aip = outgoing->kai = get_known_addresses(n);
- }
-
- if(!outgoing->kai) {
- logger(verbose ? DEBUG_ALWAYS : DEBUG_CONNECTIONS, LOG_DEBUG, "No address known for %s", outgoing->name);
- goto remove;
- }
+ if(!outgoing->address_cache) {
+ outgoing->address_cache = open_address_cache(n);
}
do_outgoing_connection(outgoing);
static void free_outgoing(outgoing_t *outgoing) {
timeout_del(&outgoing->ev);
- if(outgoing->ai) {
- freeaddrinfo(outgoing->ai);
- }
-
- if(outgoing->kai) {
- free_known_addresses(outgoing->kai);
- }
-
- if(outgoing->config_tree) {
- exit_configuration(&outgoing->config_tree);
- }
-
- if(outgoing->name) {
- free(outgoing->name);
+ if(outgoing->address_cache) {
+ close_address_cache(outgoing->address_cache);
}
free(outgoing);
bool found = false;
for list_each(outgoing_t, outgoing, outgoing_list) {
- if(!strcmp(outgoing->name, name)) {
+ if(!strcmp(outgoing->node->name, name)) {
found = true;
outgoing->timeout = 0;
break;
if(!found) {
outgoing_t *outgoing = xzalloc(sizeof(*outgoing));
- outgoing->name = name;
+ node_t *n = lookup_node(name);
+ if(!n) {
+ n = new_node();
+ n->name = xstrdup(name);
+ node_add(n);
+ }
+ outgoing->node = n;
list_insert_tail(outgoing_list, outgoing);
setup_outgoing_connection(outgoing, true);
}
#include "system.h"
+#include "address_cache.h"
#include "control_common.h"
#include "hash.h"
#include "logger.h"
free(n->late);
}
+ if(n->address_cache) {
+ close_address_cache(n->address_cache);
+ }
+
free(n);
}
uint64_t in_bytes;
uint64_t out_packets;
uint64_t out_bytes;
+
+ struct address_cache_t *address_cache;
} node_t;
extern struct node_t *myself;
#include "system.h"
+#include "address_cache.h"
#include "conf.h"
#include "connection.h"
#include "logger.h"
if(c->outgoing) {
c->outgoing->timeout = 0;
- c->outgoing->cfg = NULL;
-
- if(c->outgoing->ai) {
- freeaddrinfo(c->outgoing->ai);
- }
-
- c->outgoing->ai = NULL;
- c->outgoing->aip = NULL;
+ reset_address_cache(c->outgoing->address_cache, &c->address);
}
return true;