mirror of
https://github.com/ntop/n2n.git
synced 2024-09-19 16:41:11 +02:00
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
This commit is contained in:
parent
a274818854
commit
99b6b6b66d
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
4
edge.8
4
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
|
||||
|
|
70
include/json.h
Normal file
70
include/json.h
Normal file
|
@ -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 <http://www.gnu.org/licenses/>
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
// 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 <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#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
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
47
src/edge.c
47
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 <pw>] "
|
||||
"\n "
|
||||
"[-v] "
|
||||
"[-n <cidr:gateway>] "
|
||||
#ifndef WIN32
|
||||
"\n "
|
||||
"[-u <numerical user id>] "
|
||||
|
@ -342,8 +340,6 @@ static void help (int level) {
|
|||
printf(" --management_... | management port password, defaults to '%s'\n"
|
||||
" ...password <pw> | \n", N2N_MGMT_PASSWORD);
|
||||
printf(" -v | make more verbose, repeat as required\n");
|
||||
printf(" -n <cidr:gateway> | 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 <UID> | numeric user ID to use when privileges are dropped\n");
|
||||
printf(" -g <GID> | 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,41 +682,11 @@ 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);
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
case 'S': {
|
||||
int solitude;
|
||||
if(optargument) {
|
||||
|
@ -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;
|
||||
|
|
|
@ -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)),
|
||||
|
|
412
src/edge_utils.c
412
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; i<num_routes; i++) {
|
||||
n2n_route_t *route = &routes[i];
|
||||
|
||||
if((route->net_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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
202
src/json.c
Normal file
202
src/json.c
Normal file
|
@ -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 <http://www.gnu.org/licenses/>
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
// 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;
|
||||
}
|
16
src/n2n.c
16
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);
|
||||
|
|
|
@ -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" : "",
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 $@
|
||||
|
|
860
tools/n2n-route.c
Normal file
860
tools/n2n-route.c
Normal file
|
@ -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 <http://www.gnu.org/licenses/>
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include "n2n.h"
|
||||
|
||||
|
||||
#ifdef __linux__ /* currently, Linux only */
|
||||
|
||||
|
||||
#include <net/route.h>
|
||||
|
||||
|
||||
#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" /* <ip address>/1 */
|
||||
#define HOST_MASK "255.255.255.255" /* <ip address>/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 <manangement_port>] [-p <management_port_password>] [-V] [-v]"
|
||||
"\n [-g <default gateway>] [-n <network address>/bitlen] <vpn gateway>"
|
||||
"\n"
|
||||
"\n This tool sets new routes for all the traffic to be routed via the"
|
||||
"\n <vpn gateway> 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 <default gateway> 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 */
|
|
@ -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))) {
|
||||
|
|
Loading…
Reference in New Issue
Block a user