edge.c (n2n-2.8) | : | edge.c (n2n-3.0) | ||
---|---|---|---|---|
/** | /** | |||
* (C) 2007-20 - ntop.org and contributors | * (C) 2007-21 - ntop.org and contributors | |||
* | * | |||
* This program is free software; you can redistribute it and/or modify | * 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 | * it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation; either version 3 of the License, or | * the Free Software Foundation; either version 3 of the License, or | |||
* (at your option) any later version. | * (at your option) any later version. | |||
* | * | |||
* This program is distributed in the hope that it will be useful, | * This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | * GNU General Public License for more details. | |||
* | * | |||
* You should have received a copy of the GNU General Public License | * You should have received a copy of the GNU General Public License | |||
* along with this program; if not see see <http://www.gnu.org/licenses/> | * along with this program; if not see see <http://www.gnu.org/licenses/> | |||
* | * | |||
*/ | */ | |||
#include "n2n.h" | #include "n2n.h" | |||
/* *************************************************** */ | /* *************************************************** */ | |||
/** maximum length of command line arguments */ | /** maximum length of command line arguments */ | |||
#define MAX_CMDLINE_BUFFER_LENGTH 4096 | #define MAX_CMDLINE_BUFFER_LENGTH 4096 | |||
/** maximum length of a line in the configuration file */ | /** maximum length of a line in the configuration file */ | |||
#define MAX_CONFFILE_LINE_LENGTH 1024 | #define MAX_CONFFILE_LINE_LENGTH 1024 | |||
/* ***************************************************** */ | /* ***************************************************** */ | |||
#ifdef HAVE_LIBCAP | #ifdef HAVE_LIBCAP | |||
#include <sys/capability.h> | #include <sys/capability.h> | |||
#include <sys/prctl.h> | #include <sys/prctl.h> | |||
#include "network_traffic_filter.h" | ||||
static cap_value_t cap_values[] = { | static cap_value_t cap_values[] = { | |||
//CAP_NET_RAW, /* Use RAW and PACKET sock | //CAP_NET_RAW, /* Use RAW and PACKET sockets */ | |||
ets */ | CAP_NET_ADMIN /* Needed to performs routes cleanup at exit */ | |||
CAP_NET_ADMIN /* Needed to performs routes | ||||
cleanup at exit */ | ||||
}; | }; | |||
int num_cap = sizeof(cap_values)/sizeof(cap_value_t); | int num_cap = sizeof(cap_values)/sizeof(cap_value_t); | |||
#endif | #endif | |||
// forward declaration for use in main() | ||||
void send_register_super (n2n_edge_t *eee); | ||||
void send_query_peer (n2n_edge_t *eee, const n2n_mac_t dst_mac); | ||||
int supernode_connect (n2n_edge_t *eee); | ||||
int supernode_disconnect (n2n_edge_t *eee); | ||||
int fetch_and_eventually_process_data (n2n_edge_t *eee, SOCKET sock, | ||||
uint8_t *pktbuf, uint16_t *expected, uint | ||||
16_t *position, | ||||
time_t now); | ||||
int resolve_check (n2n_resolve_parameter_t *param, uint8_t resolution_request, t | ||||
ime_t now); | ||||
int edge_init_routes (n2n_edge_t *eee, n2n_route_t *routes, uint16_t num_routes) | ||||
; | ||||
/* ***************************************************** */ | /* ***************************************************** */ | |||
/** Find the address and IP mode for the tuntap device. | /** Find the address and IP mode for the tuntap device. | |||
* | * | |||
* s is one of these forms: | * s is of the form: | |||
* | ||||
* <host> := <hostname> | A.B.C.D | ||||
* | * | |||
* <host> | static:<host> | dhcp:<host> | * ["static"|"dhcp",":"] (<host>|<ip>) [/<cidr subnet mask>] | |||
* | * | |||
* If the mode is present (colon required) then fill ip_mode with that value | * for example static:192.168.8.5/24 | |||
* otherwise do not change ip_mode. Fill ip_mode with everything after the | ||||
* colon if it is present; or s if colon is not present. | ||||
* | * | |||
* ip_add and ip_mode are NULL terminated if modified. | * Fill the parts of the string into the fileds, ip_mode only if | |||
* present. All strings are NULL terminated. | ||||
* | * | |||
* return 0 on success and -1 on error | * return 0 on success and -1 on error | |||
*/ | */ | |||
static int scan_address(char * ip_addr, size_t addr_size, | static int scan_address (char * ip_addr, size_t addr_size, | |||
char * ip_mode, size_t mode_size, | char * netmask, size_t netmask_size, | |||
const char * s) { | char * ip_mode, size_t mode_size, | |||
int retval = -1; | char * s) { | |||
char * p; | ||||
int retval = -1; | ||||
if((NULL == s) || (NULL == ip_addr)) | char * start; | |||
{ | char * end; | |||
return -1; | int bitlen = N2N_EDGE_DEFAULT_CIDR_NM; | |||
if((NULL == s) || (NULL == ip_addr) || (NULL == netmask)) { | ||||
return -1; | ||||
} | ||||
memset(ip_addr, 0, addr_size); | ||||
memset(netmask, 0, netmask_size); | ||||
start = s; | ||||
end = strpbrk(s, ":"); | ||||
if(end) { | ||||
// colon is present | ||||
if(ip_mode) { | ||||
memset(ip_mode, 0, mode_size); | ||||
strncpy(ip_mode, start, (size_t)MIN(end - start, mode_size - 1)); | ||||
} | ||||
start = end + 1; | ||||
} else { | ||||
// colon is not present | ||||
} | } | |||
// start now points to first address character | ||||
retval = 0; // we have got an address | ||||
memset(ip_addr, 0, addr_size); | end = strpbrk(start, "/"); | |||
p = strpbrk(s, ":"); | if(!end) | |||
// no slash present -- default end | ||||
end = s + strlen(s); | ||||
if(p) | strncpy(ip_addr, start, (size_t)MIN(end - start, addr_size - 1)); // ensure | |||
{ | NULL term | |||
/* colon is present */ | ||||
if(ip_mode) | if(end) { | |||
{ | // slash is present | |||
size_t end=0; | ||||
// now, handle the sub-network address | ||||
memset(ip_mode, 0, mode_size); | sscanf(end + 1, "%u", &bitlen); | |||
end = MIN(p-s, (ssize_t)(mode_size-1)); /* ensure NULL term */ | bitlen = htobe32(bitlen2mask(bitlen)); | |||
strncpy(ip_mode, s, end); | inet_ntop(AF_INET, &bitlen, netmask, netmask_size); | |||
strncpy(ip_addr, p+1, addr_size-1); /* ensure NULL term */ | ||||
retval = 0; | ||||
} | ||||
} | ||||
else | ||||
{ | ||||
/* colon is not present */ | ||||
strncpy(ip_addr, s, addr_size-1); | ||||
ip_addr[addr_size-1] = '\0'; | ||||
} | } | |||
return retval; | return retval; | |||
} | } | |||
/* *************************************************** */ | /* *************************************************** */ | |||
static void help() { | static void help (int level) { | |||
print_n2n_version(); | ||||
printf("edge <config file> (see edge.conf)\n" | if(level == 0) return; /* no help required */ | |||
"or\n" | ||||
); | printf("\n"); | |||
printf("edge " | print_n2n_version(); | |||
if(level == 1) /* short help */ { | ||||
printf(" basic usage: edge <config file> (see edge.conf)\n" | ||||
"\n" | ||||
" or edge " | ||||
" -c <community name> " | ||||
"\n " | ||||
" -l <supernode host>:<port> " | ||||
"\n " | ||||
"[-a <tap IP address>] " | ||||
"\n " | ||||
#if defined(N2N_CAN_NAME_IFACE) | #if defined(N2N_CAN_NAME_IFACE) | |||
"-d <tun device> " | "[-d <tap device name>] " | |||
#endif /* #if defined(N2N_CAN_NAME_IFACE) */ | "\n " | |||
"-a [static:|dhcp:]<tun IP address> " | #endif | |||
"-c <community> " | "[-k <encryption key>] " | |||
"[-k <encrypt key>]\n" | "\n" | |||
" " | "\n -h shows a quick reference including all available option | |||
"[-s <netmask>] " | s" | |||
#ifndef WIN32 | "\n --help gives a detailed parameter description" | |||
"[-u <uid> -g <gid>]" | "\n man files for n2n, edge, and superndode contain in-depth i | |||
#endif /* #ifndef WIN32 */ | nformation" | |||
"\n\n"); | ||||
} else if(level == 2) /* quick reference */ { | ||||
printf(" general usage: edge <config file> (see edge.conf)\n" | ||||
"\n" | ||||
" or edge " | ||||
" -c <community name>" | ||||
" -l <supernode host:port>" | ||||
"\n " | ||||
"[-p [<local bind ip address>:]<local port>] " | ||||
"\n " | ||||
#ifndef WIN32 | ||||
"[-f]" | ||||
#endif /* #ifndef WIN32 */ | ||||
#ifdef __linux__ | #ifdef __linux__ | |||
"[-T <tos>]" | "[-T <type of service>] " | |||
#endif | #endif | |||
"[-n cidr:gateway] " | ||||
"[-m <MAC address>] " | ||||
"-l <supernode host:port>\n" | ||||
" " | ||||
"[-p <local port>] [-M <mtu>] " | ||||
#ifndef __APPLE__ | #ifndef __APPLE__ | |||
"[-D] " | "[-D] " | |||
#endif | #endif | |||
"[-r] [-E] [-v] [-i <reg_interval>] [-L <reg_ttl>] [-t <mgmt port>] [-A[ | "\n options for under- " | |||
<cipher>]] [-H] [-z[<compression algo>]] [-h]\n\n"); | "[-i <registration interval>] " | |||
"[-L <registration ttl>] " | ||||
"\n lying connection " | ||||
"[-k <key>] " | ||||
"[-A<cipher>] " | ||||
"[-H] " | ||||
"[-z<compression>] " | ||||
"\n " | ||||
"[-e <preferred local IP address>] [-S<level of solitude>]" | ||||
"\n " | ||||
"[--select-rtt]" | ||||
"\n\n tap device and " | ||||
"[-a [static:|dhcp:]<tap IP address>[/<cidr suffix>]] " | ||||
"\n overlay network " | ||||
"[-m <tap MAC address>] " | ||||
#if defined(N2N_CAN_NAME_IFACE) | #if defined(N2N_CAN_NAME_IFACE) | |||
printf("-d <tun device> | tun device name\n"); | "[-d <tap device name>] " | |||
#endif | ||||
"\n configuration " | ||||
"[-M <tap MTU>] " | ||||
"[-r] " | ||||
"[-E] " | ||||
"[-I <edge description>] " | ||||
"\n " | ||||
"[-J <password>] " | ||||
"[-P <public key>] " | ||||
"[-R <rule string>] " | ||||
#ifdef WIN32 | ||||
"\n " | ||||
"[-x <metric>] " | ||||
#endif | #endif | |||
"\n\n local options " | ||||
#ifndef WIN32 | ||||
"[-f] " | ||||
#endif | ||||
"[-t <management port>] " | ||||
"[--management-password <pw>] " | ||||
"\n " | ||||
"[-v] " | ||||
"[-n <cidr:gateway>] " | ||||
#ifndef WIN32 | ||||
"\n " | ||||
"[-u <numerical user id>] " | ||||
"[-g <numerical group id>] " | ||||
#endif | ||||
"\n\n environment " | ||||
"N2N_KEY instead of [-k <key>]" | ||||
"\n variables " | ||||
"N2N_COMMUNITY instead of -c <community>" | ||||
"\n " | ||||
"N2N_PASSWORD instead of [-J <password>]" | ||||
printf("-a <mode:address> | Set interface address. For DHCP use '-r -a | "\n " | |||
dhcp:0.0.0.0'\n"); | ||||
printf("-c <community> | n2n community name the edge belongs to.\n") | "\n meaning of the " | |||
; | #ifndef __APPLE__ | |||
printf("-k <encrypt key> | Encryption key (ASCII) - also N2N_KEY=<encr | "[-D] enable PMTU discovery" | |||
ypt key>.\n"); | #endif | |||
printf("-s <netmask> | Edge interface netmask in dotted decimal no | "\n flag options [-H] enable header encryption" | |||
tation (255.255.255.0).\n"); | "\n [-r] enable packet forwarding through n2n co | |||
printf("-l <supernode host:port> | Supernode IP:port\n"); | mmunity" | |||
printf("-i <reg_interval> | Registration interval, for NAT hole punchin | "\n [-E] accept multicast MAC addresses" | |||
g (default 20 seconds)\n"); | "\n [--select-rtt] select supernode by round trip time" | |||
printf("-L <reg_ttl> | TTL for registration packet when UDP NAT ho | ||||
le punching through supernode (default 0 for not set )\n"); | ||||
printf("-p <local port> | Fixed local UDP port.\n"); | ||||
#ifndef WIN32 | ||||
printf("-u <UID> | User ID (numeric) to use when privileges ar | ||||
e dropped.\n"); | ||||
printf("-g <GID> | Group ID (numeric) to use when privileges a | ||||
re dropped.\n"); | ||||
#endif /* ifndef WIN32 */ | ||||
#ifndef WIN32 | #ifndef WIN32 | |||
printf("-f | Do not fork and run as a daemon; rather run | "\n [-f] do not fork but run in foreground" | |||
in foreground.\n"); | #endif | |||
#endif /* #ifndef WIN32 */ | "\n [-v] make more verbose, repeat as required" | |||
printf("-m <MAC address> | Fix MAC address for the TAP interface (othe | "\n " | |||
rwise it may be random)\n" | ||||
" | eg. -m 01:02:03:04:05:06\n"); | "\n -h shows this quick reference including all available options" | |||
printf("-M <mtu> | Specify n2n MTU of edge interface (default | "\n --help gives a detailed parameter description" | |||
%d).\n", DEFAULT_MTU); | "\n man files for n2n, edge, and superndode contain in-depth inform | |||
ation" | ||||
"\n\n"); | ||||
} else /* long help */ { | ||||
printf(" general usage: edge <config file> (see edge.conf)\n" | ||||
"\n" | ||||
" or edge -c <community name> -l <supernode host:po | ||||
rt>\n" | ||||
" [further optional command line parameters] | ||||
\n\n" | ||||
); | ||||
printf (" OPTIONS FOR THE UNDERLYING NETWORK CONNECTION\n"); | ||||
printf (" ---------------------------------------------\n\n"); | ||||
printf(" -c <community> | n2n community name the edge belongs to\n"); | ||||
printf(" -l <host:port> | supernode ip address or name, and port\n"); | ||||
printf(" -p [<ip>:]<port> | fixed local UDP port and optionally bind to | ||||
the\n" | ||||
" | sepcified local IP address only (any by def | ||||
ault)\n"); | ||||
#ifdef __linux__ | ||||
printf(" -T <tos> | TOS for packets, e.g. 0x48 for SSH like pri | ||||
ority\n"); | ||||
#endif | ||||
#ifndef __APPLE__ | #ifndef __APPLE__ | |||
printf("-D | Enable PMTU discovery. PMTU discovery can r | printf(" -D | enable PMTU discovery, it can reduce fragme | |||
educe fragmentation but\n" | ntation but\n" | |||
" | causes connections stall when not properly | " | causes connections to stall if not properly | |||
supported.\n"); | supported\n"); | |||
#endif | #endif | |||
printf("-r | Enable packet forwarding through n2n commun | printf(" -e <local ip> | advertises the provided local IP address as | |||
ity.\n"); | preferred,\n" | |||
printf("-A1 | Disable payload encryption. Do not use with | " | useful if multicast peer detection is not a | |||
key (defaulting to Twofish then).\n"); | vailable,\n" | |||
printf("-A2 ... -A5 or -A | Choose a cipher for payload encryption, req | " | '-e auto' tries IP address auto-detection\n | |||
uires a key: -A2 = Twofish (default),\n"); | "); | |||
printf(" | " | printf(" -S1 ... -S2 | do not connect p2p, always use the supernod | |||
#ifdef N2N_HAVE_AES | e,\n" | |||
"-A3 or -A (deprecated) = AES-CBC, " | " | -S1 = via UDP" | |||
#endif | ||||
#ifdef HAVE_OPENSSL_1_1 | #ifdef N2N_HAVE_TCP | |||
"-A4 = ChaCha20, " | ", -S2 = via TCP" | |||
#endif | #endif | |||
"-A5 = Speck-CTR.\n"); | "\n"); | |||
printf("-H | Enable full header encryption. Requires sup | printf(" -i <reg_interval> | registration interval, for NAT hole punchin | |||
ernode with fixed community.\n"); | g (default\n" | |||
printf("-z1 ... -z2 or -z | Enable compression for outgoing data packet | " | %u seconds)\n", REGISTER_SUPER_INTERVAL_DFL | |||
s: -z1 or -z = lzo1x" | ); | |||
printf(" -L <reg_ttl> | TTL for registration packet for NAT hole pu | ||||
nching through\n" | ||||
" | supernode (default 0 for not set)\n"); | ||||
printf(" -k <key> | encryption key (ASCII) - also N2N_KEY=<key> | ||||
\n"); | ||||
printf(" -A1 | disable payload encryption, do not use with | ||||
key, defaults\n" | ||||
" | to AES then\n"); | ||||
printf(" -A2 ... -A5 | choose a cipher for payload encryption, req | ||||
uires a key,\n" | ||||
" | -A2 = Twofish, -A3 = AES (default if key pr | ||||
ovided),\n" | ||||
" | -A4 = ChaCha20, -A5 = Speck-CTR\n"); | ||||
printf(" -H | use header encryption, supernode needs fixe | ||||
d community\n"); | ||||
printf(" -z1 ... -z2 | compress outgoing data packets, -z1 = lzo1x | ||||
,\n" | ||||
" | " | ||||
#ifdef N2N_HAVE_ZSTD | #ifdef N2N_HAVE_ZSTD | |||
", -z2 = zstd" | "-z2 = zstd, " | |||
#endif | #endif | |||
" (default=disabled).\n"); | "disabled by default\n"); | |||
printf("-E | Accept multicast MAC addresses (default=dro | printf("--select-rtt | supernode selection based on round trip tim | |||
p).\n"); | e (default:\n" | |||
printf("-S | Do not connect P2P. Always use the supernod | " | by load)\n"); | |||
e.\n"); | ||||
#ifdef __linux__ | printf ("\n"); | |||
printf("-T <tos> | TOS for packets (e.g. 0x48 for SSH like pri | printf (" TAP DEVICE AND OVERLAY NETWORK CONFIGURATION\n"); | |||
ority)\n"); | printf (" --------------------------------------------\n\n"); | |||
printf(" -a [mode]<ip>[/n] | interface address and optional CIDR subnet, | ||||
default '/24',\n" | ||||
" | mode = [static|dhcp]:, for DHCP use '-r -a | ||||
dhcp:0.0.0.0',\n" | ||||
" | edge draws IP address from supernode if no | ||||
'-a ...' given\n"); | ||||
printf(" -m <mac> | fixed MAC address for the TAP interface, e. | ||||
g.\n" | ||||
" | '-m 10:20:30:40:50:60', random otherwise\n" | ||||
); | ||||
#if defined(N2N_CAN_NAME_IFACE) | ||||
printf(" -d <device> | TAP device name\n"); | ||||
#endif | #endif | |||
printf("-n <cidr:gateway> | Route an IPv4 network via the gw. Use 0.0.0 | printf(" -M <mtu> | specify n2n MTU of TAP interface, default % | |||
.0/0 for the default gw. Can be set multiple times.\n"); | d\n", DEFAULT_MTU); | |||
printf("-v | Make more verbose. Repeat as required.\n"); | printf(" -r | enable packet forwarding through n2n commun | |||
printf("-t <port> | Management UDP Port (for multiple edges on | ity\n"); | |||
a machine).\n"); | printf(" -E | accept multicast MAC addresses, drop by def | |||
ault\n"); | ||||
printf("\nEnvironment variables:\n"); | printf(" -I <description> | annotate the edge's description used for ea | |||
printf(" N2N_KEY | Encryption key (ASCII). Not with -k.\n"); | sier\n" | |||
" | identification in management port output or | ||||
username\n"); | ||||
printf(" -J <password> | password for user-password edge authenticat | ||||
ion\n"); | ||||
printf(" -P <public key> | federation public key for user-password aut | ||||
hentication\n"); | ||||
printf(" -R <rule> | drop or accept packets by rules, can be set | ||||
multiple times\n"); | ||||
printf(" | rule format: 'src_ip/n:[s_port,e_port],. | ||||
..\n" | ||||
" | |on same| ...dst_ip/n:[s_port,e_port],. | ||||
..\n" | ||||
" | | line | ...TCP+/-,UDP+/-,ICMP+/-'\n") | ||||
; | ||||
#ifdef WIN32 | #ifdef WIN32 | |||
printf("\nAvailable TAP adapters:\n"); | printf(" -x <metric> | set TAP interface metric, defaults to 0 (au | |||
win_print_available_adapters(); | to),\n" | |||
" | e.g. set to 1 for better multiplayer game d | ||||
etection\n"); | ||||
#endif | ||||
printf ("\n"); | ||||
printf (" LOCAL OPTIONS\n"); | ||||
printf (" -------------\n\n"); | ||||
#ifndef WIN32 | ||||
printf(" -f | do not fork and run as a daemon, rather run | ||||
in foreground\n"); | ||||
#endif | ||||
printf(" -t <port> | management UDP port, for multiple edges on | ||||
a machine,\n" | ||||
" | defaults to %u\n", N2N_EDGE_MGMT_PORT); | ||||
printf(" --management_... | management port password, defaults to '%s'\ | ||||
n" | ||||
" ...password <pw> | \n", N2N_MGMT_PASSWORD); | ||||
printf(" -v | make more verbose, repeat as required\n"); | ||||
printf(" -n <cidr:gateway> | route an IPv4 network via the gateway, use | ||||
0.0.0.0/0 for\n" | ||||
" | the default gateway, can be set multiple ti | ||||
mes\n"); | ||||
#ifndef WIN32 | ||||
printf(" -u <UID> | numeric user ID to use when privileges are | ||||
dropped\n"); | ||||
printf(" -g <GID> | numeric group ID to use when privileges are | ||||
dropped\n"); | ||||
#endif | #endif | |||
printf ("\n"); | ||||
printf (" ENVIRONMENT VARIABLES\n"); | ||||
printf (" ---------------------\n\n"); | ||||
printf(" N2N_KEY | encryption key (ASCII), not with '-k ...'\n | ||||
"); | ||||
printf(" N2N_COMMUNITY | community name (ASCII), overwritten by '-c | ||||
...'\n"); | ||||
printf(" N2N_PASSWORD | password (ASCII) for user-password authenti | ||||
cation,\n" | ||||
" | overwritten by '-J ...'\n"); | ||||
#ifdef WIN32 | ||||
printf ("\n"); | ||||
printf (" AVAILABLE TAP ADAPTERS\n"); | ||||
printf (" ----------------------\n\n"); | ||||
win_print_available_adapters(); | ||||
#endif | ||||
printf ("\n" | ||||
"\n -h shows a quick reference including all available optio | ||||
ns" | ||||
"\n --help gives this detailed parameter description" | ||||
"\n man files for n2n, edge, and superndode contain in-depth | ||||
information" | ||||
"\n\n"); | ||||
} | ||||
exit(0); | exit(0); | |||
} | } | |||
/* *************************************************** */ | /* *************************************************** */ | |||
static void setPayloadCompression(n2n_edge_conf_t *conf, int compression) { | static void setPayloadCompression (n2n_edge_conf_t *conf, int compression) { | |||
/* even though 'compression' and 'conf->compression' share the same encoding s | ||||
cheme, | /* even though 'compression' and 'conf->compression' share the same encoding | |||
* a switch-statement under conditional compilation is used to sort out the | scheme, | |||
* unsupported optarguments */ | * a switch-statement under conditional compilation is used to sort out the | |||
switch (compression) { | * unsupported optarguments */ | |||
case 1: | switch (compression) { | |||
{ | case 1: { | |||
conf->compression = N2N_COMPRESSION_ID_LZO; | conf->compression = N2N_COMPRESSION_ID_LZO; | |||
break; | break; | |||
} | } | |||
#ifdef N2N_HAVE_ZSTD | #ifdef N2N_HAVE_ZSTD | |||
case 2: | case 2: { | |||
{ | conf->compression = N2N_COMPRESSION_ID_ZSTD; | |||
conf->compression = N2N_COMPRESSION_ID_ZSTD; | break; | |||
break; | } | |||
} | ||||
#endif | #endif | |||
default: | default: { | |||
{ | conf->compression = N2N_COMPRESSION_ID_NONE; | |||
conf->compression = N2N_COMPRESSION_ID_NONE; | // internal comrpession scheme numbering differs from cli counting b | |||
traceEvent(TRACE_NORMAL, "the %s compression given by -z_ option is not su | y one, hence plus one | |||
pported in this version.", compression_str(compression)); | // (internal: 0 == invalid, 1 == none, 2 == lzo, 3 == zstd) | |||
exit(1); // to make the user aware | traceEvent(TRACE_NORMAL, "the %s compression given by -z_ option is | |||
not supported in this version", compression_str(compression + 1)); | ||||
exit(1); // to make the user aware | ||||
} | ||||
} | } | |||
} | ||||
} | } | |||
/* *************************************************** */ | /* *************************************************** */ | |||
static void setPayloadEncryption( n2n_edge_conf_t *conf, int cipher) { | static void setPayloadEncryption (n2n_edge_conf_t *conf, int cipher) { | |||
/* even though 'cipher' and 'conf->transop_id' share the same encoding scheme, | ||||
* a switch-statement under conditional compilation is used to sort out the | /* even though 'cipher' and 'conf->transop_id' share the same encoding schem | |||
* unsupported ciphers */ | e, | |||
switch (cipher) { | * a switch-statement under conditional compilation is used to sort out the | |||
case 1: | * unsupported ciphers */ | |||
{ | switch (cipher) { | |||
conf->transop_id = N2N_TRANSFORM_ID_NULL; | case 1: { | |||
break; | conf->transop_id = N2N_TRANSFORM_ID_NULL; | |||
} | break; | |||
case 2: | } | |||
{ | ||||
conf->transop_id = N2N_TRANSFORM_ID_TWOFISH; | case 2: { | |||
break; | conf->transop_id = N2N_TRANSFORM_ID_TWOFISH; | |||
} | break; | |||
#ifdef N2N_HAVE_AES | } | |||
case 3: | ||||
{ | case 3: { | |||
conf->transop_id = N2N_TRANSFORM_ID_AESCBC; | conf->transop_id = N2N_TRANSFORM_ID_AES; | |||
break; | break; | |||
} | } | |||
#endif | ||||
#ifdef HAVE_OPENSSL_1_1 | case 4: { | |||
case 4: | conf->transop_id = N2N_TRANSFORM_ID_CHACHA20; | |||
{ | break; | |||
conf->transop_id = N2N_TRANSFORM_ID_CHACHA20; | } | |||
break; | ||||
} | case 5: { | |||
#endif | conf->transop_id = N2N_TRANSFORM_ID_SPECK; | |||
case 5: | break; | |||
{ | } | |||
conf->transop_id = N2N_TRANSFORM_ID_SPECK; | ||||
break; | default: { | |||
} | conf->transop_id = N2N_TRANSFORM_ID_INVAL; | |||
default: | traceEvent(TRACE_NORMAL, "the %s cipher given by -A_ option is not s | |||
{ | upported in this version", transop_str(cipher)); | |||
conf->transop_id = N2N_TRANSFORM_ID_INVAL; | exit(1); | |||
traceEvent(TRACE_NORMAL, "the %s cipher given by -A_ option is not support | } | |||
ed in this version.", transop_str(cipher)); | ||||
exit(1); | ||||
} | } | |||
} | ||||
} | } | |||
/* *************************************************** */ | /* *************************************************** */ | |||
static int setOption(int optkey, char *optargument, n2n_tuntap_priv_config_t *ec | static int setOption (int optkey, char *optargument, n2n_tuntap_priv_config_t *e | |||
, n2n_edge_conf_t *conf) { | c, n2n_edge_conf_t *conf) { | |||
/* traceEvent(TRACE_NORMAL, "Option %c = %s", optkey, optargument ? optargumen | ||||
t : ""); */ | ||||
switch(optkey) { | /* traceEvent(TRACE_NORMAL, "Option %c = %s", optkey, optargument ? optargum | |||
case 'a': /* IP address and mode of TUNTAP interface */ | ent : ""); */ | |||
{ | ||||
scan_address(ec->ip_addr, N2N_NETMASK_STR_SIZE, | ||||
ec->ip_mode, N2N_IF_MODE_SIZE, | ||||
optargument); | ||||
break; | ||||
} | ||||
case 'c': /* community as a string */ | switch(optkey) { | |||
{ | case 'a': /* IP address and mode of TUNTAP interface */ { | |||
memset(conf->community_name, 0, N2N_COMMUNITY_SIZE); | scan_address(ec->ip_addr, N2N_NETMASK_STR_SIZE, | |||
strncpy((char *)conf->community_name, optargument, N2N_COMMUNITY_SIZE); | ec->netmask, N2N_NETMASK_STR_SIZE, | |||
conf->community_name[N2N_COMMUNITY_SIZE-1] = '\0'; | ec->ip_mode, N2N_IF_MODE_SIZE, | |||
break; | optargument); | |||
} | break; | |||
} | ||||
case 'E': /* multicast ethernet addresses accepted. */ | case 'c': /* community as a string */ { | |||
{ | strncpy((char *)conf->community_name, optargument, N2N_COMMUNITY_SIZ | |||
conf->drop_multicast=0; | E); | |||
traceEvent(TRACE_DEBUG, "Enabling ethernet multicast traffic"); | conf->community_name[N2N_COMMUNITY_SIZE - 1] = '\0'; | |||
break; | break; | |||
} | } | |||
case 'E': /* multicast ethernet addresses accepted. */ { | ||||
conf->drop_multicast = 0; | ||||
traceEvent(TRACE_INFO, "enabling ethernet multicast traffic"); | ||||
break; | ||||
} | ||||
#ifndef WIN32 | #ifndef WIN32 | |||
case 'u': /* unprivileged uid */ | case 'u': /* unprivileged uid */ { | |||
{ | ec->userid = atoi(optargument); | |||
ec->userid = atoi(optargument); | break; | |||
break; | } | |||
} | ||||
case 'g': /* unprivileged uid */ | case 'g': /* unprivileged uid */ { | |||
{ | ec->groupid = atoi(optargument); | |||
ec->groupid = atoi(optargument); | break; | |||
break; | } | |||
} | ||||
#endif | #endif | |||
#ifndef WIN32 | #ifndef WIN32 | |||
case 'f' : /* do not fork as daemon */ | case 'f' : /* do not fork as daemon */ { | |||
{ | ec->daemon = 0; | |||
ec->daemon=0; | break; | |||
break; | } | |||
} | ||||
#endif /* #ifndef WIN32 */ | #endif /* #ifndef WIN32 */ | |||
case 'm' : /* TUNTAP MAC address */ | case 'm' : /* TUNTAP MAC address */ { | |||
{ | strncpy(ec->device_mac, optargument, N2N_MACNAMSIZ); | |||
strncpy(ec->device_mac,optargument,N2N_MACNAMSIZ); | ec->device_mac[N2N_MACNAMSIZ - 1] = '\0'; | |||
ec->device_mac[N2N_MACNAMSIZ-1] = '\0'; | break; | |||
break; | } | |||
} | ||||
case 'M' : /* TUNTAP MTU */ | case 'M' : /* TUNTAP MTU */ { | |||
{ | ec->mtu = atoi(optargument); | |||
ec->mtu = atoi(optargument); | break; | |||
break; | } | |||
} | ||||
#ifndef __APPLE__ | #ifndef __APPLE__ | |||
case 'D' : /* enable PMTU discovery */ | case 'D' : /* enable PMTU discovery */ { | |||
{ | conf->disable_pmtu_discovery = 0; | |||
conf->disable_pmtu_discovery = 0; | break; | |||
break; | } | |||
} | ||||
#endif | #endif | |||
case 'k': /* encrypt key */ | case 'k': /* encrypt key */ { | |||
{ | if(conf->encrypt_key) free(conf->encrypt_key); | |||
if(conf->encrypt_key) free(conf->encrypt_key); | conf->encrypt_key = strdup(optargument); | |||
conf->encrypt_key = strdup(optargument); | traceEvent(TRACE_DEBUG, "encrypt_key = '%s'\n", conf->encrypt_key); | |||
traceEvent(TRACE_DEBUG, "encrypt_key = '%s'\n", conf->encrypt_key); | break; | |||
break; | } | |||
} | ||||
case 'r': /* enable packet routing across n2n endpoints */ | case 'r': /* enable packet routing across n2n endpoints */ { | |||
{ | conf->allow_routing = 1; | |||
conf->allow_routing = 1; | break; | |||
break; | } | |||
} | ||||
case 'A': | case 'A': { | |||
{ | int cipher; | |||
int cipher; | ||||
if (optargument) { | if(optargument) { | |||
cipher = atoi(optargument); | cipher = atoi(optargument); | |||
} else { | } else { | |||
traceEvent(TRACE_NORMAL, "the use of the solitary -A switch is deprecate | traceEvent(TRACE_WARNING, "the use of the solitary -A switch is | |||
d and might not be supported in future versions. " | deprecated and will not be supported in future versions, " | |||
"please use -A3 instead to choose a the AES-CBC cipher for pay | "please use -A3 instead to choose AES cipher for payl | |||
load encryption."); | oad encryption"); | |||
cipher = N2N_TRANSFORM_ID_AESCBC; // default, if '-A' only | cipher = N2N_TRANSFORM_ID_AES; // default, if '-A' only | |||
} | } | |||
setPayloadEncryption(conf, cipher); | setPayloadEncryption(conf, cipher); | |||
break; | break; | |||
} | } | |||
case 'H': /* indicate header encryption */ | case 'H': /* indicate header encryption */ { | |||
{ | /* we cannot be sure if this gets parsed before the community name i | |||
/* we cannot be sure if this gets parsed before the community name is set | s set. | |||
. | * so, only an indicator is set, action is taken later*/ | |||
* so, only an indicator is set, action is taken later*/ | conf->header_encryption = HEADER_ENCRYPTION_ENABLED; | |||
conf->header_encryption = HEADER_ENCRYPTION_ENABLED; | break; | |||
break; | } | |||
} | ||||
case 'z': | case 'z': { | |||
{ | int compression; | |||
int compression; | ||||
if (optargument) { | if(optargument) { | |||
compression = atoi(optargument); | compression = atoi(optargument); | |||
} else | } else { | |||
compression = N2N_COMPRESSION_ID_LZO; // default, if '-z' only | traceEvent(TRACE_WARNING, "the use of the solitary -z switch is | |||
deprecated and will not be supported in future versions, " | ||||
"please use -z1 instead to choose LZO1X algorithm for | ||||
payload compression"); | ||||
setPayloadCompression(conf, compression); | compression = 1; // default, if '-z' only, equals -z1 | |||
break; | } | |||
} | ||||
case 'l': /* supernode-list */ | setPayloadCompression(conf, compression); | |||
if(optargument) { | break; | |||
if(edge_conf_add_supernode(conf, optargument) != 0) { | } | |||
traceEvent(TRACE_WARNING, "Too many supernodes!"); | ||||
exit(1); | ||||
} | ||||
break; | ||||
} | ||||
case 'i': /* supernode registration interval */ | case 'l': /* supernode-list */ { | |||
conf->register_interval = atoi(optargument); | if(optargument) { | |||
break; | if(edge_conf_add_supernode(conf, optargument) != 0) { | |||
traceEvent(TRACE_WARNING, "failed to add supernode '%s'", op | ||||
targument); | ||||
} | ||||
} | ||||
break; | ||||
} | ||||
case 'L': /* supernode registration interval */ | case 'i': /* supernode registration interval */ | |||
conf->register_ttl = atoi(optarg); | conf->register_interval = atoi(optargument); | |||
break; | break; | |||
case 'L': /* supernode registration interval */ | ||||
conf->register_ttl = atoi(optarg); | ||||
break; | ||||
#if defined(N2N_CAN_NAME_IFACE) | #if defined(N2N_CAN_NAME_IFACE) | |||
case 'd': /* TUNTAP name */ | case 'd': /* TUNTAP name */ { | |||
{ | strncpy(ec->tuntap_dev_name, optargument, N2N_IFNAMSIZ); | |||
strncpy(ec->tuntap_dev_name, optargument, N2N_IFNAMSIZ); | ec->tuntap_dev_name[N2N_IFNAMSIZ - 1] = '\0'; | |||
ec->tuntap_dev_name[N2N_IFNAMSIZ-1] = '\0'; | break; | |||
break; | } | |||
} | ||||
#endif | #endif | |||
case 'I': /* Device Description (hint) or username */ { | ||||
strncpy((char *)conf->dev_desc, optargument, N2N_DESC_SIZE); | ||||
conf->dev_desc[N2N_DESC_SIZE - 1] = '\0'; | ||||
break; | ||||
} | ||||
case 'p': | case 'J': /* password for user-password authentication */ { | |||
{ | if(!conf->shared_secret) /* we could already have it from environmen | |||
conf->local_port = atoi(optargument); | t variable, see edge_init_conf_defaults() */ | |||
break; | conf->shared_secret = calloc(1, sizeof(n2n_private_public_key_t) | |||
} | ); | |||
if(conf->shared_secret) | ||||
generate_private_key(*(conf->shared_secret), optargument); | ||||
// the hash of the username (-I) gets xored into this key later, | ||||
// we can't be sure to already have it at this point | ||||
// also, the complete shared secret will be calculated then as we | ||||
// might still be missing the federation public key as well | ||||
break; | ||||
} | ||||
case 't': | case 'P': /* federation public key for user-password authentication */ { | |||
{ | if(strlen(optargument) < ((N2N_PRIVATE_PUBLIC_KEY_SIZE * 8 + 5)/ 6 + | |||
conf->mgmt_port = atoi(optargument); | 1)) { | |||
break; | conf->federation_public_key = calloc(1, sizeof(n2n_private_publi | |||
} | c_key_t)); | |||
if(conf->federation_public_key) { | ||||
ascii_to_bin(*(conf->federation_public_key), optargument); | ||||
} | ||||
} else { | ||||
traceEvent(TRACE_WARNING, "public key too long"); | ||||
return 2; | ||||
} | ||||
break; | ||||
} | ||||
#ifdef __linux__ | case 'p': { | |||
case 'T': | char* colon = strpbrk(optargument, ":"); | |||
{ | if(colon) { /*ip address:port */ | |||
if((optargument[0] == '0') && (optargument[1] == 'x')) | *colon = 0; | |||
conf->tos = strtol(&optargument[2], NULL, 16); | conf->bind_address = ntohl(inet_addr(optargument)); | |||
else | conf->local_port = atoi(++colon); | |||
conf->tos = atoi(optargument); | ||||
if(conf->bind_address == INADDR_NONE) { | ||||
traceEvent(TRACE_WARNING, "bad address to bind to, binding t | ||||
o any IP address"); | ||||
conf->bind_address = INADDR_ANY; | ||||
} | ||||
if(conf->local_port == 0) { | ||||
traceEvent(TRACE_WARNING, "bad local port format, using OS a | ||||
ssigned port"); | ||||
} | ||||
} else { /* ip address or port only */ | ||||
char* dot = strpbrk(optargument, "."); | ||||
if(dot) { /* ip address only */ | ||||
conf->bind_address = ntohl(inet_addr(optargument)); | ||||
if(conf->bind_address == INADDR_NONE) { | ||||
traceEvent(TRACE_WARNING, "bad address to bind to, bindi | ||||
ng to any IP address"); | ||||
conf->bind_address = INADDR_ANY; | ||||
} | ||||
} else { /* port only */ | ||||
conf->local_port = atoi(optargument); | ||||
if(conf->local_port == 0) { | ||||
traceEvent(TRACE_WARNING, "bad local port format, using | ||||
OS assigned port"); | ||||
} | ||||
} | ||||
} | ||||
break; | ||||
} | ||||
break; | case 'e': { | |||
} | in_addr_t address_tmp; | |||
#endif | ||||
case 'n': | if(optargument) { | |||
{ | ||||
char cidr_net[64], gateway[64]; | ||||
n2n_route_t route; | ||||
if(sscanf(optargument, "%63[^/]/%d:%63s", cidr_net, &route.net_bitlen, gat | if(!strcmp(optargument, "auto")) { | |||
eway) != 3) { | address_tmp = INADDR_ANY; | |||
traceEvent(TRACE_WARNING, "Bad cidr/gateway format '%d'. See -h.", optar | conf->preferred_sock_auto = 1; | |||
gument); | } else { | |||
break; | address_tmp = inet_addr(optargument); | |||
} | } | |||
memcpy(&(conf->preferred_sock.addr.v4), &(address_tmp), IPV4_SIZ | ||||
E); | ||||
if(address_tmp == INADDR_NONE) { | ||||
traceEvent(TRACE_WARNING, "bad address for preferred local s | ||||
ocket, skipping"); | ||||
conf->preferred_sock.family = AF_INVALID; | ||||
break; | ||||
} else { | ||||
conf->preferred_sock.family = AF_INET; | ||||
// port is set after parsing all cli parameters during super | ||||
node_connect() | ||||
} | ||||
route.net_addr = inet_addr(cidr_net); | } | |||
route.gateway = inet_addr(gateway); | ||||
if((route.net_bitlen < 0) || (route.net_bitlen > 32)) { | break; | |||
traceEvent(TRACE_WARNING, "Bad prefix '%d' in '%s'", route.net_bitlen, o | } | |||
ptargument); | ||||
break; | ||||
} | ||||
if(route.net_addr == INADDR_NONE) { | case 't': { | |||
traceEvent(TRACE_WARNING, "Bad network '%s' in '%s'", cidr_net, optargum | conf->mgmt_port = atoi(optargument); | |||
ent); | break; | |||
break; | } | |||
} | #ifdef __linux__ | |||
case 'T': { | ||||
if((optargument[0] == '0') && (optargument[1] == 'x')) | ||||
conf->tos = strtol(&optargument[2], NULL, 16); | ||||
else | ||||
conf->tos = atoi(optargument); | ||||
if(route.gateway == INADDR_NONE) { | break; | |||
traceEvent(TRACE_WARNING, "Bad gateway '%s' in '%s'", gateway, optargume | } | |||
nt); | #endif | |||
break; | case 'n': { | |||
} | char cidr_net[64], gateway[64]; | |||
n2n_route_t route; | ||||
if(sscanf(optargument, "%63[^/]/%hhd:%63s", cidr_net, &route.net_bit | ||||
len, gateway) != 3) { | ||||
traceEvent(TRACE_WARNING, "bad cidr/gateway format '%d'", optarg | ||||
ument); | ||||
return 2; | ||||
} | ||||
route.net_addr = inet_addr(cidr_net); | ||||
route.gateway = inet_addr(gateway); | ||||
if((route.net_bitlen < 0) || (route.net_bitlen > 32)) { | ||||
traceEvent(TRACE_WARNING, "bad prefix '%d' in '%s'", route.net_b | ||||
itlen, optargument); | ||||
return 2; | ||||
} | ||||
if(route.net_addr == INADDR_NONE) { | ||||
traceEvent(TRACE_WARNING, "bad network '%s' in '%s'", cidr_net, | ||||
optargument); | ||||
return 2; | ||||
} | ||||
if(route.gateway == INADDR_NONE) { | ||||
traceEvent(TRACE_WARNING, "bad gateway '%s' in '%s'", gateway, o | ||||
ptargument); | ||||
return 2; | ||||
} | ||||
traceEvent(TRACE_NORMAL, "adding %s/%d via %s", cidr_net, route.net_ | ||||
bitlen, gateway); | ||||
conf->routes = realloc(conf->routes, sizeof(struct n2n_route) * (con | ||||
f->num_routes + 1)); | ||||
conf->routes[conf->num_routes] = route; | ||||
conf->num_routes++; | ||||
traceEvent(TRACE_DEBUG, "Adding %s/%d via %s", cidr_net, route.net_bitlen, | break; | |||
gateway); | } | |||
conf->routes = realloc(conf->routes, sizeof(struct n2n_route) * (conf->num | case 'S': { | |||
_routes + 1)); | int solitude; | |||
conf->routes[conf->num_routes] = route; | if(optargument) { | |||
conf->num_routes++; | solitude = atoi(optargument); | |||
} else { | ||||
traceEvent(TRACE_WARNING, "the use of the solitary -S switch is | ||||
deprecated and will not be supported in future versions, " | ||||
"please use -S1 instead to choose supernode-only conn | ||||
ection via UDP"); | ||||
solitude = 1; | ||||
} | ||||
// set the level | ||||
if(solitude >= 1) | ||||
conf->allow_p2p = 0; | ||||
#ifdef N2N_HAVE_TCP | ||||
if(solitude == 2) | ||||
conf->connect_tcp = 1; | ||||
#endif | ||||
break; | ||||
} | ||||
break; | case '[': /* round-trip-time-based supernode selection strategy */ { | |||
} | // overwrites the default load-based strategy | |||
conf->sn_selection_strategy = SN_SELECTION_STRATEGY_RTT; | ||||
case 's': /* Subnet Mask */ | break; | |||
{ | } | |||
if(0 != ec->got_s) { | ||||
traceEvent(TRACE_WARNING, "Multiple subnet masks supplied"); | ||||
} | ||||
strncpy(ec->netmask, optargument, N2N_NETMASK_STR_SIZE); | ||||
ec->netmask[N2N_NETMASK_STR_SIZE - 1] = '\0'; | ||||
ec->got_s = 1; | ||||
break; | ||||
} | ||||
case 'S': | case ']': /* password for management port */ { | |||
{ | conf->mgmt_password_hash = pearson_hash_64((uint8_t*)optargument, st | |||
conf->allow_p2p = 0; | rlen(optargument)); | |||
break; | ||||
} | ||||
case 'h': /* help */ | break; | |||
{ | } | |||
help(); | ||||
break; | ||||
} | ||||
case 'v': /* verbose */ | case 'h': /* quick reference */ { | |||
setTraceLevel(getTraceLevel() + 1); | return 2; | |||
break; | } | |||
default: | case '@': /* long help */ { | |||
{ | return 3; | |||
traceEvent(TRACE_WARNING, "Unknown option -%c: Ignored", (char)optkey); | } | |||
return(-1); | ||||
case 'v': /* verbose */ | ||||
setTraceLevel(getTraceLevel() + 1); | ||||
break; | ||||
case 'R': /* network traffic filter */ { | ||||
filter_rule_t *new_rule = malloc(sizeof(filter_rule_t)); | ||||
memset(new_rule, 0, sizeof(filter_rule_t)); | ||||
if(process_traffic_filter_rule_str(optargument, new_rule)) { | ||||
HASH_ADD(hh, conf->network_traffic_filter_rules, key, sizeof(fil | ||||
ter_rule_key_t), new_rule); | ||||
} else { | ||||
free(new_rule); | ||||
traceEvent(TRACE_WARNING, "invalid filter rule: %s", optargument | ||||
); | ||||
return 2; | ||||
} | ||||
break; | ||||
} | ||||
#ifdef WIN32 | ||||
case 'x': { | ||||
conf->metric = atoi(optargument); | ||||
ec->metric = atoi(optargument); | ||||
break; | ||||
} | ||||
#endif | ||||
default: { | ||||
traceEvent(TRACE_WARNING, "unknown option -%c", (char)optkey); | ||||
return 2; | ||||
} | ||||
} | } | |||
} | ||||
return(0); | return 0; | |||
} | } | |||
/* *********************************************** */ | /* *********************************************** */ | |||
static const struct option long_options[] = | static const struct option long_options[] = | |||
{ | { | |||
{ "community", required_argument, NULL, 'c' }, | { "community", required_argument, NULL, 'c' }, | |||
{ "supernode-list", required_argument, NULL, 'l' }, | { "supernode-list", required_argument, NULL, 'l' }, | |||
{ "tun-device", required_argument, NULL, 'd' }, | { "tap-device", required_argument, NULL, 'd' }, | |||
{ "euid", required_argument, NULL, 'u' }, | { "euid", required_argument, NULL, 'u' }, | |||
{ "egid", required_argument, NULL, 'g' }, | { "egid", required_argument, NULL, 'g' }, | |||
{ "help" , no_argument, NULL, 'h' }, | { "verbose", no_argument, NULL, 'v' }, | |||
{ "verbose", no_argument, NULL, 'v' }, | { "help", no_argument, NULL, '@' }, /* internal spe | |||
{ NULL, 0, NULL, 0 } | cial character '@' to identify long help case */ | |||
}; | { "select-rtt", no_argument, NULL, '[' }, /* | |||
'[' rtt selection strategy */ | ||||
{ "management-password", required_argument, NULL, ']' }, /* | ||||
']' management port password */ | ||||
{ NULL, 0, NULL, 0 } | ||||
}; | ||||
/* *************************************************** */ | /* *************************************************** */ | |||
/* read command line options */ | /* read command line options */ | |||
static int loadFromCLI(int argc, char *argv[], n2n_edge_conf_t *conf, n2n_tuntap | static int loadFromCLI (int argc, char *argv[], n2n_edge_conf_t *conf, n2n_tunta | |||
_priv_config_t *ec) { | p_priv_config_t *ec) { | |||
u_char c; | ||||
u_char c; | ||||
while ((c = getopt_long(argc, argv, | while ((c = getopt_long(argc, argv, | |||
"k:a:bc:Eu:g:m:M:s:d:l:p:fvhrt:i:SDL:z::A::Hn:" | "k:a:c:Eu:g:m:M:s:d:l:p:fvhrt:i:I:J:P:S::DL:z::A::Hn | |||
:R:e:" | ||||
#ifdef __linux__ | #ifdef __linux__ | |||
"T:" | "T:" | |||
#endif | #endif | |||
, | #ifdef WIN32 | |||
long_options, NULL)) != '?') { | "x:" | |||
if(c == 255) break; | #endif | |||
setOption(c, optarg, ec, conf); | , | |||
} | long_options, NULL)) != '?') { | |||
return 0; | ||||
} | ||||
/* *************************************************** */ | ||||
static char *trim(char *s) { | ||||
char *end; | ||||
while(isspace(s[0]) || (s[0] == '"') || (s[0] == '\'')) s++; | if(c == 255) break; | |||
if(s[0] == 0) return s; | help(setOption(c, optarg, ec, conf)); | |||
end = &s[strlen(s) - 1]; | } | |||
while(end > s | ||||
&& (isspace(end[0])|| (end[0] == '"') || (end[0] == '\''))) | ||||
end--; | ||||
end[1] = 0; | ||||
return s; | return 0; | |||
} | } | |||
/* *************************************************** */ | /* *************************************************** */ | |||
/* parse the configuration file */ | static char *trim (char *s) { | |||
static int loadFromFile(const char *path, n2n_edge_conf_t *conf, n2n_tuntap_priv | ||||
_config_t *ec) { | ||||
char buffer[4096], *line, *key, *value; | ||||
u_int line_len, opt_name_len; | ||||
FILE *fd; | ||||
const struct option *opt; | ||||
fd = fopen(path, "r"); | ||||
if(fd == NULL) { | char *end; | |||
traceEvent(TRACE_WARNING, "Config file %s not found", path); | ||||
return -1; | ||||
} | ||||
while((line = fgets(buffer, sizeof(buffer), fd)) != NULL) { | while(isspace(s[0]) || (s[0] == '"') || (s[0] == '\'')) s++; | |||
line = trim(line); | if(s[0] == 0) return s; | |||
value = NULL; | ||||
if((line_len = strlen(line)) < 2 || line[0] == '#') | end = &s[strlen(s) - 1]; | |||
continue; | while(end > s | |||
&& (isspace(end[0])|| (end[0] == '"') || (end[0] == '\''))) | ||||
end--; | ||||
end[1] = 0; | ||||
if(!strncmp(line, "--", 2)) { /* long opt */ | return s; | |||
key = &line[2], line_len -= 2; | } | |||
opt = long_options; | ||||
while(opt->name != NULL) { | ||||
opt_name_len = strlen(opt->name); | ||||
if(!strncmp(key, opt->name, opt_name_len) | ||||
&& (line_len <= opt_name_len | ||||
|| key[opt_name_len] == '\0' | ||||
|| key[opt_name_len] == ' ' | ||||
|| key[opt_name_len] == '=')) { | ||||
if(line_len > opt_name_len) key[opt_name_len] = '\0'; | ||||
if(line_len > opt_name_len + 1) value = trim(&key[opt_name_len + 1]); | ||||
// traceEvent(TRACE_NORMAL, "long key: %s value: %s", key, value); | ||||
setOption(opt->val, value, ec, conf); | ||||
break; | ||||
} | ||||
opt++; | ||||
} | ||||
} else if(line[0] == '-') { /* short opt */ | ||||
char *equal; | ||||
key = &line[1], line_len--; | ||||
equal = strchr(line, '='); | ||||
if(equal) { | /* *************************************************** */ | |||
equal[0] = '\0'; | ||||
value = &equal[1]; | /* parse the configuration file */ | |||
static int loadFromFile (const char *path, n2n_edge_conf_t *conf, n2n_tuntap_pri | ||||
v_config_t *ec) { | ||||
} else { | char buffer[4096], *line; | |||
char *line_vec[3]; | ||||
int tmp; | ||||
value = NULL; | FILE *fd; | |||
/* Adding an exception for -A_ -z_ which can come | fd = fopen(path, "r"); | |||
without '=' and even without any further data */ | ||||
if (key[0] == 'z') { | if(fd == NULL) { | |||
if (key[1]) value = &key[1]; | traceEvent(TRACE_WARNING, "config file %s not found", path); | |||
key = "z"; | return -1; | |||
} else if (key[0] == 'A') { | ||||
if (key[1]) value = &key[1]; | ||||
key = "A"; | ||||
} | ||||
} | ||||
// traceEvent(TRACE_NORMAL, "key: %c value: %s", key[0], value); | ||||
setOption(key[0], value, ec, conf); | ||||
} else { | ||||
traceEvent(TRACE_WARNING, "Skipping unrecognized line: %s", line); | ||||
continue; | ||||
} | } | |||
} | ||||
fclose(fd); | ||||
return 0; | ||||
} | ||||
/* ************************************** */ | ||||
#if defined(DUMMY_ID_00001) /* Disabled waiting for config option to enable it * | // we mess around with optind, better save it | |||
/ | tmp = optind; | |||
static char gratuitous_arp[] = { | while((line = fgets(buffer, sizeof(buffer), fd)) != NULL) { | |||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* Dest mac * | line = trim(line); | |||
/ | ||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Src mac */ | ||||
0x08, 0x06, /* ARP */ | ||||
0x00, 0x01, /* Ethernet */ | ||||
0x08, 0x00, /* IP */ | ||||
0x06, /* Hw Size */ | ||||
0x04, /* Protocol Size */ | ||||
0x00, 0x01, /* ARP Request */ | ||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Src mac */ | ||||
0x00, 0x00, 0x00, 0x00, /* Src IP */ | ||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Target mac | ||||
*/ | ||||
0x00, 0x00, 0x00, 0x00 /* Target IP */ | ||||
}; | ||||
/* ************************************** */ | if(strlen(line) < 2 || line[0] == '#') | |||
continue; | ||||
/** Build a gratuitous ARP packet for a /24 layer 3 (IP) network. */ | // executable, cannot be omitted, content can be anything | |||
static int build_gratuitous_arp(char *buffer, uint16_t buffer_len) { | line_vec[0] = line; | |||
if(buffer_len < sizeof(gratuitous_arp)) return(-1); | // first token, e.g. `-p` or `-A3', eventually followed by a whitespace | |||
or '=' delimiter | ||||
memcpy(buffer, gratuitous_arp, sizeof(gratuitous_arp)); | line_vec[1] = strtok(line, "\t ="); | |||
memcpy(&buffer[6], device.mac_addr, 6); | // separate parameter option, if present | |||
memcpy(&buffer[22], device.mac_addr, 6); | line_vec[2] = strtok(NULL, ""); | |||
memcpy(&buffer[28], &device.ip_addr, 4); | if(line_vec[2]) | |||
line_vec[2] = trim(line_vec[2]); | ||||
/* REVISIT: BbMaj7 - use a real netmask here. This is valid only by accident | // not to duplicate the option parser code, call loadFromCLI and pretend | |||
* for /24 IPv4 networks. */ | we have no option read yet at all | |||
buffer[31] = 0xFF; /* Use a faked broadcast address */ | optind = 0; | |||
memcpy(&buffer[38], &device.ip_addr, 4); | // if second token present (optional argument, not part of first), then | |||
return(sizeof(gratuitous_arp)); | announce 3 vector members | |||
} | loadFromCLI(line_vec[2] ? 3 : 2, line_vec, conf, ec); | |||
} | ||||
/* ************************************** */ | fclose(fd); | |||
optind = tmp; | ||||
/** Called from update_supernode_reg to periodically send gratuitous ARP | return 0; | |||
* broadcasts. */ | ||||
static void send_grat_arps(n2n_edge_t * eee,) { | ||||
char buffer[48]; | ||||
size_t len; | ||||
traceEvent(TRACE_NORMAL, "Sending gratuitous ARP..."); | ||||
len = build_gratuitous_arp(buffer, sizeof(buffer)); | ||||
send_packet2net(eee, buffer, len); | ||||
send_packet2net(eee, buffer, len); /* Two is better than one :-) */ | ||||
} | } | |||
#endif /* #if defined(DUMMY_ID_00001) */ | ||||
/* ************************************** */ | /* ************************************** */ | |||
static void daemonize() { | ||||
#ifndef WIN32 | #ifndef WIN32 | |||
int childpid; | static void daemonize () { | |||
int childpid; | ||||
traceEvent(TRACE_NORMAL, "Parent process is exiting (this is normal)"); | traceEvent(TRACE_NORMAL, "parent process is exiting (this is normal)"); | |||
signal(SIGPIPE, SIG_IGN); | signal(SIGPIPE, SIG_IGN); | |||
signal(SIGHUP, SIG_IGN); | signal(SIGHUP, SIG_IGN); | |||
signal(SIGCHLD, SIG_IGN); | signal(SIGCHLD, SIG_IGN); | |||
signal(SIGQUIT, SIG_IGN); | signal(SIGQUIT, SIG_IGN); | |||
if((childpid = fork()) < 0) | if((childpid = fork()) < 0) | |||
traceEvent(TRACE_ERROR, "Occurred while daemonizing (errno=%d)", | traceEvent(TRACE_ERROR, "occurred while daemonizing (errno=%d)", | |||
errno); | errno); | |||
else { | else { | |||
if(!childpid) { /* child */ | if(!childpid) { /* child */ | |||
int rc; | int rc; | |||
//traceEvent(TRACE_NORMAL, "Bye bye: I'm becoming a daemon..."); | //traceEvent(TRACE_NORMAL, "Bye bye: I'm becoming a daemon..."); | |||
rc = chdir("/"); | rc = chdir("/"); | |||
if(rc != 0) | if(rc != 0) | |||
traceEvent(TRACE_ERROR, "Error while moving to / directory"); | traceEvent(TRACE_ERROR, "error while moving to / directory"); | |||
setsid(); /* detach from the terminal */ | setsid(); /* detach from the terminal */ | |||
fclose(stdin); | fclose(stdin); | |||
fclose(stdout); | fclose(stdout); | |||
/* fclose(stderr); */ | /* fclose(stderr); */ | |||
/* | /* | |||
* clear any inherited file mode creation mask | * clear any inherited file mode creation mask | |||
*/ | */ | |||
//umask(0); | //umask(0); | |||
/* | /* | |||
* Use line buffered stdout | * Use line buffered stdout | |||
*/ | */ | |||
/* setlinebuf (stdout); */ | /* setlinebuf (stdout); */ | |||
setvbuf(stdout, (char *)NULL, _IOLBF, 0); | setvbuf(stdout, (char *)NULL, _IOLBF, 0); | |||
} else /* father */ | } else /* father */ | |||
exit(0); | exit(0); | |||
} | } | |||
#endif | ||||
} | } | |||
#endif | ||||
/* *************************************************** */ | /* *************************************************** */ | |||
static int keep_on_running; | static int keep_on_running; | |||
#if defined(__linux__) || defined(WIN32) | #if defined(__linux__) || defined(WIN32) | |||
#ifdef WIN32 | #ifdef WIN32 | |||
BOOL WINAPI term_handler(DWORD sig) | BOOL WINAPI term_handler(DWORD sig) | |||
#else | #else | |||
static void term_handler(int sig) | static void term_handler(int sig) | |||
#endif | #endif | |||
{ | { | |||
static int called = 0; | static int called = 0; | |||
if(called) { | if(called) { | |||
traceEvent(TRACE_NORMAL, "Ok I am leaving now"); | traceEvent(TRACE_NORMAL, "ok, I am leaving now"); | |||
_exit(0); | _exit(0); | |||
} else { | } else { | |||
traceEvent(TRACE_NORMAL, "Shutting down..."); | traceEvent(TRACE_NORMAL, "shutting down..."); | |||
called = 1; | called = 1; | |||
} | } | |||
keep_on_running = 0; | keep_on_running = 0; | |||
#ifdef WIN32 | #ifdef WIN32 | |||
return(TRUE); | return(TRUE); | |||
#endif | #endif | |||
} | } | |||
#endif /* defined(__linux__) || defined(WIN32) */ | #endif /* defined(__linux__) || defined(WIN32) */ | |||
/* *************************************************** */ | /* *************************************************** */ | |||
/** Entry point to program from kernel. */ | /** Entry point to program from kernel. */ | |||
int main(int argc, char* argv[]) { | int main (int argc, char* argv[]) { | |||
int rc; | ||||
tuntap_dev tuntap; /* a tuntap device */ | int rc; | |||
n2n_edge_t *eee; /* single instance for this program */ | tuntap_dev tuntap; /* a tuntap device */ | |||
n2n_edge_conf_t conf; /* generic N2N edge config */ | n2n_edge_t *eee; /* single instance for this program */ | |||
n2n_tuntap_priv_config_t ec; /* config used for standalone program execution * | n2n_edge_conf_t conf; /* generic N2N edge config */ | |||
/ | n2n_tuntap_priv_config_t ec; /* config used for standalone program executio | |||
n */ | ||||
uint8_t runlevel = 0; /* bootstrap: runlevel */ | ||||
uint8_t seek_answer = 1; /* expecting answer from supernode | ||||
*/ | ||||
time_t now, last_action = 0; /* timeout */ | ||||
macstr_t mac_buf; /* output mac address */ | ||||
fd_set socket_mask; /* for supernode answer */ | ||||
struct timeval wait_time; /* timeout for sn answer */ | ||||
peer_info_t *scan, *scan_tmp; /* supernode iteration */ | ||||
uint16_t expected = sizeof(uint16_t); | ||||
uint16_t position = 0; | ||||
uint8_t pktbuf[N2N_SN_PKTBUF_SIZE + sizeof(uint16_t)]; /* buffer + prepende | ||||
d buffer length in case of tcp */ | ||||
#ifndef WIN32 | #ifndef WIN32 | |||
struct passwd *pw = NULL; | struct passwd *pw = NULL; | |||
#endif | #endif | |||
#ifdef HAVE_LIBCAP | #ifdef HAVE_LIBCAP | |||
cap_t caps; | cap_t caps; | |||
#endif | ||||
#ifdef WIN32 | ||||
initWin32(); | ||||
#endif | #endif | |||
/* Defaults */ | /* Defaults */ | |||
edge_init_conf_defaults(&conf); | edge_init_conf_defaults(&conf); | |||
memset(&ec, 0, sizeof(ec)); | memset(&ec, 0, sizeof(ec)); | |||
ec.mtu = DEFAULT_MTU; | ec.mtu = DEFAULT_MTU; | |||
ec.daemon = 1; /* By default run in daemon mode. */ | ec.daemon = 1; /* By default run in daemon mode. */ | |||
#ifndef WIN32 | #ifndef WIN32 | |||
if(((pw = getpwnam("n2n")) != NULL) || | if(((pw = getpwnam("n2n")) != NULL) || | |||
((pw = getpwnam("nobody")) != NULL)) { | ((pw = getpwnam("nobody")) != NULL)) { | |||
ec.userid = pw->pw_uid; | ec.userid = pw->pw_uid; | |||
ec.groupid = pw->pw_gid; | ec.groupid = pw->pw_gid; | |||
} | } | |||
#endif | #endif | |||
#ifdef WIN32 | #ifdef WIN32 | |||
ec.tuntap_dev_name[0] = '\0'; | ec.tuntap_dev_name[0] = '\0'; | |||
ec.metric = 0; | ||||
#else | #else | |||
snprintf(ec.tuntap_dev_name, sizeof(ec.tuntap_dev_name), "edge0"); | snprintf(ec.tuntap_dev_name, sizeof(ec.tuntap_dev_name), N2N_EDGE_DEFAULT_DE V_NAME); | |||
#endif | #endif | |||
snprintf(ec.ip_mode, sizeof(ec.ip_mode), "static"); | snprintf(ec.netmask, sizeof(ec.netmask), N2N_EDGE_DEFAULT_NETMASK); | |||
snprintf(ec.netmask, sizeof(ec.netmask), "255.255.255.0"); | ||||
if((argc >= 2) && (argv[1][0] != '-')) { | ||||
rc = loadFromFile(argv[1], &conf, &ec); | ||||
if(argc > 2) | ||||
rc = loadFromCLI(argc, argv, &conf, &ec); | ||||
} else if(argc > 1) | ||||
rc = loadFromCLI(argc, argv, &conf, &ec); | ||||
else | ||||
if((argc >= 2) && (argv[1][0] != '-')) { | ||||
rc = loadFromFile(argv[1], &conf, &ec); | ||||
if(argc > 2) | ||||
rc = loadFromCLI(argc, argv, &conf, &ec); | ||||
} else if(argc > 1) | ||||
rc = loadFromCLI(argc, argv, &conf, &ec); | ||||
else | ||||
#ifdef WIN32 | #ifdef WIN32 | |||
/* Load from current directory */ | // load from current directory | |||
rc = loadFromFile("edge.conf", &conf, &ec); | rc = loadFromFile("edge.conf", &conf, &ec); | |||
#else | #else | |||
rc = -1; | rc = -1; | |||
#endif | #endif | |||
if(conf.transop_id == N2N_TRANSFORM_ID_NULL) { | // --- additional crypto setup; REVISIT: move to edge_init()? | |||
if(conf.encrypt_key) { | // payload | |||
/* make sure that Twofish is default cipher if key only (and no cipher) is | if(conf.transop_id == N2N_TRANSFORM_ID_NULL) { | |||
specified */ | if(conf.encrypt_key) { | |||
traceEvent(TRACE_WARNING, "Switching to Twofish as key was provided."); | // make sure that AES is default cipher if key only (and no cipher) | |||
conf.transop_id = N2N_TRANSFORM_ID_TWOFISH; | is specified | |||
traceEvent(TRACE_WARNING, "switching to AES as key was provided"); | ||||
conf.transop_id = N2N_TRANSFORM_ID_AES; | ||||
} | ||||
} | ||||
// user auth | ||||
if(conf.shared_secret /* containing private key only so far*/) { | ||||
// if user-password auth and no federation public key provided, use defa | ||||
ult | ||||
if(!conf.federation_public_key) { | ||||
conf.federation_public_key = calloc(1, sizeof(n2n_private_public_key | ||||
_t)); | ||||
if(conf.federation_public_key) { | ||||
traceEvent(TRACE_WARNING, "using default federation public key; | ||||
FOR TESTING ONLY, usage of a custom federation name and key (-P) is highly recom | ||||
mended!"); | ||||
generate_private_key(*(conf.federation_public_key), FEDERATION_N | ||||
AME + 1); | ||||
generate_public_key(*(conf.federation_public_key), *(conf.federa | ||||
tion_public_key)); | ||||
} | ||||
} | ||||
// calculate public key and shared secret | ||||
if(conf.federation_public_key) { | ||||
traceEvent(TRACE_NORMAL, "using username and password for edge authe | ||||
ntication"); | ||||
bind_private_key_to_username(*(conf.shared_secret), (char *)conf.dev | ||||
_desc); | ||||
conf.public_key = calloc(1, sizeof(n2n_private_public_key_t)); | ||||
if(conf.public_key) | ||||
generate_public_key(*conf.public_key, *(conf.shared_secret)); | ||||
generate_shared_secret(*(conf.shared_secret), *(conf.shared_secret), | ||||
*(conf.federation_public_key)); | ||||
// prepare (first 128 bit) for use as key | ||||
conf.shared_secret_ctx = (he_context_t*)calloc(1, sizeof(speck_conte | ||||
xt_t)); | ||||
speck_init((speck_context_t**)&(conf.shared_secret_ctx), *(conf.shar | ||||
ed_secret), 128); | ||||
} | ||||
// force header encryption | ||||
if(conf.header_encryption != HEADER_ENCRYPTION_ENABLED) { | ||||
traceEvent(TRACE_NORMAL, "enabling header encryption for edge authen | ||||
tication"); | ||||
conf.header_encryption = HEADER_ENCRYPTION_ENABLED; | ||||
} | ||||
} | } | |||
} | ||||
if(rc < 0) | if(rc < 0) | |||
help(); | help(1); /* short help */ | |||
if(edge_verify_conf(&conf) != 0) | if(edge_verify_conf(&conf) != 0) | |||
help(); | help(1); /* short help */ | |||
traceEvent(TRACE_NORMAL, "Starting n2n edge %s %s", PACKAGE_VERSION, PACKAGE_B UILDDATE); | traceEvent(TRACE_NORMAL, "starting n2n edge %s %s", PACKAGE_VERSION, PACKAGE _BUILDDATE); | |||
#if defined(HAVE_OPENSSL_1_1) | #if defined(HAVE_OPENSSL_1_1) | |||
traceEvent(TRACE_NORMAL, "Using %s", OpenSSL_version(0)); | traceEvent(TRACE_NORMAL, "using %s", OpenSSL_version(0)); | |||
#endif | #endif | |||
traceEvent(TRACE_NORMAL, "Using compression: %s.", compression_str(conf.compre | traceEvent(TRACE_NORMAL, "using compression: %s.", compression_str(conf.comp | |||
ssion)); | ression)); | |||
traceEvent(TRACE_NORMAL, "Using %s cipher.", transop_str(conf.transop_id)); | traceEvent(TRACE_NORMAL, "using %s cipher.", transop_str(conf.transop_id)); | |||
/* Random seed */ | /* Random seed */ | |||
n2n_srand (n2n_seed()); | n2n_srand (n2n_seed()); | |||
if(0 == strcmp("dhcp", ec.ip_mode)) { | #ifndef WIN32 | |||
traceEvent(TRACE_NORMAL, "Dynamic IP address assignment enabled."); | /* If running suid root then we need to setuid before using the force. */ | |||
if(setuid(0) != 0) | ||||
traceEvent(TRACE_ERROR, "unable to become root [%u/%s]", errno, strerror | ||||
(errno)); | ||||
/* setgid(0); */ | ||||
#endif | ||||
conf.dyn_ip_mode = 1; | if(conf.encrypt_key && !strcmp((char*)conf.community_name, conf.encrypt_key) | |||
} else | ) | |||
traceEvent(TRACE_NORMAL, "ip_mode='%s'", ec.ip_mode); | traceEvent(TRACE_WARNING, "community and encryption key must differ, oth | |||
erwise security will be compromised"); | ||||
if(!( | if((eee = edge_init(&conf, &rc)) == NULL) { | |||
#ifdef __linux__ | traceEvent(TRACE_ERROR, "failed in edge_init"); | |||
(ec.tuntap_dev_name[0] != 0) && | exit(1); | |||
#endif | } | |||
(ec.ip_addr[0] != 0) | ||||
)) | ||||
help(); | ||||
#ifndef WIN32 | memcpy(&(eee->tuntap_priv_conf), &ec, sizeof(ec)); | |||
/* If running suid root then we need to setuid before using the force. */ | ||||
if(setuid(0) != 0) | ||||
traceEvent(TRACE_ERROR, "Unable to become root [%u/%s]", errno, strerror(err | ||||
no)); | ||||
/* setgid(0); */ | ||||
#endif | ||||
if(conf.encrypt_key && !strcmp((char*)conf.community_name, conf.encrypt_key)) | if((0 == strcmp("static", eee->tuntap_priv_conf.ip_mode)) || | |||
traceEvent(TRACE_WARNING, "Community and encryption key must differ, otherwi | ((eee->tuntap_priv_conf.ip_mode[0] == '\0') && (eee->tuntap_priv_conf.i | |||
se security will be compromised"); | p_addr[0] != '\0'))) { | |||
traceEvent(TRACE_NORMAL, "use manually set IP address"); | ||||
eee->conf.tuntap_ip_mode = TUNTAP_IP_MODE_STATIC; | ||||
} else if(0 == strcmp("dhcp", eee->tuntap_priv_conf.ip_mode)) { | ||||
traceEvent(TRACE_NORMAL, "obtain IP from other edge DHCP services"); | ||||
eee->conf.tuntap_ip_mode = TUNTAP_IP_MODE_DHCP; | ||||
} else { | ||||
traceEvent(TRACE_NORMAL, "automatically assign IP address by supernode") | ||||
; | ||||
eee->conf.tuntap_ip_mode = TUNTAP_IP_MODE_SN_ASSIGN; | ||||
} | ||||
if(tuntap_open(&tuntap, ec.tuntap_dev_name, ec.ip_mode, ec.ip_addr, ec.ne | // mini main loop for bootstrap, not using main loop code because some of it | |||
tmask, ec.device_mac, ec.mtu) < 0) | s mechanisms do not fit in here | |||
exit(1); | // for the sake of quickly establishing connection. REVISIT when a more eleg | |||
ant way to re-use main loop code | ||||
// is found | ||||
// find at least one supernode alive to faster establish connection | ||||
// exceptions: | ||||
if((HASH_COUNT(eee->conf.supernodes) <= 1) || (eee->conf.connect_tcp) || (ee | ||||
e->conf.shared_secret)) { | ||||
// skip the initial supernode ping | ||||
traceEvent(TRACE_DEBUG, "skip PING to supernode"); | ||||
runlevel = 2; | ||||
} | ||||
eee->last_sup = 0; /* if it wasn't zero yet */ | ||||
eee->curr_sn = eee->conf.supernodes; | ||||
supernode_connect(eee); | ||||
while(runlevel < 5) { | ||||
now = time(NULL); | ||||
// we do not use switch-case because we also check for 'greater than' | ||||
if(runlevel == 0) { /* PING to all known supernodes */ | ||||
last_action = now; | ||||
eee->sn_pong = 0; | ||||
// (re-)initialize the number of max concurrent pings (decreases by | ||||
calling send_query_peer) | ||||
eee->conf.number_max_sn_pings = NUMBER_SN_PINGS_INITIAL; | ||||
send_query_peer(eee, null_mac); | ||||
traceEvent(TRACE_NORMAL, "send PING to supernodes"); | ||||
runlevel++; | ||||
} | ||||
if((eee = edge_init(&tuntap, &conf, &rc)) == NULL) { | if(runlevel == 1) { /* PING has been sent to all known supernodes */ | |||
traceEvent(TRACE_ERROR, "Failed in edge_init"); | if(eee->sn_pong) { | |||
exit(1); | // first answer | |||
} | eee->sn_pong = 0; | |||
memcpy(&(eee->tuntap_priv_conf), &ec, sizeof(ec)); | sn_selection_sort(&(eee->conf.supernodes)); | |||
eee->curr_sn = eee->conf.supernodes; | ||||
supernode_connect(eee); | ||||
traceEvent(TRACE_NORMAL, "received first PONG from supernode [%s | ||||
]", eee->curr_sn->ip_addr); | ||||
runlevel++; | ||||
} else if(last_action <= (now - BOOTSTRAP_TIMEOUT)) { | ||||
// timeout | ||||
runlevel--; | ||||
// skip waiting for answer to direcly go to send PING again | ||||
seek_answer = 0; | ||||
traceEvent(TRACE_DEBUG, "PONG timeout"); | ||||
} | ||||
} | ||||
#ifndef WIN32 | // by the way, have every later PONG cause the remaining (!) list to be | |||
if(ec.daemon) { | sorted because the entries | |||
setUseSyslog(1); /* traceEvent output now goes to syslog. */ | // before have already been tried; as opposed to initial PONG, do not ch | |||
daemonize(); | ange curr_sn | |||
} | if(runlevel > 1) { | |||
#endif /* #ifndef WIN32 */ | if(eee->sn_pong) { | |||
eee->sn_pong = 0; | ||||
if(eee->curr_sn->hh.next) { | ||||
sn_selection_sort((peer_info_t**)&(eee->curr_sn->hh.next)); | ||||
traceEvent(TRACE_DEBUG, "received additional PONG from super | ||||
node"); | ||||
// here, it is hard to detemine from which one, so no detail | ||||
s to output | ||||
} | ||||
} | ||||
} | ||||
if(runlevel == 2) { /* send REGISTER_SUPER to get auto ip address from a | ||||
supernode */ | ||||
if(eee->conf.tuntap_ip_mode == TUNTAP_IP_MODE_SN_ASSIGN) { | ||||
last_action = now; | ||||
eee->sn_wait = 1; | ||||
send_register_super(eee); | ||||
runlevel++; | ||||
traceEvent(TRACE_NORMAL, "send REGISTER_SUPER to supernode [%s] | ||||
asking for IP address", | ||||
eee->curr_sn->ip_addr); | ||||
} else { | ||||
runlevel += 2; /* skip waiting for TUNTAP IP address */ | ||||
traceEvent(TRACE_DEBUG, "skip auto IP address asignment"); | ||||
} | ||||
} | ||||
if(runlevel == 3) { /* REGISTER_SUPER to get auto ip address from a sn h | ||||
as been sent */ | ||||
if(!eee->sn_wait) { /* TUNTAP IP address received */ | ||||
runlevel++; | ||||
traceEvent(TRACE_NORMAL, "received REGISTER_SUPER_ACK from super | ||||
node for IP address asignment"); | ||||
// it should be from curr_sn, but we can't determine definitely | ||||
here, so no details to output | ||||
} else if(last_action <= (now - BOOTSTRAP_TIMEOUT)) { | ||||
// timeout, so try next supernode | ||||
if(eee->curr_sn->hh.next) | ||||
eee->curr_sn = eee->curr_sn->hh.next; | ||||
else | ||||
eee->curr_sn = eee->conf.supernodes; | ||||
supernode_connect(eee); | ||||
runlevel--; | ||||
// skip waiting for answer to direcly go to send REGISTER_SUPER | ||||
again | ||||
seek_answer = 0; | ||||
traceEvent(TRACE_DEBUG, "REGISTER_SUPER_ACK timeout"); | ||||
} | ||||
} | ||||
if(runlevel == 4) { /* configure the TUNTAP device, including routes */ | ||||
if(tuntap_open(&tuntap, eee->tuntap_priv_conf.tuntap_dev_name, eee-> | ||||
tuntap_priv_conf.ip_mode, | ||||
eee->tuntap_priv_conf.ip_addr, eee->tuntap_priv_conf. | ||||
netmask, | ||||
eee->tuntap_priv_conf.device_mac, eee->tuntap_priv_co | ||||
nf.mtu | ||||
#ifdef WIN32 | ||||
, eee->tuntap_priv_conf.metric | ||||
#endif | ||||
) < 0) | ||||
exit(1); | ||||
memcpy(&eee->device, &tuntap, sizeof(tuntap)); | ||||
traceEvent(TRACE_NORMAL, "created local tap device IP: %s, Mask: %s, | ||||
MAC: %s", | ||||
eee->tuntap_priv_conf.ip_addr, | ||||
eee->tuntap_priv_conf.netmask, | ||||
macaddr_str(mac_buf, eee->device.mac_addr)) | ||||
; | ||||
// routes | ||||
if(edge_init_routes(eee, eee->conf.routes, eee->conf.num_routes) < 0 | ||||
) { | ||||
traceEvent(TRACE_ERROR, "routes setup failed"); | ||||
exit(1); | ||||
} | ||||
runlevel = 5; | ||||
// no more answers required | ||||
seek_answer = 0; | ||||
} | ||||
// we usually wait for some answer, there however are exceptions when go | ||||
ing back to a previous runlevel | ||||
if(seek_answer) { | ||||
FD_ZERO(&socket_mask); | ||||
FD_SET(eee->sock, &socket_mask); | ||||
wait_time.tv_sec = BOOTSTRAP_TIMEOUT; | ||||
wait_time.tv_usec = 0; | ||||
if(select(eee->sock + 1, &socket_mask, NULL, NULL, &wait_time) > 0) | ||||
{ | ||||
if(FD_ISSET(eee->sock, &socket_mask)) { | ||||
fetch_and_eventually_process_data (eee, eee->sock, | ||||
pktbuf, &expected, &posit | ||||
ion, | ||||
now); | ||||
} | ||||
} | ||||
} | ||||
seek_answer = 1; | ||||
resolve_check(eee->resolve_parameter, 0 /* no intermediate resolution re | ||||
quirement at this point */, now); | ||||
} | ||||
// allow a higher number of pings for first regular round of ping | ||||
// to quicker get an inital 'supernode selection criterion overview' | ||||
eee->conf.number_max_sn_pings = NUMBER_SN_PINGS_INITIAL; | ||||
// shape supernode list; make current one the first on the list | ||||
HASH_ITER(hh, eee->conf.supernodes, scan, scan_tmp) { | ||||
if(scan == eee->curr_sn) | ||||
sn_selection_criterion_good(&(scan->selection_criterion)); | ||||
else | ||||
sn_selection_criterion_default(&(scan->selection_criterion)); | ||||
} | ||||
sn_selection_sort(&(eee->conf.supernodes)); | ||||
// do not immediately ping again, allow some time | ||||
eee->last_sweep = now - SWEEP_TIME + 2 * BOOTSTRAP_TIMEOUT; | ||||
eee->sn_wait = 1; | ||||
eee->last_register_req = 0; | ||||
#ifndef WIN32 | #ifndef WIN32 | |||
if(eee->tuntap_priv_conf.daemon) { | ||||
setUseSyslog(1); /* traceEvent output now goes to syslog. */ | ||||
daemonize(); | ||||
} | ||||
#ifdef HAVE_LIBCAP | #ifdef HAVE_LIBCAP | |||
/* Before dropping the privileges, retain capabilities to regain them in futur | /* Before dropping the privileges, retain capabilities to regain them in fut | |||
e. */ | ure. */ | |||
caps = cap_get_proc(); | caps = cap_get_proc(); | |||
cap_set_flag(caps, CAP_PERMITTED, num_cap, cap_values, CAP_SET); | cap_set_flag(caps, CAP_PERMITTED, num_cap, cap_values, CAP_SET); | |||
cap_set_flag(caps, CAP_EFFECTIVE, num_cap, cap_values, CAP_SET); | cap_set_flag(caps, CAP_EFFECTIVE, num_cap, cap_values, CAP_SET); | |||
if((cap_set_proc(caps) != 0) || (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) != 0)) | if((cap_set_proc(caps) != 0) || (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) != 0)) | |||
traceEvent(TRACE_WARNING, "Unable to retain permitted capabilities [%s]\n", | traceEvent(TRACE_WARNING, "unable to retain permitted capabilities [%s]\ | |||
strerror(errno)); | n", strerror(errno)); | |||
#else | #else | |||
#ifndef __APPLE__ | #ifndef __APPLE__ | |||
traceEvent(TRACE_WARNING, "n2n has not been compiled with libcap-dev. Some com mands may fail."); | traceEvent(TRACE_WARNING, "n2n has not been compiled with libcap-dev; some c ommands may fail"); | |||
#endif | #endif | |||
#endif /* HAVE_LIBCAP */ | #endif /* HAVE_LIBCAP */ | |||
if((ec.userid != 0) || (ec.groupid != 0)) { | if((eee->tuntap_priv_conf.userid != 0) || (eee->tuntap_priv_conf.groupid != | |||
traceEvent(TRACE_NORMAL, "Dropping privileges to uid=%d, gid=%d", | 0)) { | |||
(signed int)ec.userid, (signed int)ec.groupid); | traceEvent(TRACE_NORMAL, "dropping privileges to uid=%d, gid=%d", | |||
(signed int)eee->tuntap_priv_conf.userid, (signed int)eee->tu | ||||
/* Finished with the need for root privileges. Drop to unprivileged user. */ | ntap_priv_conf.groupid); | |||
if((setgid(ec.groupid) != 0) | ||||
|| (setuid(ec.userid) != 0)) { | /* Finished with the need for root privileges. Drop to unprivileged user | |||
traceEvent(TRACE_ERROR, "Unable to drop privileges [%u/%s]", errno, strerr | . */ | |||
or(errno)); | if((setgid(eee->tuntap_priv_conf.groupid) != 0) | |||
exit(1); | || (setuid(eee->tuntap_priv_conf.userid) != 0)) { | |||
traceEvent(TRACE_ERROR, "unable to drop privileges [%u/%s]", errno, | ||||
strerror(errno)); | ||||
exit(1); | ||||
} | ||||
} | } | |||
} | ||||
if((getuid() == 0) || (getgid() == 0)) | if((getuid() == 0) || (getgid() == 0)) | |||
traceEvent(TRACE_WARNING, "Running as root is discouraged, check out the -u/ | traceEvent(TRACE_WARNING, "running as root is discouraged, check out the | |||
-g options"); | -u/-g options"); | |||
#endif | #endif | |||
#ifdef __linux__ | #ifdef __linux__ | |||
signal(SIGTERM, term_handler); | signal(SIGPIPE, SIG_IGN); | |||
signal(SIGINT, term_handler); | signal(SIGTERM, term_handler); | |||
signal(SIGINT, term_handler); | ||||
#endif | #endif | |||
#ifdef WIN32 | #ifdef WIN32 | |||
SetConsoleCtrlHandler(term_handler, TRUE); | SetConsoleCtrlHandler(term_handler, TRUE); | |||
#endif | #endif | |||
keep_on_running = 1; | keep_on_running = 1; | |||
traceEvent(TRACE_NORMAL, "edge started"); | eee->keep_running = &keep_on_running; | |||
rc = run_edge_loop(eee, &keep_on_running); | traceEvent(TRACE_NORMAL, "edge started"); | |||
print_edge_stats(eee); | rc = run_edge_loop(eee); | |||
print_edge_stats(eee); | ||||
#ifdef HAVE_LIBCAP | #ifdef HAVE_LIBCAP | |||
/* Before completing the cleanup, regain the capabilities as some | /* Before completing the cleanup, regain the capabilities as some | |||
* cleanup tasks require them (e.g. routes cleanup). */ | * cleanup tasks require them (e.g. routes cleanup). */ | |||
cap_set_flag(caps, CAP_EFFECTIVE, num_cap, cap_values, CAP_SET); | cap_set_flag(caps, CAP_EFFECTIVE, num_cap, cap_values, CAP_SET); | |||
if(cap_set_proc(caps) != 0) | if(cap_set_proc(caps) != 0) | |||
traceEvent(TRACE_WARNING, "Could not regain the capabilities [%s]\n", strerr | traceEvent(TRACE_WARNING, "could not regain the capabilities [%s]\n", st | |||
or(errno)); | rerror(errno)); | |||
cap_free(caps); | cap_free(caps); | |||
#endif | #endif | |||
/* Cleanup */ | /* Cleanup */ | |||
edge_term(eee); | edge_term_conf(&eee->conf); | |||
edge_term_conf(&conf); | tuntap_close(&eee->device); | |||
tuntap_close(&tuntap); | edge_term(eee); | |||
if(conf.encrypt_key) free(conf.encrypt_key); | #ifdef WIN32 | |||
destroyWin32(); | ||||
#endif | ||||
return(rc); | return(rc); | |||
} | } | |||
/* ************************************** */ | /* ************************************** */ | |||
End of changes. 170 change blocks. | ||||
757 lines changed or deleted | 1242 lines changed or added |