diff --git a/community.list b/community.list index 3674e3b..181e015 100644 --- a/community.list +++ b/community.list @@ -35,3 +35,31 @@ ntop[0-1][0-9] # '\d' Digits, [0-9] # '\D' Non-digits # +# fixed-name communities can optionally be followed by a network using the +# network/bitlen syntax such as the following line +# +home 192.168.168.0/24 +# +# the supernode draws ip addresses to assign to the edges (if they omit the `-a` +# parameter) from this network. note that the network is delimited by [SPACE] so +# community names cannot contain [SPACE] either. +# +# if no network is provided here, the supernode assigns some other network to each +# community. networks are taken from the default range 10.128.0.0 - 10.255.255.0/24 +# as long as no other network range is provided through the supernode's command line +# option `-d`. those sub-networks are distinct so several edges with different +# communities can be used at the same computer (being served ip addresses from the +# same supernode). also, the sub-networks described in this file are avoided. +# +# however, all networks assigned in this file are not mutually checked for colliding +# ranges so different communities can use same or overlapping sub-networks. that does +# not impose a problem if the communities do not share edge nodes. +# +# there seems to be no sense in pre-assigning sub-networks to communities whose +# names are defined by regular expressions. those will be assigned distinct +# sub-networks from the default range or the `-d` range. +# +# if `-a` is used with the edge, the edge uses the ip address specified with the +# `-a xxx.xxx.xxx.xxx` option. also, the enhanced syntax `-r -a dhcp:0.0.0.0` is +# still available to have more professional needs served by a full dhcp server. +# diff --git a/include/n2n.h b/include/n2n.h index 121b854..05810f9 100644 --- a/include/n2n.h +++ b/include/n2n.h @@ -370,6 +370,7 @@ struct sn_community he_context_t *header_iv_ctx; /* Header IV ecnryption cipher context, REMOVE as soon as seperate fields for checksum and replay protection available */ struct peer_info *edges; /* Link list of registered edges. */ int64_t number_enc_packets; /* Number of encrypted packets handled so far, required for sorting from time to time */ + n2n_ip_subnet_t auto_ip_net; /* Address range of auto ip address service. */ UT_hash_handle hh; /* makes this structure hashable */ }; @@ -390,7 +391,8 @@ typedef struct n2n_sn uint16_t mport; /* Management UDP port to bind to. */ int sock; /* Main socket for UDP traffic with edges. */ int mgmt_sock; /* management socket. */ - n2n_ip_subnet_t dhcp_addr; /* Address range of dhcp service. */ + n2n_ip_subnet_t min_auto_ip_net; /* Address range of auto_ip service. */ + n2n_ip_subnet_t max_auto_ip_net; /* Address range of auto_ip service. */ #ifndef WIN32 uid_t userid; gid_t groupid; @@ -502,6 +504,7 @@ int quick_edge_init(char *device_name, char *community_name, int sn_init(n2n_sn_t *sss); void sn_term(n2n_sn_t *sss); int run_sn_loop(n2n_sn_t *sss, int *keep_running); +int assign_one_ip_subnet(n2n_sn_t *sss, struct sn_community *comm); const char* compression_str(uint8_t cmpr); const char* transop_str(enum n2n_transform tr); diff --git a/include/n2n_define.h b/include/n2n_define.h index ca8e997..b9933bd 100644 --- a/include/n2n_define.h +++ b/include/n2n_define.h @@ -101,9 +101,10 @@ #define TUNTAP_IP_MODE_STATIC 1 #define TUNTAP_IP_MODE_DHCP 2 -/* Default network segment of the dhcp service provided by sn. */ -#define N2N_SN_DHCP_NET_ADDR_DEFAULT "172.17.12.0" -#define N2N_SN_DHCP_NET_BIT_DEFAULT 24 +/* Default network segment of the auto ip address service provided by sn. */ +#define N2N_SN_MIN_AUTO_IP_NET_DEFAULT "10.128.0.0" +#define N2N_SN_MAX_AUTO_IP_NET_DEFAULT "10.255.255.0" +#define N2N_SN_AUTO_IP_NET_BIT_DEFAULT 24 /* ************************************** */ diff --git a/include/pearson.h b/include/pearson.h index 24e51fe..a5ebb45 100644 --- a/include/pearson.h +++ b/include/pearson.h @@ -20,6 +20,8 @@ void pearson_hash_256 (uint8_t *out, const uint8_t *in, size_t len); void pearson_hash_128 (uint8_t *out, const uint8_t *in, size_t len); +uint32_t pearson_hash_32 (const uint8_t *in, size_t len); + uint16_t pearson_hash_16 (const uint8_t *in, size_t len); void pearson_hash_init(); diff --git a/src/pearson.c b/src/pearson.c index a515b52..0844ddf 100644 --- a/src/pearson.c +++ b/src/pearson.c @@ -270,7 +270,7 @@ void pearson_hash_128 (uint8_t *out, const uint8_t *in, size_t len) { *o = lower_hash; } -/* --- for later use --- + // 32-bit hash: the return value has to be interpreted as uint32_t and // follows machine-specific endianess in memory uint32_t pearson_hash_32 (const uint8_t *in, size_t len) { @@ -300,7 +300,7 @@ uint32_t pearson_hash_32 (const uint8_t *in, size_t len) { } // output return hash; -} --- pearson_hash_32 for later use --- */ +} // 16-bit hash: the return value has to be interpreted as uint16_t and diff --git a/src/sn.c b/src/sn.c index 59c8eb7..2470e8f 100644 --- a/src/sn.c +++ b/src/sn.c @@ -27,12 +27,17 @@ static n2n_sn_t sss_node; * */ static int load_allowed_sn_community(n2n_sn_t *sss, char *path) { - char buffer[4096], *line; + char buffer[4096], *line, *cmn_str, net_str[20]; + dec_ip_str_t ip_str = {'\0'}; + uint8_t bitlen; + in_addr_t net; + uint32_t mask; FILE *fd = fopen(path, "r"); struct sn_community *s, *tmp; uint32_t num_communities = 0; struct sn_community_regular_expression *re, *tmp_re; uint32_t num_regex = 0; + int has_net; if(fd == NULL) { traceEvent(TRACE_WARNING, "File %s not found", path); @@ -66,15 +71,20 @@ static int load_allowed_sn_community(n2n_sn_t *sss, char *path) { break; } + // cut off any IP sub-network upfront + cmn_str = (char*)calloc(len+1, sizeof(char)); + has_net = ( sscanf (line, "%s %s", cmn_str, net_str) == 2 ); + // if it contains typical characters... - if(NULL != strpbrk(line, ".^$*+?[]\\")) { + if(NULL != strpbrk(cmn_str, ".^$*+?[]\\")) { // ...it is treated as regular expression re = (struct sn_community_regular_expression*)calloc(1,sizeof(struct sn_community_regular_expression)); if (re) { - re->rule = re_compile(line); + re->rule = re_compile(cmn_str); HASH_ADD_PTR(sss->rules, rule, re); num_regex++; - traceEvent(TRACE_INFO, "Added regular expression for allowed communities '%s'", line); + traceEvent(TRACE_INFO, "Added regular expression for allowed communities '%s'", cmn_str); + free(cmn_str); continue; } } @@ -82,7 +92,7 @@ static int load_allowed_sn_community(n2n_sn_t *sss, char *path) { s = (struct sn_community*)calloc(1,sizeof(struct sn_community)); if(s != NULL) { - strncpy((char*)s->community, line, N2N_COMMUNITY_SIZE-1); + strncpy((char*)s->community, cmn_str, N2N_COMMUNITY_SIZE-1); s->community[N2N_COMMUNITY_SIZE-1] = '\0'; /* loaded from file, this community is unpurgeable */ s->purgeable = COMMUNITY_UNPURGEABLE; @@ -95,7 +105,42 @@ static int load_allowed_sn_community(n2n_sn_t *sss, char *path) { num_communities++; traceEvent(TRACE_INFO, "Added allowed community '%s' [total: %u]", (char*)s->community, num_communities); + + // check for sub-network address + if(has_net) { + if(sscanf(net_str, "%15[^/]/%hhu", ip_str, &bitlen) != 2) { + traceEvent(TRACE_WARNING, "Bad net/bit format '%s' for community '%c', ignoring. See comments inside community.list file.", + net_str, cmn_str); + has_net = 0; + } + net = inet_addr(ip_str); + mask = bitlen2mask(bitlen); + if((net == (in_addr_t)(-1)) || (net == INADDR_NONE) || (net == INADDR_ANY) + || ((ntohl(net) & ~mask) != 0) ) { + traceEvent(TRACE_WARNING, "Bad network '%s/%u' in '%s' for community '%s', ignoring.", + ip_str, bitlen, net_str, cmn_str); + has_net = 0; + } + if ((bitlen > 30) || (bitlen == 0)) { + traceEvent(TRACE_WARNING, "Bad prefix '%hhu' in '%s' for community '%s', ignoring.", + bitlen, net_str, cmn_str); + has_net = 0; + } + } + if(has_net) { + s->auto_ip_net.net_addr = ntohl(net); + s->auto_ip_net.net_bitlen = bitlen; + traceEvent(TRACE_INFO, "Assigned sub-network %s/%u to community '%s'.", + inet_ntoa(*(struct in_addr *) &net), + s->auto_ip_net.net_bitlen, + s->community); + } else { + assign_one_ip_subnet(sss, s); + } } + + free(cmn_str); + } fclose(fd); @@ -138,23 +183,24 @@ static void help() { printf("[-u -g ] "); #endif /* ifndef WIN32 */ printf("[-t ] "); - printf("[-d ] "); + printf("[-a ] "); printf("[-v] "); printf("\n\n"); - printf("-l | Set UDP main listen port to \n"); - printf("-c | File containing the allowed communities.\n"); + printf("-l | Set UDP main listen port to \n"); + printf("-c | File containing the allowed communities.\n"); #if defined(N2N_HAVE_DAEMON) - printf("-f | Run in foreground.\n"); + printf("-f | Run in foreground.\n"); #endif /* #if defined(N2N_HAVE_DAEMON) */ #ifndef WIN32 - printf("-u | User ID (numeric) to use when privileges are dropped.\n"); - printf("-g | Group ID (numeric) to use when privileges are dropped.\n"); + printf("-u | User ID (numeric) to use when privileges are dropped.\n"); + printf("-g | Group ID (numeric) to use when privileges are dropped.\n"); #endif /* ifndef WIN32 */ - printf("-t | Management UDP Port (for multiple supernodes on a machine).\n"); - printf("-d | Subnet that provides dhcp service for edge. eg. -d 172.17.12.0/24\n"); - printf("-v | Increase verbosity. Can be used multiple times.\n"); - printf("-h | This help message.\n"); + printf("-t | Management UDP Port (for multiple supernodes on a machine).\n"); + printf("-a | Subnet range for auto ip address service, e.g.\n"); + printf(" | -a 192.168.0.0-192.168.255.0/24, defaults to 10.128.255.0-10.255.255.0/24\n"); + printf("-v | Increase verbosity. Can be used multiple times.\n"); + printf("-h | This help message.\n"); printf("\n"); exit(1); @@ -174,35 +220,44 @@ static int setOption(int optkey, char *_optarg, n2n_sn_t *sss) { sss->mport = atoi(_optarg); break; - case 'd': { - dec_ip_str_t ip_str = {'\0'}; - in_addr_t net; + case 'a': { + dec_ip_str_t ip_min_str = {'\0'}; + dec_ip_str_t ip_max_str = {'\0'}; + in_addr_t net_min, net_max; uint8_t bitlen; + uint32_t mask; - if (sscanf(_optarg, "%15[^/]/%hhu", ip_str, &bitlen) != 2) { - traceEvent(TRACE_WARNING, "Bad net/bit format '%s'. See -h.", _optarg); + if (sscanf(_optarg, "%15[^\\-]-%15[^/]/%hhu", ip_min_str, ip_max_str, &bitlen) != 3) { + traceEvent(TRACE_WARNING, "Bad net-net/bit format '%s'. See -h.", _optarg); break; } - net = inet_addr(ip_str); - if ((net == (in_addr_t)(-1)) || (net == INADDR_NONE) || (net == INADDR_ANY)) { - traceEvent(TRACE_WARNING, "Bad network '%s' in '%s', Use default: '%s/%d'", - ip_str, _optarg, - N2N_SN_DHCP_NET_ADDR_DEFAULT, N2N_SN_DHCP_NET_BIT_DEFAULT); + net_min = inet_addr(ip_min_str); + net_max = inet_addr(ip_max_str); + mask = bitlen2mask(bitlen); + if ((net_min == (in_addr_t)(-1)) || (net_min == INADDR_NONE) || (net_min == INADDR_ANY) + || (net_max == (in_addr_t)(-1)) || (net_max == INADDR_NONE) || (net_max == INADDR_ANY) + || (ntohl(net_min) > ntohl(net_max)) + || ((ntohl(net_min) & ~mask) != 0) || ((ntohl(net_max) & ~mask) != 0) ) { + traceEvent(TRACE_WARNING, "Bad network range '%s...%s/%u' in '%s', defaulting to '%s...%s/%d'", + ip_min_str, ip_max_str, bitlen, _optarg, + N2N_SN_MIN_AUTO_IP_NET_DEFAULT, N2N_SN_MAX_AUTO_IP_NET_DEFAULT, N2N_SN_AUTO_IP_NET_BIT_DEFAULT); break; } - if (bitlen > 32) { - traceEvent(TRACE_WARNING, "Bad prefix '%hhu' in '%s', Use default: '%s/%d'", + if ((bitlen > 30) || (bitlen == 0)) { + traceEvent(TRACE_WARNING, "Bad prefix '%hhu' in '%s', defaulting to '%s...%s/%d'", bitlen, _optarg, - N2N_SN_DHCP_NET_ADDR_DEFAULT, N2N_SN_DHCP_NET_BIT_DEFAULT); + N2N_SN_MIN_AUTO_IP_NET_DEFAULT, N2N_SN_MAX_AUTO_IP_NET_DEFAULT, N2N_SN_AUTO_IP_NET_BIT_DEFAULT); break; } - traceEvent(TRACE_NORMAL, "The subnet of DHCP service is: '%s/%hhu'.", ip_str, bitlen); + traceEvent(TRACE_NORMAL, "The network range for community ip address service is '%s...%s/%hhu'.", ip_min_str, ip_max_str, bitlen); - sss->dhcp_addr.net_addr = ntohl(net); - sss->dhcp_addr.net_bitlen = bitlen; + sss->min_auto_ip_net.net_addr = ntohl(net_min); + sss->min_auto_ip_net.net_bitlen = bitlen; + sss->max_auto_ip_net.net_addr = ntohl(net_max); + sss->max_auto_ip_net.net_bitlen = bitlen; break; } @@ -249,7 +304,7 @@ static const struct option long_options[] = { {"foreground", no_argument, NULL, 'f'}, {"local-port", required_argument, NULL, 'l'}, {"mgmt-port", required_argument, NULL, 't'}, - {"dhcp", required_argument, NULL, 'd'}, + {"autoip", required_argument, NULL, 'a'}, {"help", no_argument, NULL, 'h'}, {"verbose", no_argument, NULL, 'v'}, {NULL, 0, NULL, 0} @@ -261,7 +316,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:u:g:t:d:c:vh", + while((c = getopt_long(argc, argv, "fl:u:g:t:a:c:vh", long_options, NULL)) != '?') { if(c == 255) break; setOption(c, optarg, sss); diff --git a/src/sn_utils.c b/src/sn_utils.c index ade2892..8e62b43 100644 --- a/src/sn_utils.c +++ b/src/sn_utils.c @@ -218,9 +218,12 @@ int sn_init(n2n_sn_t *sss) { sss->mport = N2N_SN_MGMT_PORT; sss->sock = -1; sss->mgmt_sock = -1; - sss->dhcp_addr.net_addr = inet_addr(N2N_SN_DHCP_NET_ADDR_DEFAULT); - sss->dhcp_addr.net_addr = ntohl(sss->dhcp_addr.net_addr); - sss->dhcp_addr.net_bitlen = N2N_SN_DHCP_NET_BIT_DEFAULT; + sss->min_auto_ip_net.net_addr = inet_addr(N2N_SN_MIN_AUTO_IP_NET_DEFAULT); + sss->min_auto_ip_net.net_addr = ntohl(sss->min_auto_ip_net.net_addr); + sss->min_auto_ip_net.net_bitlen = N2N_SN_AUTO_IP_NET_BIT_DEFAULT; + sss->max_auto_ip_net.net_addr = inet_addr(N2N_SN_MAX_AUTO_IP_NET_DEFAULT); + sss->max_auto_ip_net.net_addr = ntohl(sss->max_auto_ip_net.net_addr); + sss->max_auto_ip_net.net_bitlen = N2N_SN_AUTO_IP_NET_BIT_DEFAULT; return 0; /* OK */ } @@ -334,16 +337,15 @@ static signed int peer_tap_ip_sort(struct peer_info *a, struct peer_info *b) { } -/** The IP address assigned to the edge by the DHCP function of sn. */ -static int assign_one_ip_addr(n2n_sn_t *sss, - struct sn_community *comm, +/** The IP address assigned to the edge by the auto ip address function of sn. */ +static int assign_one_ip_addr(struct sn_community *comm, n2n_ip_subnet_t *ipaddr) { struct peer_info *peer, *tmpPeer; uint32_t net_id, mask, max_host, host_id = 1; dec_ip_bit_str_t ip_bit_str = {'\0'}; - mask = bitlen2mask(sss->dhcp_addr.net_bitlen); - net_id = sss->dhcp_addr.net_addr & mask; + mask = bitlen2mask(comm->auto_ip_net.net_bitlen); + net_id = comm->auto_ip_net.net_addr & mask; max_host = ~mask; HASH_SORT(comm->edges, peer_tap_ip_sort); @@ -364,13 +366,87 @@ static int assign_one_ip_addr(n2n_sn_t *sss, } } ipaddr->net_addr = net_id | host_id; - ipaddr->net_bitlen = sss->dhcp_addr.net_bitlen; + ipaddr->net_bitlen = comm->auto_ip_net.net_bitlen; traceEvent(TRACE_INFO, "Assign IP %s to tap adapter of edge.", ip_subnet_to_str(ip_bit_str, ipaddr)); return 0; } +/** checks if a certain sub-network is still available, i.e. does not cut any other community's sub-network */ +int subnet_available(n2n_sn_t *sss, + struct sn_community *comm, + uint32_t net_id, + uint32_t mask) { + + struct sn_community *cmn, *tmpCmn; + int success = 1; + + HASH_ITER(hh, sss->communities, cmn, tmpCmn) { + if (cmn == comm) continue; + if( (net_id <= (cmn->auto_ip_net.net_addr + ~bitlen2mask(cmn->auto_ip_net.net_bitlen))) + &&(net_id + ~mask >= cmn->auto_ip_net.net_addr) ) { + success = 0; + break; + } + } + + return success; +} + + +/** The IP address range (subnet) assigned to the community by the auto ip address function of sn. */ +int assign_one_ip_subnet(n2n_sn_t *sss, + struct sn_community *comm) { + + uint32_t net_id, net_id_i, mask, net_increment; + uint32_t no_subnets; + uint8_t success; + in_addr_t net; + + mask = bitlen2mask(sss->min_auto_ip_net.net_bitlen); + // number of possible sub-networks + no_subnets = (sss->max_auto_ip_net.net_addr - sss->min_auto_ip_net.net_addr); + no_subnets >>= (32 - sss->min_auto_ip_net.net_bitlen); + no_subnets += 1; + + // proposal for sub-network to choose + net_id = pearson_hash_32(comm->community, N2N_COMMUNITY_SIZE) % no_subnets; + net_id = sss->min_auto_ip_net.net_addr + (net_id << (32 - sss->min_auto_ip_net.net_bitlen)); + + // check for availability starting from net_id, then downwards, ... + net_increment = (~mask+1); + for(net_id_i=net_id; net_id_i >= sss->min_auto_ip_net.net_addr; net_id_i -= net_increment) { + success = subnet_available(sss, comm, net_id_i, mask); + if(success) break; + } + // ... then upwards + if(!success) { + for(net_id_i=net_id + net_increment; net_id_i <= sss->max_auto_ip_net.net_addr; net_id_i += net_increment) { + success = subnet_available(sss, comm, net_id_i, mask); + if(success) break; + } + } + + if(success) { + comm->auto_ip_net.net_addr = net_id_i; + comm->auto_ip_net.net_bitlen = sss->min_auto_ip_net.net_bitlen; + net = htonl(comm->auto_ip_net.net_addr); + traceEvent(TRACE_INFO, "Assigned sub-network %s/%u to community '%s'.", + inet_ntoa(*(struct in_addr *) &net), + comm->auto_ip_net.net_bitlen, + comm->community); + return 0; + } else { + comm->auto_ip_net.net_addr = 0; + comm->auto_ip_net.net_bitlen = 0; + traceEvent(TRACE_WARNING, "No assignable sub-network left for community '%s'.", + comm->community); + return -1; + } +} + + /*** * * For a given packet, find the apporopriate internal last valid time stamp for lookup @@ -905,6 +981,7 @@ static int process_udp(n2n_sn_t * sss, HASH_ADD_STR(sss->communities, community, comm); traceEvent(TRACE_INFO, "New community: %s", comm->community); + assign_one_ip_subnet(sss, comm); } } @@ -918,7 +995,7 @@ static int process_udp(n2n_sn_t * sss, memcpy(ack.edgeMac, reg.edgeMac, sizeof(n2n_mac_t)); if ((reg.dev_addr.net_addr == 0) || (reg.dev_addr.net_addr == 0xFFFFFFFF) || (reg.dev_addr.net_bitlen == 0) || ((reg.dev_addr.net_addr & 0xFFFF0000) == 0xA9FE0000 /* 169.254.0.0 */)) { - assign_one_ip_addr(sss, comm, &ipaddr); + assign_one_ip_addr(comm, &ipaddr); ack.dev_addr.net_addr = ipaddr.net_addr; ack.dev_addr.net_bitlen = ipaddr.net_bitlen; }