1449 lines
42 KiB
C
1449 lines
42 KiB
C
/*
|
|
* (C) 2007-09 - Luca Deri <deri@ntop.org>
|
|
* Richard Andrews <andrews@ntop.org>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not see see <http://www.gnu.org/licenses/>
|
|
*
|
|
* Code contributions courtesy of:
|
|
* Don Bindner <don.bindner@gmail.com>
|
|
* Sylwester Sosnowski <syso-n2n@no-route.org>
|
|
* Wilfried "Wonka" Klaebe
|
|
*
|
|
*/
|
|
|
|
#include "minilzo.h"
|
|
#include "n2n.h"
|
|
#include <assert.h>
|
|
#include <sys/stat.h>
|
|
|
|
/** Time between logging system STATUS messages */
|
|
#define STATUS_UPDATE_INTERVAL (30 * 60) /*secs*/
|
|
|
|
/* maximum length of command line arguments */
|
|
#define MAX_CMDLINE_BUFFER_LENGTH 4096
|
|
/* maximum length of a line in the configuration file */
|
|
#define MAX_CONFFILE_LINE_LENGTH 1024
|
|
|
|
struct n2n_edge
|
|
{
|
|
u_char re_resolve_supernode_ip;
|
|
struct peer_addr supernode;
|
|
char supernode_ip[48];
|
|
char * community_name /*= NULL*/;
|
|
|
|
/* int sock; */
|
|
/* char is_udp_socket /\*= 1*\/; */
|
|
n2n_sock_info_t sinfo;
|
|
|
|
u_int pkt_sent /*= 0*/;
|
|
tuntap_dev device;
|
|
int allow_routing /*= 0*/;
|
|
int drop_ipv6_ndp /*= 0*/;
|
|
char * encrypt_key /* = NULL*/;
|
|
TWOFISH * enc_tf;
|
|
TWOFISH * dec_tf;
|
|
|
|
struct peer_info * known_peers /* = NULL*/;
|
|
struct peer_info * pending_peers /* = NULL*/;
|
|
time_t last_register /* = 0*/;
|
|
};
|
|
|
|
static void supernode2addr(n2n_edge_t * eee, char* addr);
|
|
|
|
static void send_packet2net(n2n_edge_t * eee,
|
|
char *decrypted_msg, size_t len);
|
|
|
|
|
|
/* ************************************** */
|
|
|
|
/* parse the configuration file */
|
|
static int readConfFile(const char * filename, char * const linebuffer) {
|
|
struct stat stats;
|
|
FILE * fd;
|
|
char * buffer = NULL;
|
|
|
|
buffer = (char *)malloc(MAX_CONFFILE_LINE_LENGTH);
|
|
if (!buffer) {
|
|
traceEvent( TRACE_ERROR, "Unable to allocate memory");
|
|
return -1;
|
|
}
|
|
|
|
if (stat(filename, &stats)) {
|
|
if (errno == ENOENT)
|
|
traceEvent(TRACE_ERROR, "parameter file %s not found/unable to access\n", filename);
|
|
else
|
|
traceEvent(TRACE_ERROR, "cannot stat file %s, errno=%d\n",filename, errno);
|
|
free(buffer);
|
|
return -1;
|
|
}
|
|
|
|
fd = fopen(filename, "rb");
|
|
if (!fd) {
|
|
traceEvent(TRACE_ERROR, "Unable to open parameter file '%s' (%d)...\n",filename,errno);
|
|
free(buffer);
|
|
return -1;
|
|
}
|
|
while(fgets(buffer, MAX_CONFFILE_LINE_LENGTH,fd)) {
|
|
char * p = NULL;
|
|
|
|
/* strip out comments */
|
|
p = strchr(buffer, '#');
|
|
if (p) *p ='\0';
|
|
|
|
/* remove \n */
|
|
p = strchr(buffer, '\n');
|
|
if (p) *p ='\0';
|
|
|
|
/* strip out heading spaces */
|
|
p = buffer;
|
|
while(*p == ' ' && *p != '\0') ++p;
|
|
if (p != buffer) strncpy(buffer,p,strlen(p)+1);
|
|
|
|
/* strip out trailing spaces */
|
|
while(strlen(buffer) && buffer[strlen(buffer)-1]==' ')
|
|
buffer[strlen(buffer)-1]= '\0';
|
|
|
|
/* check for nested @file option */
|
|
if (strchr(buffer, '@')) {
|
|
traceEvent(TRACE_ERROR, "@file in file nesting is not supported\n");
|
|
free(buffer);
|
|
return -1;
|
|
}
|
|
if ((strlen(linebuffer)+strlen(buffer)+2)< MAX_CMDLINE_BUFFER_LENGTH) {
|
|
strncat(linebuffer, " ", 1);
|
|
strncat(linebuffer, buffer, strlen(buffer));
|
|
} else {
|
|
traceEvent(TRACE_ERROR, "too many argument");
|
|
free(buffer);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
free(buffer);
|
|
fclose(fd);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Create the argv vector */
|
|
static char ** buildargv(char * const linebuffer) {
|
|
const int INITIAL_MAXARGC = 16; /* Number of args + NULL in initial argv */
|
|
int maxargc;
|
|
int argc=0;
|
|
char ** argv;
|
|
char * buffer, * buff;
|
|
|
|
buffer = (char *)calloc(1, strlen(linebuffer)+2);
|
|
if (!buffer) {
|
|
traceEvent( TRACE_ERROR, "Unable to allocate memory");
|
|
return NULL;
|
|
}
|
|
strncpy(buffer, linebuffer,strlen(linebuffer));
|
|
|
|
maxargc = INITIAL_MAXARGC;
|
|
argv = (char **)malloc(maxargc * sizeof(char*));
|
|
if (argv == NULL) {
|
|
traceEvent( TRACE_ERROR, "Unable to allocate memory");
|
|
return NULL;
|
|
}
|
|
buff = buffer;
|
|
while(buff) {
|
|
char * p = strchr(buff,' ');
|
|
if (p) {
|
|
*p='\0';
|
|
argv[argc++] = strdup(buff);
|
|
while(*++p == ' ' && *p != '\0');
|
|
buff=p;
|
|
if (argc >= maxargc) {
|
|
maxargc *= 2;
|
|
argv = (char **)realloc(argv, maxargc * sizeof(char*));
|
|
if (argv == NULL) {
|
|
traceEvent(TRACE_ERROR, "Unable to re-allocate memory");
|
|
free(buffer);
|
|
return NULL;
|
|
}
|
|
}
|
|
} else {
|
|
argv[argc++] = strdup(buff);
|
|
break;
|
|
}
|
|
}
|
|
argv[argc] = NULL;
|
|
free(buffer);
|
|
return argv;
|
|
}
|
|
|
|
|
|
|
|
/* ************************************** */
|
|
|
|
static int edge_init(n2n_edge_t * eee) {
|
|
#ifdef WIN32
|
|
initWin32();
|
|
#endif
|
|
memset(eee, 0, sizeof(n2n_edge_t));
|
|
|
|
eee->re_resolve_supernode_ip = 0;
|
|
eee->community_name = NULL;
|
|
eee->sinfo.sock = -1;
|
|
eee->sinfo.is_udp_socket = 1;
|
|
eee->pkt_sent = 0;
|
|
eee->allow_routing = 0;
|
|
eee->drop_ipv6_ndp = 0;
|
|
eee->encrypt_key = NULL;
|
|
eee->enc_tf = NULL;
|
|
eee->dec_tf = NULL;
|
|
eee->known_peers = NULL;
|
|
eee->pending_peers = NULL;
|
|
eee->last_register = 0;
|
|
|
|
if(lzo_init() != LZO_E_OK) {
|
|
traceEvent(TRACE_ERROR, "LZO compression error");
|
|
return(-1);
|
|
}
|
|
|
|
return(0);
|
|
}
|
|
|
|
static int edge_init_twofish( n2n_edge_t * eee, u_int8_t *encrypt_pwd, u_int32_t encrypt_pwd_len )
|
|
{
|
|
eee->enc_tf = TwoFishInit(encrypt_pwd, encrypt_pwd_len);
|
|
eee->dec_tf = TwoFishInit(encrypt_pwd, encrypt_pwd_len);
|
|
|
|
if ( (eee->enc_tf) && (eee->dec_tf) )
|
|
{
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
/* ************************************** */
|
|
|
|
static void edge_deinit(n2n_edge_t * eee) {
|
|
TwoFishDestroy(eee->enc_tf);
|
|
TwoFishDestroy(eee->dec_tf);
|
|
if ( eee->sinfo.sock >=0 )
|
|
{
|
|
close( eee->sinfo.sock );
|
|
}
|
|
}
|
|
|
|
static void readFromIPSocket( n2n_edge_t * eee );
|
|
|
|
static void help() {
|
|
print_n2n_version();
|
|
|
|
printf("edge "
|
|
#ifdef __linux__
|
|
"-d <tun device> "
|
|
#endif
|
|
"-a <tun IP address> "
|
|
"-c <community> "
|
|
"-k <encrypt key> "
|
|
"-s <netmask> "
|
|
#ifndef WIN32
|
|
"[-u <uid> -g <gid>]"
|
|
"[-f]"
|
|
#endif
|
|
"[-m <MAC address>]"
|
|
"\n"
|
|
"-l <supernode host:port> "
|
|
"[-p <local port>] [-M <mtu>] "
|
|
"[-t] [-r] [-v] [-b] [-h]\n\n");
|
|
|
|
#ifdef __linux__
|
|
printf("-d <tun device> | tun device name\n");
|
|
#endif
|
|
|
|
printf("-a <tun IP address> | n2n IP address\n");
|
|
printf("-c <community> | n2n community name\n");
|
|
printf("-k <encrypt key> | Encryption key (ASCII) - also N2N_KEY=<encrypt key>\n");
|
|
printf("-s <netmask> | Edge interface netmask in dotted decimal notation (255.255.255.0)\n");
|
|
printf("-l <supernode host:port> | Supernode IP:port\n");
|
|
printf("-b | Periodically resolve supernode IP\n");
|
|
printf(" | (when supernodes are running on dynamic IPs)\n");
|
|
printf("-p <local port> | Local port used for connecting to supernode\n");
|
|
#ifndef WIN32
|
|
printf("-u <UID> | User ID (numeric) to use when privileges are dropped\n");
|
|
printf("-g <GID> | Group ID (numeric) to use when privileges are dropped\n");
|
|
printf("-f | Fork and run as a daemon. Use syslog.\n");
|
|
#endif
|
|
printf("-m <MAC address> | Choose a MAC address for the TAP interface\n"
|
|
" | eg. -m 01:02:03:04:05:06\n");
|
|
printf("-M <mtu> | Specify n2n MTU (default %d)\n", DEFAULT_MTU);
|
|
printf("-t | Use http tunneling (experimental)\n");
|
|
printf("-r | Enable packet forwarding through n2n community\n");
|
|
printf("-v | Verbose\n");
|
|
|
|
printf("\nEnvironment variables:\n");
|
|
printf(" N2N_KEY | Encryption key (ASCII)\n" );
|
|
|
|
exit(0);
|
|
}
|
|
|
|
/* *********************************************** */
|
|
|
|
static void send_register( n2n_edge_t * eee,
|
|
const struct peer_addr *remote_peer,
|
|
u_char is_ack) {
|
|
struct n2n_packet_header hdr;
|
|
char pkt[N2N_PKT_HDR_SIZE];
|
|
size_t len = sizeof(hdr);
|
|
ipstr_t ip_buf;
|
|
|
|
fill_standard_header_fields( &(eee->sinfo), &hdr, (char*)(eee->device.mac_addr));
|
|
hdr.sent_by_supernode = 0;
|
|
hdr.msg_type = (is_ack == 0) ? MSG_TYPE_REGISTER : MSG_TYPE_REGISTER_ACK;
|
|
memcpy(hdr.community_name, eee->community_name, COMMUNITY_LEN);
|
|
|
|
marshall_n2n_packet_header( (u_int8_t *)pkt, &hdr );
|
|
send_packet( &(eee->sinfo), pkt, &len, remote_peer, N2N_COMPRESSION_ENABLED );
|
|
|
|
traceEvent(TRACE_INFO, "Sent %s message to %s:%hd",
|
|
((hdr.msg_type==MSG_TYPE_REGISTER)?"MSG_TYPE_REGISTER":"MSG_TYPE_REGISTER_ACK"),
|
|
intoa(ntohl(remote_peer->addr_type.v4_addr), ip_buf, sizeof(ip_buf)),
|
|
ntohs(remote_peer->port));
|
|
}
|
|
|
|
/* *********************************************** */
|
|
|
|
static void send_deregister(n2n_edge_t * eee,
|
|
struct peer_addr *remote_peer) {
|
|
struct n2n_packet_header hdr;
|
|
char pkt[N2N_PKT_HDR_SIZE];
|
|
size_t len = sizeof(hdr);
|
|
|
|
fill_standard_header_fields( &(eee->sinfo), &hdr, (char*)(eee->device.mac_addr) );
|
|
hdr.sent_by_supernode = 0;
|
|
hdr.msg_type = MSG_TYPE_DEREGISTER;
|
|
memcpy(hdr.community_name, eee->community_name, COMMUNITY_LEN);
|
|
|
|
marshall_n2n_packet_header( (u_int8_t *)pkt, &hdr );
|
|
send_packet( &(eee->sinfo), pkt, &len, remote_peer, N2N_COMPRESSION_ENABLED);
|
|
}
|
|
|
|
/* *********************************************** */
|
|
|
|
static void update_peer_address(n2n_edge_t * eee,
|
|
const struct n2n_packet_header * hdr,
|
|
time_t when);
|
|
void trace_registrations( struct peer_info * scan );
|
|
int is_ip6_discovery( const void * buf, size_t bufsize );
|
|
void check_peer( n2n_edge_t * eee,
|
|
const struct n2n_packet_header * hdr );
|
|
void try_send_register( n2n_edge_t * eee,
|
|
const struct n2n_packet_header * hdr );
|
|
void set_peer_operational( n2n_edge_t * eee, const struct n2n_packet_header * hdr );
|
|
|
|
|
|
|
|
/** Start the registration process.
|
|
*
|
|
* If the peer is already in pending_peers, ignore the request.
|
|
* If not in pending_peers, add it and send a REGISTER.
|
|
*
|
|
* If hdr is for a direct peer-to-peer packet, try to register back to sender
|
|
* even if the MAC is in pending_peers. This is because an incident direct
|
|
* packet indicates that peer-to-peer exchange should work so more aggressive
|
|
* registration can be permitted (once per incoming packet) as this should only
|
|
* last for a small number of packets..
|
|
*
|
|
* Called from the main loop when Rx a packet for our device mac.
|
|
*/
|
|
void try_send_register( n2n_edge_t * eee,
|
|
const struct n2n_packet_header * hdr )
|
|
{
|
|
ipstr_t ip_buf;
|
|
|
|
/* REVISIT: purge of pending_peers not yet done. */
|
|
struct peer_info * scan = find_peer_by_mac( eee->pending_peers, hdr->src_mac );
|
|
|
|
if ( NULL == scan )
|
|
{
|
|
scan = calloc( 1, sizeof( struct peer_info ) );
|
|
|
|
memcpy(scan->mac_addr, hdr->src_mac, 6);
|
|
scan->public_ip = hdr->public_ip;
|
|
scan->last_seen = time(NULL); /* Don't change this it marks the pending peer for removal. */
|
|
|
|
peer_list_add( &(eee->pending_peers), scan );
|
|
|
|
traceEvent( TRACE_NORMAL, "Pending peers list size=%ld",
|
|
peer_list_size( eee->pending_peers ) );
|
|
|
|
traceEvent( TRACE_NORMAL, "Sending REGISTER request to %s:%hd",
|
|
intoa(ntohl(scan->public_ip.addr_type.v4_addr), ip_buf, sizeof(ip_buf)),
|
|
ntohs(scan->public_ip.port));
|
|
|
|
send_register(eee,
|
|
&(scan->public_ip),
|
|
0 /* is not ACK */ );
|
|
|
|
/* pending_peers now owns scan. */
|
|
}
|
|
else
|
|
{
|
|
/* scan already in pending_peers. */
|
|
|
|
if ( 0 == hdr->sent_by_supernode )
|
|
{
|
|
/* over-write supernode-based socket with direct socket. */
|
|
scan->public_ip = hdr->public_ip;
|
|
|
|
traceEvent( TRACE_NORMAL, "Sending additional REGISTER request to %s:%hd",
|
|
intoa(ntohl(scan->public_ip.addr_type.v4_addr), ip_buf, sizeof(ip_buf)),
|
|
ntohs(scan->public_ip.port));
|
|
|
|
|
|
send_register(eee,
|
|
&(scan->public_ip),
|
|
0 /* is not ACK */ );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/** Update the last_seen time for this peer, or get registered. */
|
|
void check_peer( n2n_edge_t * eee,
|
|
const struct n2n_packet_header * hdr )
|
|
{
|
|
struct peer_info * scan = find_peer_by_mac( eee->known_peers, hdr->src_mac );
|
|
|
|
if ( NULL == scan )
|
|
{
|
|
/* Not in known_peers - start the REGISTER process. */
|
|
try_send_register( eee, hdr );
|
|
}
|
|
else
|
|
{
|
|
/* Already in known_peers. */
|
|
update_peer_address( eee, hdr, time(NULL) );
|
|
}
|
|
}
|
|
|
|
|
|
/* Move the peer from the pending_peers list to the known_peers lists.
|
|
*
|
|
* peer must be a pointer to an element of the pending_peers list.
|
|
*
|
|
* Called by main loop when Rx a REGISTER_ACK.
|
|
*/
|
|
void set_peer_operational( n2n_edge_t * eee, const struct n2n_packet_header * hdr )
|
|
{
|
|
struct peer_info * prev = NULL;
|
|
struct peer_info * scan;
|
|
macstr_t mac_buf;
|
|
ipstr_t ip_buf;
|
|
|
|
scan=eee->pending_peers;
|
|
|
|
while ( NULL != scan )
|
|
{
|
|
if ( 0 != memcmp( scan->mac_addr, hdr->dst_mac, 6 ) )
|
|
{
|
|
break; /* found. */
|
|
}
|
|
|
|
prev = scan;
|
|
scan = scan->next;
|
|
}
|
|
|
|
if ( scan )
|
|
{
|
|
|
|
/* Remove scan from pending_peers. */
|
|
if ( prev )
|
|
{
|
|
prev->next = scan->next;
|
|
}
|
|
else
|
|
{
|
|
eee->pending_peers = scan->next;
|
|
}
|
|
|
|
/* Add scan to known_peers. */
|
|
scan->next = eee->known_peers;
|
|
eee->known_peers = scan;
|
|
|
|
scan->public_ip = hdr->public_ip;
|
|
|
|
traceEvent(TRACE_INFO, "=== new peer [mac=%s][socket=%s:%hd]",
|
|
macaddr_str(scan->mac_addr, mac_buf, sizeof(mac_buf)),
|
|
intoa(ntohl(scan->public_ip.addr_type.v4_addr), ip_buf, sizeof(ip_buf)),
|
|
ntohs(scan->public_ip.port));
|
|
|
|
traceEvent( TRACE_NORMAL, "Pending peers list size=%ld",
|
|
peer_list_size( eee->pending_peers ) );
|
|
|
|
traceEvent( TRACE_NORMAL, "Operational peers list size=%ld",
|
|
peer_list_size( eee->known_peers ) );
|
|
|
|
|
|
scan->last_seen = time(NULL);
|
|
}
|
|
else
|
|
{
|
|
traceEvent( TRACE_WARNING, "Failed to find sender in pending_peers." );
|
|
}
|
|
}
|
|
|
|
|
|
void trace_registrations( struct peer_info * scan )
|
|
{
|
|
macstr_t mac_buf;
|
|
ipstr_t ip_buf;
|
|
|
|
while ( scan )
|
|
{
|
|
traceEvent(TRACE_INFO, "=== peer [mac=%s][socket=%s:%hd]",
|
|
macaddr_str(scan->mac_addr, mac_buf, sizeof(mac_buf)),
|
|
intoa(ntohl(scan->public_ip.addr_type.v4_addr), ip_buf, sizeof(ip_buf)),
|
|
ntohs(scan->public_ip.port));
|
|
|
|
scan = scan->next;
|
|
}
|
|
|
|
}
|
|
|
|
u_int8_t broadcast_mac[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
|
|
|
|
|
|
/** Keep the known_peers list straight.
|
|
*
|
|
* Ignore broadcast L2 packets, and packets with invalid public_ip.
|
|
* If the dst_mac is in known_peers make sure the entry is correct:
|
|
* - if the public_ip socket has changed, erase the entry
|
|
* - if the same, update its last_seen = when
|
|
*/
|
|
static void update_peer_address(n2n_edge_t * eee,
|
|
const struct n2n_packet_header * hdr,
|
|
time_t when)
|
|
{
|
|
ipstr_t ip_buf;
|
|
struct peer_info *scan = eee->known_peers;
|
|
struct peer_info *prev = NULL; /* use to remove bad registrations. */
|
|
|
|
if ( 0 == hdr->public_ip.addr_type.v4_addr )
|
|
{
|
|
/* Not to be registered. */
|
|
return;
|
|
}
|
|
|
|
if ( 0 == memcmp( hdr->dst_mac, broadcast_mac, 6 ) )
|
|
{
|
|
/* Not to be registered. */
|
|
return;
|
|
}
|
|
|
|
|
|
while(scan != NULL)
|
|
{
|
|
if(memcmp(hdr->dst_mac, scan->mac_addr, 6) == 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
prev = scan;
|
|
scan = scan->next;
|
|
}
|
|
|
|
if ( NULL == scan )
|
|
{
|
|
/* Not in known_peers. */
|
|
return;
|
|
}
|
|
|
|
if ( 0 != memcmp( &(scan->public_ip), &(hdr->public_ip), sizeof(struct peer_addr)))
|
|
{
|
|
if ( 0 == hdr->sent_by_supernode )
|
|
{
|
|
traceEvent( TRACE_NORMAL, "Peer changed public socket, Was %s:%hd",
|
|
intoa(ntohl(hdr->public_ip.addr_type.v4_addr), ip_buf, sizeof(ip_buf)),
|
|
ntohs(hdr->public_ip.port));
|
|
|
|
/* The peer has changed public socket. It can no longer be assumed to be reachable. */
|
|
/* Remove the peer. */
|
|
if ( NULL == prev )
|
|
{
|
|
/* scan was head of list */
|
|
eee->known_peers = scan->next;
|
|
}
|
|
else
|
|
{
|
|
prev->next = scan->next;
|
|
}
|
|
free(scan);
|
|
|
|
try_send_register( eee, hdr );
|
|
}
|
|
else
|
|
{
|
|
/* Don't worry about what the supernode reports, it could be seeing a different socket. */
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Found and unchanged. */
|
|
scan->last_seen = when;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
#if defined(DUMMY_ID_00001) /* Disabled waiting for config option to enable it */
|
|
|
|
/* *********************************************** */
|
|
|
|
static char gratuitous_arp[] = {
|
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* Dest mac */
|
|
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 */
|
|
};
|
|
|
|
static int build_gratuitous_arp(char *buffer, u_short buffer_len) {
|
|
if(buffer_len < sizeof(gratuitous_arp)) return(-1);
|
|
|
|
memcpy(buffer, gratuitous_arp, sizeof(gratuitous_arp));
|
|
memcpy(&buffer[6], device.mac_addr, 6);
|
|
memcpy(&buffer[22], device.mac_addr, 6);
|
|
memcpy(&buffer[28], &device.ip_addr, 4);
|
|
|
|
/* REVISIT: BbMaj7 - use a real netmask here. This is valid only by accident
|
|
* for /24 IPv4 networks. */
|
|
buffer[31] = 0xFF; /* Use a faked broadcast address */
|
|
memcpy(&buffer[38], &device.ip_addr, 4);
|
|
return(sizeof(gratuitous_arp));
|
|
}
|
|
|
|
/** Called from update_registrations to periodically send gratuitous ARP
|
|
* 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) */
|
|
|
|
|
|
|
|
/* *********************************************** */
|
|
|
|
/** @brief Check to see if we should re-register with our peers and the
|
|
* supernode.
|
|
*
|
|
* This is periodically called by the main loop. The list of registrations is
|
|
* not modified. Registration packets may be sent.
|
|
*/
|
|
static void update_registrations( n2n_edge_t * eee ) {
|
|
/* REVISIT: BbMaj7: have shorter timeout to REGISTER to supernode if this has
|
|
* not yet succeeded. */
|
|
|
|
if(time(NULL) < (eee->last_register+REGISTER_FREQUENCY)) return; /* Too early */
|
|
|
|
traceEvent(TRACE_NORMAL, "Registering with supernode");
|
|
if(eee->re_resolve_supernode_ip)
|
|
supernode2addr(eee, eee->supernode_ip);
|
|
|
|
send_register(eee, &(eee->supernode), 0); /* Register with supernode */
|
|
|
|
/* REVISIT: turn-on gratuitous ARP with config option. */
|
|
/* send_grat_arps(sock_fd, is_udp_sock); */
|
|
|
|
eee->last_register = time(NULL);
|
|
}
|
|
|
|
/* ***************************************************** */
|
|
|
|
static int find_peer_destination(n2n_edge_t * eee,
|
|
const u_char *mac_address,
|
|
struct peer_addr *destination) {
|
|
const struct peer_info *scan = eee->known_peers;
|
|
macstr_t mac_buf;
|
|
ipstr_t ip_buf;
|
|
int retval=0;
|
|
|
|
traceEvent(TRACE_INFO, "Searching destination peer for MAC %02X:%02X:%02X:%02X:%02X:%02X",
|
|
mac_address[0] & 0xFF, mac_address[1] & 0xFF, mac_address[2] & 0xFF,
|
|
mac_address[3] & 0xFF, mac_address[4] & 0xFF, mac_address[5] & 0xFF);
|
|
|
|
while(scan != NULL) {
|
|
traceEvent(TRACE_INFO, "Evaluating peer [MAC=%02X:%02X:%02X:%02X:%02X:%02X][ip=%s:%hd]",
|
|
scan->mac_addr[0] & 0xFF, scan->mac_addr[1] & 0xFF, scan->mac_addr[2] & 0xFF,
|
|
scan->mac_addr[3] & 0xFF, scan->mac_addr[4] & 0xFF, scan->mac_addr[5] & 0xFF,
|
|
intoa(ntohl(scan->public_ip.addr_type.v4_addr), ip_buf, sizeof(ip_buf)),
|
|
ntohs(scan->public_ip.port));
|
|
|
|
if((scan->last_seen > 0) &&
|
|
(memcmp(mac_address, scan->mac_addr, 6) == 0))
|
|
{
|
|
memcpy(destination, &scan->public_ip, sizeof(struct sockaddr_in));
|
|
retval=1;
|
|
break;
|
|
}
|
|
scan = scan->next;
|
|
}
|
|
|
|
if ( 0 == retval )
|
|
{
|
|
memcpy(destination, &(eee->supernode), sizeof(struct sockaddr_in));
|
|
}
|
|
|
|
traceEvent(TRACE_INFO, "find_peer_address(%s) -> [socket=%s:%hd]",
|
|
macaddr_str( (char *)mac_address, mac_buf, sizeof(mac_buf)),
|
|
intoa(ntohl(destination->addr_type.v4_addr), ip_buf, sizeof(ip_buf)),
|
|
ntohs(destination->port));
|
|
|
|
return retval;
|
|
}
|
|
|
|
/* *********************************************** */
|
|
|
|
static const struct option long_options[] = {
|
|
{ "community", required_argument, NULL, 'c' },
|
|
{ "supernode-list", required_argument, NULL, 'l' },
|
|
{ "tun-device", required_argument, NULL, 'd' },
|
|
{ "euid", required_argument, NULL, 'u' },
|
|
{ "egid", required_argument, NULL, 'g' },
|
|
{ "help" , no_argument, NULL, 'h' },
|
|
{ "verbose", no_argument, NULL, 'v' },
|
|
{ NULL, 0, NULL, 0 }
|
|
};
|
|
|
|
/* ***************************************************** */
|
|
|
|
|
|
/** A layer-2 packet was received at the tunnel and needs to be sent via UDP. */
|
|
static void send_packet2net(n2n_edge_t * eee,
|
|
char *decrypted_msg, size_t len) {
|
|
ipstr_t ip_buf;
|
|
char packet[2048];
|
|
int data_sent_len;
|
|
struct n2n_packet_header hdr;
|
|
struct peer_addr destination;
|
|
macstr_t mac_buf;
|
|
macstr_t mac2_buf;
|
|
struct ether_header *eh = (struct ether_header*)decrypted_msg;
|
|
|
|
/* Discard IP packets that are not originated by this hosts */
|
|
if(!(eee->allow_routing)) {
|
|
if(ntohs(eh->ether_type) == 0x0800) {
|
|
/* This is an IP packet from the local source address - not forwarded. */
|
|
#define ETH_FRAMESIZE 14
|
|
#define IP4_SRCOFFSET 12
|
|
u_int32_t *dst = (u_int32_t*)&decrypted_msg[ETH_FRAMESIZE + IP4_SRCOFFSET];
|
|
|
|
/* Note: all elements of the_ip are in network order */
|
|
if( *dst != eee->device.ip_addr) {
|
|
/* This is a packet that needs to be routed */
|
|
traceEvent(TRACE_INFO, "Discarding routed packet [%s]",
|
|
intoa(ntohl(*dst), ip_buf, sizeof(ip_buf)));
|
|
return;
|
|
} else {
|
|
/* This packet is originated by us */
|
|
/* traceEvent(TRACE_INFO, "Sending non-routed packet"); */
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Encrypt "decrypted_msg" into the second half of the n2n packet. */
|
|
len = TwoFishEncryptRaw((u_int8_t *)decrypted_msg,
|
|
(u_int8_t *)&packet[N2N_PKT_HDR_SIZE], len, eee->enc_tf);
|
|
|
|
/* Add the n2n header to the start of the n2n packet. */
|
|
fill_standard_header_fields( &(eee->sinfo), &hdr, (char*)(eee->device.mac_addr) );
|
|
hdr.msg_type = MSG_TYPE_PACKET;
|
|
hdr.sent_by_supernode = 0;
|
|
memcpy(hdr.community_name, eee->community_name, COMMUNITY_LEN);
|
|
memcpy(hdr.dst_mac, decrypted_msg, 6);
|
|
|
|
marshall_n2n_packet_header( (u_int8_t *)packet, &hdr );
|
|
|
|
len += N2N_PKT_HDR_SIZE;
|
|
|
|
if(find_peer_destination(eee, eh->ether_dhost, &destination))
|
|
traceEvent(TRACE_INFO, "** Going direct [dst_mac=%s][dest=%s:%hd]",
|
|
macaddr_str((char*)eh->ether_dhost, mac_buf, sizeof(mac_buf)),
|
|
intoa(ntohl(destination.addr_type.v4_addr), ip_buf, sizeof(ip_buf)),
|
|
ntohs(destination.port));
|
|
else
|
|
traceEvent(TRACE_INFO, " Going via supernode [src_mac=%s][dst_mac=%s]",
|
|
macaddr_str((char*)eh->ether_shost, mac_buf, sizeof(mac_buf)),
|
|
macaddr_str((char*)eh->ether_dhost, mac2_buf, sizeof(mac2_buf)));
|
|
|
|
data_sent_len = reliable_sendto( &(eee->sinfo), packet, &len, &destination,
|
|
N2N_COMPRESSION_ENABLED);
|
|
|
|
if(data_sent_len != len)
|
|
traceEvent(TRACE_WARNING, "sendto() [sent=%d][attempted_to_send=%d] [%s]\n",
|
|
data_sent_len, len, strerror(errno));
|
|
else {
|
|
++(eee->pkt_sent);
|
|
traceEvent(TRACE_INFO, "Sent %d byte MSG_TYPE_PACKET ok", data_sent_len);
|
|
}
|
|
}
|
|
|
|
/* ***************************************************** */
|
|
|
|
/** Destination MAC 33:33:0:00:00:00 - 33:33:FF:FF:FF:FF is reserved for IPv6
|
|
* neighbour discovery.
|
|
*/
|
|
int is_ip6_discovery( const void * buf, size_t bufsize )
|
|
{
|
|
int retval = 0;
|
|
|
|
if ( bufsize >= sizeof(struct ether_header) )
|
|
{
|
|
struct ether_header *eh = (struct ether_header*)buf;
|
|
if ( (0x33 == eh->ether_dhost[0]) &&
|
|
(0x33 == eh->ether_dhost[1]) )
|
|
{
|
|
retval = 1; /* This is an IPv6 neighbour discovery packet. */
|
|
}
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
|
|
/* ***************************************************** */
|
|
|
|
/*
|
|
* Return: 0 = ok, -1 = invalid packet
|
|
*
|
|
*/
|
|
static int check_received_packet(n2n_edge_t * eee, char *pkt,
|
|
u_int pkt_len) {
|
|
|
|
if(pkt_len == 42) {
|
|
/* ARP */
|
|
if((pkt[12] != 0x08) || (pkt[13] != 0x06)) return(0); /* No ARP */
|
|
if((pkt[20] != 0x00) || (pkt[21] != 0x02)) return(0); /* No ARP Reply */
|
|
if(memcmp(&pkt[28], &(eee->device.ip_addr), 4)) return(0); /* This is not me */
|
|
|
|
if(memcmp(eee->device.mac_addr, &pkt[22], 6) == 0) {
|
|
traceEvent(TRACE_WARNING, "Bounced packet received: supernode bug?");
|
|
return(0);
|
|
}
|
|
|
|
traceEvent(TRACE_ERROR, "Duplicate address found. Your IP is used by MAC %02X:%02X:%02X:%02X:%02X:%02X",
|
|
pkt[22+0] & 0xFF, pkt[22+1] & 0xFF, pkt[22+2] & 0xFF,
|
|
pkt[22+3] & 0xFF, pkt[22+4] & 0xFF, pkt[22+5] & 0xFF);
|
|
exit(0);
|
|
} else if(pkt_len > 32 /* IP + Ethernet */) {
|
|
/* Check if this packet is for us or if it's routed */
|
|
struct ether_header *eh = (struct ether_header*)pkt;
|
|
|
|
const struct in_addr bcast = { 0xffffffff };
|
|
|
|
if(ntohs(eh->ether_type) == 0x0800) {
|
|
|
|
/* Note: all elements of the_ip are in network order */
|
|
struct ip *the_ip = (struct ip*)(pkt+sizeof(struct ether_header));
|
|
|
|
if((the_ip->ip_dst.s_addr != eee->device.ip_addr)
|
|
&& ((the_ip->ip_dst.s_addr & eee->device.device_mask) != (eee->device.ip_addr & eee->device.device_mask)) /* Not a broadcast */
|
|
&& ((the_ip->ip_dst.s_addr & 0xE0000000) != (0xE0000000 /* 224.0.0.0-239.255.255.255 */)) /* Not a multicast */
|
|
&& ((the_ip->ip_dst.s_addr) != (bcast.s_addr)) /* always broadcast (RFC919) */
|
|
&& (!(eee->allow_routing)) /* routing is enabled so let it in */
|
|
)
|
|
{
|
|
/* Dropping the packet */
|
|
|
|
ipstr_t ip_buf;
|
|
ipstr_t ip_buf2;
|
|
|
|
/* This is a packet that needs to be routed */
|
|
traceEvent(TRACE_INFO, "Discarding routed packet [rcvd=%s][expected=%s]",
|
|
intoa(ntohl(the_ip->ip_dst.s_addr), ip_buf, sizeof(ip_buf)),
|
|
intoa(ntohl(eee->device.ip_addr), ip_buf2, sizeof(ip_buf2)));
|
|
} else {
|
|
/* This packet is for us */
|
|
|
|
/* traceEvent(TRACE_INFO, "Received non-routed packet"); */
|
|
return(0);
|
|
}
|
|
} else
|
|
return(0);
|
|
} else {
|
|
traceEvent(TRACE_INFO, "Packet too short (%d bytes): discarded", pkt_len);
|
|
}
|
|
|
|
return(-1);
|
|
}
|
|
|
|
/* ***************************************************** */
|
|
|
|
/** Read a single packet from the TAP interface, process it and write out the
|
|
* corresponding packet to the cooked socket.
|
|
*
|
|
* REVISIT: fails if more than one packet is waiting to be read.
|
|
*/
|
|
static void readFromTAPSocket( n2n_edge_t * eee )
|
|
{
|
|
/* tun -> remote */
|
|
u_char decrypted_msg[2048];
|
|
size_t len;
|
|
|
|
len = tuntap_read(&(eee->device), decrypted_msg, sizeof(decrypted_msg));
|
|
|
|
if((len <= 0) || (len > sizeof(decrypted_msg)))
|
|
traceEvent(TRACE_WARNING, "read()=%d [%d/%s]\n",
|
|
len, errno, strerror(errno));
|
|
else {
|
|
traceEvent(TRACE_INFO, "### Rx L2 Msg (%d) tun -> network", len);
|
|
|
|
if ( eee->drop_ipv6_ndp && is_ip6_discovery( decrypted_msg, len ) ) {
|
|
traceEvent(TRACE_WARNING, "Dropping unsupported IPv6 neighbour discovery packet");
|
|
} else {
|
|
send_packet2net(eee, (char*)decrypted_msg, len);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ***************************************************** */
|
|
|
|
|
|
void readFromIPSocket( n2n_edge_t * eee )
|
|
{
|
|
ipstr_t ip_buf;
|
|
macstr_t mac_buf;
|
|
char packet[2048], decrypted_msg[2048];
|
|
size_t len;
|
|
int data_sent_len;
|
|
struct peer_addr sender;
|
|
|
|
/* remote -> tun */
|
|
u_int8_t discarded_pkt;
|
|
struct n2n_packet_header hdr_storage;
|
|
|
|
len = receive_data( &(eee->sinfo), packet, sizeof(packet), &sender,
|
|
&discarded_pkt, (char*)(eee->device.mac_addr),
|
|
N2N_COMPRESSION_ENABLED, &hdr_storage);
|
|
|
|
if(len <= 0) return;
|
|
|
|
traceEvent(TRACE_INFO, "### Rx N2N Msg network -> tun");
|
|
|
|
if(discarded_pkt) {
|
|
traceEvent(TRACE_INFO, "Discarded incoming pkt");
|
|
} else {
|
|
if(len <= 0)
|
|
traceEvent(TRACE_WARNING, "receive_data()=%d [%s]\n", len, strerror(errno));
|
|
else {
|
|
if(len < N2N_PKT_HDR_SIZE)
|
|
traceEvent(TRACE_WARNING, "received packet too short [len=%d]\n", len);
|
|
else {
|
|
struct n2n_packet_header *hdr = &hdr_storage;
|
|
|
|
traceEvent(TRACE_INFO, "Received packet from %s:%hd",
|
|
intoa(ntohl(sender.addr_type.v4_addr), ip_buf, sizeof(ip_buf)),
|
|
ntohs(sender.port));
|
|
|
|
traceEvent(TRACE_INFO, "Received message [msg_type=%s] from %s [dst mac=%s]",
|
|
msg_type2str(hdr->msg_type),
|
|
hdr->sent_by_supernode ? "supernode" : "peer",
|
|
macaddr_str(hdr->dst_mac, mac_buf, sizeof(mac_buf)));
|
|
|
|
if(hdr->version != N2N_PKT_VERSION) {
|
|
traceEvent(TRACE_WARNING,
|
|
"Received packet with unknown protocol version (%d): discarded\n",
|
|
hdr->version);
|
|
return;
|
|
}
|
|
|
|
/* FIX - Add IPv6 support */
|
|
if(hdr->public_ip.addr_type.v4_addr == 0) {
|
|
hdr->public_ip.addr_type.v4_addr = sender.addr_type.v4_addr;
|
|
hdr->public_ip.port = sender.port;
|
|
hdr->public_ip.family = AF_INET;
|
|
}
|
|
|
|
if(strncmp(hdr->community_name, eee->community_name, COMMUNITY_LEN) != 0) {
|
|
traceEvent(TRACE_WARNING, "Received packet with invalid community [expected=%s][received=%s]\n",
|
|
eee->community_name, hdr->community_name);
|
|
} else {
|
|
if(hdr->msg_type == MSG_TYPE_PACKET) {
|
|
/* assert: the packet received is destined for device.mac_addr or broadcast MAC. */
|
|
|
|
len -= N2N_PKT_HDR_SIZE;
|
|
|
|
/* Decrypt message first */
|
|
len = TwoFishDecryptRaw((u_int8_t *)&packet[N2N_PKT_HDR_SIZE],
|
|
(u_int8_t *)decrypted_msg, len, eee->dec_tf);
|
|
|
|
if(len > 0) {
|
|
if(check_received_packet(eee, decrypted_msg, len) == 0) {
|
|
|
|
if ( 0 == memcmp(hdr->dst_mac, eee->device.mac_addr, 6) )
|
|
{
|
|
check_peer( eee, hdr );
|
|
}
|
|
|
|
data_sent_len = tuntap_write(&(eee->device), (u_char*)decrypted_msg, len);
|
|
|
|
if(data_sent_len != len)
|
|
traceEvent(TRACE_WARNING, "tuntap_write() [sent=%d][attempted_to_send=%d] [%s]\n",
|
|
data_sent_len, len, strerror(errno));
|
|
else {
|
|
/* Normal situation. */
|
|
traceEvent(TRACE_INFO, "### Tx L2 Msg -> tun");
|
|
}
|
|
} else {
|
|
traceEvent(TRACE_WARNING, "Bad destination: message discarded");
|
|
}
|
|
}
|
|
/* else silently ignore empty packet. */
|
|
|
|
} else if(hdr->msg_type == MSG_TYPE_REGISTER) {
|
|
traceEvent(TRACE_INFO, "Received registration request from remote peer [ip=%s:%hd]",
|
|
intoa(ntohl(hdr->public_ip.addr_type.v4_addr), ip_buf, sizeof(ip_buf)),
|
|
ntohs(hdr->public_ip.port));
|
|
if ( 0 == memcmp(hdr->dst_mac, (eee->device.mac_addr), 6) )
|
|
{
|
|
check_peer( eee, hdr );
|
|
}
|
|
|
|
|
|
send_register(eee, &hdr->public_ip, 1); /* Send ACK back */
|
|
} else if(hdr->msg_type == MSG_TYPE_REGISTER_ACK) {
|
|
traceEvent(TRACE_NORMAL, "Received REGISTER_ACK from remote peer [ip=%s:%hd]",
|
|
intoa(ntohl(hdr->public_ip.addr_type.v4_addr), ip_buf, sizeof(ip_buf)),
|
|
ntohs(hdr->public_ip.port));
|
|
|
|
/* if ( 0 == memcmp(hdr->dst_mac, eee->device.mac_addr, 6) ) */
|
|
{
|
|
if ( hdr->sent_by_supernode )
|
|
{
|
|
/* Response to supernode registration. Supernode is not in the pending_peers list. */
|
|
}
|
|
else
|
|
{
|
|
/* Move from pending_peers to known_peers; ignore if not in pending. */
|
|
set_peer_operational( eee, hdr );
|
|
}
|
|
}
|
|
|
|
} else {
|
|
traceEvent(TRACE_WARNING, "Unable to handle packet type %d: ignored\n", hdr->msg_type);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ***************************************************** */
|
|
|
|
|
|
#ifdef WIN32
|
|
static DWORD tunReadThread(LPVOID lpArg )
|
|
{
|
|
n2n_edge_t *eee = (n2n_edge_t*)lpArg;
|
|
|
|
while(1) {
|
|
readFromTAPSocket(eee);
|
|
}
|
|
|
|
return((DWORD)NULL);
|
|
}
|
|
|
|
/* ***************************************************** */
|
|
|
|
static void startTunReadThread(n2n_edge_t *eee) {
|
|
HANDLE hThread;
|
|
DWORD dwThreadId;
|
|
|
|
hThread = CreateThread(NULL, /* no security attributes */
|
|
0, /* use default stack size */
|
|
(LPTHREAD_START_ROUTINE)tunReadThread, /* thread function */
|
|
(void*)eee, /* argument to thread function */
|
|
0, /* use default creation flags */
|
|
&dwThreadId); /* returns the thread identifier */
|
|
}
|
|
#endif
|
|
|
|
/* ***************************************************** */
|
|
|
|
static void supernode2addr(n2n_edge_t * eee, char* addr) {
|
|
char *supernode_host = strtok(addr, ":");
|
|
|
|
if(supernode_host) {
|
|
char *supernode_port = strtok(NULL, ":");
|
|
const struct addrinfo aihints = {0, PF_INET, 0, 0, 0, NULL, NULL, NULL};
|
|
struct addrinfo * ainfo = NULL;
|
|
int nameerr;
|
|
ipstr_t ip_buf;
|
|
|
|
if ( supernode_port )
|
|
eee->supernode.port = htons(atoi(supernode_port));
|
|
else
|
|
traceEvent(TRACE_WARNING, "Bad supernode parameter (-l <host:port>)");
|
|
|
|
nameerr = getaddrinfo( supernode_host, NULL, &aihints, &ainfo );
|
|
|
|
if( 0 == nameerr )
|
|
{
|
|
struct sockaddr_in * saddr;
|
|
|
|
/* ainfo s the head of a linked list if non-NULL. */
|
|
if ( ainfo && (PF_INET == ainfo->ai_family) )
|
|
{
|
|
/* It is definitely and IPv4 address -> sockaddr_in */
|
|
saddr = (struct sockaddr_in *)ainfo->ai_addr;
|
|
|
|
eee->supernode.addr_type.v4_addr = saddr->sin_addr.s_addr;
|
|
}
|
|
else
|
|
{
|
|
/* Should only return IPv4 addresses due to aihints. */
|
|
traceEvent(TRACE_WARNING, "Failed to resolve supernode IPv4 address for %s", supernode_host);
|
|
}
|
|
|
|
freeaddrinfo(ainfo); /* free everything allocated by getaddrinfo(). */
|
|
ainfo = NULL;
|
|
} else {
|
|
traceEvent(TRACE_WARNING, "Failed to resolve supernode host %s, assuming numeric", supernode_host);
|
|
eee->supernode.addr_type.v4_addr = inet_addr(supernode_host);
|
|
}
|
|
|
|
traceEvent(TRACE_NORMAL, "Using supernode %s:%hd",
|
|
intoa(ntohl(eee->supernode.addr_type.v4_addr), ip_buf, sizeof(ip_buf)),
|
|
ntohs(eee->supernode.port));
|
|
} else
|
|
traceEvent(TRACE_WARNING, "Wrong supernode parameter (-l <host:port>)");
|
|
}
|
|
|
|
/* ***************************************************** */
|
|
|
|
extern int useSyslog;
|
|
|
|
#define N2N_NETMASK_STR_SIZE 16 /* dotted decimal 12 numbers + 3 dots */
|
|
|
|
|
|
int main(int argc, char* argv[]) {
|
|
int opt, local_port = 0 /* any port */;
|
|
char *tuntap_dev_name = "edge0";
|
|
char *ip_addr = NULL;
|
|
char netmask[N2N_NETMASK_STR_SIZE]="255.255.255.0";
|
|
int mtu = DEFAULT_MTU;
|
|
int got_s = 0;
|
|
|
|
#ifndef WIN32
|
|
uid_t userid=0; /* root is the only guaranteed ID */
|
|
gid_t groupid=0; /* root is the only guaranteed ID */
|
|
int fork_as_daemon=0;
|
|
#endif
|
|
|
|
size_t numPurged;
|
|
time_t lastStatus=0;
|
|
|
|
char * device_mac=NULL;
|
|
char * encrypt_key=NULL;
|
|
|
|
int i, effectiveargc=0;
|
|
char ** effectiveargv=NULL;
|
|
char * linebuffer = NULL;
|
|
|
|
n2n_edge_t eee; /* single instance for this program */
|
|
|
|
if (-1 == edge_init(&eee) ){
|
|
traceEvent( TRACE_ERROR, "Failed in edge_init" );
|
|
exit(1);
|
|
}
|
|
|
|
if( getenv( "N2N_KEY" )) {
|
|
encrypt_key = strdup( getenv( "N2N_KEY" ));
|
|
}
|
|
|
|
#ifdef WIN32
|
|
tuntap_dev_name = "";
|
|
#endif
|
|
memset(&(eee.supernode), 0, sizeof(eee.supernode));
|
|
eee.supernode.family = AF_INET;
|
|
|
|
linebuffer = (char *)malloc(MAX_CMDLINE_BUFFER_LENGTH);
|
|
if (!linebuffer) {
|
|
traceEvent( TRACE_ERROR, "Unable to allocate memory");
|
|
exit(1);
|
|
}
|
|
snprintf(linebuffer, MAX_CMDLINE_BUFFER_LENGTH, "%s",argv[0]);
|
|
|
|
#ifdef WIN32
|
|
for(i=0; i<strlen(linebuffer); i++)
|
|
if(linebuffer[i] == '\\') linebuffer[i] = '/';
|
|
#endif
|
|
|
|
for(i=1;i<argc;++i) {
|
|
if(argv[i][0] == '@') {
|
|
if (readConfFile(&argv[i][1], linebuffer)<0) exit(1); /* <<<<----- check */
|
|
} else
|
|
if ((strlen(linebuffer)+strlen(argv[i])+2) < MAX_CMDLINE_BUFFER_LENGTH) {
|
|
strncat(linebuffer, " ", 1);
|
|
strncat(linebuffer, argv[i], strlen(argv[i]));
|
|
} else {
|
|
traceEvent( TRACE_ERROR, "too many argument");
|
|
exit(1);
|
|
}
|
|
}
|
|
/* strip trailing spaces */
|
|
while(strlen(linebuffer) && linebuffer[strlen(linebuffer)-1]==' ')
|
|
linebuffer[strlen(linebuffer)-1]= '\0';
|
|
|
|
/* build the new argv from the linebuffer */
|
|
effectiveargv = buildargv(linebuffer);
|
|
|
|
effectiveargc =0;
|
|
while (effectiveargv[effectiveargc]) ++effectiveargc;
|
|
effectiveargv[effectiveargc] = 0;
|
|
if (linebuffer) {
|
|
free(linebuffer);
|
|
linebuffer = NULL;
|
|
}
|
|
|
|
/* {int k;for(k=0;k<effectiveargc;++k) printf("%s\n",effectiveargv[k]);} */
|
|
|
|
optarg = NULL;
|
|
while((opt = getopt_long(effectiveargc, effectiveargv, "k:a:bc:u:g:m:M:s:d:l:p:fvhrt", long_options, NULL)) != EOF) {
|
|
switch (opt) {
|
|
case 'a':
|
|
printf("%s\n", optarg);
|
|
ip_addr = strdup(optarg);
|
|
break;
|
|
case 'c': /* community */
|
|
eee.community_name = strdup(optarg);
|
|
if(strlen(eee.community_name) > COMMUNITY_LEN)
|
|
eee.community_name[COMMUNITY_LEN] = '\0';
|
|
break;
|
|
#ifndef WIN32
|
|
|
|
case 'u': /* uid */
|
|
{
|
|
userid = atoi(optarg);
|
|
break;
|
|
}
|
|
case 'g': /* uid */
|
|
{
|
|
groupid = atoi(optarg);
|
|
break;
|
|
}
|
|
case 'f' : /* fork as daemon */
|
|
{
|
|
fork_as_daemon = 1;
|
|
break;
|
|
}
|
|
#endif
|
|
case 'm' : /* device_mac */
|
|
{
|
|
device_mac = strdup(optarg);
|
|
break;
|
|
}
|
|
case 'M' : /* device_mac */
|
|
{
|
|
mtu = atoi(optarg);
|
|
break;
|
|
}
|
|
case 'k': /* encrypt key */
|
|
encrypt_key = strdup(optarg);
|
|
break;
|
|
case 'r': /* enable packet routing across n2n endpoints */
|
|
eee.allow_routing = 1;
|
|
break;
|
|
case 'l': /* supernode-list */
|
|
snprintf(eee.supernode_ip, sizeof(eee.supernode_ip), "%s", optarg);
|
|
supernode2addr(&eee, eee.supernode_ip);
|
|
break;
|
|
#ifdef __linux__
|
|
case 'd': /* tun-device */
|
|
tuntap_dev_name = strdup(optarg);
|
|
break;
|
|
#endif
|
|
case 't': /* Use HTTP tunneling */
|
|
eee.sinfo.is_udp_socket = 0;
|
|
break;
|
|
case 'b':
|
|
eee.re_resolve_supernode_ip = 1;
|
|
break;
|
|
case 'p':
|
|
local_port = atoi(optarg);
|
|
break;
|
|
case 's': /* Subnet Mask */
|
|
if (0 != got_s) {
|
|
traceEvent(TRACE_WARNING, "Multiple subnet masks supplied.");
|
|
}
|
|
strncpy(netmask, optarg, N2N_NETMASK_STR_SIZE);
|
|
got_s = 1;
|
|
break;
|
|
case 'h': /* help */
|
|
help();
|
|
break;
|
|
case 'v': /* verbose */
|
|
traceLevel = 3;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(!(
|
|
#ifdef __linux__
|
|
tuntap_dev_name &&
|
|
#endif
|
|
eee.community_name &&
|
|
ip_addr &&
|
|
eee.supernode.addr_type.v4_addr &&
|
|
encrypt_key))
|
|
help();
|
|
|
|
#ifndef WIN32
|
|
/* If running suid root then we need to setuid before using the force. */
|
|
setuid( 0 );
|
|
/* setgid( 0 ); */
|
|
#endif
|
|
|
|
if(tuntap_open(&(eee.device), tuntap_dev_name, ip_addr, netmask, device_mac, mtu) < 0)
|
|
return(-1);
|
|
|
|
#ifndef WIN32
|
|
if ( (userid != 0) || (groupid != 0 ) ) {
|
|
traceEvent(TRACE_NORMAL, "Interface up. Dropping privileges to uid=%d, gid=%d", userid, groupid);
|
|
|
|
/* Finished with the need for root privileges. Drop to unprivileged user. */
|
|
setreuid( userid, userid );
|
|
setregid( groupid, groupid );
|
|
}
|
|
#endif
|
|
|
|
if(local_port > 0)
|
|
traceEvent(TRACE_NORMAL, "Binding to local port %d", local_port);
|
|
|
|
if(edge_init_twofish( &eee, (u_int8_t *)(encrypt_key), strlen(encrypt_key) ) < 0) return(-1);
|
|
eee.sinfo.sock = open_socket(local_port, eee.sinfo.is_udp_socket, 0);
|
|
if(eee.sinfo.sock < 0) return(-1);
|
|
|
|
if( !(eee.sinfo.is_udp_socket) ) {
|
|
int rc = connect_socket(eee.sinfo.sock, &(eee.supernode));
|
|
|
|
if(rc == -1) {
|
|
traceEvent(TRACE_WARNING, "Error while connecting to supernode\n");
|
|
return(-1);
|
|
}
|
|
}
|
|
|
|
#ifndef WIN32
|
|
if ( fork_as_daemon )
|
|
{
|
|
useSyslog=1; /* traceEvent output now goes to syslog. */
|
|
daemon( 0, 0 );
|
|
}
|
|
#endif
|
|
|
|
update_registrations(&eee);
|
|
|
|
traceEvent(TRACE_NORMAL, "");
|
|
traceEvent(TRACE_NORMAL, "Ready");
|
|
|
|
#ifdef WIN32
|
|
startTunReadThread(&eee);
|
|
#endif
|
|
|
|
/* Main loop
|
|
*
|
|
* select() is used to wait for input on either the TAP fd or the UDP/TCP
|
|
* socket. When input is present the data is read and processed by either
|
|
* readFromIPSocket() or readFromTAPSocket()
|
|
*/
|
|
|
|
while(1) {
|
|
int rc, max_sock = 0;
|
|
fd_set socket_mask;
|
|
struct timeval wait_time;
|
|
time_t nowTime;
|
|
|
|
FD_ZERO(&socket_mask);
|
|
FD_SET(eee.sinfo.sock, &socket_mask);
|
|
#ifndef WIN32
|
|
FD_SET(eee.device.fd, &socket_mask);
|
|
max_sock = max( eee.sinfo.sock, eee.device.fd );
|
|
#endif
|
|
|
|
wait_time.tv_sec = SOCKET_TIMEOUT_INTERVAL_SECS; wait_time.tv_usec = 0;
|
|
|
|
rc = select(max_sock+1, &socket_mask, NULL, NULL, &wait_time);
|
|
nowTime=time(NULL);
|
|
|
|
if(rc > 0)
|
|
{
|
|
/* Any or all of the FDs could have input; check them all. */
|
|
|
|
if(FD_ISSET(eee.sinfo.sock, &socket_mask))
|
|
{
|
|
/* Read a cooked socket from the internet socket. Writes on the TAP
|
|
* socket. */
|
|
readFromIPSocket(&eee);
|
|
}
|
|
|
|
#ifndef WIN32
|
|
if(FD_ISSET(eee.device.fd, &socket_mask))
|
|
{
|
|
/* Read an ethernet frame from the TAP socket. Write on the IP
|
|
* socket. */
|
|
readFromTAPSocket(&eee);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
update_registrations(&eee);
|
|
|
|
numPurged = purge_expired_registrations( &(eee.known_peers) );
|
|
numPurged += purge_expired_registrations( &(eee.pending_peers) );
|
|
if ( numPurged > 0 )
|
|
{
|
|
traceEvent( TRACE_NORMAL, "Peer removed: pending=%ld, operational=%ld",
|
|
peer_list_size( eee.pending_peers ), peer_list_size( eee.known_peers ) );
|
|
}
|
|
|
|
if ( ( nowTime - lastStatus ) > STATUS_UPDATE_INTERVAL )
|
|
{
|
|
lastStatus = nowTime;
|
|
|
|
traceEvent( TRACE_NORMAL, "STATUS: pending=%ld, operational=%ld",
|
|
peer_list_size( eee.pending_peers ), peer_list_size( eee.known_peers ) );
|
|
}
|
|
} /* while */
|
|
|
|
send_deregister( &eee, &(eee.supernode));
|
|
|
|
closesocket(eee.sinfo.sock);
|
|
tuntap_close(&(eee.device));
|
|
|
|
edge_deinit( &eee );
|
|
|
|
return(0);
|
|
}
|
|
|
|
|