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))) {