From 99b6b6b66d0785bed69cada149c0a84a3c638ebe Mon Sep 17 00:00:00 2001 From: Logan oos Even <46396513+Logan007@users.noreply.github.com> Date: Tue, 24 May 2022 20:38:51 +0200 Subject: [PATCH] added n2n-route tool (#982) * moved dev to version 3.1.1 * laid ground for n2n-route tool * adapted code style * overhauled n2n-route's tool program logic and removed route code from edge * added missing initialization of federation's purgeable field * lifted un/purgeable confusion * added warning about removed -n cli option * realized that Windows does not offer inet_aton() * removed -n option documentation from edge's man page * slightly simplified n2n-route program logic * applied more logic changes to n2n-route tool * added 'info' read command to edge's management port * corrected indention * added Linux route control to n2n-route tool * temporarily restricted n2n-route tool to Linux only * We must be over the routing! * pulled default gateway change detection into main loop to cover mobile use, and added devstr_t type * corrected use of new UNPURGEABLE - so far gone unnoticed * addresses possiible address issue * i broke it * reverted bad ideas * added command line options, help text, and prevented vpn gateway being used for supernode/peer traffic routes * added option to manually provide default gateway, also verbosity options * getting there * added option to limit networks to be routed (-n), adapted documentation * fine-tuned minor things --- CMakeLists.txt | 5 +- doc/Advanced.md | 2 +- doc/Routing.md | 10 +- edge.8 | 4 - include/json.h | 70 ++++ include/n2n.h | 3 + include/n2n_define.h | 8 +- include/n2n_typedefs.h | 40 +- src/edge.c | 49 +-- src/edge_management.c | 30 +- src/edge_utils.c | 412 +------------------- src/json.c | 202 ++++++++++ src/n2n.c | 16 +- src/sn_management.c | 4 +- src/sn_utils.c | 8 +- src/supernode.c | 3 +- src/tuntap_linux.c | 2 +- tools/Makefile.in | 3 +- tools/n2n-route.c | 860 +++++++++++++++++++++++++++++++++++++++++ win32/wintap.c | 2 +- 20 files changed, 1228 insertions(+), 505 deletions(-) create mode 100644 include/json.h create mode 100644 src/json.c create mode 100644 tools/n2n-route.c diff --git a/CMakeLists.txt b/CMakeLists.txt index b8dc6b1..20f7989 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -202,7 +202,8 @@ add_library(n2n STATIC src/sn_selection.c src/auth.c src/curve25519.c - src/n2n_port_mapping.c) + src/n2n_port_mapping.c + src/json.c) if(DEFINED WIN32) @@ -261,6 +262,8 @@ add_executable(n2n-benchmark tools/n2n-benchmark.c) target_link_libraries(n2n-benchmark n2n) add_executable(n2n-keygen tools/n2n-keygen.c) target_link_libraries(n2n-keygen n2n) +add_executable(n2n-route tools/n2n-route.c) +target_link_libraries(n2n-route n2n) add_executable(tests-auth tools/tests-auth.c) target_link_libraries(tests-auth n2n) diff --git a/doc/Advanced.md b/doc/Advanced.md index e118cb8..e7d9c83 100644 --- a/doc/Advanced.md +++ b/doc/Advanced.md @@ -33,7 +33,7 @@ The [TAP Configuration Guide](TapConfiguration.md) contains hints on various set ## Routing the Traffic -Reaching a remote network or tunneling all the internet traffic via n2n are two common tasks which require a proper routing setup. n2n supports routing needs providing options for packet forwarding (`-r`) including broadcasts (`-E`) as well as temporarily modifying the routing table (`-n`). Details can be found in the [Routing document](Routing.md). +Reaching a remote network or tunneling all the internet traffic via n2n are two common tasks which require a proper routing setup. n2n supports routing needs providing options for packet forwarding (`-r`) including broadcasts (`-E`) as well as temporarily modifying the routing table (`tools/n2n-route`). Details can be found in the [Routing document](Routing.md). ## Traffic Restrictions diff --git a/doc/Routing.md b/doc/Routing.md index c4d57f2..afa3ac8 100644 --- a/doc/Routing.md +++ b/doc/Routing.md @@ -12,14 +12,16 @@ In order to enable routing, the `server` must be configured as follows: 2. Enable packet forwarding with `sudo sysctl -w net.ipv4.ip_forward=1` 3. Enable IP masquerading: `sudo iptables -t nat -A POSTROUTING -j MASQUERADE` -On the client side, the easiest way to configure routing is via the `-n` option. For example: +On the client side, the easiest way to configure routing is via the `tools/n2n-route` utility. For example: -- In order to connect to the remote network `192.168.100.0/24`, use `-n 192.168.100.0/24:10.0.0.1` -- In order to tunnel all the internet traffic, use `-n 0.0.0.0/0:10.0.0.1` +- In order to tunnel all the internet traffic, use `tools/n2n-route 10.0.0.1` +- In order to connect to the remote network `192.168.100.0/24`, use `tools/n2n-route -n 192.168.100.0/24 10.0.0.1` 10.0.0.1 is the IP address of the gateway to use to route the specified network. It should correspond to the IP address of the `server` within n2n. Multiple `-n` options can be specified. -As an alternative to the `-n` option, the `ip route` linux command can be manually used. See the [n2n-gateway.sh](scripts/n2n-gateway.sh) script for an example. See also the following description of other use cases and in depth explanation. +The utility connects to the local edge's management port to receive information about peers and supernodes. It currently works on Linux only. + +As an alternative to the `tools/n2n-route` utility, the `ip route` linux command can be manually used. See the [n2n-gateway.sh](scripts/n2n-gateway.sh) script for an example. See also the following description of other use cases and in depth explanation. ## Special Scenarios diff --git a/edge.8 b/edge.8 index 75861f6..54b1b73 100644 --- a/edge.8 +++ b/edge.8 @@ -203,10 +203,6 @@ access to JSON API at the management port. \fB\-v\fR, \fB\-\-verbose\fR make more verbose, repeat as required .TP -\fB\-n \fR<\fIcidr:gateway\fR> -route an IPv4 network via the gateway, use 0.0.0.0/0 for -the default gateway, can be set multiple times -.TP \fB\-u \fR<\fIUID\fR>, \fB\-\-euid\fR=<\fIUID\fR> numeric user ID to use when privileges are dropped .TP diff --git a/include/json.h b/include/json.h new file mode 100644 index 0000000..aabdbb1 --- /dev/null +++ b/include/json.h @@ -0,0 +1,70 @@ +/** + * (C) 2007-22 - ntop.org and contributors + * + * 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 + * + */ + + +// taken from (and modified) +// https://github.com/Logan007/C-Simple-JSON-Parser +// which is declared license-free code by the author according to +// https://github.com/forkachild/C-Simple-JSON-Parser/issues/3#issuecomment-1073520808 + + +#ifndef JSON_H +#define JSON_H + + +#include +#include + +#define json_str_is_whitespace(x) x == '\r' || x == '\n' || x == '\t' || x == ' ' +#define json_str_is_numeral(x) (x >= '0' && x <= '9') || x == 'e' || x == 'E' \ + || x == '.' || x == '+' || x == '-' +#define json_str_remove_whitespace_calc_offset(x, y) while(json_str_is_whitespace(*x)) { x++; y++; } + +struct _jsonobject; +struct _jsonpair; +union _jsonvalue; + +typedef enum { + JSON_STRING = 0, + JSON_DOUBLE, + JSON_OBJECT +} json_value_type; + +typedef struct _jsonobject { + struct _jsonpair *pairs; + int count; +} json_object_t; + +typedef struct _jsonpair { + char *key; + union _jsonvalue *value; + json_value_type type; +} json_pair_t; + +typedef union _jsonvalue { + char *string_value; + double double_value; + struct _jsonobject *json_object; +} json_value_t; + + +json_object_t *json_parse (char *str); +void json_free (json_object_t *obj); + + +#endif diff --git a/include/n2n.h b/include/n2n.h index 17150fa..28c8aaa 100644 --- a/include/n2n.h +++ b/include/n2n.h @@ -162,6 +162,8 @@ #include "n2n_port_mapping.h" #endif // HAVE_MINIUPNP || HAVE_NATPMP +#include "json.h" + /* ************************************** */ #include "header_encryption.h" @@ -210,6 +212,7 @@ void tuntap_close (struct tuntap_dev *tuntap); void tuntap_get_address (struct tuntap_dev *tuntap); /* Utils */ +char* inaddrtoa (ipstr_t out, struct in_addr addr); char* intoa (uint32_t addr, char* buf, uint16_t buf_len); uint32_t bitlen2mask (uint8_t bitlen); uint8_t mask2bitlen (uint32_t mask); diff --git a/include/n2n_define.h b/include/n2n_define.h index bbb9110..19c3306 100644 --- a/include/n2n_define.h +++ b/include/n2n_define.h @@ -97,12 +97,8 @@ #define FEDERATION_NAME "*Federation" enum federation {IS_NO_FEDERATION = 0,IS_FEDERATION = 1}; -/* (un)purgeable community indicator (supernode) */ -#define COMMUNITY_UNPURGEABLE 0 -#define COMMUNITY_PURGEABLE 1 - -/* (un)purgeable supernode indicator */ -enum sn_purge {SN_PURGEABLE = 0, SN_UNPURGEABLE = 1}; +/* (un)purgeable indicator for supernodes, communities, routes, ... */ +enum sn_purge {UNPURGEABLE = 0, PURGEABLE = 1}; /* Header encryption indicators */ #define HEADER_ENCRYPTION_UNKNOWN 0 diff --git a/include/n2n_typedefs.h b/include/n2n_typedefs.h index 633f28c..30dc32f 100644 --- a/include/n2n_typedefs.h +++ b/include/n2n_typedefs.h @@ -213,6 +213,21 @@ typedef struct filter_rule { } filter_rule_t; +/** Uncomment this to enable the MTU check, then try to ssh to generate a fragmented packet. */ +/** NOTE: see doc/MTU.md for an explanation on the 1400 value */ +//#define MTU_ASSERT_VALUE 1400 + +/** Common type used to hold stringified IP addresses. */ +typedef char ipstr_t[INET_ADDRSTRLEN]; + +/** Common type used to hold stringified MAC addresses. */ +#define N2N_MACSTR_SIZE 32 +typedef char macstr_t[N2N_MACSTR_SIZE]; +typedef char dec_ip_str_t[N2N_NETMASK_STR_SIZE]; +typedef char dec_ip_bit_str_t[N2N_NETMASK_STR_SIZE + 4]; +typedef char devstr_t[N2N_IFNAMSIZ]; + + #ifndef WIN32 typedef struct tuntap_dev { int fd; @@ -221,24 +236,12 @@ typedef struct tuntap_dev { uint32_t ip_addr; uint32_t device_mask; uint16_t mtu; - char dev_name[N2N_IFNAMSIZ]; + devstr_t dev_name; } tuntap_dev; #define SOCKET int #endif /* #ifndef WIN32 */ -/** Uncomment this to enable the MTU check, then try to ssh to generate a fragmented packet. */ -/** NOTE: see doc/MTU.md for an explanation on the 1400 value */ -//#define MTU_ASSERT_VALUE 1400 - -/** Common type used to hold stringified IP addresses. */ -typedef char ipstr_t[32]; - -/** Common type used to hold stringified MAC addresses. */ -#define N2N_MACSTR_SIZE 32 -typedef char macstr_t[N2N_MACSTR_SIZE]; -typedef char dec_ip_str_t[N2N_NETMASK_STR_SIZE]; -typedef char dec_ip_bit_str_t[N2N_NETMASK_STR_SIZE + 4]; typedef struct speck_context_t he_context_t; typedef char n2n_sn_name_t[N2N_EDGE_SN_HOST_SIZE]; @@ -461,12 +464,6 @@ struct peer_info { typedef struct peer_info peer_info_t; -typedef struct n2n_route { - in_addr_t net_addr; - uint8_t net_bitlen; - in_addr_t gateway; -} n2n_route_t; - typedef struct n2n_edge n2n_edge_t; /* *************************************************** */ @@ -551,7 +548,7 @@ typedef struct n2n_edge_callbacks { } n2n_edge_callbacks_t; typedef struct n2n_tuntap_priv_config { - char tuntap_dev_name[N2N_IFNAMSIZ]; + devstr_t tuntap_dev_name; char ip_mode[N2N_IF_MODE_SIZE]; dec_ip_str_t ip_addr; dec_ip_str_t netmask; @@ -654,7 +651,6 @@ typedef struct n2n_port_map_parameter { typedef struct n2n_edge_conf { struct peer_info *supernodes; /**< List of supernodes */ - n2n_route_t *routes; /**< Networks to route through n2n */ n2n_community_t community_name; /**< The community. 16 full octets. */ n2n_desc_t dev_desc; /**< The device description (hint) */ n2n_private_public_key_t *public_key; /**< edge's public key (for user/password based authentication) */ @@ -668,7 +664,6 @@ typedef struct n2n_edge_conf { he_context_t *header_iv_ctx_dynamic; /**< Header IV ecnryption cipher context, REMOVE as soon as separate fileds for checksum and replay protection available */ n2n_transform_t transop_id; /**< The transop to use. */ uint8_t compression; /**< Compress outgoing data packets before encryption */ - uint16_t num_routes; /**< Number of routes in routes */ uint8_t tuntap_ip_mode; /**< Interface IP address allocated mode, eg. DHCP. */ uint8_t allow_routing; /**< Accept packet no to interface address. */ uint8_t drop_multicast; /**< Multicast ethernet addresses. */ @@ -719,7 +714,6 @@ struct n2n_edge { #ifdef HAVE_ZSTD n2n_trans_op_t transop_zstd; /**< The transop for ZSTD compression */ #endif - n2n_route_t *sn_route_to_clean; /**< Supernode route to clean */ n2n_edge_callbacks_t cb; /**< API callbacks */ void *user_data; /**< Can hold user data */ SN_SELECTION_CRITERION_DATA_TYPE sn_selection_criterion_common_data; diff --git a/src/edge.c b/src/edge.c index d2d6585..672ee89 100644 --- a/src/edge.c +++ b/src/edge.c @@ -51,7 +51,6 @@ int fetch_and_eventually_process_data (n2n_edge_t *eee, SOCKET sock, uint8_t *pktbuf, uint16_t *expected, uint16_t *position, time_t now); int resolve_check (n2n_resolve_parameter_t *param, uint8_t resolution_request, time_t now); -int edge_init_routes (n2n_edge_t *eee, n2n_route_t *routes, uint16_t num_routes); /* ***************************************************** */ @@ -212,7 +211,6 @@ static void help (int level) { "[--management-password ] " "\n " "[-v] " - "[-n ] " #ifndef WIN32 "\n " "[-u ] " @@ -342,8 +340,6 @@ static void help (int level) { printf(" --management_... | management port password, defaults to '%s'\n" " ...password | \n", N2N_MGMT_PASSWORD); printf(" -v | make more verbose, repeat as required\n"); - printf(" -n | route an IPv4 network via the gateway, use 0.0.0.0/0 for\n" - " | the default gateway, can be set multiple times\n"); #ifndef WIN32 printf(" -u | numeric user ID to use when privileges are dropped\n"); printf(" -g | numeric group ID to use when privileges are dropped\n"); @@ -574,8 +570,8 @@ static int setOption (int optkey, char *optargument, n2n_tuntap_priv_config_t *e #if defined(N2N_CAN_NAME_IFACE) case 'd': /* TUNTAP name */ { - strncpy(ec->tuntap_dev_name, optargument, N2N_IFNAMSIZ); - ec->tuntap_dev_name[N2N_IFNAMSIZ - 1] = '\0'; + strncpy(ec->tuntap_dev_name, optargument, sizeof(devstr_t)); + ec->tuntap_dev_name[sizeof(devstr_t) - 1] = '\0'; break; } #endif @@ -686,39 +682,9 @@ static int setOption (int optkey, char *optargument, n2n_tuntap_priv_config_t *e } #endif case 'n': { - char cidr_net[64], gateway[64]; - n2n_route_t route; - - if(sscanf(optargument, "%63[^/]/%hhd:%63s", cidr_net, &route.net_bitlen, gateway) != 3) { - traceEvent(TRACE_WARNING, "bad cidr/gateway format '%d'", optargument); - 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_bitlen, 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, optargument); - 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) * (conf->num_routes + 1)); - conf->routes[conf->num_routes] = route; - conf->num_routes++; - - break; + traceEvent(TRACE_WARNING, "route support (-n) has been removed from n2n's core since version 3.1, " + "please try tools/n2n-route instead"); + return 2; } case 'S': { @@ -1259,11 +1225,6 @@ int main (int argc, char* argv[]) { 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; diff --git a/src/edge_management.c b/src/edge_management.c index 2982a01..d1115f6 100644 --- a/src/edge_management.c +++ b/src/edge_management.c @@ -160,6 +160,33 @@ static void mgmt_edges (mgmt_req_t *req, strbuf_t *buf) { } } +static void mgmt_edge_info (mgmt_req_t *req, strbuf_t *buf) { + size_t msg_len; + macstr_t mac_buf; + struct in_addr ip_addr, ip_addr_mask; + ipstr_t ip_address, ip_address_mask; + + ip_addr.s_addr = req->eee->device.ip_addr; + inaddrtoa(ip_address, ip_addr); + ip_addr_mask.s_addr = req->eee->device.device_mask; + inaddrtoa(ip_address_mask, ip_addr_mask); + + msg_len = snprintf(buf->str, buf->size, + "{" + "\"_tag\":\"%s\"," + "\"_type\":\"row\"," + "\"version\":\"%s\"," + "\"macaddr\":\"%s\"," + "\"ip4addr\":\"%s\"," + "\"ip4netmask\":\"%s\"}\n", + req->tag, + PACKAGE_VERSION, + is_null_mac(req->eee->device.mac_addr) ? "" : macaddr_str(mac_buf, req->eee->device.mac_addr), + ip_address, ip_address_mask); + + send_reply(req, buf, msg_len); +} + static void mgmt_timestamps (mgmt_req_t *req, strbuf_t *buf) { size_t msg_len; @@ -252,6 +279,7 @@ static const mgmt_handler_t mgmt_handlers[] = { { .cmd = "communities", .help = "Show current community", .func = mgmt_communities}, { .cmd = "edges", .help = "List current edges/peers", .func = mgmt_edges}, { .cmd = "supernodes", .help = "List current supernodes", .func = mgmt_supernodes}, + { .cmd = "info", .help = "Provide basic edge information", .func = mgmt_edge_info}, { .cmd = "timestamps", .help = "Event timestamps", .func = mgmt_timestamps}, { .cmd = "packetstats", .help = "traffic counters", .func = mgmt_packetstats}, { .cmd = "post.test", .help = "send a test event", .func = mgmt_post_test}, @@ -556,7 +584,7 @@ void readFromMgmtSocket (n2n_edge_t *eee) { msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_len), "%-19s %1s%1s | %-17s | %-21s | %-15s | %9s | %10s\n", peer->version, - (peer->purgeable == SN_UNPURGEABLE) ? "l" : "", + (peer->purgeable == UNPURGEABLE) ? "l" : "", (peer == eee->curr_sn) ? (eee->sn_wait ? "." : "*" ) : "", is_null_mac(peer->mac_addr) ? "" : macaddr_str(mac_buf, peer->mac_addr), sock_to_cstr(sockbuf, &(peer->sock)), diff --git a/src/edge_utils.c b/src/edge_utils.c index c6a9ad9..437f590 100644 --- a/src/edge_utils.c +++ b/src/edge_utils.c @@ -45,8 +45,6 @@ static void check_peer_registration_needed (n2n_edge_t *eee, const n2n_sock_t *peer); static int edge_init_sockets (n2n_edge_t *eee); -int edge_init_routes (n2n_edge_t *eee, n2n_route_t *routes, uint16_t num_routes); -static void edge_cleanup_routes (n2n_edge_t *eee); static void check_known_peer_sock_change (n2n_edge_t *eee, uint8_t from_supernode, @@ -1812,7 +1810,7 @@ static char *get_ip_from_arp (dec_ip_str_t buf, const n2n_mac_t req_mac) { FILE *fd; dec_ip_str_t ip_str = {'\0'}; - char dev_str[N2N_IFNAMSIZ] = {'\0'}; + devstr_t dev_str = {'\0'}; macstr_t mac_str = {'\0'}; n2n_mac_t mac = {'\0'}; @@ -3020,8 +3018,6 @@ void edge_term (n2n_edge_t * eee) { eee->transop_zstd.deinit(&eee->transop_zstd); #endif - edge_cleanup_routes(eee); - destroy_network_traffic_filter(eee->network_traffic_filter); closeTraceFile(); @@ -3029,6 +3025,7 @@ void edge_term (n2n_edge_t * eee) { free(eee); } + /* ************************************** */ @@ -3074,411 +3071,9 @@ static int edge_init_sockets (n2n_edge_t *eee) { return(0); } -/* ************************************** */ - -#ifdef __linux__ - -static uint32_t get_gateway_ip () { - - FILE *fd; - char *token = NULL; - char *gateway_ip_str = NULL; - char buf[256]; - uint32_t gateway = 0; - - if(!(fd = fopen("/proc/net/route", "r"))) - return(0); - - while(fgets(buf, sizeof(buf), fd)) { - if(strtok(buf, "\t") && (token = strtok(NULL, "\t")) && (!strcmp(token, "00000000"))) { - token = strtok(NULL, "\t"); - - if(token) { - struct in_addr addr; - - addr.s_addr = strtoul(token, NULL, 16); - gateway_ip_str = inet_ntoa(addr); - - if(gateway_ip_str) { - gateway = addr.s_addr; - break; - } - } - } - } - - fclose(fd); - - return(gateway); -} - -static char* route_cmd_to_str (int cmd, const n2n_route_t *route, char *buf, size_t bufsize) { - - const char *cmd_str; - struct in_addr addr; - char netbuf[64], gwbuf[64]; - - switch(cmd) { - case RTM_NEWROUTE: - cmd_str = "Add"; - break; - - case RTM_DELROUTE: - cmd_str = "Delete"; - break; - - default: - cmd_str = "?"; - } - - addr.s_addr = route->net_addr; - inet_ntop(AF_INET, &addr, netbuf, sizeof(netbuf)); - addr.s_addr = route->gateway; - inet_ntop(AF_INET, &addr, gwbuf, sizeof(gwbuf)); - - snprintf(buf, bufsize, "%s %s/%d via %s", cmd_str, netbuf, route->net_bitlen, gwbuf); - - return(buf); -} - -/* Adapted from https://olegkutkov.me/2019/08/29/modifying-linux-network-routes-using-netlink/ */ -#define NLMSG_TAIL(nmsg) \ - ((struct rtattr *) (((char *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len))) - -/* Add new data to rtattr */ -static int rtattr_add (struct nlmsghdr *n, int maxlen, int type, const void *data, int alen) { - - int len = RTA_LENGTH(alen); - struct rtattr *rta; - - if(NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len) > maxlen) { - traceEvent(TRACE_ERROR, "rtattr_add error: message exceeded bound of %d\n", maxlen); - return -1; - } - - rta = NLMSG_TAIL(n); - rta->rta_type = type; - rta->rta_len = len; - - if(alen) - memcpy(RTA_DATA(rta), data, alen); - - n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len); - - return 0; -} - -static int routectl (int cmd, int flags, n2n_route_t *route, int if_idx) { - - int rv = -1; - int rv2; - char nl_buf[8192]; /* >= 8192 to avoid truncation, see "man 7 netlink" */ - char route_buf[256]; - struct iovec iov; - struct msghdr msg; - struct sockaddr_nl sa; - uint8_t read_reply = 1; - int nl_sock; - - struct { - struct nlmsghdr n; - struct rtmsg r; - char buf[4096]; - } nl_request; - - if((nl_sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) == -1) { - traceEvent(TRACE_ERROR, "netlink socket creation failed [%d]: %s", errno, strerror(errno)); - return(-1); - } - - /* Subscribe to route change events */ - iov.iov_base = nl_buf; - iov.iov_len = sizeof(nl_buf); - - memset(&sa, 0, sizeof(sa)); - sa.nl_family = PF_NETLINK; - sa.nl_groups = RTMGRP_IPV4_ROUTE | RTMGRP_NOTIFY; - sa.nl_pid = getpid(); - - memset(&msg, 0, sizeof(msg)); - msg.msg_name = &sa; - msg.msg_namelen = sizeof(sa); - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - - /* Subscribe to route events */ - if(bind(nl_sock, (struct sockaddr*)&sa, sizeof(sa)) == -1) { - traceEvent(TRACE_ERROR, "netlink socket bind failed [%d]: %s", errno, strerror(errno)); - goto out; - } - - /* Initialize request structure */ - memset(&nl_request, 0, sizeof(nl_request)); - nl_request.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); - nl_request.n.nlmsg_flags = NLM_F_REQUEST | flags; - nl_request.n.nlmsg_type = cmd; - nl_request.r.rtm_family = AF_INET; - nl_request.r.rtm_table = RT_TABLE_MAIN; - nl_request.r.rtm_scope = RT_SCOPE_NOWHERE; - - /* Set additional flags if NOT deleting route */ - if(cmd != RTM_DELROUTE) { - nl_request.r.rtm_protocol = RTPROT_BOOT; - nl_request.r.rtm_type = RTN_UNICAST; - } - - nl_request.r.rtm_family = AF_INET; - nl_request.r.rtm_dst_len = route->net_bitlen; - - /* Select scope, for simplicity we supports here only IPv6 and IPv4 */ - if(nl_request.r.rtm_family == AF_INET6) - nl_request.r.rtm_scope = RT_SCOPE_UNIVERSE; - else - nl_request.r.rtm_scope = RT_SCOPE_LINK; - - /* Set gateway */ - if(route->net_bitlen) { - if(rtattr_add(&nl_request.n, sizeof(nl_request), RTA_GATEWAY, &route->gateway, 4) < 0) - goto out; - - nl_request.r.rtm_scope = 0; - nl_request.r.rtm_family = AF_INET; - } - - /* Don't set destination and interface in case of default gateways */ - if(route->net_bitlen) { - /* Set destination network */ - if(rtattr_add(&nl_request.n, sizeof(nl_request), /*RTA_NEWDST*/ RTA_DST, &route->net_addr, 4) < 0) - goto out; - - /* Set interface */ - if(if_idx > 0) { - if(rtattr_add(&nl_request.n, sizeof(nl_request), RTA_OIF, &if_idx, sizeof(int)) < 0) - goto out; - } - } - - /* Send message to the netlink */ - if((rv2 = send(nl_sock, &nl_request, sizeof(nl_request), 0)) != sizeof(nl_request)) { - traceEvent(TRACE_ERROR, "netlink send failed [%d]: %s", errno, strerror(errno)); - goto out; - } - - /* Wait for the route notification. Assume that the first reply we get is the correct one. */ - traceEvent(TRACE_DEBUG, "waiting for netlink response..."); - - while(read_reply) { - ssize_t len = recvmsg(nl_sock, &msg, 0); - struct nlmsghdr *nh; - - for(nh = (struct nlmsghdr *)nl_buf; NLMSG_OK(nh, len); nh = NLMSG_NEXT(nh, len)) { - /* Stop after the first reply */ - read_reply = 0; - - if(nh->nlmsg_type == NLMSG_ERROR) { - struct nlmsgerr *err = NLMSG_DATA(nh); - int errcode = err->error; - - if(errcode < 0) - errcode = -errcode; - - /* Ignore EEXIST as existing rules are ok */ - if(errcode != EEXIST) { - traceEvent(TRACE_ERROR, "[err=%d] route: %s", errcode, route_cmd_to_str(cmd, route, route_buf, sizeof(route_buf))); - goto out; - } - } - - if(nh->nlmsg_type == NLMSG_DONE) - break; - - if(nh->nlmsg_type == cmd) { - traceEvent(TRACE_DEBUG, "Found netlink reply"); - break; - } - } - } - - traceEvent(TRACE_DEBUG, route_cmd_to_str(cmd, route, route_buf, sizeof(route_buf))); - rv = 0; - -out: - close(nl_sock); - - return(rv); -} -#endif /* ************************************** */ -#ifdef __linux__ - -static int edge_init_routes_linux (n2n_edge_t *eee, n2n_route_t *routes, uint16_t num_routes) { - int i; - for(i = 0; inet_addr == 0) && (route->net_bitlen == 0)) { - /* This is a default gateway rule. We need to: - * - * 1. Add a route to the supernode via the host internet gateway - * 2. Add the new default gateway route - * - * Instead of modifying the system default gateway, we use the trick - * of adding a route to the networks 0.0.0.0/1 and 128.0.0.0/1, thus - * covering the whole IPv4 range. Such routes in linux take precedence - * over the default gateway (0.0.0.0/0) since are more specific. - * This leaves the default gateway unchanged so that after n2n is - * stopped the cleanup is easier. - * See https://github.com/zerotier/ZeroTierOne/issues/178#issuecomment-204599227 - */ - n2n_sock_t sn; - n2n_route_t custom_route; - uint32_t *a; - - if(eee->sn_route_to_clean) { - traceEvent(TRACE_ERROR, "only one default gateway route allowed"); - return(-1); - } - - if(eee->conf.sn_num != 1) { - traceEvent(TRACE_ERROR, "only one supernode supported with routes"); - return(-1); - } - - if(supernode2sock(&sn, eee->conf.supernodes->ip_addr) < 0) - return(-1); - - if(sn.family != AF_INET) { - traceEvent(TRACE_ERROR, "only IPv4 routes supported"); - return(-1); - } - - a = (u_int32_t*)sn.addr.v4; - custom_route.net_addr = *a; - custom_route.net_bitlen = 32; - custom_route.gateway = get_gateway_ip(); - - if(!custom_route.gateway) { - traceEvent(TRACE_ERROR, "could not determine the gateway IP address"); - return(-1); - } - - /* ip route add supernode via internet_gateway */ - if(routectl(RTM_NEWROUTE, NLM_F_CREATE | NLM_F_EXCL, &custom_route, -1) < 0) - return(-1); - - /* Save the route to delete it when n2n is stopped */ - eee->sn_route_to_clean = calloc(1, sizeof(n2n_route_t)); - - /* Store a copy of the rules into the runtime to delete it during shutdown */ - if(eee->sn_route_to_clean) - *eee->sn_route_to_clean = custom_route; - - /* ip route add 0.0.0.0/1 via n2n_gateway */ - custom_route.net_addr = 0; - custom_route.net_bitlen = 1; - custom_route.gateway = route->gateway; - - if(routectl(RTM_NEWROUTE, NLM_F_CREATE | NLM_F_EXCL, &custom_route, eee->device.if_idx) < 0) - return(-1); - - /* ip route add 128.0.0.0/1 via n2n_gateway */ - custom_route.net_addr = 128; - custom_route.net_bitlen = 1; - custom_route.gateway = route->gateway; - - if(routectl(RTM_NEWROUTE, NLM_F_CREATE | NLM_F_EXCL, &custom_route, eee->device.if_idx) < 0) - return(-1); - } else { - /* ip route add net via n2n_gateway */ - if(routectl(RTM_NEWROUTE, NLM_F_CREATE | NLM_F_EXCL, route, eee->device.if_idx) < 0) - return(-1); - } - } - - return(0); -} -#endif - -/* ************************************** */ - -#ifdef WIN32 -static int edge_init_routes_win (n2n_edge_t *eee, n2n_route_t *routes, uint16_t num_routes, uint8_t verb /* 0 = add, 1 = delete */) { - int i; - struct in_addr net_addr, gateway; - char c_net_addr[32]; - char c_gateway[32]; - char c_interface[32]; - char c_verb[32]; - char cmd[256]; - - for(i = 0; i < num_routes; i++) { - n2n_route_t *route = &routes[i]; - if((route->net_addr == 0) && (route->net_bitlen == 0)) { - // REVISIT: there might be a chance to get it working on Windows following the hints at - // https://docs.microsoft.com/en-us/windows/win32/api/netioapi/ns-netioapi-mib_ipinterface_row - // - // " The DisableDefaultRoutes member of the MIB_IPINTERFACE_ROW structure can be used to disable - // using the default route on an interface. This member can be used as a security measure by - // VPN clients to restrict split tunneling when split tunneling is not required by the VPN client. - // A VPN client can call the SetIpInterfaceEntry function to set the DisableDefaultRoutes member - // to TRUE when required. A VPN client can query the current state of the DisableDefaultRoutes - // member by calling the GetIpInterfaceEntry function. " - traceEvent(TRACE_WARNING, "the 0.0.0.0/0 route settings are not supported on Windows"); - return(-1); - } else { - /* ip route add net via n2n_gateway */ - memcpy(&net_addr, &(route->net_addr), sizeof(net_addr)); - memcpy(&gateway, &(route->gateway), sizeof(gateway)); - _snprintf(c_net_addr, sizeof(c_net_addr), inet_ntoa(net_addr)); - _snprintf(c_gateway, sizeof(c_gateway), inet_ntoa(gateway)); - _snprintf(c_interface, sizeof(c_interface), "if %u", eee->device.if_idx); - _snprintf(c_verb, sizeof(c_verb), verb ? "delete" : "add"); - _snprintf(cmd, sizeof(cmd), "route %s %s/%d %s %s > nul", c_verb, c_net_addr, route->net_bitlen, c_gateway, c_interface); - traceEvent(TRACE_NORMAL, "ROUTE CMD = '%s'\n", cmd); - system(cmd); - } - } - - return (0); -} -#endif // WIN32 - -/* ************************************** */ - -/* Add the user-provided routes to the linux routing table. Network routes - * are bound to the n2n TAP device, so they are automatically removed when - * the TAP device is destroyed. */ -int edge_init_routes (n2n_edge_t *eee, n2n_route_t *routes, uint16_t num_routes) { -#ifdef __linux__ - return edge_init_routes_linux(eee, routes, num_routes); -#endif - -#ifdef WIN32 - return edge_init_routes_win(eee, routes, num_routes, 0 /* add */); -#endif - return 0; -} - -/* ************************************** */ - -static void edge_cleanup_routes (n2n_edge_t *eee) { -#ifdef __linux__ - if(eee->sn_route_to_clean) { - /* ip route del supernode via internet_gateway */ - routectl(RTM_DELROUTE, 0, eee->sn_route_to_clean, -1); - free(eee->sn_route_to_clean); - } -#endif - -#ifdef WIN32 - edge_init_routes_win(eee, eee->conf.routes, eee->conf.num_routes, 1 /* del */); -#endif - -} - -/* ************************************** */ void edge_init_conf_defaults (n2n_edge_conf_t *conf) { @@ -3534,7 +3129,6 @@ void edge_init_conf_defaults (n2n_edge_conf_t *conf) { void edge_term_conf (n2n_edge_conf_t *conf) { - if(conf->routes) free(conf->routes); if(conf->encrypt_key) free(conf->encrypt_key); if(conf->network_traffic_filter_rules) { @@ -3581,7 +3175,7 @@ int edge_conf_add_supernode (n2n_edge_conf_t *conf, const char *ip_and_port) { strncpy(sn->ip_addr, ip_and_port, N2N_EDGE_SN_HOST_SIZE - 1); memcpy(&(sn->sock), sock, sizeof(n2n_sock_t)); memcpy(sn->mac_addr, null_mac, sizeof(n2n_mac_t)); - sn->purgeable = SN_UNPURGEABLE; + sn->purgeable = UNPURGEABLE; } } diff --git a/src/json.c b/src/json.c new file mode 100644 index 0000000..d048b36 --- /dev/null +++ b/src/json.c @@ -0,0 +1,202 @@ +/** + * (C) 2007-22 - ntop.org and contributors + * + * 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 + * + */ + + +// taken from (and modified) +// https://github.com/Logan007/C-Simple-JSON-Parser +// which is declared license-free code by the author according to +// https://github.com/forkachild/C-Simple-JSON-Parser/issues/3#issuecomment-1073520808 + + +#include "json.h" + + +static int json_str_next_occurence (char *str, char ch); +static int json_str_next_non_numeral (char *str); +static json_object_t *_json_parse (char *str, int *offset); + + +json_object_t *json_parse (char *str) { + + int offset = 0; + + json_object_t *temp_obj = _json_parse(str, &offset); + + return temp_obj; +} + + +void json_free (json_object_t *obj) { + + int i; + + if(obj == NULL) + return; + + if(obj->pairs == NULL) { + free(obj); + return; + } + + for(i = 0; i < obj->count; i++) { + if(obj->pairs[i].key != NULL) + free(obj->pairs[i].key); + if(obj->pairs[i].value != NULL) { + switch(obj->pairs[i].type) { + case JSON_STRING: + free(obj->pairs[i].value->string_value); + break; + case JSON_DOUBLE: + break; + case JSON_OBJECT: + json_free(obj->pairs[i].value->json_object); + } + free(obj->pairs[i].value); + } + } +} + + +static int json_str_next_occurence (char *str, char ch) { + + int pos = 0; + + if(str == NULL) + return -1; + + while(*str != ch && *str != '\0') { + str++; + pos++; + } + + return (*str == '\0') ? -1 : pos; +} + + +static int json_str_next_non_numeral (char *str) { + + int pos = 0; + + if(str == NULL) + return -1; + + while((json_str_is_numeral(*str)) && (*str != '\0')) { + str++; + pos++; + } + return (*str == '\0') ? -1 : pos; +} + + +static json_object_t *_json_parse (char *str, int *offset) { + + int _offset = 0; + + json_object_t *obj = (json_object_t*)malloc(sizeof(json_object_t)); + obj->count = 1; + obj->pairs = (json_pair_t*)malloc(sizeof(json_pair_t)); + + while(*str != '\0') { + json_str_remove_whitespace_calc_offset(str, _offset); + if(*str == '{') { + str++; + _offset++; + } else if(*str == '"') { + + int i = json_str_next_occurence(++str, '"'); + if(i <= 0) { + json_free(obj); + return NULL; + } + + json_pair_t tempPtr = obj->pairs[obj->count - 1]; + + tempPtr.key = (char*)malloc((i + 1) * sizeof(char)); + memcpy(tempPtr.key, str, i * sizeof(char)); + tempPtr.key[i] = '\0'; + + str += i + 1; + _offset += i + 2; + + i = json_str_next_occurence(str, ':'); + if(i == -1) + return NULL; + str += i + 1; + _offset += i + 1; + + json_str_remove_whitespace_calc_offset(str, _offset); + + if(*str == '{') { + int _offsetBeforeParsingChildObject = _offset; + int _sizeOfChildObject; + + tempPtr.value = (json_value_t*)malloc(sizeof(json_value_t)); + tempPtr.type = JSON_OBJECT; + tempPtr.value->json_object = _json_parse(str, &_offset); + if(tempPtr.value->json_object == NULL) { + json_free(obj); + return NULL; + } + // Advance the string pointer by the size of the processed child object + _sizeOfChildObject = _offset - _offsetBeforeParsingChildObject; + str += _sizeOfChildObject; + } else if(*str == '"') { + i = json_str_next_occurence(++str, '"'); + if(i == -1) { + json_free(obj); + return NULL; + } + tempPtr.value = (json_value_t*)malloc(sizeof(json_value_t)); + tempPtr.type = JSON_STRING; + tempPtr.value->string_value = (char*)malloc((i + 1) * sizeof(char)); + memcpy(tempPtr.value->string_value, str, i * sizeof(char)); + tempPtr.value->string_value[i] = '\0'; + str += i + 1; + _offset += i + 2; + } else if(json_str_is_numeral(*str)) { + i = json_str_next_non_numeral(str); + if(i == -1) { + json_free(obj); + return NULL; + } + char *tempStr = (char*)malloc((i + 1) * sizeof(char)); + memcpy(tempStr, str, i * sizeof(char)); + tempStr[i] = '\0'; + + tempPtr.value = (json_value_t*)malloc(sizeof(json_value_t)); + tempPtr.type = JSON_DOUBLE; + tempPtr.value->double_value = atof(tempStr); + + free(tempStr); + str += i; + _offset += i + 1; + } + obj->pairs[obj->count - 1] = tempPtr; + + } else if (*str == ',') { + obj->count++; + obj->pairs = (json_pair_t*)realloc(obj->pairs, obj->count * sizeof(json_pair_t)); + str++; + _offset++; + } else if (*str == '}') { + (*offset) += _offset + 1; + return obj; + } + } + return obj; +} diff --git a/src/n2n.c b/src/n2n.c index f53585f..6868c30 100644 --- a/src/n2n.c +++ b/src/n2n.c @@ -165,8 +165,20 @@ void _traceEvent (int eventTraceLevel, char* file, int line, char * format, ...) } + /* *********************************************** */ + +/* stringify in_addr type to ipstr_t */ +char* inaddrtoa (ipstr_t out, struct in_addr addr) { + + if(!inet_ntop(AF_INET, &addr, out, sizeof(ipstr_t))) + out[0] = '\0'; + + return out; +} + + /* addr should be in network order. Things are so much simpler that way. */ char* intoa (uint32_t /* host order */ addr, char* buf, uint16_t buf_len) { @@ -623,7 +635,7 @@ size_t purge_peer_list (struct peer_info **peer_list, size_t retval = 0; HASH_ITER(hh, *peer_list, scan, tmp) { - if((scan->purgeable == SN_PURGEABLE) && (scan->last_seen < purge_before)) { + if((scan->purgeable == PURGEABLE) && (scan->last_seen < purge_before)) { if((scan->socket_fd >=0) && (scan->socket_fd != socket_not_to_close)) { if(tcp_connections) { HASH_FIND_INT(*tcp_connections, &scan->socket_fd, conn); @@ -653,7 +665,7 @@ size_t clear_peer_list (struct peer_info ** peer_list) { size_t retval = 0; HASH_ITER(hh, *peer_list, scan, tmp) { - if (scan->purgeable == SN_UNPURGEABLE && scan->ip_addr) { + if (scan->purgeable == UNPURGEABLE && scan->ip_addr) { free(scan->ip_addr); } HASH_DEL(*peer_list, scan); diff --git a/src/sn_management.c b/src/sn_management.c index 313c4c2..db9ac84 100644 --- a/src/sn_management.c +++ b/src/sn_management.c @@ -355,7 +355,7 @@ int process_mgmt (n2n_sn_t *sss, ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize, "%s '%s'\n", - (community->is_federation) ? "FEDERATION" : ((community->purgeable == COMMUNITY_UNPURGEABLE) ? "FIXED NAME COMMUNITY" : "COMMUNITY"), + (community->is_federation) ? "FEDERATION" : ((community->purgeable == UNPURGEABLE) ? "FIXED NAME COMMUNITY" : "COMMUNITY"), (community->is_federation) ? "-/-" : community->community); sendto_mgmt(sss, sender_sock, (const uint8_t *) resbuf, ressize); ressize = 0; @@ -366,7 +366,7 @@ int process_mgmt (n2n_sn_t *sss, ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize, "%4u | %-19s | %-17s | %-21s %-3s | %-15s | %9s\n", ++num, - (peer->dev_addr.net_addr == 0) ? ((peer->purgeable == SN_UNPURGEABLE) ? "-l" : "") : ip_subnet_to_str(ip_bit_str, &peer->dev_addr), + (peer->dev_addr.net_addr == 0) ? ((peer->purgeable == UNPURGEABLE) ? "-l" : "") : ip_subnet_to_str(ip_bit_str, &peer->dev_addr), (is_null_mac(peer->mac_addr)) ? "" : macaddr_str(mac_buf, peer->mac_addr), sock_to_cstr(sockbuf, &(peer->sock)), ((peer->socket_fd >= 0) && (peer->socket_fd != sss->sock)) ? "TCP" : "", diff --git a/src/sn_utils.c b/src/sn_utils.c index 2b24fa3..6d84176 100644 --- a/src/sn_utils.c +++ b/src/sn_utils.c @@ -379,7 +379,7 @@ int load_allowed_sn_community (n2n_sn_t *sss) { if(comm != NULL) { comm_init(comm, cmn_str); /* loaded from file, this community is unpurgeable */ - comm->purgeable = COMMUNITY_UNPURGEABLE; + comm->purgeable = UNPURGEABLE; /* we do not know if header encryption is used in this community, * first packet will show. just in case, setup the key. */ comm->header_encryption = HEADER_ENCRYPTION_UNKNOWN; @@ -766,7 +766,7 @@ int sn_init_defaults (n2n_sn_t *sss) { sss->federation->community[N2N_COMMUNITY_SIZE - 1] = '\0'; /* enable the flag for federation */ sss->federation->is_federation = IS_FEDERATION; - sss->federation->purgeable = COMMUNITY_UNPURGEABLE; + sss->federation->purgeable = UNPURGEABLE; /* header encryption enabled by default */ sss->federation->header_encryption = HEADER_ENCRYPTION_ENABLED; /*setup the encryption key */ @@ -1443,7 +1443,7 @@ static int purge_expired_communities (n2n_sn_t *sss, } } - if((comm->edges == NULL) && (comm->purgeable == COMMUNITY_PURGEABLE)) { + if((comm->edges == NULL) && (comm->purgeable == PURGEABLE)) { traceEvent(TRACE_INFO, "purging idle community %s", comm->community); if(NULL != comm->header_encryption_ctx_static) { /* this should not happen as 'purgeable' and thus only communities w/o encrypted header here */ @@ -1909,7 +1909,7 @@ static int process_udp (n2n_sn_t * sss, comm->header_encryption_ctx_static = NULL; comm->header_encryption_ctx_dynamic = NULL; /* ... and also are purgeable during periodic purge */ - comm->purgeable = COMMUNITY_PURGEABLE; + comm->purgeable = PURGEABLE; comm->number_enc_packets = 0; HASH_ADD_STR(sss->communities, community, comm); diff --git a/src/supernode.c b/src/supernode.c index 848f74c..166ea07 100644 --- a/src/supernode.c +++ b/src/supernode.c @@ -223,7 +223,7 @@ static int setOption (int optkey, char *_optarg, n2n_sn_t *sss) { strncpy(anchor_sn->ip_addr, _optarg, N2N_EDGE_SN_HOST_SIZE - 1); memcpy(&(anchor_sn->sock), socket, sizeof(n2n_sock_t)); memcpy(anchor_sn->mac_addr, null_mac, sizeof(n2n_mac_t)); - anchor_sn->purgeable = SN_UNPURGEABLE; + anchor_sn->purgeable = UNPURGEABLE; anchor_sn->last_valid_time_stamp = initial_time_stamp(); } } @@ -286,6 +286,7 @@ static int setOption (int optkey, char *_optarg, n2n_sn_t *sss) { case 'F': { /* federation name */ snprintf(sss->federation->community, N2N_COMMUNITY_SIZE - 1 ,"*%s", _optarg); sss->federation->community[N2N_COMMUNITY_SIZE - 1] = '\0'; + sss->federation->purgeable = UNPURGEABLE; break; } #ifdef SN_MANUAL_MAC diff --git a/src/tuntap_linux.c b/src/tuntap_linux.c index b110e9c..75bda23 100644 --- a/src/tuntap_linux.c +++ b/src/tuntap_linux.c @@ -139,7 +139,7 @@ int tuntap_open (tuntap_dev *device, } // store the device name for later reuse - strncpy(device->dev_name, ifr.ifr_name, MIN(IFNAMSIZ, N2N_IFNAMSIZ)); + strncpy(device->dev_name, ifr.ifr_name, MIN(IFNAMSIZ, sizeof(devstr_t))); if(device_mac && device_mac[0]) { // use the user-provided MAC diff --git a/tools/Makefile.in b/tools/Makefile.in index 4241c15..815ed5b 100644 --- a/tools/Makefile.in +++ b/tools/Makefile.in @@ -14,7 +14,7 @@ LDFLAGS+=-L.. N2N_LIB=../libn2n.a -TOOLS=n2n-benchmark n2n-keygen +TOOLS=n2n-benchmark n2n-keygen n2n-route TOOLS+=@ADDITIONAL_TOOLS@ TESTS=tests-compress tests-elliptic tests-hashing tests-transform @@ -26,6 +26,7 @@ all: $(TOOLS) $(TESTS) n2n-benchmark.o: $(N2N_LIB) $(HEADERS) ../Makefile Makefile n2n-keygen.o: $(N2N_LIB) $(HEADERS) ../Makefile Makefile +n2n-route.o: $(N2N_LIB) $(HEADERS) ../Makefile Makefile n2n-decode: n2n-decode.c $(N2N_LIB) $(HEADERS) ../Makefile Makefile $(CC) $(CFLAGS) $< $(LDFLAGS) $(LDLIBS) -lpcap -o $@ diff --git a/tools/n2n-route.c b/tools/n2n-route.c new file mode 100644 index 0000000..c554305 --- /dev/null +++ b/tools/n2n-route.c @@ -0,0 +1,860 @@ +/** + * (C) 2007-22 - ntop.org and contributors + * + * 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 + * + */ + + +#include "n2n.h" + + +#ifdef __linux__ /* currently, Linux only */ + + +#include + + +#define WITH_ADDRESS 1 +#define CORRECT_TAG 2 + +#define SOCKET_TIMEOUT 2 +#define GATEWAY_INTERVAL 5 +#define INFO_INTERVAL 5 +#define REFRESH_INTERVAL 10 +#define PURGE_INTERVAL 30 +#define REMOVE_ROUTE_AGE 75 + +#define LOWER_HALF "0.0.0.0" +#define UPPER_HALF "128.0.0.0" +#define MASK_HALF "128.0.0.0" /* /1 */ +#define HOST_MASK "255.255.255.255" /* /32 */ +#define ROUTE_ADD 0 +#define ROUTE_DEL 1 +#define NO_DETECT 0 +#define AUTO_DETECT 1 + + +typedef struct n2n_route { + struct in_addr net_addr; /* network address to be routed, also key for hash table*/ + struct in_addr net_mask; /* network address mask */ + struct in_addr gateway; /* gateway address */ + + uint8_t purgeable; /* unpurgeable user-supplied or new default route */ + time_t last_seen; /* last seen at management port output */ + + UT_hash_handle hh; /* makes this structure hashable */ +} n2n_route_t; + +typedef struct n2n_route_conf { + struct in_addr gateway_vpn; /* vpn gateway address */ + struct in_addr gateway_org; /* original default gateway used for peer/supernode traffic */ + uint8_t gateway_detect; /* have the gateway automatically detected */ + char* password; /* pointer to management port password */ + uint16_t port; /* management port */ + n2n_route_t *routes; /* list of routes */ +} n2n_route_conf_t; + + +static int keep_running = 1; /* for main loop, handled by signals */ + + +// ------------------------------------------------------------------------------------------------------- +// PLATFORM-DEPENDANT CODE + + +// taken from https://stackoverflow.com/questions/4159910/check-if-user-is-root-in-c +int is_privileged (void) { + + uid_t euid = geteuid(); + + return euid == 0; +} + + +// ------------------------------------------------------------------------------------------------------- +// PLATFORM-DEPENDANT CODE + + +void set_term_handler(const void *handler) { + +#ifdef __linux__ + signal(SIGPIPE, SIG_IGN); + signal(SIGTERM, handler); + signal(SIGINT, handler); +#endif +#ifdef WIN32 /* the beginning of Windows support ...? */ + SetConsoleCtrlHandler(handler, TRUE); +#endif +} + + +#ifdef WIN32 /* the beginning of Windows support ...? */ +BOOL WINAPI term_handler (DWORD sig) { +#else +static void term_handler (int sig) { +#endif + + static int called = 0; + + if(called) { + traceEvent(TRACE_NORMAL, "ok, leaving now"); + _exit(0); + } else { + traceEvent(TRACE_NORMAL, "shutting down..."); + called = 1; + } + + keep_running = 0; +#ifdef WIN32 /* the beginning of Windows support ...? */ + return TRUE; +#endif +} + + +// ------------------------------------------------------------------------------------------------------- +// PLATFORM-DEPENDANT CODE + + +// taken from https://gist.github.com/javiermon/6272065 +// with modifications +// originally licensed under GPLV2, Apache, and/or MIT + +#define RTLINK_BUFFER_SIZE 8192 + +int find_default_gateway (struct in_addr *gateway_addr, struct in_addr *exclude) { + + int ret = 0; + int received_bytes = 0, msg_len = 0, route_attribute_len = 0; + SOCKET sock = -1; + int msgseq = 0; + struct nlmsghdr *nlh, *nlmsg; + struct rtmsg *route_entry; + struct rtattr *route_attribute; /* this contains route attributes (route type) */ + ipstr_t gateway_address; + devstr_t interface; + char msgbuf[RTLINK_BUFFER_SIZE], buffer[RTLINK_BUFFER_SIZE]; + char *ptr = buffer; + struct timeval tv; + + if((sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0) { + traceEvent(TRACE_WARNING, "error from socket() while determining gateway"); + // return immediately + return EXIT_FAILURE; + } + + memset(msgbuf, 0, sizeof(msgbuf)); + memset(buffer, 0, sizeof(buffer)); + memset(gateway_address, 0, sizeof(gateway_address)); + memset(interface, 0, sizeof(interface)); + + // point the header and the msg structure pointers into the buffer + nlmsg = (struct nlmsghdr*)msgbuf; + + // fill in the nlmsg header + nlmsg->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); + nlmsg->nlmsg_type = RTM_GETROUTE; /* get the routes from kernel routing table */ + nlmsg->nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST; /* the message is a request for dump */ + nlmsg->nlmsg_seq = msgseq++; /* sequence of the message packet */ + nlmsg->nlmsg_pid = getpid(); /* PID of process sending the request */ + + // 1 sec timeout to avoid stall + tv.tv_sec = 1; + setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (struct timeval *)&tv, sizeof(struct timeval)); + + // send msg + if (send(sock, nlmsg, nlmsg->nlmsg_len, 0) < 0) { + traceEvent(TRACE_WARNING, "error from send() while determining gateway"); + ret = EXIT_FAILURE; + goto find_default_gateway_end; + } + + // receive response + do { + received_bytes = recv(sock, ptr, sizeof(buffer) - msg_len, 0); + if(received_bytes < 0) { + traceEvent(TRACE_WARNING, "error from recv() while determining gateway"); + ret = EXIT_FAILURE; + goto find_default_gateway_end; + } + + nlh = (struct nlmsghdr *) ptr; + + // check if the header is valid + if((NLMSG_OK(nlmsg, received_bytes) == 0) || + (nlmsg->nlmsg_type == NLMSG_ERROR)) { + traceEvent(TRACE_WARNING, "error in received packet while determining gateway"); + ret = EXIT_FAILURE; + goto find_default_gateway_end; + } + + // if we received all data break + if(nlh->nlmsg_type == NLMSG_DONE) { + break; + } else { + ptr += received_bytes; + msg_len += received_bytes; + } + + // break if its not a multi part message + if((nlmsg->nlmsg_flags & NLM_F_MULTI) == 0) + break; + } while((nlmsg->nlmsg_seq != msgseq) || (nlmsg->nlmsg_pid != getpid())); + + // parse response + for ( ; NLMSG_OK(nlh, received_bytes); nlh = NLMSG_NEXT(nlh, received_bytes)) { + // get the route data + route_entry = (struct rtmsg *) NLMSG_DATA(nlh); + + // we are just interested in main routing table + if (route_entry->rtm_table != RT_TABLE_MAIN) + continue; + + route_attribute = (struct rtattr*)RTM_RTA(route_entry); + route_attribute_len = RTM_PAYLOAD(nlh); + + gateway_address[0] = '\0'; + interface[0] = '\0'; + // loop through all attributes + for( ; RTA_OK(route_attribute, route_attribute_len); + route_attribute = RTA_NEXT(route_attribute, route_attribute_len)) { + switch(route_attribute->rta_type) { + case RTA_OIF: + // for informational purposes only + if_indextoname(*(int*)RTA_DATA(route_attribute), interface); + break; + case RTA_GATEWAY: + inaddrtoa(gateway_address, *(struct in_addr*)RTA_DATA(route_attribute)); + break; + default: + break; + } + } + + if((*gateway_address) && (*interface)) { + // REVISIT: inet_ntop followed by inet_pton... maybe not too elegant + if(inet_pton(AF_INET, gateway_address, gateway_addr)) { + // do not use the one to be excluded + if(!memcmp(gateway_addr, exclude, sizeof(*gateway_addr))) + continue; + traceEvent(TRACE_DEBUG, "assuming default gateway %s on interface %s", + gateway_address, interface); + break; + } + } + + } + +find_default_gateway_end: + + closesocket(sock); + return ret; +} + + +// ------------------------------------------------------------------------------------------------------- +// PLATFORM-DEPENDANT CODE + + +/* adds (verb == ROUTE_ADD) or deletes (verb == ROUTE_DEL) a route */ +void handle_route (n2n_route_t* in_route, int verb) { + + struct sockaddr_in *addr_tmp; + struct rtentry route; + SOCKET sock; + struct sockaddr_in *dst, *mask, *gateway; + ipstr_t dst_ip_str, gateway_ip_str; + in_addr_t mask_addr; + int bitlen = 0; + + // prepare rtentry-typed route entry + memset(&route, 0, sizeof(route)); + addr_tmp = (struct sockaddr_in*)&route.rt_dst; + addr_tmp->sin_family = AF_INET; + addr_tmp->sin_addr.s_addr = in_route->net_addr.s_addr; + addr_tmp = (struct sockaddr_in*)&route.rt_genmask; + addr_tmp->sin_family = AF_INET; + addr_tmp->sin_addr.s_addr = in_route->net_mask.s_addr; + addr_tmp = (struct sockaddr_in*)&route.rt_gateway; + addr_tmp->sin_family = AF_INET; + addr_tmp->sin_addr.s_addr = in_route->gateway.s_addr; + route.rt_flags = RTF_UP | RTF_GATEWAY; + route.rt_metric = 0; + + // open a socket + sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP); + + // prepare route data for eventual text output + dst = (struct sockaddr_in*)&route.rt_dst; + mask = (struct sockaddr_in*)&route.rt_genmask; + mask_addr = ntohl(mask->sin_addr.s_addr); + for(bitlen = 0; (int)mask_addr < 0; mask_addr <<= 1) + bitlen++; + gateway = (struct sockaddr_in*)&route.rt_gateway; + + // try to set route through ioctl + if(ioctl(sock, verb == ROUTE_ADD ? SIOCADDRT : SIOCDELRT, &route) < 0) { + traceEvent(TRACE_WARNING, "error '%s' while %s route to %s/%u via %s", + strerror(errno), + !verb ? "adding" : "deleting", + inaddrtoa(dst_ip_str, dst->sin_addr), + bitlen, + inaddrtoa(gateway_ip_str, gateway->sin_addr)); + } else { + traceEvent(TRACE_INFO, "%s route to %s/%u via %s", + !verb ? "added" : "deleted", + inaddrtoa(dst_ip_str, dst->sin_addr), + bitlen, + inaddrtoa(gateway_ip_str, gateway->sin_addr)); + } + + closesocket(sock); +} + + +// ------------------------------------------------------------------------------------------------------- + + +void fill_route (n2n_route_t* route, struct in_addr net_addr, struct in_addr net_mask, struct in_addr gateway) { + + route->net_addr = net_addr; + route->net_mask = net_mask; + route->gateway = gateway; +} + + +// applies inet_pton on input string and returns address struct-in_addr-typed address +struct in_addr inet_address (char* in) { + + struct in_addr out; + + if(inet_pton(AF_INET, in, &out) <= 0) { + out.s_addr = INADDR_NONE; + } + + return out; +} + + +int inet_address_valid (struct in_addr in) { + + if(in.s_addr == INADDR_NONE) + return 0; + else + return 1; +} + + +int same_subnet (struct in_addr addr0, struct in_addr addr1, struct in_addr subnet) { + + return (addr0.s_addr & subnet.s_addr) == (addr1.s_addr & subnet.s_addr); +} + + +// ------------------------------------------------------------------------------------------------------- + + +SOCKET connect_to_management_port (n2n_route_conf_t *rrr) { + + SOCKET ret; + struct sockaddr_in sock_addr; + + ret = socket (PF_INET, SOCK_DGRAM, 0); + if((int)ret < 0) + return -1; + + sock_addr.sin_family = AF_INET; + sock_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + sock_addr.sin_port = htons(rrr->port); + if(0 != connect(ret, (struct sockaddr *)&sock_addr, sizeof(sock_addr))) + return -1; + + return ret; +} + + +// ------------------------------------------------------------------------------------------------------- + + +int get_addr_from_json (struct in_addr *addr, json_object_t *json, char *key, int tag, int flags) { + + int i; + char *colon = NULL; + + if(NULL == json) + return 0; + + for(i = 0; i < json->count; i++) { + if(json->pairs[i].type == JSON_STRING) { + if(!strcmp(json->pairs[i].key, key)) { + // cut off port from IP address + if((colon = strchr(json->pairs[i].value->string_value, ':'))) { + *colon = '\0'; + } + *addr = inet_address(json->pairs[i].value->string_value); + flags |= WITH_ADDRESS; + } + if(!strcmp(json->pairs[i].key, "_tag" )) { + if(atoi(json->pairs[i].value->string_value) == tag) { + flags |= CORRECT_TAG; + } + } + } else if(json->pairs[i].type == JSON_OBJECT) { + flags |= get_addr_from_json(addr, json, key, tag, flags); + } + } + + return flags; +} + + +// ------------------------------------------------------------------------------------------------------- + + +// taken from https://web.archive.org/web/20170407122137/http://cc.byexamples.com/2007/04/08/non-blocking-user-input-in-loop-without-ncurses/ +int kbhit () { + + struct timeval tv; + fd_set fds; + + tv.tv_sec = 0; + tv.tv_usec = 0; + FD_ZERO(&fds); + FD_SET(STDIN_FILENO, &fds); //STDIN_FILENO is 0 + select(STDIN_FILENO+1, &fds, NULL, NULL, &tv); + + return FD_ISSET(STDIN_FILENO, &fds); +} + + +// ------------------------------------------------------------------------------------------------------- + + +static void help (int level) { + + if(level == 0) return; /* no help required */ + + printf(" n2n-route [-t ] [-p ] [-V] [-v]" + "\n [-g ] [-n /bitlen] " + "\n" + "\n This tool sets new routes for all the traffic to be routed via the" + "\n and polls the management port of a local n2n edge for" + "\n it can add routes to supernodes and peers via the original default" + "\n gateway. Adapt port (default: %d) and password (default: '%s')" + "\n to match your edge's configuration." + "\n\n If no provided, the tool will try to auto-detect." + "\n\n To not route all traffic through vpn, inidicate the networks to be" + "\n routed with '-n' option and use as many as required." + "\n\n Verbosity can be increased or decreased with -V or -v , repeat as" + "\n as needed." + "\n\n Run with sufficient rights to let the tool add and delete routes." + "\n\n", + N2N_EDGE_MGMT_PORT, N2N_MGMT_PASSWORD); + + exit(0); +} + + +static int set_option (n2n_route_conf_t *rrr, int optkey, char *optargument) { + + switch(optkey) { + case 't': /* management port */ { + uint16_t port = atoi(optargument); + if(port) { + rrr->port = port; + } else { + traceEvent(TRACE_WARNING, "invalid management port provided with '-t'"); + } + break; + } + + case 'p': /* management port password string */ { + rrr->password = optargument; + break; + } + + case 'g': /* user-provided original default route */ { + rrr->gateway_org = inet_address(optargument); + if(inet_address_valid(rrr->gateway_org)) { + rrr->gateway_detect = NO_DETECT; + } else { + traceEvent(TRACE_WARNING, "invalid original default gateway provided with '-g'"); + } + break; + } + + case 'n': /* user-provided network to be routed */ { + char cidr_net[64], bitlen; + n2n_route_t *route; + struct in_addr mask; + + if(sscanf(optargument, "%63[^/]/%hhd", cidr_net, &bitlen) != 2) { + traceEvent(TRACE_WARNING, "bad cidr network format '%d'", optargument); + return 1; + } + if((bitlen < 0) || (bitlen > 32)) { + traceEvent(TRACE_WARNING, "bad prefix '%d' in '%s'", bitlen, optargument); + return 1; + } + if(!inet_address_valid(inet_address(cidr_net))) { + traceEvent(TRACE_WARNING, "bad network '%s' in '%s'", cidr_net, optargument); + return 1; + } + + traceEvent(TRACE_NORMAL, "routing %s/%d", cidr_net, bitlen); + + route = calloc(1, sizeof(*route)); + if(route) { + mask.s_addr = htonl(bitlen2mask(bitlen)); + // gateway is unknown at this point, will be rectified later + fill_route(route, inet_address(cidr_net), mask, inet_address("")); + HASH_ADD(hh, rrr->routes, net_addr, sizeof(route->net_addr), route); + // will be added to system table later + } + break; + } + + case 'V': /* more verbose */ { + setTraceLevel(getTraceLevel() + 1); + break; + } + + case 'v': /* less verbose */ { + setTraceLevel(getTraceLevel() - 1); + break; + } + + default: /* unknown option */ { + return 1; /* for help */ + } + } + + return 0; +} + + +// ------------------------------------------------------------------------------------------------------- + + +int main (int argc, char* argv[]) { + + n2n_route_conf_t rrr; + uint8_t c; + SOCKET sock; + size_t msg_len; + char udp_buf[N2N_PKT_BUF_SIZE]; + fd_set socket_mask; + struct timeval wait_time; + time_t now = 0; + time_t last_gateway_check = 0; + time_t last_info_req = 0; + time_t last_read_req = 0; + time_t last_purge = 0; + json_object_t *json; + int ret; + int tag_info, tag_route_ip; + struct in_addr addr, edge, edge_netmask, addr_tmp; + ipstr_t ip_str; + n2n_route_t *route, *tmp_route; + + // version + print_n2n_version(); + + // handle signals to properly end the tool + set_term_handler(term_handler); + + // init data structure + rrr.gateway_vpn = inet_address(""); + rrr.gateway_org = inet_address(""); + rrr.gateway_detect = AUTO_DETECT; + rrr.password = N2N_MGMT_PASSWORD; + rrr.port = N2N_EDGE_MGMT_PORT; + rrr.routes = NULL; + setTraceLevel(2); /* NORMAL, should already be default */ + n2n_srand(n2n_seed()); + + // get command line options and eventually overwrite initialized conf + while((c = getopt_long(argc, argv, "t:p:g:n:vV", NULL, NULL)) != '?') { + if(c == 255) break; + help(set_option(&rrr, c, optarg)); + } + + // get mandatory vpn gateway from command line and ... + if(argv[optind]) { + rrr.gateway_vpn = inet_address(argv[optind]); + } + // ... output help if invalid + help(!inet_address_valid(rrr.gateway_vpn)); + traceEvent(TRACE_NORMAL, "using vpn gateway %s", inaddrtoa(ip_str, rrr.gateway_vpn)); + + // verify conf and react with output to conf-related changes + if(rrr.gateway_detect == NO_DETECT) { + traceEvent(TRACE_NORMAL, "using default gateway %s", inaddrtoa(ip_str, rrr.gateway_org)); + } + // if nothing else set, set new default route + if(!rrr.routes) { + route = calloc(1, sizeof(n2n_route_t)); + if(route) { + traceEvent(TRACE_NORMAL, "routing 0.0.0.1/1"); + fill_route(route, inet_address(LOWER_HALF), inet_address(MASK_HALF), rrr.gateway_vpn); + HASH_ADD(hh, rrr.routes, net_addr, sizeof(route->net_addr), route); + } + route = calloc(1, sizeof(n2n_route_t)); + if(route) { + traceEvent(TRACE_NORMAL, "routing 128.0.0.1/1"); + fill_route(route, inet_address(UPPER_HALF), inet_address(MASK_HALF), rrr.gateway_vpn); + HASH_ADD(hh, rrr.routes, net_addr, sizeof(route->net_addr), route); + } + } + // set gateway for all so far present routes as '-n'-provided do not have it yet, + // make them UNPURGEABLE and add them to system table + HASH_ITER(hh, rrr.routes, route, tmp_route) { + route->gateway = rrr.gateway_vpn; + route->purgeable = UNPURGEABLE; + handle_route(route, ROUTE_ADD); + } + + // additional checks + // check for sufficient rights for adding/deleting routes + if(!is_privileged()) { + traceEvent(TRACE_WARNING, "did not detect sufficient privileges to exercise route control"); + } + // REVISIT: can we check if forwarding is enabled and, if not so, warn the user? + + // connect to mamagement port + traceEvent(TRACE_NORMAL, "connecting to edge management port %d", rrr.port); + sock = connect_to_management_port(&rrr); + if(sock == -1) { + traceEvent(TRACE_ERROR, "unable to open socket for management port connection"); + goto end_route_tool; + } + + // output status + traceEvent(TRACE_NORMAL, "press ENTER to end the program"); + +reset_main_loop: + + wait_time.tv_sec = SOCKET_TIMEOUT; + wait_time.tv_usec = 0; + edge = inet_address(""); + edge_netmask = inet_address(""); + addr_tmp = inet_address(""); + tag_info = 0; + tag_route_ip = 0; + + // main loop + // read answer packet by packet which are only accepted if a corresponding request was sent before + // of which we know about by having set the related tag, tag_info or tag_route_ip resp. + // a valid edge ip address indicates that we have seen a valid answer to the info request + while(keep_running && !kbhit()) { + // current time + now = time(NULL); + + // in case of AUTO_DETECT, check for (changed) default gateway from time to time (and initially) + if((rrr.gateway_detect == AUTO_DETECT) && (now > last_gateway_check + GATEWAY_INTERVAL)) { + // determine the original default gateway excluding the VPN gateway from search + find_default_gateway(&addr_tmp, &rrr.gateway_vpn); + if(memcmp(&addr_tmp, &rrr.gateway_org, sizeof(rrr.gateway_org))) { + // store the detected change + rrr.gateway_org = addr_tmp; + // delete all purgeable routes as they are still relying on old original default gateway + HASH_ITER(hh, rrr.routes, route, tmp_route) { + if((route->purgeable == PURGEABLE)) { + handle_route(route, ROUTE_DEL); + HASH_DEL(rrr.routes, route); + free(route); + } + } + // give way for new info and read requests + last_info_req = 0; + last_read_req = 0; + + traceEvent(TRACE_NORMAL, "using default gateway %s", inaddrtoa(ip_str, rrr.gateway_org)); + } + last_gateway_check = now; + } + + // check if we need to send info request again + if(now > last_info_req + INFO_INTERVAL) { + // send info read request + while(!(tag_info = ((uint32_t)n2n_rand()) >> 23)); + msg_len = 0; + msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_len), + "r %u info\n", tag_info); + ret = send(sock, udp_buf, msg_len, 0); + last_info_req = now; + } + + // check if we need to send read request again + if(now > last_read_req + REFRESH_INTERVAL) { + // the following requests shall only be sent if we have a valid local edge ip address, + // i.e. a valid answer to the info request + if(inet_address_valid(edge)) { + + // REVISIT: send unsubscribe request to management port if required to re-subscribe + + // send subscribe request to management port, generate fresh tag + while(!(tag_route_ip = ((uint32_t)n2n_rand()) >> 23)); /* >> 23: tags too long can crash the mgmt */ + msg_len = 0; + msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_len), + "s %u:1:%s peer\n", tag_route_ip, rrr.password); + // REVISIT: something smashes the edge when sending subscritpion request or when edge sends event + // so, the subscription request is not sent yet + // ret = send(sock, udp_buf, msg_len, 0); + + // send read requests to management port with same tag + msg_len = 0; + msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_len), + "r %u edges\n", tag_route_ip); + ret = send(sock, udp_buf, msg_len, 0); + msg_len = 0; + msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_len), + "r %u supernodes\n", tag_route_ip); + ret = send(sock, udp_buf, msg_len, 0); + + last_read_req = now; + } + } + + // purge the routes from time to time + if(now > last_purge + PURGE_INTERVAL) { + last_purge = now; + HASH_ITER(hh, rrr.routes, route, tmp_route) { + if((route->purgeable == PURGEABLE) && (now > route->last_seen + REMOVE_ROUTE_AGE)) { + handle_route(route, ROUTE_DEL); + HASH_DEL(rrr.routes, route); + free(route); + } + } + } + + // REVISIT: check all routes from rrr.routes for still being in system table from time to time? + // or even apply some rtlink magic to get notified on route changes? + + // wait for any answer to info or read request + FD_ZERO(&socket_mask); + FD_SET(sock, &socket_mask); + ret = select(sock + 1, &socket_mask, NULL, NULL, &wait_time); + + // refresh current time after having waited + now = time(NULL); + + if(ret > 0) { + if(FD_ISSET(sock, &socket_mask)) { + msg_len = recv(sock, udp_buf, sizeof(udp_buf), 0); + if((msg_len > 0) && (msg_len < sizeof(udp_buf))) { + // make sure it is a string and replace all newlines with spaces + udp_buf[msg_len] = '\0'; + for (char *p = udp_buf; (p = strchr(p, '\n')) != NULL; p++) *p = ' '; + traceEvent(TRACE_DEBUG, "received '%s' from management port", udp_buf); + + // handle the answer, json needs to be freed later + json = json_parse(udp_buf); + + // look for local edge information + if(tag_info) { + // local IP address (for information) + ret = get_addr_from_json(&addr, json, "ip4addr", tag_info, 0); + if(ret == (WITH_ADDRESS | CORRECT_TAG)) { + traceEvent(TRACE_DEBUG, "received information about %s being edge's IP address", inaddrtoa(ip_str, addr)); + if(memcmp(&edge, &addr, sizeof(edge))) { + edge = addr; + traceEvent(TRACE_NORMAL, "found %s being edge's IP address", inaddrtoa(ip_str, addr)); + } + } + // local netmask + ret = get_addr_from_json(&addr, json, "ip4netmask", tag_info, 0); + if(ret == (WITH_ADDRESS | CORRECT_TAG)) { + traceEvent(TRACE_DEBUG, "received information about %s being edge's IP netmask", inaddrtoa(ip_str, addr)); + if(memcmp(&edge_netmask, &addr, sizeof(edge_netmask))) { + edge_netmask = addr; + traceEvent(TRACE_NORMAL, "found %s being edge's IP netmask", inaddrtoa(ip_str, addr)); + // check if vpn gateway matches edge information and warn user if not so + if(!same_subnet(edge, rrr.gateway_vpn, edge_netmask)) { + traceEvent(TRACE_WARNING, "vpn gateway and edge do not share the same subnet"); + } + } + } + } + + // look for edge/supernode ip addresses + if(tag_route_ip) { + ret = get_addr_from_json(&addr, json, "sockaddr", tag_route_ip, 0); + if(ret == (WITH_ADDRESS | CORRECT_TAG)) { + // add to hash list if required + traceEvent(TRACE_DEBUG, "received information about %s to be routed via default gateway", inaddrtoa(ip_str, addr)); + HASH_FIND(hh, rrr.routes, &addr, sizeof(route->net_addr), route); + if(!route) + route = calloc(1, sizeof(n2n_route_t)); + else + HASH_DEL(rrr.routes, route); + if(route) { + fill_route(route, addr, inet_address(HOST_MASK), rrr.gateway_org); + route->purgeable = PURGEABLE; + if(!(route->last_seen)) { + handle_route(route, ROUTE_ADD); + } + route->last_seen = now; + HASH_ADD(hh, rrr.routes, net_addr, sizeof(route->net_addr), route); + } + } + } + + // no need for current json object anymore + json_free(json); + } + } else { + // can this happen? reset the loop + traceEvent(TRACE_ERROR, "loop reset"); + goto reset_main_loop; + } + } else if(ret == 0) { + // select() timeout + // action required? + } else { + // select() error + // action required? + } + } + + // REVISIT: send unsubscribe request to management port if required + +end_route_tool: + + // delete all routes + HASH_ITER(hh, rrr.routes, route, tmp_route) { + handle_route(route, ROUTE_DEL); + HASH_DEL(rrr.routes, route); + free(route); + } + // close connection + closesocket(sock); + + return 0; +} + + +#else /* ifdef __linux__ -- currently, Linux only */ + + +int main (int argc, char* argv[]) { + + traceEvent(TRACE_WARNING, "currently, only Linux is supported"); + traceEvent(TRACE_WARNING, "if you want to port to other OS, please find the source code having clearly marked the platform-dependant portions"); + + return 0; +} + + +#endif /* ifdef __linux__ -- currently, Linux only */ diff --git a/win32/wintap.c b/win32/wintap.c index 0777a59..e228ca1 100644 --- a/win32/wintap.c +++ b/win32/wintap.c @@ -120,7 +120,7 @@ static int lookup_adapter_info_reg(const char *target_adapter, char *regpath, si long len, rc; char index[16]; int err, i; - char adapter_name[N2N_IFNAMSIZ]; + devstr_t adapter_name; int rv = 0; if((rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, ADAPTER_INFO_KEY, 0, KEY_READ, &key))) {