"Fossies" - the Fresh Open Source Software Archive  

Source code changes of the file "src/edge.c" between
n2n-2.8.tar.gz and n2n-3.0.tar.gz

About: n2n is a layer-two peer-to-peer virtual private network (VPN) which allows bypassing intermediate firewalls.

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

Home  |  About  |  Features  |  All  |  Newest  |  Dox  |  Diffs  |  RSS Feeds  |  Screenshots  |  Comments  |  Imprint  |  Privacy  |  HTTP(S)