Added the ability to specify (-c) on the supernode the list of allowed communities

kill -HUP on the supernode lists the registered edges
This commit is contained in:
Luca Deri 2018-09-28 22:31:45 +02:00
parent cb9e758d58
commit fd356cde64
6 changed files with 1561 additions and 414 deletions

5
community.list Normal file
View File

@ -0,0 +1,5 @@
#
# List of allowed communities
#
mynetwork
netleo

View File

@ -921,7 +921,7 @@ static void readFromMgmtSocket(n2n_edge_t * eee, int * keep_running) {
(unsigned int)peer_list_size(eee->known_peers));
msg_len += snprintf((char *)(udp_buf+msg_len), (N2N_PKT_BUF_SIZE-msg_len),
"last super:%lu(%ld sec ago) p2p:%lu(%lD sec ago)\n",
"last super:%lu(%ld sec ago) p2p:%lu(%ld sec ago)\n",
eee->last_sup, (now-eee->last_sup), eee->last_p2p,
(now-eee->last_p2p));

2
n2n.h
View File

@ -111,6 +111,8 @@ typedef struct ether_hdr ether_hdr_t;
#include <stdarg.h>
#include "uthash.h"
#ifdef WIN32
#include "win32/wintap.h"
#endif /* #ifdef WIN32 */

View File

@ -9,3 +9,6 @@
#
# -l=7777
#
# Specify in supernodes.list the list of allowed communities
#
# -c=supernodes.list

303
sn.c
View File

@ -20,7 +20,6 @@
#include "n2n.h"
#define N2N_SN_LPORT_DEFAULT 7654
#define N2N_SN_PKTBUF_SIZE 2048
@ -50,6 +49,12 @@ struct n2n_sn {
typedef struct n2n_sn n2n_sn_t;
struct n2n_allowed_communities {
char community[N2N_COMMUNITY_SIZE];
UT_hash_handle hh; /* makes this structure hashable */
};
static struct n2n_allowed_communities *allowed_communities = NULL;
static int try_forward(n2n_sn_t * sss,
const n2n_common_t * cmn,
@ -63,11 +68,10 @@ static int try_broadcast( n2n_sn_t * sss,
const uint8_t * pktbuf,
size_t pktsize);
static n2n_sn_t sss_node;
/** Initialise the supernode structure */
static int init_sn( n2n_sn_t * sss )
{
static int init_sn(n2n_sn_t * sss) {
#ifdef WIN32
initWin32();
#endif
@ -107,8 +111,7 @@ static void deinit_sn( n2n_sn_t * sss )
* If the supernode has been put into a pre-shutdown phase then this lifetime
* should not allow registrations to continue beyond the shutdown point.
*/
static uint16_t reg_lifetime( n2n_sn_t * sss )
{
static uint16_t reg_lifetime(n2n_sn_t * sss) {
return 120;
}
@ -119,8 +122,7 @@ static int update_edge( n2n_sn_t * sss,
const n2n_mac_t edgeMac,
const n2n_community_t community,
const n2n_sock_t * sender_sock,
time_t now)
{
time_t now) {
macstr_t mac_buf;
n2n_sock_str_t sockbuf;
struct peer_info * scan;
@ -131,8 +133,7 @@ static int update_edge( n2n_sn_t * sss,
scan = find_peer_by_mac(sss->edges, edgeMac);
if ( NULL == scan )
{
if(NULL == scan) {
/* Not known */
scan = (struct peer_info*)calloc(1, sizeof(struct peer_info)); /* deallocated in purge_expired_registrations */
@ -148,9 +149,7 @@ static int update_edge( n2n_sn_t * sss,
traceEvent(TRACE_INFO, "update_edge created %s ==> %s",
macaddr_str(mac_buf, edgeMac),
sock_to_cstr(sockbuf, sender_sock));
}
else
{
} else {
/* Known */
if((0 != memcmp(community, scan->community_name, sizeof(n2n_community_t))) ||
(0 != sock_equal(sender_sock, &(scan->sock))))
@ -378,6 +377,74 @@ static int process_mgmt( n2n_sn_t * sss,
return 0;
}
/** Check if the specified community is allowed by the
* supernode configuration
* @return 0 = community not allowed, 1 = community allowed
*
*/
static int allowed_n2n_community(n2n_common_t *cmn) {
if(allowed_communities != NULL) {
struct n2n_allowed_communities *c;
HASH_FIND_STR(allowed_communities, (const char*)cmn->community, c);
return((c == NULL) ? 0 : 1);
} else {
/* If no allowed community is defined, all communities are allowed */
}
return(1);
}
/** Load the list of allowed communities. Existing/previous ones will be removed
*
*/
static int load_allowed_n2n_communities(char *path) {
char buffer[4096], *line;
FILE *fd = fopen(path, "r");
struct n2n_allowed_communities *s, *tmp;
u_int32_t num_communities = 0;
if(fd == NULL) {
traceEvent(TRACE_WARNING, "File %s not found", path);
return -1;
}
HASH_ITER(hh, allowed_communities, s, tmp)
free(s);
while((line = fgets(buffer, sizeof(buffer), fd)) != NULL) {
int len = strlen(line);
if((len < 2) || line[0] == '#')
continue;
len--;
while(len > 0) {
if((line[len] == '\n') || (line[len] == '\r')) {
line[len] = '\0';
len--;
} else
break;
}
s = (struct n2n_allowed_communities*)malloc(sizeof(struct n2n_allowed_communities));
if(s != NULL) {
strncpy((char*)s->community, line, N2N_COMMUNITY_SIZE);
HASH_ADD_STR(allowed_communities, community, s);
num_communities++;
traceEvent(TRACE_INFO, "Added allowed community '%s' [total: %u]",
(char*)s->community, num_communities);
}
}
fclose(fd);
traceEvent(TRACE_NORMAL, "Loaded %u communities from %s",
num_communities, path);
return(0);
}
/** Examine a datagram and determine what to do with it.
*
@ -396,9 +463,11 @@ static int process_udp( n2n_sn_t * sss,
macstr_t mac_buf;
macstr_t mac_buf2;
n2n_sock_str_t sockbuf;
char buf[32];
traceEvent( TRACE_DEBUG, "process_udp(%lu)", udp_size );
traceEvent(TRACE_DEBUG, "Processing incoming UDP packet [len: %lu][sender: %s:%u]",
udp_size, intoa(ntohl(sender_sock->sin_addr.s_addr), buf, sizeof(buf)),
ntohs(sender_sock->sin_port));
/* Use decode_common() to determine the kind of packet then process it:
*
@ -411,8 +480,7 @@ static int process_udp( n2n_sn_t * sss,
rem = udp_size; /* Counts down bytes of packet to protect against buffer overruns. */
idx = 0; /* marches through packet header as parts are decoded. */
if ( decode_common(&cmn, udp_buf, &rem, &idx) < 0 )
{
if(decode_common(&cmn, udp_buf, &rem, &idx) < 0) {
traceEvent(TRACE_ERROR, "Failed to decode common section");
return -1; /* failed to decode packet */
}
@ -420,16 +488,14 @@ static int process_udp( n2n_sn_t * sss,
msg_type = cmn.pc; /* packet code */
from_supernode= cmn.flags & N2N_FLAGS_FROM_SUPERNODE;
if ( cmn.ttl < 1 )
{
if(cmn.ttl < 1) {
traceEvent(TRACE_WARNING, "Expired TTL");
return 0; /* Don't process further */
}
--(cmn.ttl); /* The value copied into all forwarded packets. */
if ( msg_type == MSG_TYPE_PACKET )
{
if(msg_type == MSG_TYPE_PACKET) {
/* PACKET from one edge to another edge via supernode. */
/* pkt will be modified in place and recoded to an output of potentially
@ -447,14 +513,13 @@ static int process_udp( n2n_sn_t * sss,
unicast = (0 == is_multi_broadcast(pkt.dstMac));
traceEvent( TRACE_DEBUG, "Rx PACKET (%s) %s -> %s %s",
traceEvent(TRACE_DEBUG, "RX PACKET (%s) %s -> %s %s",
(unicast?"unicast":"multicast"),
macaddr_str(mac_buf, pkt.srcMac),
macaddr_str(mac_buf2, pkt.dstMac),
(from_supernode?"from sn":"local"));
if ( !from_supernode )
{
if(!from_supernode) {
memcpy(&cmn2, &cmn, sizeof(n2n_common_t));
/* We are going to add socket even if it was not there before */
@ -471,9 +536,7 @@ static int process_udp( n2n_sn_t * sss,
/* Copy the original payload unchanged */
encode_buf(encbuf, &encx, (udp_buf + idx), (udp_size - idx));
}
else
{
} else {
/* Already from a supernode. Nothing to modify, just pass to
* destination. */
@ -485,16 +548,11 @@ static int process_udp( n2n_sn_t * sss,
/* Common section to forward the final product. */
if(unicast)
{
try_forward(sss, &cmn, pkt.dstMac, rec_buf, encx);
}
else
{
try_broadcast(sss, &cmn, pkt.srcMac, rec_buf, encx);
}
}/* MSG_TYPE_PACKET */
else if ( msg_type == MSG_TYPE_REGISTER )
{
else if(msg_type == MSG_TYPE_REGISTER) {
/* Forwarding a REGISTER from one edge to the next */
n2n_REGISTER_t reg;
@ -509,15 +567,13 @@ static int process_udp( n2n_sn_t * sss,
unicast = (0 == is_multi_broadcast(reg.dstMac));
if ( unicast )
{
if(unicast) {
traceEvent(TRACE_DEBUG, "Rx REGISTER %s -> %s %s",
macaddr_str(mac_buf, reg.srcMac),
macaddr_str(mac_buf2, reg.dstMac),
((cmn.flags & N2N_FLAGS_FROM_SUPERNODE)?"from sn":"local"));
if ( 0 != (cmn.flags & N2N_FLAGS_FROM_SUPERNODE) )
{
if(0 != (cmn.flags & N2N_FLAGS_FROM_SUPERNODE)) {
memcpy(&cmn2, &cmn, sizeof(n2n_common_t));
/* We are going to add socket even if it was not there before */
@ -534,9 +590,7 @@ static int process_udp( n2n_sn_t * sss,
/* Copy the original payload unchanged */
encode_buf(encbuf, &encx, (udp_buf + idx), (udp_size - idx));
}
else
{
} else {
/* Already from a supernode. Nothing to modify, just pass to
* destination. */
@ -545,19 +599,11 @@ static int process_udp( n2n_sn_t * sss,
}
try_forward(sss, &cmn, reg.dstMac, rec_buf, encx); /* unicast only */
}
else
{
} else
traceEvent(TRACE_ERROR, "Rx REGISTER with multicast destination");
}
}
else if ( msg_type == MSG_TYPE_REGISTER_ACK )
{
} else if(msg_type == MSG_TYPE_REGISTER_ACK)
traceEvent(TRACE_DEBUG, "Rx REGISTER_ACK (NOT IMPLEMENTED) SHould not be via supernode");
}
else if ( msg_type == MSG_TYPE_REGISTER_SUPER )
{
else if(msg_type == MSG_TYPE_REGISTER_SUPER) {
n2n_REGISTER_SUPER_t reg;
n2n_REGISTER_SUPER_ACK_t ack;
n2n_common_t cmn2;
@ -565,11 +611,17 @@ static int process_udp( n2n_sn_t * sss,
size_t encx=0;
/* Edge requesting registration with us. */
sss->stats.last_reg_super=now;
++(sss->stats.reg_super);
decode_REGISTER_SUPER(&reg, &cmn, udp_buf, &rem, &idx);
/*
Before we move any further, we need to check if the requested
community is allowed by the supernode. In case it is not we do
not report any message back to the edge to hide the supernode
existance (better from the security standpoint)
*/
if(allowed_n2n_community(&cmn)) {
cmn2.ttl = N2N_DEFAULT_TTL;
cmn2.pc = n2n_register_super_ack;
cmn2.flags = N2N_FLAGS_SOCKET | N2N_FLAGS_FROM_SUPERNODE;
@ -600,7 +652,10 @@ static int process_udp( n2n_sn_t * sss,
traceEvent(TRACE_DEBUG, "Tx REGISTER_SUPER_ACK for %s [%s]",
macaddr_str(mac_buf, reg.edgeMac),
sock_to_cstr(sockbuf, &(ack.sock)));
} else {
traceEvent(TRACE_INFO, "Discarded registration: unallowed community '%s'",
(char*)cmn.community);
}
}
return 0;
@ -609,8 +664,7 @@ static int process_udp( n2n_sn_t * sss,
/* *************************************************** */
/** Help message to print if the command line arguments are not valid. */
static void help()
{
static void help() {
print_n2n_version();
printf("supernode <config file> (see supernode.conf)\n"
@ -618,11 +672,13 @@ static void help()
);
printf("supernode ");
printf("-l <lport> ");
printf("-c <path> ");
printf("[-f] ");
printf("[-v] ");
printf("\n\n");
printf("-l <lport>\tSet UDP main listen port to <lport>\n");
printf("-c <path>\tFile containing the allowed communities.\n");
#if defined(N2N_HAVE_DAEMON)
printf("-f \tRun in foreground.\n");
#endif /* #if defined(N2N_HAVE_DAEMON) */
@ -639,41 +695,34 @@ static int run_loop( n2n_sn_t * sss );
/* *************************************************** */
static int setOption(int optkey, char *optargument, n2n_sn_t *sss) {
//traceEvent(TRACE_NORMAL, "Option %c = %s", optkey, optargument ? optargument : "");
static int setOption(int optkey, char *_optarg, n2n_sn_t *sss) {
//traceEvent(TRACE_NORMAL, "Option %c = %s", optkey, _optarg ? _optarg : "");
switch(optkey) {
case 'l': /* local-port */
{
sss->lport = atoi(optargument);
sss->lport = atoi(_optarg);
break;
case 'c': /* community file */
load_allowed_n2n_communities(optarg);
break;
}
case 'f': /* foreground */
{
sss->daemon = 0;
break;
}
case 'h': /* help */
{
help();
break;
}
case 'v': /* verbose */
{
++traceLevel;
traceLevel = 4; /* DEBUG */
break;
}
default:
{
traceEvent(TRACE_WARNING, "Unknown option -%c: Ignored.", (char)optkey);
return(-1);
}
}
return(0);
}
@ -681,6 +730,7 @@ static int setOption(int optkey, char *optargument, n2n_sn_t *sss) {
/* *********************************************** */
static const struct option long_options[] = {
{ "communities", required_argument, NULL, 'c' },
{ "foreground", no_argument, NULL, 'f' },
{ "local-port", required_argument, NULL, 'l' },
{ "help" , no_argument, NULL, 'h' },
@ -694,8 +744,7 @@ static const struct option long_options[] = {
static int loadFromCLI(int argc, char * const argv[], n2n_sn_t *sss) {
u_char c;
while((c = getopt_long(argc, argv,
"fl:vh",
while((c = getopt_long(argc, argv, "fl:c:vh",
long_options, NULL)) != '?') {
if(c == 255) break;
setOption(c, optarg, sss);
@ -709,7 +758,9 @@ static int loadFromCLI(int argc, char * const argv[], n2n_sn_t *sss) {
static char *trim(char *s) {
char *end;
while(isspace(s[0]) || (s[0] == '"') || (s[0] == '\'')) s++;
while(isspace(s[0]) || (s[0] == '"') || (s[0] == '\''))
s++;
if(s[0] == 0) return s;
end = &s[strlen(s) - 1];
@ -787,36 +838,62 @@ static int loadFromFile(const char *path, n2n_sn_t *sss) {
/* *************************************************** */
static void dump_registrations(int signo) {
struct peer_info * list = sss_node.edges;
char buf[32];
time_t now = time(NULL);
u_int num = 0;
traceEvent(TRACE_NORMAL, "====================================");
while(list != NULL) {
if(list->sock.family == AF_INET)
traceEvent(TRACE_NORMAL, "[id: %u][MAC: %s][edge: %u.%u.%u.%u:%u][community: %s][last seen: %u sec ago]",
++num, macaddr_str(buf, list->mac_addr),
list->sock.addr.v4[0], list->sock.addr.v4[1], list->sock.addr.v4[2], list->sock.addr.v4[3],
list->sock.port,
(char*)list->community_name,
now-list->last_seen);
else
traceEvent(TRACE_NORMAL, "[id: %u][MAC: %s][edge: IPv6:%u][community: %s][last seen: %u sec ago]",
++num, macaddr_str(buf, list->mac_addr), list->sock.port,
(char*)list->community_name,
now-list->last_seen);
list = list->next;
}
traceEvent(TRACE_NORMAL, "====================================");
}
/* *************************************************** */
/** Main program entry point from kernel. */
int main( int argc, char * const argv[] )
{
int main(int argc, char * const argv[]) {
int rc;
n2n_sn_t sss;
if(argc == 1)
help();
init_sn(&sss);
init_sn(&sss_node);
#ifndef WIN32
if((argc >= 2) && (argv[1][0] != '-'))
{
rc = loadFromFile(argv[1], &sss);
if((argc >= 2) && (argv[1][0] != '-')) {
rc = loadFromFile(argv[1], &sss_node);
if(argc > 2)
rc = loadFromCLI(argc, argv, &sss);
rc = loadFromCLI(argc, argv, &sss_node);
} else
#endif
rc = loadFromCLI(argc, argv, &sss);
rc = loadFromCLI(argc, argv, &sss_node);
if(rc < 0)
return(-1);
#if defined(N2N_HAVE_DAEMON)
if (sss.daemon)
{
if(sss_node.daemon) {
useSyslog=1; /* traceEvent output now goes to syslog. */
if ( -1 == daemon( 0, 0 ) )
{
if(-1 == daemon(0, 0)) {
traceEvent(TRACE_ERROR, "Failed to become daemon.");
exit(-5);
}
@ -825,45 +902,38 @@ int main( int argc, char * const argv[] )
traceEvent(TRACE_DEBUG, "traceLevel is %d", traceLevel);
sss.sock = open_socket(sss.lport, 1 /*bind ANY*/ );
if ( -1 == sss.sock )
{
sss_node.sock = open_socket(sss_node.lport, 1 /*bind ANY*/);
if(-1 == sss_node.sock) {
traceEvent(TRACE_ERROR, "Failed to open main socket. %s", strerror(errno));
exit(-2);
}
else
{
traceEvent( TRACE_NORMAL, "supernode is listening on UDP %u (main)", sss.lport );
} else {
traceEvent(TRACE_NORMAL, "supernode is listening on UDP %u (main)", sss_node.lport);
}
sss.mgmt_sock = open_socket(N2N_SN_MGMT_PORT, 0 /* bind LOOPBACK */ );
if ( -1 == sss.mgmt_sock )
{
sss_node.mgmt_sock = open_socket(N2N_SN_MGMT_PORT, 0 /* bind LOOPBACK */);
if(-1 == sss_node.mgmt_sock) {
traceEvent(TRACE_ERROR, "Failed to open management socket. %s", strerror(errno));
exit(-2);
}
else
{
} else
traceEvent(TRACE_NORMAL, "supernode is listening on UDP %u (management)", N2N_SN_MGMT_PORT);
}
traceEvent(TRACE_NORMAL, "supernode started");
return run_loop(&sss);
signal(SIGHUP, dump_registrations);
return run_loop(&sss_node);
}
/** Long lived processing entry point. Split out from main to simply
* daemonisation on some platforms. */
static int run_loop( n2n_sn_t * sss )
{
static int run_loop(n2n_sn_t * sss) {
uint8_t pktbuf[N2N_SN_PKTBUF_SIZE];
int keep_running=1;
sss->start_time = time(NULL);
while(keep_running)
{
while(keep_running) {
int rc;
ssize_t bread;
int max_sock;
@ -882,10 +952,8 @@ static int run_loop( n2n_sn_t * sss )
now = time(NULL);
if(rc > 0)
{
if (FD_ISSET(sss->sock, &socket_mask))
{
if(rc > 0) {
if(FD_ISSET(sss->sock, &socket_mask)) {
struct sockaddr_in sender_sock;
socklen_t i;
@ -893,8 +961,8 @@ static int run_loop( n2n_sn_t * sss )
bread = recvfrom(sss->sock, pktbuf, N2N_SN_PKTBUF_SIZE, 0/*flags*/,
(struct sockaddr *)&sender_sock, (socklen_t*)&i);
if ( bread < 0 ) /* For UDP bread of zero just means no data (unlike TCP). */
{
if(bread < 0) {
/* For UDP bread of zero just means no data (unlike TCP). */
/* The fd is no good now. Maybe we lost our interface. */
traceEvent(TRACE_ERROR, "recvfrom() failed %d errno %d (%s)", bread, errno, strerror(errno));
keep_running=0;
@ -902,15 +970,13 @@ static int run_loop( n2n_sn_t * sss )
}
/* We have a datagram to process */
if ( bread > 0 )
{
if(bread > 0) {
/* And the datagram has data (not just a header) */
process_udp(sss, &sender_sock, pktbuf, bread, now);
}
}
if (FD_ISSET(sss->mgmt_sock, &socket_mask))
{
if(FD_ISSET(sss->mgmt_sock, &socket_mask)) {
struct sockaddr_in sender_sock;
size_t i;
@ -918,8 +984,7 @@ static int run_loop( n2n_sn_t * sss )
bread = recvfrom(sss->mgmt_sock, pktbuf, N2N_SN_PKTBUF_SIZE, 0/*flags*/,
(struct sockaddr *)&sender_sock, (socklen_t*)&i);
if ( bread <= 0 )
{
if(bread <= 0) {
traceEvent(TRACE_ERROR, "recvfrom() failed %d errno %d (%s)", bread, errno, strerror(errno));
keep_running=0;
break;
@ -928,9 +993,7 @@ static int run_loop( n2n_sn_t * sss )
/* We have a datagram to process */
process_mgmt(sss, &sender_sock, pktbuf, bread, now);
}
}
else
{
} else {
traceEvent(TRACE_DEBUG, "timeout");
}

1074
uthash.h Normal file

File diff suppressed because it is too large Load Diff