2002-02-18 16:25:19 +00:00
|
|
|
/*
|
|
|
|
net_setup.c -- Setup.
|
2006-04-26 13:52:58 +00:00
|
|
|
Copyright (C) 1998-2005 Ivo Timmermans,
|
2015-05-19 22:55:00 +00:00
|
|
|
2000-2015 Guus Sliepen <guus@tinc-vpn.org>
|
2009-09-25 19:14:56 +00:00
|
|
|
2006 Scott Lamb <slamb@slamb.org>
|
2010-11-16 16:28:41 +00:00
|
|
|
2010 Brandon Black <blblack@gmail.com>
|
2002-02-18 16:25:19 +00:00
|
|
|
|
|
|
|
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.
|
|
|
|
|
2009-09-24 22:01:00 +00:00
|
|
|
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.
|
2002-02-18 16:25:19 +00:00
|
|
|
*/
|
|
|
|
|
2003-07-17 15:06:27 +00:00
|
|
|
#include "system.h"
|
2002-02-18 16:25:19 +00:00
|
|
|
|
2008-12-11 14:44:44 +00:00
|
|
|
#include "cipher.h"
|
2002-02-18 16:25:19 +00:00
|
|
|
#include "conf.h"
|
|
|
|
#include "connection.h"
|
2007-05-18 16:52:34 +00:00
|
|
|
#include "control.h"
|
2003-07-17 15:06:27 +00:00
|
|
|
#include "device.h"
|
2008-12-11 14:44:44 +00:00
|
|
|
#include "digest.h"
|
2011-07-07 20:28:25 +00:00
|
|
|
#include "ecdsa.h"
|
2003-07-17 15:06:27 +00:00
|
|
|
#include "graph.h"
|
|
|
|
#include "logger.h"
|
2013-01-17 15:39:02 +00:00
|
|
|
#include "names.h"
|
2002-02-18 16:25:19 +00:00
|
|
|
#include "net.h"
|
|
|
|
#include "netutl.h"
|
|
|
|
#include "process.h"
|
|
|
|
#include "protocol.h"
|
|
|
|
#include "route.h"
|
2008-12-11 14:44:44 +00:00
|
|
|
#include "rsa.h"
|
2013-08-23 17:24:36 +00:00
|
|
|
#include "script.h"
|
2003-07-17 15:06:27 +00:00
|
|
|
#include "subnet.h"
|
|
|
|
#include "utils.h"
|
|
|
|
#include "xalloc.h"
|
2002-02-18 16:25:19 +00:00
|
|
|
|
|
|
|
char *myport;
|
2014-12-24 16:06:05 +00:00
|
|
|
static char *myname;
|
2012-11-29 11:28:23 +00:00
|
|
|
static io_t device_io;
|
2011-12-04 00:20:59 +00:00
|
|
|
devops_t devops;
|
2014-06-22 09:48:34 +00:00
|
|
|
bool device_standby = false;
|
2002-02-18 16:25:19 +00:00
|
|
|
|
2012-04-18 21:19:40 +00:00
|
|
|
char *proxyhost;
|
|
|
|
char *proxyport;
|
|
|
|
char *proxyuser;
|
|
|
|
char *proxypass;
|
|
|
|
proxytype_t proxytype;
|
2014-05-06 12:11:55 +00:00
|
|
|
bool autoconnect;
|
2013-04-12 15:15:05 +00:00
|
|
|
bool disablebuggypeers;
|
2012-04-18 21:19:40 +00:00
|
|
|
|
2012-10-07 15:53:23 +00:00
|
|
|
char *scriptinterpreter;
|
|
|
|
char *scriptextension;
|
|
|
|
|
2011-07-16 18:21:44 +00:00
|
|
|
bool node_read_ecdsa_public_key(node_t *n) {
|
2013-05-01 15:17:22 +00:00
|
|
|
if(ecdsa_active(n->ecdsa))
|
2011-07-16 18:21:44 +00:00
|
|
|
return true;
|
|
|
|
|
|
|
|
splay_tree_t *config_tree;
|
|
|
|
FILE *fp;
|
2013-05-11 12:05:28 +00:00
|
|
|
char *pubname = NULL;
|
2011-07-16 18:21:44 +00:00
|
|
|
char *p;
|
|
|
|
|
|
|
|
init_configuration(&config_tree);
|
2013-05-11 12:05:28 +00:00
|
|
|
if(!read_host_config(config_tree, n->name))
|
2011-07-16 18:21:44 +00:00
|
|
|
goto exit;
|
|
|
|
|
2014-05-18 18:47:04 +00:00
|
|
|
/* First, check for simple Ed25519PublicKey statement */
|
2011-07-16 18:21:44 +00:00
|
|
|
|
2014-05-18 18:47:04 +00:00
|
|
|
if(get_config_string(lookup_config(config_tree, "Ed25519PublicKey"), &p)) {
|
2013-05-01 15:17:22 +00:00
|
|
|
n->ecdsa = ecdsa_set_base64_public_key(p);
|
2011-07-16 18:21:44 +00:00
|
|
|
free(p);
|
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
|
2014-05-18 18:47:04 +00:00
|
|
|
/* Else, check for Ed25519PublicKeyFile statement and read it */
|
2011-07-16 18:21:44 +00:00
|
|
|
|
2014-05-18 18:47:04 +00:00
|
|
|
if(!get_config_string(lookup_config(config_tree, "Ed25519PublicKeyFile"), &pubname))
|
2012-10-07 15:53:23 +00:00
|
|
|
xasprintf(&pubname, "%s" SLASH "hosts" SLASH "%s", confbase, n->name);
|
2011-07-16 18:21:44 +00:00
|
|
|
|
2012-10-07 15:53:23 +00:00
|
|
|
fp = fopen(pubname, "r");
|
2011-07-16 18:21:44 +00:00
|
|
|
|
2013-12-08 20:32:21 +00:00
|
|
|
if(!fp)
|
2011-07-16 18:21:44 +00:00
|
|
|
goto exit;
|
|
|
|
|
2013-05-01 15:17:22 +00:00
|
|
|
n->ecdsa = ecdsa_read_pem_public_key(fp);
|
2011-07-16 18:21:44 +00:00
|
|
|
fclose(fp);
|
|
|
|
|
|
|
|
exit:
|
|
|
|
exit_configuration(&config_tree);
|
2012-10-07 15:53:23 +00:00
|
|
|
free(pubname);
|
2013-05-01 15:17:22 +00:00
|
|
|
return n->ecdsa;
|
2011-07-16 18:21:44 +00:00
|
|
|
}
|
|
|
|
|
2011-07-07 20:28:25 +00:00
|
|
|
bool read_ecdsa_public_key(connection_t *c) {
|
2013-05-01 15:17:22 +00:00
|
|
|
if(ecdsa_active(c->ecdsa))
|
|
|
|
return true;
|
|
|
|
|
2011-07-07 20:28:25 +00:00
|
|
|
FILE *fp;
|
|
|
|
char *fname;
|
|
|
|
char *p;
|
|
|
|
|
2013-05-11 12:04:39 +00:00
|
|
|
if(!c->config_tree) {
|
|
|
|
init_configuration(&c->config_tree);
|
|
|
|
if(!read_host_config(c->config_tree, c->name))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2014-05-18 18:47:04 +00:00
|
|
|
/* First, check for simple Ed25519PublicKey statement */
|
2011-07-07 20:28:25 +00:00
|
|
|
|
2014-05-18 18:47:04 +00:00
|
|
|
if(get_config_string(lookup_config(c->config_tree, "Ed25519PublicKey"), &p)) {
|
2013-05-01 15:17:22 +00:00
|
|
|
c->ecdsa = ecdsa_set_base64_public_key(p);
|
2011-07-07 20:28:25 +00:00
|
|
|
free(p);
|
2013-05-01 15:17:22 +00:00
|
|
|
return c->ecdsa;
|
2011-07-07 20:28:25 +00:00
|
|
|
}
|
|
|
|
|
2014-05-18 18:47:04 +00:00
|
|
|
/* Else, check for Ed25519PublicKeyFile statement and read it */
|
2011-07-07 20:28:25 +00:00
|
|
|
|
2014-05-18 18:47:04 +00:00
|
|
|
if(!get_config_string(lookup_config(c->config_tree, "Ed25519PublicKeyFile"), &fname))
|
2012-07-21 14:26:55 +00:00
|
|
|
xasprintf(&fname, "%s" SLASH "hosts" SLASH "%s", confbase, c->name);
|
2011-07-07 20:28:25 +00:00
|
|
|
|
|
|
|
fp = fopen(fname, "r");
|
|
|
|
|
|
|
|
if(!fp) {
|
2014-05-18 18:47:04 +00:00
|
|
|
logger(DEBUG_ALWAYS, LOG_ERR, "Error reading Ed25519 public key file `%s': %s",
|
2011-07-07 20:28:25 +00:00
|
|
|
fname, strerror(errno));
|
|
|
|
free(fname);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-05-01 15:17:22 +00:00
|
|
|
c->ecdsa = ecdsa_read_pem_public_key(fp);
|
2011-07-07 20:28:25 +00:00
|
|
|
|
2015-02-16 07:42:30 +00:00
|
|
|
if(!c->ecdsa && errno != ENOENT)
|
2014-05-18 18:47:04 +00:00
|
|
|
logger(DEBUG_ALWAYS, LOG_ERR, "Parsing Ed25519 public key file `%s' failed.", fname);
|
2015-02-16 07:42:30 +00:00
|
|
|
|
|
|
|
fclose(fp);
|
2011-07-07 20:28:25 +00:00
|
|
|
free(fname);
|
2013-05-01 15:17:22 +00:00
|
|
|
return c->ecdsa;
|
2011-07-07 20:28:25 +00:00
|
|
|
}
|
|
|
|
|
2014-12-29 21:57:18 +00:00
|
|
|
#ifndef DISABLE_LEGACY
|
2007-05-18 10:00:00 +00:00
|
|
|
bool read_rsa_public_key(connection_t *c) {
|
2002-09-09 21:25:28 +00:00
|
|
|
FILE *fp;
|
|
|
|
char *fname;
|
2008-12-11 14:44:44 +00:00
|
|
|
char *n;
|
2002-09-09 21:25:28 +00:00
|
|
|
|
2007-05-23 13:45:49 +00:00
|
|
|
/* First, check for simple PublicKey statement */
|
|
|
|
|
2008-12-11 14:44:44 +00:00
|
|
|
if(get_config_string(lookup_config(c->config_tree, "PublicKey"), &n)) {
|
2013-05-01 15:17:22 +00:00
|
|
|
c->rsa = rsa_set_hex_public_key(n, "FFFF");
|
2008-12-11 14:44:44 +00:00
|
|
|
free(n);
|
2013-05-01 15:17:22 +00:00
|
|
|
return c->rsa;
|
2007-05-23 13:45:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Else, check for PublicKeyFile statement and read it */
|
|
|
|
|
2008-12-11 14:44:44 +00:00
|
|
|
if(!get_config_string(lookup_config(c->config_tree, "PublicKeyFile"), &fname))
|
2012-07-21 14:26:55 +00:00
|
|
|
xasprintf(&fname, "%s" SLASH "hosts" SLASH "%s", confbase, c->name);
|
2007-11-07 02:47:05 +00:00
|
|
|
|
2008-12-11 14:44:44 +00:00
|
|
|
fp = fopen(fname, "r");
|
2007-11-07 02:47:05 +00:00
|
|
|
|
2008-12-11 14:44:44 +00:00
|
|
|
if(!fp) {
|
2012-02-26 17:37:36 +00:00
|
|
|
logger(DEBUG_ALWAYS, LOG_ERR, "Error reading RSA public key file `%s': %s", fname, strerror(errno));
|
2008-12-11 14:44:44 +00:00
|
|
|
free(fname);
|
2003-07-22 20:55:21 +00:00
|
|
|
return false;
|
2002-09-09 21:25:28 +00:00
|
|
|
}
|
|
|
|
|
2013-05-01 15:17:22 +00:00
|
|
|
c->rsa = rsa_read_pem_public_key(fp);
|
2008-12-11 14:44:44 +00:00
|
|
|
fclose(fp);
|
2007-11-07 02:47:05 +00:00
|
|
|
|
2013-05-01 15:17:22 +00:00
|
|
|
if(!c->rsa)
|
2012-02-26 17:37:36 +00:00
|
|
|
logger(DEBUG_ALWAYS, LOG_ERR, "Reading RSA public key file `%s' failed: %s", fname, strerror(errno));
|
2007-11-07 02:47:05 +00:00
|
|
|
free(fname);
|
2013-05-01 15:17:22 +00:00
|
|
|
return c->rsa;
|
2002-02-18 16:25:19 +00:00
|
|
|
}
|
2014-12-29 21:57:18 +00:00
|
|
|
#endif
|
2002-02-18 16:25:19 +00:00
|
|
|
|
2011-07-07 20:28:25 +00:00
|
|
|
static bool read_ecdsa_private_key(void) {
|
|
|
|
FILE *fp;
|
|
|
|
char *fname;
|
|
|
|
|
|
|
|
/* Check for PrivateKeyFile statement and read it */
|
|
|
|
|
2014-05-18 18:47:04 +00:00
|
|
|
if(!get_config_string(lookup_config(config_tree, "Ed25519PrivateKeyFile"), &fname))
|
|
|
|
xasprintf(&fname, "%s" SLASH "ed25519_key.priv", confbase);
|
2011-07-07 20:28:25 +00:00
|
|
|
|
|
|
|
fp = fopen(fname, "r");
|
|
|
|
|
|
|
|
if(!fp) {
|
2014-05-18 18:47:04 +00:00
|
|
|
logger(DEBUG_ALWAYS, LOG_ERR, "Error reading Ed25519 private key file `%s': %s", fname, strerror(errno));
|
2013-05-10 19:11:45 +00:00
|
|
|
if(errno == ENOENT)
|
2014-05-18 18:47:04 +00:00
|
|
|
logger(DEBUG_ALWAYS, LOG_INFO, "Create an Ed25519 keypair with `tinc -n %s generate-ed25519-keys'.", netname ?: ".");
|
2011-07-07 20:28:25 +00:00
|
|
|
free(fname);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if !defined(HAVE_MINGW) && !defined(HAVE_CYGWIN)
|
|
|
|
struct stat s;
|
|
|
|
|
|
|
|
if(fstat(fileno(fp), &s)) {
|
2014-05-18 18:47:04 +00:00
|
|
|
logger(DEBUG_ALWAYS, LOG_ERR, "Could not stat Ed25519 private key file `%s': %s'", fname, strerror(errno));
|
2011-07-07 20:28:25 +00:00
|
|
|
free(fname);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(s.st_mode & ~0100700)
|
2014-05-18 18:47:04 +00:00
|
|
|
logger(DEBUG_ALWAYS, LOG_WARNING, "Warning: insecure file permissions for Ed25519 private key file `%s'!", fname);
|
2011-07-07 20:28:25 +00:00
|
|
|
#endif
|
|
|
|
|
2013-05-01 15:17:22 +00:00
|
|
|
myself->connection->ecdsa = ecdsa_read_pem_private_key(fp);
|
2011-07-07 20:28:25 +00:00
|
|
|
fclose(fp);
|
|
|
|
|
2013-05-01 15:17:22 +00:00
|
|
|
if(!myself->connection->ecdsa)
|
2014-05-18 18:47:04 +00:00
|
|
|
logger(DEBUG_ALWAYS, LOG_ERR, "Reading Ed25519 private key file `%s' failed", fname);
|
2011-07-07 20:28:25 +00:00
|
|
|
free(fname);
|
2013-05-01 15:17:22 +00:00
|
|
|
return myself->connection->ecdsa;
|
2011-07-07 20:28:25 +00:00
|
|
|
}
|
|
|
|
|
Add an invitation protocol.
Using the tinc command, an administrator of an existing VPN can generate
invitations for new nodes. The invitation is a small URL that can easily
be copy&pasted into email or live chat. Another person can have tinc
automatically setup the necessary configuration files and exchange keys
with the server, by only using the invitation URL.
The invitation protocol uses temporary ECDSA keys. The invitation URL
consists of the hostname and port of the server, a hash of the server's
temporary ECDSA key and a cookie. When the client wants to accept an
invitation, it also creates a temporary ECDSA key, connects to the server
and says it wants to accept an invitation. Both sides exchange their
temporary keys. The client verifies that the server's key matches the hash
in the invitation URL. After setting up an SPTPS connection using the
temporary keys, the client gives the cookie to the server. If the cookie
is valid, the server sends the client an invitation file containing the
client's new name and a copy of the server's host config file. If everything
is ok, the client will generate a long-term ECDSA key and send it to the
server, which will add it to a new host config file for the client.
The invitation protocol currently allows multiple host config files to be
send from the server to the client. However, the client filters out
most configuration variables for its own host configuration file. In
particular, it only accepts Name, Mode, Broadcast, ConnectTo, Subnet and
AutoConnect. Also, at the moment no tinc-up script is generated.
When an invitation has succesfully been accepted, the client needs to start
the tinc daemon manually.
2013-05-29 16:31:10 +00:00
|
|
|
static bool read_invitation_key(void) {
|
|
|
|
FILE *fp;
|
2015-05-19 22:55:00 +00:00
|
|
|
char fname[PATH_MAX];
|
Add an invitation protocol.
Using the tinc command, an administrator of an existing VPN can generate
invitations for new nodes. The invitation is a small URL that can easily
be copy&pasted into email or live chat. Another person can have tinc
automatically setup the necessary configuration files and exchange keys
with the server, by only using the invitation URL.
The invitation protocol uses temporary ECDSA keys. The invitation URL
consists of the hostname and port of the server, a hash of the server's
temporary ECDSA key and a cookie. When the client wants to accept an
invitation, it also creates a temporary ECDSA key, connects to the server
and says it wants to accept an invitation. Both sides exchange their
temporary keys. The client verifies that the server's key matches the hash
in the invitation URL. After setting up an SPTPS connection using the
temporary keys, the client gives the cookie to the server. If the cookie
is valid, the server sends the client an invitation file containing the
client's new name and a copy of the server's host config file. If everything
is ok, the client will generate a long-term ECDSA key and send it to the
server, which will add it to a new host config file for the client.
The invitation protocol currently allows multiple host config files to be
send from the server to the client. However, the client filters out
most configuration variables for its own host configuration file. In
particular, it only accepts Name, Mode, Broadcast, ConnectTo, Subnet and
AutoConnect. Also, at the moment no tinc-up script is generated.
When an invitation has succesfully been accepted, the client needs to start
the tinc daemon manually.
2013-05-29 16:31:10 +00:00
|
|
|
|
|
|
|
if(invitation_key) {
|
|
|
|
ecdsa_free(invitation_key);
|
|
|
|
invitation_key = NULL;
|
|
|
|
}
|
|
|
|
|
2015-05-19 22:55:00 +00:00
|
|
|
snprintf(fname, sizeof fname, "%s" SLASH "invitations" SLASH "ed25519_key.priv", confbase);
|
Add an invitation protocol.
Using the tinc command, an administrator of an existing VPN can generate
invitations for new nodes. The invitation is a small URL that can easily
be copy&pasted into email or live chat. Another person can have tinc
automatically setup the necessary configuration files and exchange keys
with the server, by only using the invitation URL.
The invitation protocol uses temporary ECDSA keys. The invitation URL
consists of the hostname and port of the server, a hash of the server's
temporary ECDSA key and a cookie. When the client wants to accept an
invitation, it also creates a temporary ECDSA key, connects to the server
and says it wants to accept an invitation. Both sides exchange their
temporary keys. The client verifies that the server's key matches the hash
in the invitation URL. After setting up an SPTPS connection using the
temporary keys, the client gives the cookie to the server. If the cookie
is valid, the server sends the client an invitation file containing the
client's new name and a copy of the server's host config file. If everything
is ok, the client will generate a long-term ECDSA key and send it to the
server, which will add it to a new host config file for the client.
The invitation protocol currently allows multiple host config files to be
send from the server to the client. However, the client filters out
most configuration variables for its own host configuration file. In
particular, it only accepts Name, Mode, Broadcast, ConnectTo, Subnet and
AutoConnect. Also, at the moment no tinc-up script is generated.
When an invitation has succesfully been accepted, the client needs to start
the tinc daemon manually.
2013-05-29 16:31:10 +00:00
|
|
|
|
|
|
|
fp = fopen(fname, "r");
|
|
|
|
|
|
|
|
if(fp) {
|
|
|
|
invitation_key = ecdsa_read_pem_private_key(fp);
|
|
|
|
fclose(fp);
|
|
|
|
if(!invitation_key)
|
2014-05-18 18:47:04 +00:00
|
|
|
logger(DEBUG_ALWAYS, LOG_ERR, "Reading Ed25519 private key file `%s' failed", fname);
|
Add an invitation protocol.
Using the tinc command, an administrator of an existing VPN can generate
invitations for new nodes. The invitation is a small URL that can easily
be copy&pasted into email or live chat. Another person can have tinc
automatically setup the necessary configuration files and exchange keys
with the server, by only using the invitation URL.
The invitation protocol uses temporary ECDSA keys. The invitation URL
consists of the hostname and port of the server, a hash of the server's
temporary ECDSA key and a cookie. When the client wants to accept an
invitation, it also creates a temporary ECDSA key, connects to the server
and says it wants to accept an invitation. Both sides exchange their
temporary keys. The client verifies that the server's key matches the hash
in the invitation URL. After setting up an SPTPS connection using the
temporary keys, the client gives the cookie to the server. If the cookie
is valid, the server sends the client an invitation file containing the
client's new name and a copy of the server's host config file. If everything
is ok, the client will generate a long-term ECDSA key and send it to the
server, which will add it to a new host config file for the client.
The invitation protocol currently allows multiple host config files to be
send from the server to the client. However, the client filters out
most configuration variables for its own host configuration file. In
particular, it only accepts Name, Mode, Broadcast, ConnectTo, Subnet and
AutoConnect. Also, at the moment no tinc-up script is generated.
When an invitation has succesfully been accepted, the client needs to start
the tinc daemon manually.
2013-05-29 16:31:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return invitation_key;
|
|
|
|
}
|
|
|
|
|
2014-12-29 21:57:18 +00:00
|
|
|
#ifndef DISABLE_LEGACY
|
2011-05-28 01:56:06 +00:00
|
|
|
static bool read_rsa_private_key(void) {
|
2002-09-09 21:25:28 +00:00
|
|
|
FILE *fp;
|
2008-12-11 14:44:44 +00:00
|
|
|
char *fname;
|
|
|
|
char *n, *d;
|
2002-09-09 21:25:28 +00:00
|
|
|
|
2008-12-11 14:44:44 +00:00
|
|
|
/* First, check for simple PrivateKey statement */
|
|
|
|
|
|
|
|
if(get_config_string(lookup_config(config_tree, "PrivateKey"), &d)) {
|
2010-11-12 15:15:29 +00:00
|
|
|
if(!get_config_string(lookup_config(config_tree, "PublicKey"), &n)) {
|
2012-02-26 17:37:36 +00:00
|
|
|
logger(DEBUG_ALWAYS, LOG_ERR, "PrivateKey used but no PublicKey found!");
|
2008-12-11 14:44:44 +00:00
|
|
|
free(d);
|
2007-05-23 13:45:49 +00:00
|
|
|
return false;
|
|
|
|
}
|
2013-05-01 15:17:22 +00:00
|
|
|
myself->connection->rsa = rsa_set_hex_private_key(n, "FFFF", d);
|
2008-12-11 14:44:44 +00:00
|
|
|
free(n);
|
|
|
|
free(d);
|
2013-05-01 15:17:22 +00:00
|
|
|
return myself->connection->rsa;
|
2007-05-23 13:45:49 +00:00
|
|
|
}
|
|
|
|
|
2008-12-11 14:44:44 +00:00
|
|
|
/* Else, check for PrivateKeyFile statement and read it */
|
|
|
|
|
2002-09-09 21:25:28 +00:00
|
|
|
if(!get_config_string(lookup_config(config_tree, "PrivateKeyFile"), &fname))
|
2012-07-21 14:26:55 +00:00
|
|
|
xasprintf(&fname, "%s" SLASH "rsa_key.priv", confbase);
|
2002-09-09 21:25:28 +00:00
|
|
|
|
2003-08-08 22:11:54 +00:00
|
|
|
fp = fopen(fname, "r");
|
2002-09-09 21:25:28 +00:00
|
|
|
|
2003-08-08 22:11:54 +00:00
|
|
|
if(!fp) {
|
2012-02-26 17:37:36 +00:00
|
|
|
logger(DEBUG_ALWAYS, LOG_ERR, "Error reading RSA private key file `%s': %s",
|
2003-08-08 22:11:54 +00:00
|
|
|
fname, strerror(errno));
|
2014-12-26 13:38:01 +00:00
|
|
|
if(errno == ENOENT)
|
|
|
|
logger(DEBUG_ALWAYS, LOG_INFO, "Create an RSA keypair with `tinc -n %s generate-rsa-keys'.", netname ?: ".");
|
2003-08-08 22:11:54 +00:00
|
|
|
free(fname);
|
|
|
|
return false;
|
|
|
|
}
|
2002-09-09 21:25:28 +00:00
|
|
|
|
2003-08-08 22:11:54 +00:00
|
|
|
#if !defined(HAVE_MINGW) && !defined(HAVE_CYGWIN)
|
2008-12-11 14:44:44 +00:00
|
|
|
struct stat s;
|
|
|
|
|
2003-08-08 22:11:54 +00:00
|
|
|
if(fstat(fileno(fp), &s)) {
|
2012-02-26 17:37:36 +00:00
|
|
|
logger(DEBUG_ALWAYS, LOG_ERR, "Could not stat RSA private key file `%s': %s'", fname, strerror(errno));
|
2002-09-09 21:25:28 +00:00
|
|
|
free(fname);
|
2003-08-08 22:11:54 +00:00
|
|
|
return false;
|
|
|
|
}
|
2002-09-09 21:25:28 +00:00
|
|
|
|
2003-08-14 14:21:35 +00:00
|
|
|
if(s.st_mode & ~0100700)
|
2012-02-26 17:37:36 +00:00
|
|
|
logger(DEBUG_ALWAYS, LOG_WARNING, "Warning: insecure file permissions for RSA private key file `%s'!", fname);
|
2003-08-08 22:11:54 +00:00
|
|
|
#endif
|
2002-09-09 21:25:28 +00:00
|
|
|
|
2013-05-01 15:17:22 +00:00
|
|
|
myself->connection->rsa = rsa_read_pem_private_key(fp);
|
2003-08-08 22:11:54 +00:00
|
|
|
fclose(fp);
|
|
|
|
|
2013-05-01 15:17:22 +00:00
|
|
|
if(!myself->connection->rsa)
|
2012-02-26 17:37:36 +00:00
|
|
|
logger(DEBUG_ALWAYS, LOG_ERR, "Reading RSA private key file `%s' failed: %s", fname, strerror(errno));
|
2002-09-09 21:25:28 +00:00
|
|
|
free(fname);
|
2013-05-01 15:17:22 +00:00
|
|
|
return myself->connection->rsa;
|
2002-02-18 16:25:19 +00:00
|
|
|
}
|
2014-12-29 21:57:18 +00:00
|
|
|
#endif
|
2002-02-18 16:25:19 +00:00
|
|
|
|
2012-11-29 11:28:23 +00:00
|
|
|
static timeout_t keyexpire_timeout;
|
2015-04-11 13:27:28 +00:00
|
|
|
static timeout_t edgeupdate_timeout;
|
2007-05-17 23:57:48 +00:00
|
|
|
|
2012-11-29 11:28:23 +00:00
|
|
|
static void keyexpire_handler(void *data) {
|
2007-05-17 23:57:48 +00:00
|
|
|
regenerate_key();
|
2012-11-29 11:28:23 +00:00
|
|
|
timeout_set(data, &(struct timeval){keylifetime, rand() % 100000});
|
2007-05-17 23:57:48 +00:00
|
|
|
}
|
|
|
|
|
2015-04-11 13:27:28 +00:00
|
|
|
static void edgeupdate_handler(void *data) {
|
|
|
|
update_edge_weight();
|
|
|
|
timeout_set(data, &(struct timeval){edgeupdateinterval, rand() % 100000});
|
|
|
|
}
|
|
|
|
|
2011-05-28 01:57:20 +00:00
|
|
|
void regenerate_key(void) {
|
2012-11-29 11:28:23 +00:00
|
|
|
logger(DEBUG_STATUS, LOG_INFO, "Expiring symmetric keys");
|
|
|
|
send_key_changed();
|
2015-01-10 21:26:33 +00:00
|
|
|
for splay_each(node_t, n, node_tree)
|
|
|
|
n->status.validkey_in = false;
|
2007-05-17 23:57:48 +00:00
|
|
|
}
|
|
|
|
|
2015-04-11 13:27:28 +00:00
|
|
|
void update_edge_weight(void) {
|
|
|
|
edge_t *t = NULL;
|
|
|
|
logger(DEBUG_STATUS, LOG_INFO, "Update edge weight");
|
|
|
|
|
|
|
|
for list_each(connection_t, c, connection_list) {
|
|
|
|
if (c->status.control || !c->edge)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (c->edge->avg_rtt) {
|
|
|
|
|
|
|
|
t = clone_edge(c->edge);
|
|
|
|
send_del_edge(c, c->edge);
|
|
|
|
edge_del(c->edge);
|
|
|
|
/* avg_rtt is in ms */
|
|
|
|
t->weight = t->avg_rtt*10;
|
|
|
|
c->edge = t;
|
|
|
|
edge_add(t);
|
|
|
|
send_add_edge(c, t);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-03-01 22:44:56 +00:00
|
|
|
/*
|
|
|
|
Read Subnets from all host config files
|
|
|
|
*/
|
2010-04-10 22:50:42 +00:00
|
|
|
void load_all_subnets(void) {
|
2010-03-01 22:44:56 +00:00
|
|
|
DIR *dir;
|
|
|
|
struct dirent *ent;
|
2015-05-19 22:55:00 +00:00
|
|
|
char dname[PATH_MAX];
|
2010-03-01 22:44:56 +00:00
|
|
|
|
2015-05-19 22:55:00 +00:00
|
|
|
snprintf(dname, sizeof dname, "%s" SLASH "hosts", confbase);
|
2010-03-01 22:44:56 +00:00
|
|
|
dir = opendir(dname);
|
|
|
|
if(!dir) {
|
2012-02-26 17:37:36 +00:00
|
|
|
logger(DEBUG_ALWAYS, LOG_ERR, "Could not open %s: %s", dname, strerror(errno));
|
2010-03-01 22:44:56 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
while((ent = readdir(dir))) {
|
|
|
|
if(!check_id(ent->d_name))
|
|
|
|
continue;
|
|
|
|
|
2012-10-07 22:35:38 +00:00
|
|
|
node_t *n = lookup_node(ent->d_name);
|
2010-03-01 22:44:56 +00:00
|
|
|
#ifdef _DIRENT_HAVE_D_TYPE
|
|
|
|
//if(ent->d_type != DT_REG)
|
|
|
|
// continue;
|
|
|
|
#endif
|
|
|
|
|
2012-10-07 22:35:38 +00:00
|
|
|
splay_tree_t *config_tree;
|
2010-03-01 22:44:56 +00:00
|
|
|
init_configuration(&config_tree);
|
2012-03-07 09:40:06 +00:00
|
|
|
read_config_options(config_tree, ent->d_name);
|
2013-05-11 12:05:28 +00:00
|
|
|
read_host_config(config_tree, ent->d_name);
|
2010-03-01 22:44:56 +00:00
|
|
|
|
2010-04-11 02:35:16 +00:00
|
|
|
if(!n) {
|
|
|
|
n = new_node();
|
|
|
|
n->name = xstrdup(ent->d_name);
|
|
|
|
node_add(n);
|
|
|
|
}
|
2010-03-01 22:44:56 +00:00
|
|
|
|
2012-10-07 22:35:38 +00:00
|
|
|
for(config_t *cfg = lookup_config(config_tree, "Subnet"); cfg; cfg = lookup_config_next(config_tree, cfg)) {
|
|
|
|
subnet_t *s, *s2;
|
|
|
|
|
2010-03-01 22:44:56 +00:00
|
|
|
if(!get_config_subnet(cfg, &s))
|
|
|
|
continue;
|
|
|
|
|
2010-04-10 22:50:42 +00:00
|
|
|
if((s2 = lookup_subnet(n, s))) {
|
|
|
|
s2->expires = -1;
|
|
|
|
} else {
|
|
|
|
subnet_add(n, s);
|
|
|
|
}
|
2010-03-01 22:44:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
exit_configuration(&config_tree);
|
|
|
|
}
|
|
|
|
|
|
|
|
closedir(dir);
|
|
|
|
}
|
|
|
|
|
2012-10-21 15:35:13 +00:00
|
|
|
void load_all_nodes(void) {
|
|
|
|
DIR *dir;
|
|
|
|
struct dirent *ent;
|
2015-05-19 22:55:00 +00:00
|
|
|
char dname[PATH_MAX];
|
2012-10-21 15:35:13 +00:00
|
|
|
|
2015-05-19 22:55:00 +00:00
|
|
|
snprintf(dname, sizeof dname, "%s" SLASH "hosts", confbase);
|
2012-10-21 15:35:13 +00:00
|
|
|
dir = opendir(dname);
|
|
|
|
if(!dir) {
|
|
|
|
logger(DEBUG_ALWAYS, LOG_ERR, "Could not open %s: %s", dname, strerror(errno));
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-03-29 15:45:25 +00:00
|
|
|
char *get_name(void) {
|
|
|
|
char *name = NULL;
|
2014-08-25 02:49:27 +00:00
|
|
|
char *returned_name;
|
2012-03-29 15:45:25 +00:00
|
|
|
|
|
|
|
get_config_string(lookup_config(config_tree, "Name"), &name);
|
|
|
|
|
|
|
|
if(!name)
|
|
|
|
return NULL;
|
|
|
|
|
2014-08-25 02:49:27 +00:00
|
|
|
returned_name = replace_name(name);
|
|
|
|
free(name);
|
|
|
|
return returned_name;
|
2012-03-29 15:45:25 +00:00
|
|
|
}
|
|
|
|
|
2012-07-20 17:59:47 +00:00
|
|
|
bool setup_myself_reloadable(void) {
|
2012-04-18 21:19:40 +00:00
|
|
|
char *proxy = NULL;
|
2012-10-07 15:53:23 +00:00
|
|
|
char *rmode = NULL;
|
|
|
|
char *fmode = NULL;
|
|
|
|
char *bmode = NULL;
|
2012-07-20 17:59:47 +00:00
|
|
|
char *afname = NULL;
|
2012-04-18 21:19:40 +00:00
|
|
|
char *space;
|
2003-07-22 20:55:21 +00:00
|
|
|
bool choice;
|
2010-04-03 08:46:45 +00:00
|
|
|
|
2012-10-07 15:53:23 +00:00
|
|
|
free(scriptinterpreter);
|
|
|
|
scriptinterpreter = NULL;
|
|
|
|
get_config_string(lookup_config(config_tree, "ScriptsInterpreter"), &scriptinterpreter);
|
|
|
|
|
2012-10-10 15:17:49 +00:00
|
|
|
|
2012-10-07 15:53:23 +00:00
|
|
|
free(scriptextension);
|
|
|
|
if(!get_config_string(lookup_config(config_tree, "ScriptsExtension"), &scriptextension))
|
|
|
|
scriptextension = xstrdup("");
|
|
|
|
|
2012-04-18 21:19:40 +00:00
|
|
|
get_config_string(lookup_config(config_tree, "Proxy"), &proxy);
|
|
|
|
if(proxy) {
|
|
|
|
if((space = strchr(proxy, ' ')))
|
|
|
|
*space++ = 0;
|
|
|
|
|
|
|
|
if(!strcasecmp(proxy, "none")) {
|
|
|
|
proxytype = PROXY_NONE;
|
|
|
|
} else if(!strcasecmp(proxy, "socks4")) {
|
|
|
|
proxytype = PROXY_SOCKS4;
|
|
|
|
} else if(!strcasecmp(proxy, "socks4a")) {
|
|
|
|
proxytype = PROXY_SOCKS4A;
|
|
|
|
} else if(!strcasecmp(proxy, "socks5")) {
|
|
|
|
proxytype = PROXY_SOCKS5;
|
|
|
|
} else if(!strcasecmp(proxy, "http")) {
|
|
|
|
proxytype = PROXY_HTTP;
|
|
|
|
} else if(!strcasecmp(proxy, "exec")) {
|
|
|
|
proxytype = PROXY_EXEC;
|
|
|
|
} else {
|
2012-06-26 11:24:20 +00:00
|
|
|
logger(DEBUG_ALWAYS, LOG_ERR, "Unknown proxy type %s!", proxy);
|
2012-04-18 21:19:40 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch(proxytype) {
|
|
|
|
case PROXY_NONE:
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PROXY_EXEC:
|
|
|
|
if(!space || !*space) {
|
2012-06-26 11:24:20 +00:00
|
|
|
logger(DEBUG_ALWAYS, LOG_ERR, "Argument expected for proxy type exec!");
|
2012-04-18 21:19:40 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
proxyhost = xstrdup(space);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PROXY_SOCKS4:
|
|
|
|
case PROXY_SOCKS4A:
|
|
|
|
case PROXY_SOCKS5:
|
|
|
|
case PROXY_HTTP:
|
|
|
|
proxyhost = space;
|
|
|
|
if(space && (space = strchr(space, ' ')))
|
|
|
|
*space++ = 0, proxyport = space;
|
|
|
|
if(space && (space = strchr(space, ' ')))
|
|
|
|
*space++ = 0, proxyuser = space;
|
|
|
|
if(space && (space = strchr(space, ' ')))
|
|
|
|
*space++ = 0, proxypass = space;
|
|
|
|
if(!proxyhost || !*proxyhost || !proxyport || !*proxyport) {
|
2012-06-26 11:24:20 +00:00
|
|
|
logger(DEBUG_ALWAYS, LOG_ERR, "Host and port argument expected for proxy!");
|
2012-04-18 21:19:40 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
proxyhost = xstrdup(proxyhost);
|
|
|
|
proxyport = xstrdup(proxyport);
|
|
|
|
if(proxyuser && *proxyuser)
|
|
|
|
proxyuser = xstrdup(proxyuser);
|
|
|
|
if(proxypass && *proxypass)
|
2012-04-19 12:10:54 +00:00
|
|
|
proxypass = xstrdup(proxypass);
|
2012-04-18 21:19:40 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
free(proxy);
|
|
|
|
}
|
|
|
|
|
2003-12-20 19:47:53 +00:00
|
|
|
if(get_config_bool(lookup_config(config_tree, "IndirectData"), &choice) && choice)
|
|
|
|
myself->options |= OPTION_INDIRECT;
|
2002-09-09 21:25:28 +00:00
|
|
|
|
2003-12-20 19:47:53 +00:00
|
|
|
if(get_config_bool(lookup_config(config_tree, "TCPOnly"), &choice) && choice)
|
|
|
|
myself->options |= OPTION_TCPONLY;
|
2002-09-09 21:25:28 +00:00
|
|
|
|
|
|
|
if(myself->options & OPTION_TCPONLY)
|
|
|
|
myself->options |= OPTION_INDIRECT;
|
|
|
|
|
Add UDP discovery mechanism.
This adds a new mechanism by which tinc can determine if a node is
reachable via UDP. The new mechanism is currently redundant with the
PMTU discovery mechanism - that will be fixed in a future commit.
Conceptually, the UDP discovery mechanism works similarly to PMTU
discovery: it sends UDP probes (of minmtu size, to make sure the tunnel
is fully usable), and assumes UDP is usable if it gets replies. It
assumes UDP is broken if too much time has passed since the last reply.
The big difference with the current PMTU discovery mechanism, however,
is that UDP discovery probes are only triggered as part of the
packet TX path (through try_tx()). This is quite interesting, because
it means tinc will never send UDP pings more often than normal packets,
and most importantly, it will automatically stop sending pings as soon
as packets stop flowing, thereby nicely reducing network chatter.
Of course, there are small drawbacks in some edge cases: for example,
if a node only sends one packet every minute to another node, these
packets will only be sent over TCP, because the interval between packets
is too long for tinc to maintain the UDP tunnel. I consider this a
feature, not a bug: I believe it is appropriate to use TCP in scenarios
where traffic is negligible, so that we don't pollute the network with
pings just to maintain a UDP tunnel that's seeing negligible usage.
2014-12-29 10:34:39 +00:00
|
|
|
get_config_bool(lookup_config(config_tree, "UDPDiscovery"), &udp_discovery);
|
2015-01-03 10:05:57 +00:00
|
|
|
get_config_int(lookup_config(config_tree, "UDPDiscoveryKeepaliveInterval"), &udp_discovery_keepalive_interval);
|
Add UDP discovery mechanism.
This adds a new mechanism by which tinc can determine if a node is
reachable via UDP. The new mechanism is currently redundant with the
PMTU discovery mechanism - that will be fixed in a future commit.
Conceptually, the UDP discovery mechanism works similarly to PMTU
discovery: it sends UDP probes (of minmtu size, to make sure the tunnel
is fully usable), and assumes UDP is usable if it gets replies. It
assumes UDP is broken if too much time has passed since the last reply.
The big difference with the current PMTU discovery mechanism, however,
is that UDP discovery probes are only triggered as part of the
packet TX path (through try_tx()). This is quite interesting, because
it means tinc will never send UDP pings more often than normal packets,
and most importantly, it will automatically stop sending pings as soon
as packets stop flowing, thereby nicely reducing network chatter.
Of course, there are small drawbacks in some edge cases: for example,
if a node only sends one packet every minute to another node, these
packets will only be sent over TCP, because the interval between packets
is too long for tinc to maintain the UDP tunnel. I consider this a
feature, not a bug: I believe it is appropriate to use TCP in scenarios
where traffic is negligible, so that we don't pollute the network with
pings just to maintain a UDP tunnel that's seeing negligible usage.
2014-12-29 10:34:39 +00:00
|
|
|
get_config_int(lookup_config(config_tree, "UDPDiscoveryInterval"), &udp_discovery_interval);
|
|
|
|
get_config_int(lookup_config(config_tree, "UDPDiscoveryTimeout"), &udp_discovery_timeout);
|
|
|
|
|
2015-03-08 20:17:27 +00:00
|
|
|
get_config_int(lookup_config(config_tree, "MTUInfoInterval"), &mtu_info_interval);
|
2015-03-08 19:54:44 +00:00
|
|
|
get_config_int(lookup_config(config_tree, "UDPInfoInterval"), &udp_info_interval);
|
|
|
|
|
2010-03-02 21:55:24 +00:00
|
|
|
get_config_bool(lookup_config(config_tree, "DirectOnly"), &directonly);
|
2012-02-22 22:17:43 +00:00
|
|
|
get_config_bool(lookup_config(config_tree, "LocalDiscovery"), &localdiscovery);
|
2012-10-10 15:17:49 +00:00
|
|
|
|
2012-10-07 15:53:23 +00:00
|
|
|
if(get_config_string(lookup_config(config_tree, "Mode"), &rmode)) {
|
|
|
|
if(!strcasecmp(rmode, "router"))
|
2002-09-09 21:25:28 +00:00
|
|
|
routing_mode = RMODE_ROUTER;
|
2012-10-07 15:53:23 +00:00
|
|
|
else if(!strcasecmp(rmode, "switch"))
|
2002-09-09 21:25:28 +00:00
|
|
|
routing_mode = RMODE_SWITCH;
|
2012-10-07 15:53:23 +00:00
|
|
|
else if(!strcasecmp(rmode, "hub"))
|
2002-09-09 21:25:28 +00:00
|
|
|
routing_mode = RMODE_HUB;
|
|
|
|
else {
|
2012-02-26 17:37:36 +00:00
|
|
|
logger(DEBUG_ALWAYS, LOG_ERR, "Invalid routing mode!");
|
2003-07-22 20:55:21 +00:00
|
|
|
return false;
|
2002-09-09 21:25:28 +00:00
|
|
|
}
|
2012-10-07 15:53:23 +00:00
|
|
|
free(rmode);
|
2010-03-02 21:34:26 +00:00
|
|
|
}
|
2002-09-09 21:25:28 +00:00
|
|
|
|
2012-10-07 15:53:23 +00:00
|
|
|
if(get_config_string(lookup_config(config_tree, "Forwarding"), &fmode)) {
|
|
|
|
if(!strcasecmp(fmode, "off"))
|
2010-03-02 22:27:50 +00:00
|
|
|
forwarding_mode = FMODE_OFF;
|
2012-10-07 15:53:23 +00:00
|
|
|
else if(!strcasecmp(fmode, "internal"))
|
2010-03-02 22:27:50 +00:00
|
|
|
forwarding_mode = FMODE_INTERNAL;
|
2012-10-07 15:53:23 +00:00
|
|
|
else if(!strcasecmp(fmode, "kernel"))
|
2010-03-02 22:27:50 +00:00
|
|
|
forwarding_mode = FMODE_KERNEL;
|
2010-03-02 21:34:26 +00:00
|
|
|
else {
|
2012-02-26 17:37:36 +00:00
|
|
|
logger(DEBUG_ALWAYS, LOG_ERR, "Invalid forwarding mode!");
|
2010-03-02 21:34:26 +00:00
|
|
|
return false;
|
|
|
|
}
|
2012-10-07 15:53:23 +00:00
|
|
|
free(fmode);
|
2010-03-02 21:34:26 +00:00
|
|
|
}
|
2009-09-12 11:40:32 +00:00
|
|
|
|
2009-12-24 11:42:21 +00:00
|
|
|
choice = true;
|
|
|
|
get_config_bool(lookup_config(config_tree, "PMTUDiscovery"), &choice);
|
|
|
|
if(choice)
|
2009-09-12 11:40:32 +00:00
|
|
|
myself->options |= OPTION_PMTU_DISCOVERY;
|
2009-03-09 12:48:54 +00:00
|
|
|
|
2010-01-16 19:16:33 +00:00
|
|
|
choice = true;
|
|
|
|
get_config_bool(lookup_config(config_tree, "ClampMSS"), &choice);
|
|
|
|
if(choice)
|
|
|
|
myself->options |= OPTION_CLAMP_MSS;
|
|
|
|
|
2003-07-22 20:55:21 +00:00
|
|
|
get_config_bool(lookup_config(config_tree, "PriorityInheritance"), &priorityinheritance);
|
2012-02-20 15:34:02 +00:00
|
|
|
get_config_bool(lookup_config(config_tree, "DecrementTTL"), &decrement_ttl);
|
2012-10-07 15:53:23 +00:00
|
|
|
if(get_config_string(lookup_config(config_tree, "Broadcast"), &bmode)) {
|
|
|
|
if(!strcasecmp(bmode, "no"))
|
2012-04-15 23:57:25 +00:00
|
|
|
broadcast_mode = BMODE_NONE;
|
2012-10-07 15:53:23 +00:00
|
|
|
else if(!strcasecmp(bmode, "yes") || !strcasecmp(bmode, "mst"))
|
2012-04-15 23:57:25 +00:00
|
|
|
broadcast_mode = BMODE_MST;
|
2012-10-07 15:53:23 +00:00
|
|
|
else if(!strcasecmp(bmode, "direct"))
|
2012-04-15 23:57:25 +00:00
|
|
|
broadcast_mode = BMODE_DIRECT;
|
|
|
|
else {
|
2012-06-26 11:24:20 +00:00
|
|
|
logger(DEBUG_ALWAYS, LOG_ERR, "Invalid broadcast mode!");
|
2012-04-15 23:57:25 +00:00
|
|
|
return false;
|
|
|
|
}
|
2012-10-07 15:53:23 +00:00
|
|
|
free(bmode);
|
2012-04-15 23:57:25 +00:00
|
|
|
}
|
2003-07-22 20:55:21 +00:00
|
|
|
|
2014-06-29 13:15:58 +00:00
|
|
|
const char* const DEFAULT_BROADCAST_SUBNETS[] = { "ff:ff:ff:ff:ff:ff", "255.255.255.255", "224.0.0.0/4", "ff00::/8" };
|
2014-06-29 12:18:25 +00:00
|
|
|
for (size_t i = 0; i < sizeof(DEFAULT_BROADCAST_SUBNETS) / sizeof(*DEFAULT_BROADCAST_SUBNETS); i++) {
|
|
|
|
subnet_t *s = new_subnet();
|
|
|
|
if (!str2net(s, DEFAULT_BROADCAST_SUBNETS[i]))
|
|
|
|
abort();
|
|
|
|
subnet_add(NULL, s);
|
|
|
|
}
|
|
|
|
for (config_t* cfg = lookup_config(config_tree, "BroadcastSubnet"); cfg; cfg = lookup_config_next(config_tree, cfg)) {
|
|
|
|
subnet_t *s;
|
|
|
|
if (!get_config_subnet(cfg, &s))
|
|
|
|
continue;
|
|
|
|
subnet_add(NULL, s);
|
|
|
|
}
|
|
|
|
|
2002-03-01 15:14:29 +00:00
|
|
|
#if !defined(SOL_IP) || !defined(IP_TOS)
|
2002-09-09 21:25:28 +00:00
|
|
|
if(priorityinheritance)
|
2012-02-26 17:37:36 +00:00
|
|
|
logger(DEBUG_ALWAYS, LOG_WARNING, "%s not supported on this platform", "PriorityInheritance");
|
2002-03-01 15:14:29 +00:00
|
|
|
#endif
|
2002-03-01 12:25:58 +00:00
|
|
|
|
2002-09-09 21:25:28 +00:00
|
|
|
if(!get_config_int(lookup_config(config_tree, "MACExpire"), &macexpire))
|
|
|
|
macexpire = 600;
|
|
|
|
|
2003-12-07 14:28:39 +00:00
|
|
|
if(get_config_int(lookup_config(config_tree, "MaxTimeout"), &maxtimeout)) {
|
2002-09-09 21:25:28 +00:00
|
|
|
if(maxtimeout <= 0) {
|
2012-02-26 17:37:36 +00:00
|
|
|
logger(DEBUG_ALWAYS, LOG_ERR, "Bogus maximum timeout!");
|
2003-07-22 20:55:21 +00:00
|
|
|
return false;
|
2002-09-09 21:25:28 +00:00
|
|
|
}
|
|
|
|
} else
|
|
|
|
maxtimeout = 900;
|
|
|
|
|
2012-07-20 17:59:47 +00:00
|
|
|
if(get_config_string(lookup_config(config_tree, "AddressFamily"), &afname)) {
|
|
|
|
if(!strcasecmp(afname, "IPv4"))
|
|
|
|
addressfamily = AF_INET;
|
|
|
|
else if(!strcasecmp(afname, "IPv6"))
|
|
|
|
addressfamily = AF_INET6;
|
|
|
|
else if(!strcasecmp(afname, "any"))
|
|
|
|
addressfamily = AF_UNSPEC;
|
|
|
|
else {
|
|
|
|
logger(DEBUG_ALWAYS, LOG_ERR, "Invalid address family!");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
free(afname);
|
|
|
|
}
|
|
|
|
|
|
|
|
get_config_bool(lookup_config(config_tree, "Hostnames"), &hostnames);
|
|
|
|
|
|
|
|
if(!get_config_int(lookup_config(config_tree, "KeyExpire"), &keylifetime))
|
|
|
|
keylifetime = 3600;
|
|
|
|
|
2015-04-11 13:27:28 +00:00
|
|
|
if(!get_config_int(lookup_config(config_tree, "EdgeUpdateInterval"), &edgeupdateinterval))
|
|
|
|
edgeupdateinterval = 0;
|
|
|
|
|
2014-05-06 12:11:55 +00:00
|
|
|
config_t *cfg = lookup_config(config_tree, "AutoConnect");
|
|
|
|
if(cfg) {
|
|
|
|
if(!get_config_bool(cfg, &autoconnect)) {
|
|
|
|
// Some backwards compatibility with when this option was an int
|
|
|
|
int val = 0;
|
|
|
|
get_config_int(cfg, &val);
|
|
|
|
autoconnect = val;
|
|
|
|
}
|
|
|
|
}
|
2012-10-21 15:35:13 +00:00
|
|
|
|
2013-04-12 15:15:05 +00:00
|
|
|
get_config_bool(lookup_config(config_tree, "DisableBuggyPeers"), &disablebuggypeers);
|
|
|
|
|
Add an invitation protocol.
Using the tinc command, an administrator of an existing VPN can generate
invitations for new nodes. The invitation is a small URL that can easily
be copy&pasted into email or live chat. Another person can have tinc
automatically setup the necessary configuration files and exchange keys
with the server, by only using the invitation URL.
The invitation protocol uses temporary ECDSA keys. The invitation URL
consists of the hostname and port of the server, a hash of the server's
temporary ECDSA key and a cookie. When the client wants to accept an
invitation, it also creates a temporary ECDSA key, connects to the server
and says it wants to accept an invitation. Both sides exchange their
temporary keys. The client verifies that the server's key matches the hash
in the invitation URL. After setting up an SPTPS connection using the
temporary keys, the client gives the cookie to the server. If the cookie
is valid, the server sends the client an invitation file containing the
client's new name and a copy of the server's host config file. If everything
is ok, the client will generate a long-term ECDSA key and send it to the
server, which will add it to a new host config file for the client.
The invitation protocol currently allows multiple host config files to be
send from the server to the client. However, the client filters out
most configuration variables for its own host configuration file. In
particular, it only accepts Name, Mode, Broadcast, ConnectTo, Subnet and
AutoConnect. Also, at the moment no tinc-up script is generated.
When an invitation has succesfully been accepted, the client needs to start
the tinc daemon manually.
2013-05-29 16:31:10 +00:00
|
|
|
read_invitation_key();
|
|
|
|
|
2012-07-20 17:59:47 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-01-20 20:19:13 +00:00
|
|
|
/*
|
|
|
|
Add listening sockets.
|
|
|
|
*/
|
|
|
|
static bool add_listen_address(char *address, bool bindto) {
|
|
|
|
char *port = myport;
|
|
|
|
|
|
|
|
if(address) {
|
|
|
|
char *space = strchr(address, ' ');
|
|
|
|
if(space) {
|
|
|
|
*space++ = 0;
|
|
|
|
port = space;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!strcmp(address, "*"))
|
|
|
|
*address = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct addrinfo *ai, hint = {0};
|
|
|
|
hint.ai_family = addressfamily;
|
|
|
|
hint.ai_socktype = SOCK_STREAM;
|
|
|
|
hint.ai_protocol = IPPROTO_TCP;
|
|
|
|
hint.ai_flags = AI_PASSIVE;
|
|
|
|
|
2015-03-14 16:17:32 +00:00
|
|
|
#if HAVE_DECL_RES_INIT
|
2015-02-09 14:16:36 +00:00
|
|
|
res_init();
|
|
|
|
#endif
|
2014-01-20 20:19:13 +00:00
|
|
|
int err = getaddrinfo(address && *address ? address : NULL, port, &hint, &ai);
|
|
|
|
free(address);
|
|
|
|
|
|
|
|
if(err || !ai) {
|
|
|
|
logger(DEBUG_ALWAYS, LOG_ERR, "System call `%s' failed: %s", "getaddrinfo", err == EAI_SYSTEM ? strerror(err) : gai_strerror(err));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
for(struct addrinfo *aip = ai; aip; aip = aip->ai_next) {
|
|
|
|
// Ignore duplicate addresses
|
|
|
|
bool found = false;
|
|
|
|
|
|
|
|
for(int i = 0; i < listen_sockets; i++)
|
|
|
|
if(!memcmp(&listen_socket[i].sa, aip->ai_addr, aip->ai_addrlen)) {
|
|
|
|
found = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(found)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if(listen_sockets >= MAXSOCKETS) {
|
|
|
|
logger(DEBUG_ALWAYS, LOG_ERR, "Too many listening sockets");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
int tcp_fd = setup_listen_socket((sockaddr_t *) aip->ai_addr);
|
|
|
|
|
|
|
|
if(tcp_fd < 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
int udp_fd = setup_vpn_in_socket((sockaddr_t *) aip->ai_addr);
|
|
|
|
|
|
|
|
if(tcp_fd < 0) {
|
|
|
|
close(tcp_fd);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
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) {
|
|
|
|
char *hostname = sockaddr2hostname((sockaddr_t *) aip->ai_addr);
|
|
|
|
logger(DEBUG_CONNECTIONS, LOG_NOTICE, "Listening on %s", hostname);
|
|
|
|
free(hostname);
|
|
|
|
}
|
|
|
|
|
|
|
|
listen_socket[listen_sockets].bindto = bindto;
|
|
|
|
memcpy(&listen_socket[listen_sockets].sa, aip->ai_addr, aip->ai_addrlen);
|
|
|
|
listen_sockets++;
|
|
|
|
}
|
|
|
|
|
|
|
|
freeaddrinfo(ai);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-06-22 09:48:34 +00:00
|
|
|
void device_enable(void) {
|
2014-06-22 09:48:34 +00:00
|
|
|
if (devops.enable)
|
|
|
|
devops.enable();
|
|
|
|
|
2014-06-22 09:48:34 +00:00
|
|
|
/* Run tinc-up script to further initialize the tap interface */
|
|
|
|
|
|
|
|
char *envp[5] = {NULL};
|
|
|
|
xasprintf(&envp[0], "NETNAME=%s", netname ? : "");
|
|
|
|
xasprintf(&envp[1], "DEVICE=%s", device ? : "");
|
|
|
|
xasprintf(&envp[2], "INTERFACE=%s", iface ? : "");
|
2014-12-24 16:06:05 +00:00
|
|
|
xasprintf(&envp[3], "NAME=%s", myname);
|
2014-06-22 09:48:34 +00:00
|
|
|
|
|
|
|
execute_script("tinc-up", envp);
|
|
|
|
|
|
|
|
for(int i = 0; i < 4; i++)
|
|
|
|
free(envp[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
void device_disable(void) {
|
|
|
|
char *envp[5] = {NULL};
|
|
|
|
xasprintf(&envp[0], "NETNAME=%s", netname ? : "");
|
|
|
|
xasprintf(&envp[1], "DEVICE=%s", device ? : "");
|
|
|
|
xasprintf(&envp[2], "INTERFACE=%s", iface ? : "");
|
2014-12-24 16:06:05 +00:00
|
|
|
xasprintf(&envp[3], "NAME=%s", myname);
|
2014-06-22 09:48:34 +00:00
|
|
|
|
|
|
|
execute_script("tinc-down", envp);
|
|
|
|
|
|
|
|
for(int i = 0; i < 4; i++)
|
|
|
|
free(envp[i]);
|
2014-06-22 09:48:34 +00:00
|
|
|
|
|
|
|
if (devops.disable)
|
|
|
|
devops.disable();
|
2014-06-22 09:48:34 +00:00
|
|
|
}
|
|
|
|
|
2012-07-20 17:59:47 +00:00
|
|
|
/*
|
|
|
|
Configure node_t myself and set up the local sockets (listen only)
|
|
|
|
*/
|
|
|
|
static bool setup_myself(void) {
|
|
|
|
char *name, *hostname, *cipher, *digest, *type;
|
|
|
|
char *address = NULL;
|
2013-12-05 14:01:30 +00:00
|
|
|
bool port_specified = false;
|
2012-07-20 17:59:47 +00:00
|
|
|
|
2012-09-30 20:43:48 +00:00
|
|
|
if(!(name = get_name())) {
|
|
|
|
logger(DEBUG_ALWAYS, LOG_ERR, "Name for tinc daemon required!");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2014-12-24 16:06:05 +00:00
|
|
|
myname = xstrdup(name);
|
2012-07-20 17:59:47 +00:00
|
|
|
myself = new_node();
|
|
|
|
myself->connection = new_connection();
|
2012-09-30 20:43:48 +00:00
|
|
|
myself->name = name;
|
|
|
|
myself->connection->name = xstrdup(name);
|
2013-05-11 12:05:28 +00:00
|
|
|
read_host_config(config_tree, name);
|
2012-07-20 17:59:47 +00:00
|
|
|
|
2012-09-26 21:18:32 +00:00
|
|
|
if(!get_config_string(lookup_config(config_tree, "Port"), &myport))
|
2012-10-01 08:39:15 +00:00
|
|
|
myport = xstrdup("655");
|
2013-12-05 14:01:30 +00:00
|
|
|
else
|
|
|
|
port_specified = true;
|
2012-07-20 17:59:47 +00:00
|
|
|
|
|
|
|
myself->connection->options = 0;
|
|
|
|
myself->connection->protocol_major = PROT_MAJOR;
|
|
|
|
myself->connection->protocol_minor = PROT_MINOR;
|
|
|
|
|
|
|
|
myself->options |= PROT_MINOR << 24;
|
|
|
|
|
2014-12-29 21:57:18 +00:00
|
|
|
#ifdef DISABLE_LEGACY
|
|
|
|
experimental = read_ecdsa_private_key();
|
|
|
|
if(!experimental) {
|
|
|
|
logger(DEBUG_ALWAYS, LOG_ERR, "No private key available, cannot start tinc!");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
#else
|
2013-12-08 20:06:03 +00:00
|
|
|
if(!get_config_bool(lookup_config(config_tree, "ExperimentalProtocol"), &experimental)) {
|
|
|
|
experimental = read_ecdsa_private_key();
|
2013-12-08 20:37:56 +00:00
|
|
|
if(!experimental)
|
|
|
|
logger(DEBUG_ALWAYS, LOG_WARNING, "Support for SPTPS disabled.");
|
2013-12-08 20:06:03 +00:00
|
|
|
} else {
|
|
|
|
if(experimental && !read_ecdsa_private_key())
|
|
|
|
return false;
|
|
|
|
}
|
2012-07-20 17:59:47 +00:00
|
|
|
|
2014-12-26 13:38:01 +00:00
|
|
|
if(!read_rsa_private_key()) {
|
|
|
|
if(experimental) {
|
|
|
|
logger(DEBUG_ALWAYS, LOG_WARNING, "Support for legacy protocol disabled.");
|
|
|
|
} else {
|
|
|
|
logger(DEBUG_ALWAYS, LOG_ERR, "No private keys available, cannot start tinc!");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2014-12-29 21:57:18 +00:00
|
|
|
#endif
|
2012-07-20 17:59:47 +00:00
|
|
|
|
2013-12-05 14:01:30 +00:00
|
|
|
/* Ensure myport is numeric */
|
|
|
|
|
2012-07-20 17:59:47 +00:00
|
|
|
if(!atoi(myport)) {
|
|
|
|
struct addrinfo *ai = str2addrinfo("localhost", myport, SOCK_DGRAM);
|
|
|
|
sockaddr_t sa;
|
|
|
|
if(!ai || !ai->ai_addr)
|
|
|
|
return false;
|
|
|
|
free(myport);
|
|
|
|
memcpy(&sa, ai->ai_addr, ai->ai_addrlen);
|
|
|
|
sockaddr2str(&sa, NULL, &myport);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Read in all the subnets specified in the host configuration file */
|
|
|
|
|
2012-10-07 22:35:38 +00:00
|
|
|
for(config_t *cfg = lookup_config(config_tree, "Subnet"); cfg; cfg = lookup_config_next(config_tree, cfg)) {
|
|
|
|
subnet_t *subnet;
|
2012-07-20 17:59:47 +00:00
|
|
|
|
|
|
|
if(!get_config_subnet(cfg, &subnet))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
subnet_add(myself, subnet);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check some options */
|
|
|
|
|
|
|
|
if(!setup_myself_reloadable())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
get_config_bool(lookup_config(config_tree, "StrictSubnets"), &strictsubnets);
|
|
|
|
get_config_bool(lookup_config(config_tree, "TunnelServer"), &tunnelserver);
|
|
|
|
strictsubnets |= tunnelserver;
|
|
|
|
|
2013-07-11 21:38:38 +00:00
|
|
|
if(get_config_int(lookup_config(config_tree, "MaxConnectionBurst"), &max_connection_burst)) {
|
|
|
|
if(max_connection_burst <= 0) {
|
|
|
|
logger(DEBUG_ALWAYS, LOG_ERR, "MaxConnectionBurst cannot be negative!");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2012-07-20 17:59:47 +00:00
|
|
|
|
2010-11-13 18:05:49 +00:00
|
|
|
if(get_config_int(lookup_config(config_tree, "UDPRcvBuf"), &udp_rcvbuf)) {
|
Set the default for UDPRcvBuf and UDPSndBuf to 1M.
It may not be obvious, but due to the way tinc operates (single-threaded
control loop with no intermediate packet buffer), UDP send and receive
buffers can have a massive impact on performance. It is therefore of
paramount importance that the buffers be large enough to prevent packet
drops that could occur while tinc is processing a packet.
Leaving that value to the OS default could be reasonable if we weren't
relying on it so much. Instead, this makes performance somewhat
unpredictable.
In practice, the worst case scenario occurs on Windows, where Microsoft
had the brillant idea of making the buffers 8K in size by default, no
matter what the link speed is. Considering that 8K flies past in a
matter of microseconds on >1G links, this is extremely inappropriate. On
these systems, changing the buffer size to 1M results in *obscene*
raw throughput improvements; I have observed a 10X jump from 40 Mbit/s
to 400 Mbit/s on my system.
In this commit, we stop trusting the OS to get this right and we use a
fixed 1M value instead, which should be enough for <=1G links.
2015-03-15 17:50:53 +00:00
|
|
|
if(udp_rcvbuf < 0) {
|
2012-02-26 17:37:36 +00:00
|
|
|
logger(DEBUG_ALWAYS, LOG_ERR, "UDPRcvBuf cannot be negative!");
|
2010-11-13 18:05:49 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(get_config_int(lookup_config(config_tree, "UDPSndBuf"), &udp_sndbuf)) {
|
Set the default for UDPRcvBuf and UDPSndBuf to 1M.
It may not be obvious, but due to the way tinc operates (single-threaded
control loop with no intermediate packet buffer), UDP send and receive
buffers can have a massive impact on performance. It is therefore of
paramount importance that the buffers be large enough to prevent packet
drops that could occur while tinc is processing a packet.
Leaving that value to the OS default could be reasonable if we weren't
relying on it so much. Instead, this makes performance somewhat
unpredictable.
In practice, the worst case scenario occurs on Windows, where Microsoft
had the brillant idea of making the buffers 8K in size by default, no
matter what the link speed is. Considering that 8K flies past in a
matter of microseconds on >1G links, this is extremely inappropriate. On
these systems, changing the buffer size to 1M results in *obscene*
raw throughput improvements; I have observed a 10X jump from 40 Mbit/s
to 400 Mbit/s on my system.
In this commit, we stop trusting the OS to get this right and we use a
fixed 1M value instead, which should be enough for <=1G links.
2015-03-15 17:50:53 +00:00
|
|
|
if(udp_sndbuf < 0) {
|
2012-02-26 17:37:36 +00:00
|
|
|
logger(DEBUG_ALWAYS, LOG_ERR, "UDPSndBuf cannot be negative!");
|
2010-11-13 18:05:49 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-10-07 22:35:38 +00:00
|
|
|
int replaywin_int;
|
2010-11-13 18:05:50 +00:00
|
|
|
if(get_config_int(lookup_config(config_tree, "ReplayWindow"), &replaywin_int)) {
|
|
|
|
if(replaywin_int < 0) {
|
2012-02-26 17:37:36 +00:00
|
|
|
logger(DEBUG_ALWAYS, LOG_ERR, "ReplayWindow cannot be negative!");
|
2010-11-13 18:05:50 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
replaywin = (unsigned)replaywin_int;
|
2012-10-14 12:48:35 +00:00
|
|
|
sptps_replaywin = replaywin;
|
2010-11-13 18:05:50 +00:00
|
|
|
}
|
|
|
|
|
2014-12-29 21:57:18 +00:00
|
|
|
#ifndef DISABLE_LEGACY
|
2002-09-09 21:25:28 +00:00
|
|
|
/* Generate packet encryption key */
|
|
|
|
|
2010-11-12 15:15:29 +00:00
|
|
|
if(!get_config_string(lookup_config(config_tree, "Cipher"), &cipher))
|
2009-11-02 13:24:27 +00:00
|
|
|
cipher = xstrdup("blowfish");
|
2002-09-09 21:25:28 +00:00
|
|
|
|
2013-11-28 13:19:55 +00:00
|
|
|
if(!strcasecmp(cipher, "none")) {
|
|
|
|
myself->incipher = NULL;
|
|
|
|
} else if(!(myself->incipher = cipher_open_by_name(cipher))) {
|
2012-02-26 17:37:36 +00:00
|
|
|
logger(DEBUG_ALWAYS, LOG_ERR, "Unrecognized cipher type!");
|
2008-12-11 14:44:44 +00:00
|
|
|
return false;
|
|
|
|
}
|
2002-09-09 21:25:28 +00:00
|
|
|
|
2012-10-09 14:27:28 +00:00
|
|
|
free(cipher);
|
|
|
|
|
2012-11-29 11:28:23 +00:00
|
|
|
timeout_add(&keyexpire_timeout, keyexpire_handler, &keyexpire_timeout, &(struct timeval){keylifetime, rand() % 100000});
|
2008-12-11 14:44:44 +00:00
|
|
|
|
2015-04-11 13:27:28 +00:00
|
|
|
if (edgeupdateinterval)
|
|
|
|
timeout_add(&edgeupdate_timeout, edgeupdate_handler, &edgeupdate_timeout, &(struct timeval){edgeupdateinterval, rand() % 100000});
|
|
|
|
|
2002-09-09 21:25:28 +00:00
|
|
|
/* Check if we want to use message authentication codes... */
|
|
|
|
|
2009-06-06 17:04:04 +00:00
|
|
|
int maclength = 4;
|
2010-11-12 15:15:29 +00:00
|
|
|
get_config_int(lookup_config(config_tree, "MACLength"), &maclength);
|
2009-06-06 17:04:04 +00:00
|
|
|
|
|
|
|
if(maclength < 0) {
|
2012-02-26 17:37:36 +00:00
|
|
|
logger(DEBUG_ALWAYS, LOG_ERR, "Bogus MAC length!");
|
2008-12-11 14:44:44 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2011-12-26 22:11:27 +00:00
|
|
|
if(!get_config_string(lookup_config(config_tree, "Digest"), &digest))
|
|
|
|
digest = xstrdup("sha1");
|
|
|
|
|
2013-11-28 13:19:55 +00:00
|
|
|
if(!strcasecmp(digest, "none")) {
|
|
|
|
myself->indigest = NULL;
|
|
|
|
} else if(!(myself->indigest = digest_open_by_name(digest, maclength))) {
|
2012-02-26 17:37:36 +00:00
|
|
|
logger(DEBUG_ALWAYS, LOG_ERR, "Unrecognized digest type!");
|
2009-06-06 17:04:04 +00:00
|
|
|
return false;
|
2008-12-11 14:44:44 +00:00
|
|
|
}
|
2002-09-09 21:25:28 +00:00
|
|
|
|
2012-10-09 14:27:28 +00:00
|
|
|
free(digest);
|
2014-12-29 21:57:18 +00:00
|
|
|
#endif
|
2012-10-09 14:27:28 +00:00
|
|
|
|
2002-09-09 21:25:28 +00:00
|
|
|
/* Compression */
|
|
|
|
|
2010-10-22 10:47:12 +00:00
|
|
|
if(get_config_int(lookup_config(config_tree, "Compression"), &myself->incompression)) {
|
2009-04-02 23:05:23 +00:00
|
|
|
if(myself->incompression < 0 || myself->incompression > 11) {
|
2012-02-26 17:37:36 +00:00
|
|
|
logger(DEBUG_ALWAYS, LOG_ERR, "Bogus compression level!");
|
2003-07-22 20:55:21 +00:00
|
|
|
return false;
|
2002-09-09 21:25:28 +00:00
|
|
|
}
|
|
|
|
} else
|
2009-04-02 23:05:23 +00:00
|
|
|
myself->incompression = 0;
|
2002-09-09 21:25:28 +00:00
|
|
|
|
|
|
|
myself->connection->outcompression = 0;
|
|
|
|
|
|
|
|
/* Done */
|
|
|
|
|
|
|
|
myself->nexthop = myself;
|
|
|
|
myself->via = myself;
|
2003-07-22 20:55:21 +00:00
|
|
|
myself->status.reachable = true;
|
2013-03-08 13:11:15 +00:00
|
|
|
myself->last_state_change = now.tv_sec;
|
2012-07-31 19:43:49 +00:00
|
|
|
myself->status.sptps = experimental;
|
2002-09-09 21:25:28 +00:00
|
|
|
node_add(myself);
|
|
|
|
|
|
|
|
graph();
|
|
|
|
|
2010-03-01 23:18:44 +00:00
|
|
|
if(strictsubnets)
|
2010-03-01 22:44:56 +00:00
|
|
|
load_all_subnets();
|
2012-10-21 15:35:13 +00:00
|
|
|
else if(autoconnect)
|
|
|
|
load_all_nodes();
|
2010-03-01 22:44:56 +00:00
|
|
|
|
2003-07-22 21:13:23 +00:00
|
|
|
/* Open device */
|
|
|
|
|
2012-02-18 13:37:52 +00:00
|
|
|
devops = os_devops;
|
|
|
|
|
2011-12-04 00:20:59 +00:00
|
|
|
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;
|
2012-03-21 16:00:53 +00:00
|
|
|
else if(!strcasecmp(type, "multicast"))
|
|
|
|
devops = multicast_devops;
|
2011-12-04 00:20:59 +00:00
|
|
|
#ifdef ENABLE_UML
|
|
|
|
else if(!strcasecmp(type, "uml"))
|
|
|
|
devops = uml_devops;
|
|
|
|
#endif
|
|
|
|
#ifdef ENABLE_VDE
|
|
|
|
else if(!strcasecmp(type, "vde"))
|
|
|
|
devops = vde_devops;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2014-06-22 09:48:34 +00:00
|
|
|
get_config_bool(lookup_config(config_tree, "DeviceStandby"), &device_standby);
|
|
|
|
|
2011-12-04 00:20:59 +00:00
|
|
|
if(!devops.setup())
|
2003-07-22 21:13:23 +00:00
|
|
|
return false;
|
|
|
|
|
2012-11-29 11:28:23 +00:00
|
|
|
if(device_fd >= 0)
|
|
|
|
io_add(&device_io, handle_device_data, NULL, device_fd, IO_READ);
|
2007-02-27 01:57:01 +00:00
|
|
|
|
2002-09-09 21:25:28 +00:00
|
|
|
/* Open sockets */
|
|
|
|
|
2012-03-26 13:46:09 +00:00
|
|
|
if(!do_detach && getenv("LISTEN_FDS")) {
|
|
|
|
sockaddr_t sa;
|
|
|
|
socklen_t salen;
|
2002-09-09 21:25:28 +00:00
|
|
|
|
2012-03-26 13:46:09 +00:00
|
|
|
listen_sockets = atoi(getenv("LISTEN_FDS"));
|
|
|
|
#ifdef HAVE_UNSETENV
|
|
|
|
unsetenv("LISTEN_FDS");
|
|
|
|
#endif
|
2002-09-09 21:25:28 +00:00
|
|
|
|
2012-03-26 13:46:09 +00:00
|
|
|
if(listen_sockets > MAXSOCKETS) {
|
2012-03-26 18:06:39 +00:00
|
|
|
logger(DEBUG_ALWAYS, LOG_ERR, "Too many listening sockets");
|
2012-02-17 15:25:00 +00:00
|
|
|
return false;
|
|
|
|
}
|
2002-09-09 21:25:28 +00:00
|
|
|
|
2012-10-07 22:35:38 +00:00
|
|
|
for(int i = 0; i < listen_sockets; i++) {
|
2012-03-26 13:46:09 +00:00
|
|
|
salen = sizeof sa;
|
|
|
|
if(getsockname(i + 3, &sa.sa, &salen) < 0) {
|
2014-06-26 19:42:40 +00:00
|
|
|
logger(DEBUG_ALWAYS, LOG_ERR, "Could not get address of listen fd %d: %s", i + 3, sockstrerror(sockerrno));
|
2012-02-21 12:31:21 +00:00
|
|
|
return false;
|
|
|
|
}
|
2002-09-09 21:25:28 +00:00
|
|
|
|
2012-03-26 13:46:09 +00:00
|
|
|
#ifdef FD_CLOEXEC
|
2012-10-10 15:17:49 +00:00
|
|
|
fcntl(i + 3, F_SETFD, FD_CLOEXEC);
|
2012-03-26 13:46:09 +00:00
|
|
|
#endif
|
2007-02-27 01:57:01 +00:00
|
|
|
|
2012-11-29 11:28:23 +00:00
|
|
|
int udp_fd = setup_vpn_in_socket(&sa);
|
|
|
|
if(udp_fd < 0)
|
2012-03-26 13:46:09 +00:00
|
|
|
return false;
|
2007-02-27 01:57:01 +00:00
|
|
|
|
2012-11-29 11:28:23 +00:00
|
|
|
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);
|
2002-09-09 21:25:28 +00:00
|
|
|
|
2012-02-26 17:37:36 +00:00
|
|
|
if(debug_level >= DEBUG_CONNECTIONS) {
|
2012-03-26 13:46:09 +00:00
|
|
|
hostname = sockaddr2hostname(&sa);
|
2012-02-26 17:37:36 +00:00
|
|
|
logger(DEBUG_CONNECTIONS, LOG_NOTICE, "Listening on %s", hostname);
|
2012-02-17 15:25:00 +00:00
|
|
|
free(hostname);
|
|
|
|
}
|
2007-05-17 19:51:26 +00:00
|
|
|
|
2012-03-26 13:46:09 +00:00
|
|
|
memcpy(&listen_socket[i].sa, &sa, salen);
|
2007-05-17 19:51:26 +00:00
|
|
|
}
|
2012-03-26 13:46:09 +00:00
|
|
|
} else {
|
|
|
|
listen_sockets = 0;
|
2014-01-20 20:19:13 +00:00
|
|
|
int cfgs = 0;
|
2012-03-26 13:46:09 +00:00
|
|
|
|
2014-01-20 20:19:13 +00:00
|
|
|
for(config_t *cfg = lookup_config(config_tree, "BindToAddress"); cfg; cfg = lookup_config_next(config_tree, cfg)) {
|
|
|
|
cfgs++;
|
2012-03-26 13:46:09 +00:00
|
|
|
get_config_string(cfg, &address);
|
2014-01-20 20:19:13 +00:00
|
|
|
if(!add_listen_address(address, true))
|
2012-02-21 12:31:21 +00:00
|
|
|
return false;
|
2014-01-20 20:19:13 +00:00
|
|
|
}
|
2002-03-01 13:18:54 +00:00
|
|
|
|
2014-01-20 20:19:13 +00:00
|
|
|
for(config_t *cfg = lookup_config(config_tree, "ListenAddress"); cfg; cfg = lookup_config_next(config_tree, cfg)) {
|
|
|
|
cfgs++;
|
|
|
|
get_config_string(cfg, &address);
|
|
|
|
if(!add_listen_address(address, false))
|
|
|
|
return false;
|
|
|
|
}
|
2012-03-26 13:46:09 +00:00
|
|
|
|
2014-01-20 20:19:13 +00:00
|
|
|
if(!cfgs)
|
|
|
|
if(!add_listen_address(address, NULL))
|
|
|
|
return false;
|
2012-03-26 13:46:09 +00:00
|
|
|
}
|
2002-09-09 21:25:28 +00:00
|
|
|
|
2013-08-18 20:35:27 +00:00
|
|
|
if(!listen_sockets) {
|
2012-02-26 17:37:36 +00:00
|
|
|
logger(DEBUG_ALWAYS, LOG_ERR, "Unable to create any listening socket!");
|
2003-07-22 20:55:21 +00:00
|
|
|
return false;
|
2002-09-09 21:25:28 +00:00
|
|
|
}
|
|
|
|
|
2013-12-05 14:01:30 +00:00
|
|
|
/* If no Port option was specified, set myport to the port used by the first listening socket. */
|
|
|
|
|
2014-07-06 09:55:23 +00:00
|
|
|
if(!port_specified || atoi(myport) == 0) {
|
2013-12-05 14:01:30 +00:00
|
|
|
sockaddr_t sa;
|
|
|
|
socklen_t salen = sizeof sa;
|
|
|
|
if(!getsockname(listen_socket[0].udp.fd, &sa.sa, &salen)) {
|
|
|
|
free(myport);
|
|
|
|
sockaddr2str(&sa, NULL, &myport);
|
|
|
|
if(!myport)
|
|
|
|
myport = xstrdup("655");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
xasprintf(&myself->hostname, "MYSELF port %s", myport);
|
|
|
|
myself->connection->hostname = xstrdup(myself->hostname);
|
|
|
|
|
|
|
|
/* Done. */
|
|
|
|
|
2013-03-08 13:11:15 +00:00
|
|
|
last_config_check = now.tv_sec;
|
2012-09-28 15:05:01 +00:00
|
|
|
|
2003-07-22 20:55:21 +00:00
|
|
|
return true;
|
2002-02-18 16:25:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2009-05-18 12:25:10 +00:00
|
|
|
initialize network
|
2002-02-18 16:25:19 +00:00
|
|
|
*/
|
2009-09-24 22:14:03 +00:00
|
|
|
bool setup_network(void) {
|
2002-09-09 21:25:28 +00:00
|
|
|
init_connections();
|
|
|
|
init_subnets();
|
|
|
|
init_nodes();
|
|
|
|
init_edges();
|
|
|
|
init_requests();
|
|
|
|
|
2006-01-19 17:13:18 +00:00
|
|
|
if(get_config_int(lookup_config(config_tree, "PingInterval"), &pinginterval)) {
|
|
|
|
if(pinginterval < 1) {
|
|
|
|
pinginterval = 86400;
|
2002-09-09 21:25:28 +00:00
|
|
|
}
|
|
|
|
} else
|
2006-01-19 17:13:18 +00:00
|
|
|
pinginterval = 60;
|
|
|
|
|
|
|
|
if(!get_config_int(lookup_config(config_tree, "PingTimeout"), &pingtimeout))
|
|
|
|
pingtimeout = 5;
|
|
|
|
if(pingtimeout < 1 || pingtimeout > pinginterval)
|
|
|
|
pingtimeout = pinginterval;
|
|
|
|
|
|
|
|
if(!get_config_int(lookup_config(config_tree, "MaxOutputBufferSize"), &maxoutbufsize))
|
2009-03-09 13:04:31 +00:00
|
|
|
maxoutbufsize = 10 * MTU;
|
2002-09-09 21:25:28 +00:00
|
|
|
|
2003-07-22 20:55:21 +00:00
|
|
|
if(!setup_myself())
|
|
|
|
return false;
|
2003-01-14 12:53:59 +00:00
|
|
|
|
2013-08-18 20:35:27 +00:00
|
|
|
if(!init_control())
|
|
|
|
return false;
|
|
|
|
|
2014-06-22 09:48:34 +00:00
|
|
|
if (!device_standby)
|
|
|
|
device_enable();
|
2013-08-18 20:35:27 +00:00
|
|
|
|
|
|
|
/* Run subnet-up scripts for our own subnets */
|
|
|
|
|
|
|
|
subnet_update(myself, NULL, true);
|
|
|
|
|
2003-07-22 20:55:21 +00:00
|
|
|
return true;
|
2002-02-18 16:25:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
close all open network connections
|
|
|
|
*/
|
2007-05-18 10:00:00 +00:00
|
|
|
void close_network_connections(void) {
|
2012-10-07 19:59:53 +00:00
|
|
|
for(list_node_t *node = connection_list->head, *next; node; node = next) {
|
2002-09-09 21:25:28 +00:00
|
|
|
next = node->next;
|
2012-10-07 19:59:53 +00:00
|
|
|
connection_t *c = node->data;
|
2012-10-06 19:16:17 +00:00
|
|
|
/* Keep control connections open until the end, so they know when we really terminated */
|
|
|
|
if(c->status.control)
|
|
|
|
c->socket = -1;
|
2011-05-28 01:46:39 +00:00
|
|
|
c->outgoing = NULL;
|
2003-07-22 20:55:21 +00:00
|
|
|
terminate_connection(c, false);
|
2002-09-09 21:25:28 +00:00
|
|
|
}
|
|
|
|
|
2014-01-20 20:19:13 +00:00
|
|
|
if(outgoing_list)
|
|
|
|
list_delete_list(outgoing_list);
|
2009-01-20 12:12:41 +00:00
|
|
|
|
2004-12-01 20:06:05 +00:00
|
|
|
if(myself && myself->connection) {
|
|
|
|
subnet_update(myself, NULL, false);
|
2003-07-22 20:55:21 +00:00
|
|
|
terminate_connection(myself->connection, false);
|
2009-01-19 22:17:28 +00:00
|
|
|
free_connection(myself->connection);
|
2004-12-01 20:06:05 +00:00
|
|
|
}
|
2002-09-09 21:25:28 +00:00
|
|
|
|
2012-10-07 19:59:53 +00:00
|
|
|
for(int i = 0; i < listen_sockets; i++) {
|
2012-11-29 11:28:23 +00:00
|
|
|
io_del(&listen_socket[i].tcp);
|
|
|
|
io_del(&listen_socket[i].udp);
|
|
|
|
close(listen_socket[i].tcp.fd);
|
|
|
|
close(listen_socket[i].udp.fd);
|
2002-09-09 21:25:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
exit_requests();
|
|
|
|
exit_edges();
|
|
|
|
exit_subnets();
|
|
|
|
exit_nodes();
|
|
|
|
exit_connections();
|
|
|
|
|
2014-06-22 09:48:34 +00:00
|
|
|
if (!device_standby)
|
|
|
|
device_disable();
|
2002-09-09 21:25:28 +00:00
|
|
|
|
2014-12-24 16:31:33 +00:00
|
|
|
free(myport);
|
2009-01-09 11:36:06 +00:00
|
|
|
|
2014-06-22 13:06:44 +00:00
|
|
|
if (device_fd >= 0)
|
|
|
|
io_del(&device_io);
|
2014-07-12 12:56:01 +00:00
|
|
|
if (devops.close)
|
|
|
|
devops.close();
|
2002-09-09 21:25:28 +00:00
|
|
|
|
2013-08-18 20:35:27 +00:00
|
|
|
exit_control();
|
|
|
|
|
2014-12-24 16:06:05 +00:00
|
|
|
free(myname);
|
2014-12-24 16:31:33 +00:00
|
|
|
free(scriptextension);
|
|
|
|
free(scriptinterpreter);
|
2014-12-24 16:06:05 +00:00
|
|
|
|
2002-09-09 21:25:28 +00:00
|
|
|
return;
|
2002-02-18 16:25:19 +00:00
|
|
|
}
|