diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 1b73fb6..dfac3c6 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -137,14 +137,20 @@ jobs:
- name: Generate coverage reports
run: |
make gcov
- make cover COVERAGEDIR=coverage/${{ matrix.os }}
+ # This was working fine for tens of jobs, up until 2021-10-19T18:53+0100
+ # and it still works fine when run from my personal github actions.
+ # The next run at 2021-10-19T19:08+0100 didnt work.
+ # Assume that they changed something on the runner - I cannot debug it
+ # as I do not have a Mac.
+ #
+ # make cover COVERAGEDIR=coverage/${{ matrix.os }}
shell: bash
- - name: Upload gcovr report artifact
- uses: actions/upload-artifact@v2
- with:
- name: coverage
- path: coverage
+# - name: Upload gcovr report artifact
+# uses: actions/upload-artifact@v2
+# with:
+# name: coverage
+# path: coverage
- name: Upload data to codecov
uses: codecov/codecov-action@v2
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 31ca8c6..65b8244 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -117,7 +117,9 @@ endif(DEFINED WIN32)
#add_library(n2n STATIC ${N2N_DIR_SRCS})
add_library(n2n STATIC
src/n2n.c
+ src/edge_management.c
src/edge_utils.c
+ src/sn_management.c
src/sn_utils.c
src/wire.c
src/hexdump.c
diff --git a/doc/ManagementAPI.md b/doc/ManagementAPI.md
index 2291dcd..07130ad 100644
--- a/doc/ManagementAPI.md
+++ b/doc/ManagementAPI.md
@@ -10,11 +10,13 @@ Default Ports:
- UDP/5644 - edge
- UDP/5645 - supernode
+A Quick start example query:
+ `echo r 1 help | nc -w1 -u 127.0.0.1 5644`
+
## JSON Query interface
-As part of the management interface, A machine readable API exists for the
-edge daemon. It takes a simple text request and replies with JSON formatted
-data.
+A machine readable API is available for both the edge and supernode. It
+takes a simple text request and replies with JSON formatted data.
The request is in simple text so that the daemon does not need to include any
complex parser.
@@ -34,6 +36,11 @@ pub/sub asynchronous event channels.
The replies will also handle some small amount of re-ordering of the
packets, but that is not an specific goal of the protocol.
+Note that this API will reply with a relatively large number of UDP packets
+and that it is not intended for high frequency or high volume data transfer.
+It was written to use a low amount of memory and to support incremental
+generation of the reply data.
+
With a small amount of effort, the API is intended to be human readable,
but this is intended for debugging.
@@ -162,6 +169,10 @@ The substantial bulk of the data in the reply is contained within one or
more `row` packets. The non metadata contents of each `row` packet is
defined entirely by the method called and may change from version to version.
+Each `row` packet contains exactly one complete JSON object. The row replies
+may be processed incrementally as each row arrives and no batching of multiple
+packets will be required.
+
e.g:
`{"_tag":"108","_type":"row","mode":"p2p","ip4addr":"10.135.98.84","macaddr":"86:56:21:E4:AA:39","sockaddr":"192.168.7.191:41701","desc":"client4","lastseen":1584682200}`
diff --git a/doc/Scripts.md b/doc/Scripts.md
index e928b95..b6c02ae 100644
--- a/doc/Scripts.md
+++ b/doc/Scripts.md
@@ -20,7 +20,9 @@ This shell script is used to run automated tests during development.
## `scripts/n2nctl`
This python script provides an easy command line interface to the running
-edge. It uses UDP communications to talk to the Management API.
+n2n processes. It uses UDP communications to talk to the Management API.
+By specifying the right UDP port, it can talk to both the edge and the
+supernode daemons.
Example:
- `scripts/n2nctl --help`
@@ -33,8 +35,9 @@ a proxy for REST-like HTTP requests to talk to the Management API.
By default it runs on port 8080.
-It also provides a simple HTML page showing some information, which when
-run with default settings can be seen at http://localhost:8080/
+It also provides a simple HTML page showing some edge information, which when
+run with default settings can be seen at http://localhost:8080/ (Also
+a http://localhost:8080/supernode.html page for the supernode)
Example:
- `scripts/n2nhttpd --help`
diff --git a/include/edge_utils_win32.h b/include/edge_utils_win32.h
index 5ee812f..5a0508c 100644
--- a/include/edge_utils_win32.h
+++ b/include/edge_utils_win32.h
@@ -37,9 +37,9 @@
*/
//#define SKIP_MULTICAST_PEERS_DISCOVERY
+// TODO: this struct is pretty empty now, collapse it to just n2n_edge_t ?
struct tunread_arg {
n2n_edge_t *eee;
- int *keep_running;
};
extern HANDLE startTunReadThread (struct tunread_arg *arg);
diff --git a/include/n2n.h b/include/n2n.h
index f7bc251..046ef49 100644
--- a/include/n2n.h
+++ b/include/n2n.h
@@ -262,7 +262,7 @@ void edge_send_packet2net (n2n_edge_t *eee, uint8_t *tap_pkt, size_t len);
void edge_read_from_tap (n2n_edge_t *eee);
int edge_get_n2n_socket (n2n_edge_t *eee);
int edge_get_management_socket (n2n_edge_t *eee);
-int run_edge_loop (n2n_edge_t *eee, int *keep_running);
+int run_edge_loop (n2n_edge_t *eee);
int quick_edge_init (char *device_name, char *community_name,
char *encrypt_key, char *device_mac,
char *local_ip_address,
@@ -274,9 +274,11 @@ void sn_init (n2n_sn_t *sss);
void sn_term (n2n_sn_t *sss);
int supernode2sock (n2n_sock_t * sn, const n2n_sn_name_t addrIn);
struct peer_info* add_sn_to_list_by_mac_or_sock (struct peer_info **sn_list, n2n_sock_t *sock, const n2n_mac_t mac, int *skip_add);
-int run_sn_loop (n2n_sn_t *sss, int *keep_running);
+int run_sn_loop (n2n_sn_t *sss);
int assign_one_ip_subnet (n2n_sn_t *sss, struct sn_community *comm);
const char* compression_str (uint8_t cmpr);
const char* transop_str (enum n2n_transform tr);
+void handleMgmtJson (n2n_edge_t *eee, char *udp_buf, const struct sockaddr_in sender_sock);
+void handleMgmtJson_sn (n2n_sn_t *sss, char *udp_buf, const struct sockaddr_in sender_sock);
#endif /* _N2N_H_ */
diff --git a/include/n2n_typedefs.h b/include/n2n_typedefs.h
index 532c063..454f835 100644
--- a/include/n2n_typedefs.h
+++ b/include/n2n_typedefs.h
@@ -685,6 +685,7 @@ struct n2n_edge {
n2n_edge_conf_t conf;
/* Status */
+ int *keep_running; /**< Pointer to edge loop stop/go flag */
struct peer_info *curr_sn; /**< Currently active supernode. */
uint8_t sn_wait; /**< Whether we are waiting for a supernode response. */
uint8_t sn_pong; /**< Whether we have seen a PONG since last time reset. */
@@ -801,6 +802,7 @@ typedef struct n2n_tcp_connection {
typedef struct n2n_sn {
+ int *keep_running; /* Pointer to sn loop stop/go flag */
time_t start_time; /* Used to measure uptime. */
n2n_version_t version; /* version string sent to edges along with PEER_INFO a.k.a. PONG */
sn_stats_t stats;
@@ -833,10 +835,4 @@ typedef struct n2n_sn {
/* *************************************************** */
-typedef struct n2n_mgmt_handler {
- char *cmd;
- char *help;
- void (*func)(n2n_edge_t *eee, char *udp_buf, struct sockaddr_in sender_sock, enum n2n_mgmt_type type, char *tag, char *argv0, char *argv);
-} n2n_mgmt_handler_t;
-
#endif /* _N2N_TYPEDEFS_H_ */
diff --git a/scripts/n2nctl b/scripts/n2nctl
index 0598202..2a26220 100755
--- a/scripts/n2nctl
+++ b/scripts/n2nctl
@@ -59,6 +59,7 @@ class JsonUDP():
# assert(data['cmd'] == cmd)
result = list()
+ error = None
while True:
data, _ = self.sock.recvfrom(1024)
@@ -69,9 +70,13 @@ class JsonUDP():
continue
if data['_type'] == 'error':
- raise ValueError('Error: {}'.format(data['error']))
+ # we still expect an end packet, so save the error
+ error = ValueError('Error: {}'.format(data['error']))
+ continue
if data['_type'] == 'end':
+ if error:
+ raise error
return result
if data['_type'] != 'row':
@@ -104,10 +109,16 @@ def str_table(rows, columns):
"""Given an array of dicts, do a simple table print"""
result = list()
widths = collections.defaultdict(lambda: 0)
- for row in rows:
+
+ if len(rows) == 0:
+ # No data to show, be sure not to truncate the column headings
for col in columns:
- if col in row:
- widths[col] = max(widths[col], len(str(row[col])))
+ widths[col] = len(col)
+ else:
+ for row in rows:
+ for col in columns:
+ if col in row:
+ widths[col] = max(widths[col], len(str(row[col])))
for col in columns:
if widths[col] == 0:
@@ -128,7 +139,7 @@ def str_table(rows, columns):
def subcmd_show_supernodes(rpc, args):
- rows = rpc.read('super')
+ rows = rpc.read('supernodes')
columns = [
'version',
'current',
@@ -140,8 +151,8 @@ def subcmd_show_supernodes(rpc, args):
return str_table(rows, columns)
-def subcmd_show_peers(rpc, args):
- rows = rpc.read('peer')
+def subcmd_show_edges(rpc, args):
+ rows = rpc.read('edges')
columns = [
'mode',
'ip4addr',
@@ -160,9 +171,10 @@ def subcmd_show_help(rpc, args):
result += "\n"
result += "Possble remote commands:\n"
- result += "(those without pretty-printer, will pass-through)\n\n"
+ result += "(those without a pretty-printer will pass-through)\n\n"
rows = rpc.read('help')
- result += json.dumps(rows, sort_keys=True, indent=4)
+ for row in rows:
+ result += "{:12} {}\n".format(row['cmd'], row['help'])
return result
@@ -175,9 +187,9 @@ subcmds = {
'func': subcmd_show_supernodes,
'help': 'Show the list of supernodes',
},
- 'peers': {
- 'func': subcmd_show_peers,
- 'help': 'Show the list of peers',
+ 'edges': {
+ 'func': subcmd_show_edges,
+ 'help': 'Show the list of edges/peers',
},
}
@@ -196,17 +208,20 @@ def main():
ap = argparse.ArgumentParser(
description='Query the running local n2n edge')
ap.add_argument('-t', '--mgmtport', action='store', default=5644,
- help='Management Port (default=5644)')
+ help='Management Port (default=5644)', type=int)
ap.add_argument('-k', '--key', action='store',
help='Password for mgmt commands')
ap.add_argument('-d', '--debug', action='store_true',
help='Also show raw internal data')
+ ap.add_argument('--raw', action='store_true',
+ help='Force cmd to avoid any pretty printing')
group = ap.add_mutually_exclusive_group()
group.add_argument('--read', action='store_true',
help='Make a read request (default)')
group.add_argument('--write', action='store_true',
- help='Make a write request')
+ help='Make a write request (only to non pretty'
+ 'printed cmds)')
ap.add_argument('cmd', action='store',
help='Command to run (try "help" for list)')
@@ -215,7 +230,7 @@ def main():
args = ap.parse_args()
- if args.cmd not in subcmds:
+ if args.raw or (args.cmd not in subcmds):
func = subcmd_default
else:
func = subcmds[args.cmd]['func']
diff --git a/scripts/n2nhttpd b/scripts/n2nhttpd
index 253d46d..e063fb1 100755
--- a/scripts/n2nhttpd
+++ b/scripts/n2nhttpd
@@ -9,8 +9,8 @@
#
# Try it out with
# http://localhost:8080/
-# http://localhost:8080/edge/peer
-# http://localhost:8080/edge/super
+# http://localhost:8080/edge/edges
+# http://localhost:8080/edge/supernodes
import argparse
import socket
@@ -19,6 +19,7 @@ import socketserver
import http.server
import signal
import functools
+import base64
from http import HTTPStatus
@@ -73,6 +74,7 @@ class JsonUDP():
# assert(data['cmd'] == cmd)
result = list()
+ error = None
while True:
data, _ = self.sock.recvfrom(1024)
@@ -83,9 +85,13 @@ class JsonUDP():
continue
if data['_type'] == 'error':
- raise ValueError('Error: {}'.format(data['error']))
+ # we still expect an end packet, so save the error
+ error = ValueError('Error: {}'.format(data['error']))
+ continue
if data['_type'] == 'end':
+ if error:
+ raise error
return result
if data['_type'] != 'row':
@@ -114,22 +120,49 @@ class JsonUDP():
return self._call('w', cmdline)
-indexhtml = """
-
-
- n2n management
-
-
-
-
- Supernodes:
-
-
- Peers:
-
+pages = {
+ "/script.js": {
+ "content_type": "text/javascript",
+ "content": """
+var verbose=-1;
-
+
+
+
+""",
+ },
+ "/supernode.html": {
+ "content_type": "text/html; charset=utf-8",
+ "content": """
+
+
+ n2n supernode management
+
+
+
+
+ Last Updated:
+
+ update
+ stop supernode
+
+ Logging Verbosity:
+
+
+
+ +
+ -
+ reload communities
+
+
+ Communities:
+
+
+ Edges/Peers:
+
+
+
+
+
+
+
+
-"""
+""",
+ },
+}
class SimpleHandler(http.server.BaseHTTPRequestHandler):
- def __init__(self, rpc, *args, **kwargs):
+ def __init__(self, rpc, snrpc, *args, **kwargs):
self.rpc = rpc
+ self.snrpc = snrpc
super().__init__(*args, **kwargs)
def log_request(self, code='-', size='-'):
@@ -205,42 +403,102 @@ class SimpleHandler(http.server.BaseHTTPRequestHandler):
self.end_headers()
self.wfile.write(message.encode('utf8'))
- def do_GET(self):
- url_tail = self.path
+ def _replyjson(self, data):
+ self.send_response(HTTPStatus.OK)
+ self.send_header('Content-type', 'application/json')
+ self.end_headers()
+ self.wfile.write(json.dumps(data).encode('utf8'))
- if url_tail == "/":
- self.send_response(HTTPStatus.OK)
- self.send_header('Content-type', 'text/html; charset=utf-8')
- self.end_headers()
- self.wfile.write(indexhtml.encode('utf8'))
- return
+ def _replyunauth(self):
+ self.send_response(HTTPStatus.UNAUTHORIZED)
+ self.send_header('WWW-Authenticate', 'Basic realm="n2n"')
+ self.end_headers()
- if url_tail.startswith("/edge/"):
- tail = url_tail.split('/')
- cmd = tail[2]
- # if commands ever need args, use more of the path components
+ def _extractauth(self, rpc):
+ # Avoid caching the key inside the object for all clients
+ rpc.key = None
- try:
- data = self.rpc.read(cmd)
- except ValueError:
- self._simplereply(HTTPStatus.BAD_REQUEST, 'Bad Command')
+ header = self.headers.get('Authorization')
+ if header is not None:
+ authtype, encoded = header.split(' ')
+ if authtype == 'Basic':
+ user, key = base64.b64decode(encoded).decode('utf8').split(':')
+ rpc.key = key
+
+ if rpc.key is None:
+ rpc.key = rpc.defaultkey
+
+ def _rpc(self, method, cmdline):
+ try:
+ data = method(cmdline)
+ except ValueError as e:
+ if str(e) == "Error: badauth":
+ self._replyunauth()
return
+ self._simplereply(HTTPStatus.BAD_REQUEST, 'Bad Command')
+ return
+
+ self._replyjson(data)
+ return
+
+ def _rpc_read(self, rpc):
+ self._extractauth(rpc)
+ tail = self.path.split('/')
+ cmd = tail[2]
+ # if reads ever need args, could use more of the tail
+
+ self._rpc(rpc.read, cmd)
+
+ def _rpc_write(self, rpc):
+ self._extractauth(rpc)
+ content_length = int(self.headers['Content-Length'])
+ post_data = self.rfile.read(content_length).decode('utf8')
+
+ tail = self.path.split('/')
+ cmd = tail[2]
+ cmdline = cmd + ' ' + post_data
+
+ self._rpc(rpc.write, cmdline)
+
+ def do_GET(self):
+ if self.path.startswith("/edge/"):
+ self._rpc_read(self.rpc)
+ return
+
+ if self.path.startswith("/supernode/"):
+ self._rpc_read(self.snrpc)
+ return
+
+ if self.path in pages:
+ page = pages[self.path]
+
self.send_response(HTTPStatus.OK)
- self.send_header('Content-type', 'application/json')
+ self.send_header('Content-type', page['content_type'])
self.end_headers()
- self.wfile.write(json.dumps(data).encode('utf8'))
+ self.wfile.write(page['content'].encode('utf8'))
return
self._simplereply(HTTPStatus.NOT_FOUND, 'Not Found')
return
+ def do_POST(self):
+ if self.path.startswith("/edge/"):
+ self._rpc_write(self.rpc)
+ return
+
+ if self.path.startswith("/supernode/"):
+ self._rpc_write(self.snrpc)
+ return
+
def main():
ap = argparse.ArgumentParser(
description='Control the running local n2n edge via http')
ap.add_argument('-t', '--mgmtport', action='store', default=5644,
- help='Management Port (default=5644)')
+ help='Management Port (default=5644)', type=int)
+ ap.add_argument('--snmgmtport', action='store', default=5645,
+ help='Supernode Management Port (default=5645)', type=int)
ap.add_argument('-k', '--key', action='store',
help='Password for mgmt commands')
ap.add_argument('-d', '--debug', action='store_true',
@@ -253,12 +511,16 @@ def main():
rpc = JsonUDP(args.mgmtport)
rpc.debug = args.debug
- rpc.key = args.key
+ rpc.defaultkey = args.key
+
+ snrpc = JsonUDP(args.snmgmtport)
+ snrpc.debug = args.debug
+ snrpc.defaultkey = args.key
signal.signal(signal.SIGPIPE, signal.SIG_DFL)
socketserver.TCPServer.allow_reuse_address = True
- handler = functools.partial(SimpleHandler, rpc)
+ handler = functools.partial(SimpleHandler, rpc, snrpc)
with socketserver.TCPServer(("", args.port), handler) as httpd:
try:
httpd.serve_forever()
diff --git a/src/edge.c b/src/edge.c
index 1d3f40f..1324628 100644
--- a/src/edge.c
+++ b/src/edge.c
@@ -1300,8 +1300,9 @@ int main (int argc, char* argv[]) {
#endif
keep_on_running = 1;
+ eee->keep_running = &keep_on_running;
traceEvent(TRACE_NORMAL, "edge started");
- rc = run_edge_loop(eee, &keep_on_running);
+ rc = run_edge_loop(eee);
print_edge_stats(eee);
#ifdef HAVE_LIBCAP
diff --git a/src/edge_management.c b/src/edge_management.c
new file mode 100644
index 0000000..357e03e
--- /dev/null
+++ b/src/edge_management.c
@@ -0,0 +1,449 @@
+/**
+ * (C) 2007-21 - 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"
+#include "edge_utils_win32.h"
+
+#define FLAG_WROK 1
+typedef struct n2n_mgmt_handler {
+ int flags;
+ char *cmd;
+ char *help;
+ void (*func)(n2n_edge_t *eee, char *udp_buf, struct sockaddr_in sender_sock, enum n2n_mgmt_type type, char *tag, char *argv0, char *argv);
+} n2n_mgmt_handler_t;
+
+static void mgmt_error (n2n_edge_t *eee, char *udp_buf, const struct sockaddr_in sender_sock, char *tag, char *msg) {
+ size_t msg_len;
+ msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE,
+ "{"
+ "\"_tag\":\"%s\","
+ "\"_type\":\"error\","
+ "\"error\":\"%s\"}\n",
+ tag,
+ msg);
+ sendto(eee->udp_mgmt_sock, udp_buf, msg_len, 0,
+ (struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
+}
+
+static void mgmt_stop (n2n_edge_t *eee, char *udp_buf, const struct sockaddr_in sender_sock, enum n2n_mgmt_type type, char *tag, char *argv0, char *argv) {
+ size_t msg_len;
+
+ if(type==N2N_MGMT_WRITE) {
+ *eee->keep_running = 0;
+ }
+
+ msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE,
+ "{"
+ "\"_tag\":\"%s\","
+ "\"_type\":\"row\","
+ "\"keep_running\":%u}\n",
+ tag,
+ *eee->keep_running);
+
+ sendto(eee->udp_mgmt_sock, udp_buf, msg_len, 0,
+ (struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
+}
+
+static void mgmt_verbose (n2n_edge_t *eee, char *udp_buf, const struct sockaddr_in sender_sock, enum n2n_mgmt_type type, char *tag, char *argv0, char *argv) {
+ size_t msg_len;
+
+ if(type==N2N_MGMT_WRITE) {
+ if(argv) {
+ setTraceLevel(strtoul(argv, NULL, 0));
+ }
+ }
+
+ msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE,
+ "{"
+ "\"_tag\":\"%s\","
+ "\"_type\":\"row\","
+ "\"traceLevel\":%u}\n",
+ tag,
+ getTraceLevel());
+
+ sendto(eee->udp_mgmt_sock, udp_buf, msg_len, 0,
+ (struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
+}
+
+static void mgmt_communities (n2n_edge_t *eee, char *udp_buf, const struct sockaddr_in sender_sock, enum n2n_mgmt_type type, char *tag, char *argv0, char *argv) {
+ size_t msg_len;
+
+ if(eee->conf.header_encryption != HEADER_ENCRYPTION_NONE) {
+ mgmt_error(eee, udp_buf, sender_sock, tag, "noaccess");
+ return;
+ }
+
+ msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE,
+ "{"
+ "\"_tag\":\"%s\","
+ "\"_type\":\"row\","
+ "\"community\":\"%s\"}",
+ tag,
+ eee->conf.community_name);
+
+ sendto(eee->udp_mgmt_sock, udp_buf, msg_len, 0,
+ (struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
+}
+
+static void mgmt_supernodes (n2n_edge_t *eee, char *udp_buf, const struct sockaddr_in sender_sock, enum n2n_mgmt_type type, char *tag, char *argv0, char *argv) {
+ size_t msg_len;
+ struct peer_info *peer, *tmpPeer;
+ macstr_t mac_buf;
+ n2n_sock_str_t sockbuf;
+ selection_criterion_str_t sel_buf;
+
+ HASH_ITER(hh, eee->conf.supernodes, peer, tmpPeer) {
+
+ /*
+ * TODO:
+ * The version string provided by the remote supernode could contain
+ * chars that make our JSON invalid.
+ * - do we care?
+ */
+
+ msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE,
+ "{"
+ "\"_tag\":\"%s\","
+ "\"_type\":\"row\","
+ "\"version\":\"%s\","
+ "\"purgeable\":%i,"
+ "\"current\":%i,"
+ "\"macaddr\":\"%s\","
+ "\"sockaddr\":\"%s\","
+ "\"selection\":\"%s\","
+ "\"lastseen\":%li,"
+ "\"uptime\":%li}\n",
+ tag,
+ peer->version,
+ peer->purgeable,
+ (peer == eee->curr_sn) ? (eee->sn_wait ? 2 : 1 ) : 0,
+ is_null_mac(peer->mac_addr) ? "" : macaddr_str(mac_buf, peer->mac_addr),
+ sock_to_cstr(sockbuf, &(peer->sock)),
+ sn_selection_criterion_str(sel_buf, peer),
+ peer->last_seen,
+ peer->uptime);
+
+ sendto(eee->udp_mgmt_sock, udp_buf, msg_len, 0,
+ (struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
+ }
+}
+
+static void mgmt_edges_row (n2n_edge_t *eee, char *udp_buf, const struct sockaddr_in sender_sock, char *tag, struct peer_info *peer, char *mode) {
+ size_t msg_len;
+ macstr_t mac_buf;
+ n2n_sock_str_t sockbuf;
+ dec_ip_bit_str_t ip_bit_str = {'\0'};
+
+ msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE,
+ "{"
+ "\"_tag\":\"%s\","
+ "\"_type\":\"row\","
+ "\"mode\":\"%s\","
+ "\"ip4addr\":\"%s\","
+ "\"purgeable\":%i,"
+ "\"macaddr\":\"%s\","
+ "\"sockaddr\":\"%s\","
+ "\"desc\":\"%s\","
+ "\"last_seen\":%li}\n",
+ tag,
+ mode,
+ (peer->dev_addr.net_addr == 0) ? "" : ip_subnet_to_str(ip_bit_str, &peer->dev_addr),
+ peer->purgeable,
+ (is_null_mac(peer->mac_addr)) ? "" : macaddr_str(mac_buf, peer->mac_addr),
+ sock_to_cstr(sockbuf, &(peer->sock)),
+ peer->dev_desc,
+ peer->last_seen);
+
+ sendto(eee->udp_mgmt_sock, udp_buf, msg_len, 0 /*flags*/,
+ (struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
+}
+
+static void mgmt_edges (n2n_edge_t *eee, char *udp_buf, const struct sockaddr_in sender_sock, enum n2n_mgmt_type type, char *tag, char *argv0, char *argv) {
+ struct peer_info *peer, *tmpPeer;
+
+ // dump nodes with forwarding through supernodes
+ HASH_ITER(hh, eee->pending_peers, peer, tmpPeer) {
+ mgmt_edges_row(eee, udp_buf, sender_sock, tag, peer, "pSp");
+ }
+
+ // dump peer-to-peer nodes
+ HASH_ITER(hh, eee->known_peers, peer, tmpPeer) {
+ mgmt_edges_row(eee, udp_buf, sender_sock, tag, peer, "p2p");
+ }
+}
+
+static void mgmt_timestamps (n2n_edge_t *eee, char *udp_buf, const struct sockaddr_in sender_sock, enum n2n_mgmt_type type, char *tag, char *argv0, char *argv) {
+ size_t msg_len;
+
+ msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE,
+ "{"
+ "\"_tag\":\"%s\","
+ "\"_type\":\"row\","
+ "\"start_time\":%lu,"
+ "\"last_super\":%ld,"
+ "\"last_p2p\":%ld}\n",
+ tag,
+ eee->start_time,
+ eee->last_sup,
+ eee->last_p2p);
+
+ sendto(eee->udp_mgmt_sock, udp_buf, msg_len, 0,
+ (struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
+}
+
+static void mgmt_packetstats (n2n_edge_t *eee, char *udp_buf, const struct sockaddr_in sender_sock, enum n2n_mgmt_type type, char *tag, char *argv0, char *argv) {
+ size_t msg_len;
+
+ msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE,
+ "{"
+ "\"_tag\":\"%s\","
+ "\"_type\":\"row\","
+ "\"type\":\"transop\","
+ "\"tx_pkt\":%lu,"
+ "\"rx_pkt\":%lu}\n",
+ tag,
+ eee->transop.tx_cnt,
+ eee->transop.rx_cnt);
+
+ sendto(eee->udp_mgmt_sock, udp_buf, msg_len, 0,
+ (struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
+
+ msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE,
+ "{"
+ "\"_tag\":\"%s\","
+ "\"_type\":\"row\","
+ "\"type\":\"p2p\","
+ "\"tx_pkt\":%u,"
+ "\"rx_pkt\":%u}\n",
+ tag,
+ eee->stats.tx_p2p,
+ eee->stats.rx_p2p);
+
+ sendto(eee->udp_mgmt_sock, udp_buf, msg_len, 0,
+ (struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
+
+ msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE,
+ "{"
+ "\"_tag\":\"%s\","
+ "\"_type\":\"row\","
+ "\"type\":\"super\","
+ "\"tx_pkt\":%u,"
+ "\"rx_pkt\":%u}\n",
+ tag,
+ eee->stats.tx_sup,
+ eee->stats.rx_sup);
+
+ sendto(eee->udp_mgmt_sock, udp_buf, msg_len, 0,
+ (struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
+
+ msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE,
+ "{"
+ "\"_tag\":\"%s\","
+ "\"_type\":\"row\","
+ "\"type\":\"super_broadcast\","
+ "\"tx_pkt\":%u,"
+ "\"rx_pkt\":%u}\n",
+ tag,
+ eee->stats.tx_sup_broadcast,
+ eee->stats.rx_sup_broadcast);
+
+ sendto(eee->udp_mgmt_sock, udp_buf, msg_len, 0,
+ (struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
+}
+
+static void mgmt_unimplemented (n2n_edge_t *eee, char *udp_buf, const struct sockaddr_in sender_sock, enum n2n_mgmt_type type, char *tag, char *argv0, char *argv) {
+
+ mgmt_error(eee, udp_buf, sender_sock, tag, "unimplemented");
+}
+
+static void mgmt_help (n2n_edge_t *eee, char *udp_buf, const struct sockaddr_in sender_sock, enum n2n_mgmt_type type, char *tag, char *argv0, char *argv);
+
+n2n_mgmt_handler_t mgmt_handlers[] = {
+ { .cmd = "reload_communities", .flags = FLAG_WROK, .help = "Reserved for supernode", .func = mgmt_unimplemented},
+
+ { .cmd = "stop", .flags = FLAG_WROK, .help = "Gracefully exit edge", .func = mgmt_stop},
+ { .cmd = "verbose", .flags = FLAG_WROK, .help = "Manage verbosity level", .func = mgmt_verbose},
+ { .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 = "timestamps", .help = "Event timestamps", .func = mgmt_timestamps},
+ { .cmd = "packetstats", .help = "traffic counters", .func = mgmt_packetstats},
+ { .cmd = "help", .flags = FLAG_WROK, .help = "Show JSON commands", .func = mgmt_help},
+ { .cmd = NULL },
+};
+
+static void mgmt_help (n2n_edge_t *eee, char *udp_buf, const struct sockaddr_in sender_sock, enum n2n_mgmt_type type, char *tag, char *argv0, char *argv) {
+ size_t msg_len;
+ n2n_mgmt_handler_t *handler;
+
+ /*
+ * Even though this command is readonly, we deliberately do not check
+ * the type - allowing help replies to both read and write requests
+ */
+
+ for( handler=mgmt_handlers; handler->cmd; handler++ ) {
+ msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE,
+ "{"
+ "\"_tag\":\"%s\","
+ "\"_type\":\"row\","
+ "\"cmd\":\"%s\","
+ "\"help\":\"%s\"}\n",
+ tag,
+ handler->cmd,
+ handler->help);
+
+ sendto(eee->udp_mgmt_sock, udp_buf, msg_len, 0,
+ (struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
+ }
+}
+
+/*
+ * Check if the user is authorised for this command.
+ * - this should be more configurable!
+ * - for the moment we use some simple heuristics:
+ * Reads are not dangerous, so they are simply allowed
+ * Writes are possibly dangerous, so they need a fake password
+ */
+static int mgmt_auth (const struct sockaddr_in sender_sock, enum n2n_mgmt_type type, char *auth, char *argv0, char *argv) {
+ if(auth) {
+ /* If we have an auth key, it must match */
+ if(0 == strcmp(auth,"CHANGEME")) {
+ return 1;
+ }
+ return 0;
+ }
+ /* if we dont have an auth key, we can still read */
+ if(type==N2N_MGMT_READ) {
+ return 1;
+ }
+ return 0;
+}
+
+void handleMgmtJson (n2n_edge_t *eee, char *udp_buf, const struct sockaddr_in sender_sock) {
+
+ char cmdlinebuf[80];
+ enum n2n_mgmt_type type;
+ char *typechar;
+ char *options;
+ char *argv0;
+ char *argv;
+ char *tag;
+ char *flagstr;
+ int flags;
+ char *auth;
+ n2n_mgmt_handler_t *handler;
+ size_t msg_len;
+
+ /* save a copy of the commandline before we reuse the udp_buf */
+ strncpy(cmdlinebuf, udp_buf, sizeof(cmdlinebuf)-1);
+ cmdlinebuf[sizeof(cmdlinebuf)-1] = 0;
+
+ traceEvent(TRACE_DEBUG, "mgmt json %s", cmdlinebuf);
+
+ typechar = strtok(cmdlinebuf, " \r\n");
+ if(!typechar) {
+ /* should not happen */
+ mgmt_error(eee, udp_buf, sender_sock, "-1", "notype");
+ return;
+ }
+ if(*typechar == 'r') {
+ type=N2N_MGMT_READ;
+ } else if(*typechar == 'w') {
+ type=N2N_MGMT_WRITE;
+ } else {
+ /* dunno how we got here */
+ mgmt_error(eee, udp_buf, sender_sock, "-1", "badtype");
+ return;
+ }
+
+ /* Extract the tag to use in all reply packets */
+ options = strtok(NULL, " \r\n");
+ if(!options) {
+ mgmt_error(eee, udp_buf, sender_sock, "-1", "nooptions");
+ return;
+ }
+
+ argv0 = strtok(NULL, " \r\n");
+ if(!argv0) {
+ mgmt_error(eee, udp_buf, sender_sock, "-1", "nocmd");
+ return;
+ }
+
+ /*
+ * The entire rest of the line is the argv. We apply no processing
+ * or arg separation so that the cmd can use it however it needs.
+ */
+ argv = strtok(NULL, "\r\n");
+
+ /*
+ * There might be an auth token mixed in with the tag
+ */
+ tag = strtok(options, ":");
+ flagstr = strtok(NULL, ":");
+ if(flagstr) {
+ flags = strtoul(flagstr, NULL, 16);
+ } else {
+ flags = 0;
+ }
+
+ /* Only 1 flag bit defined at the moment - "auth option present" */
+ if(flags & 1) {
+ auth = strtok(NULL, ":");
+ } else {
+ auth = NULL;
+ }
+
+ if(!mgmt_auth(sender_sock, type, auth, argv0, argv)) {
+ mgmt_error(eee, udp_buf, sender_sock, tag, "badauth");
+ return;
+ }
+
+ for( handler=mgmt_handlers; handler->cmd; handler++ ) {
+ if(0 == strcmp(handler->cmd, argv0)) {
+ break;
+ }
+ }
+ if(!handler->cmd) {
+ mgmt_error(eee, udp_buf, sender_sock, tag, "unknowncmd");
+ return;
+ }
+
+ if((type==N2N_MGMT_WRITE) && !(handler->flags & FLAG_WROK)) {
+ mgmt_error(eee, udp_buf, sender_sock, tag, "readonly");
+ return;
+ }
+
+ /*
+ * TODO:
+ * The tag provided by the requester could contain chars
+ * that make our JSON invalid.
+ * - do we care?
+ */
+ msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE,
+ "{\"_tag\":\"%s\",\"_type\":\"begin\",\"cmd\":\"%s\"}\n", tag, argv0);
+ sendto(eee->udp_mgmt_sock, udp_buf, msg_len, 0,
+ (struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
+
+ handler->func(eee, udp_buf, sender_sock, type, tag, argv0, argv);
+
+ msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE,
+ "{\"_tag\":\"%s\",\"_type\":\"end\"}\n", tag);
+ sendto(eee->udp_mgmt_sock, udp_buf, msg_len, 0,
+ (struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
+ return;
+}
diff --git a/src/edge_utils.c b/src/edge_utils.c
index f1c59b6..b5c7981 100644
--- a/src/edge_utils.c
+++ b/src/edge_utils.c
@@ -22,7 +22,7 @@
/* heap allocation for compression as per lzo example doc */
#define HEAP_ALLOC(var,size) lzo_align_t __LZO_MMODEL var [ ((size) + (sizeof(lzo_align_t) - 1)) / sizeof(lzo_align_t) ]
-static HEAP_ALLOC (wrkmem, LZO1X_1_MEM_COMPRESS);
+static HEAP_ALLOC(wrkmem, LZO1X_1_MEM_COMPRESS);
/* ************************************** */
@@ -247,7 +247,7 @@ static int detect_local_ip_address (n2n_sock_t* out_sock, const n2n_edge_t* eee)
// open socket, close it before if TCP
// in case of TCP, 'connect()' is required
-int supernode_connect(n2n_edge_t *eee) {
+int supernode_connect (n2n_edge_t *eee) {
int sockopt;
struct sockaddr_in sn_sock;
@@ -334,7 +334,7 @@ int supernode_connect(n2n_edge_t *eee) {
// always closes the socket
-void supernode_disconnect(n2n_edge_t *eee) {
+void supernode_disconnect (n2n_edge_t *eee) {
if(eee->sock >= 0) {
closesocket(eee->sock);
@@ -480,7 +480,7 @@ n2n_edge_t* edge_init (const n2n_edge_conf_t *conf, int *rv) {
}
if(resolve_create_thread(&(eee->resolve_parameter), eee->conf.supernodes) == 0) {
- traceEvent(TRACE_NORMAL, "successfully created resolver thread");
+ traceEvent(TRACE_NORMAL, "successfully created resolver thread");
}
eee->network_traffic_filter = create_network_traffic_filter();
@@ -490,7 +490,7 @@ n2n_edge_t* edge_init (const n2n_edge_conf_t *conf, int *rv) {
*rv = 0;
return(eee);
- edge_init_error:
+edge_init_error:
if(eee)
free(eee);
*rv = rc;
@@ -859,7 +859,7 @@ static int get_local_auth (n2n_edge_t *eee, n2n_auth_t *auth) {
speck_128_encrypt(auth->token + N2N_PRIVATE_PUBLIC_KEY_SIZE, (speck_context_t*)eee->conf.shared_secret_ctx);
break;
default:
- break;
+ break;
}
return 0;
@@ -992,7 +992,7 @@ static void check_known_peer_sock_change (n2n_edge_t *eee,
/** Send a datagram to a socket file descriptor */
static ssize_t sendto_fd (n2n_edge_t *eee, const void *buf,
- size_t len, struct sockaddr_in *dest) {
+ size_t len, struct sockaddr_in *dest) {
ssize_t sent = 0;
int rc = 1;
@@ -1010,7 +1010,7 @@ static ssize_t sendto_fd (n2n_edge_t *eee, const void *buf,
rc = select(eee->sock + 1, NULL, &socket_mask, NULL, &wait_time);
}
- if (rc > 0) {
+ if(rc > 0) {
sent = sendto(eee->sock, buf, len, 0 /*flags*/,
(struct sockaddr *)dest, sizeof(struct sockaddr_in));
@@ -1175,7 +1175,7 @@ void send_query_peer (n2n_edge_t * eee,
if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) {
packet_header_encrypt(pktbuf, idx, idx,
eee->conf.header_encryption_ctx_dynamic, eee->conf.header_iv_ctx_dynamic,
- time_stamp ());
+ time_stamp());
}
sendto_sock(eee, pktbuf, idx, &(eee->curr_sn->sock));
@@ -1186,7 +1186,7 @@ void send_query_peer (n2n_edge_t * eee,
if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) {
packet_header_encrypt(pktbuf, idx, idx,
eee->conf.header_encryption_ctx_dynamic, eee->conf.header_iv_ctx_dynamic,
- time_stamp ());
+ time_stamp());
}
n_o_pings = eee->conf.number_max_sn_pings;
@@ -1525,7 +1525,7 @@ void update_supernode_reg (n2n_edge_t * eee, time_t now) {
// determine time offset to apply on last_register_req for
// all edges's next re-registration does not happen all at once
- if (eee->sn_wait == 2) {
+ if(eee->sn_wait == 2) {
// remaining 1/4 is greater than 1/10 fast retry allowance;
// '%' might be expensive but does not happen all too often
off = n2n_rand() % ((eee->conf.register_interval * 3) / 4);
@@ -1697,7 +1697,7 @@ static int handle_PACKET (n2n_edge_t * eee,
if(rx_compression_id != N2N_COMPRESSION_ID_NONE) {
traceEvent(TRACE_DEBUG, "payload decompression %s: deflated %u bytes to %u bytes",
compression_str(rx_compression_id), eth_size, (int)deflated_len);
- memcpy(eth_payload ,deflation_buffer, deflated_len );
+ memcpy(eth_payload,deflation_buffer, deflated_len );
eth_size = deflated_len;
free(deflation_buffer);
}
@@ -1806,299 +1806,9 @@ static char *get_ip_from_arp (dec_ip_str_t buf, const n2n_mac_t req_mac) {
#endif
#endif
-static void handleMgmtJson_error (n2n_edge_t *eee, char *udp_buf, struct sockaddr_in sender_sock, char *tag, char *msg) {
- size_t msg_len;
- msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE,
- "{"
- "\"_tag\":\"%s\","
- "\"_type\":\"error\","
- "\"error\":\"%s\"}\n",
- tag,
- msg);
- sendto(eee->udp_mgmt_sock, udp_buf, msg_len, 0,
- (struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
-}
-
-static void handleMgmtJson_super (n2n_edge_t *eee, char *udp_buf, struct sockaddr_in sender_sock, enum n2n_mgmt_type type, char *tag, char *argv0, char *argv) {
- size_t msg_len;
- struct peer_info *peer, *tmpPeer;
- macstr_t mac_buf;
- n2n_sock_str_t sockbuf;
- selection_criterion_str_t sel_buf;
-
- if(type!=N2N_MGMT_READ) {
- handleMgmtJson_error(eee, udp_buf, sender_sock, tag, "readonly");
- return;
- }
-
- HASH_ITER(hh, eee->conf.supernodes, peer, tmpPeer) {
-
- /*
- * TODO:
- * The version string provided by the remote supernode could contain
- * chars that make our JSON invalid.
- * - do we care?
- */
-
- msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE,
- "{"
- "\"_tag\":\"%s\","
- "\"_type\":\"row\","
- "\"version\":\"%s\","
- "\"purgeable\":%i,"
- "\"current\":%i,"
- "\"macaddr\":\"%s\","
- "\"sockaddr\":\"%s\","
- "\"selection\":\"%s\","
- "\"lastseen\":%li,"
- "\"uptime\":%li}\n",
- tag,
- peer->version,
- peer->purgeable,
- (peer == eee->curr_sn) ? (eee->sn_wait ? 2 : 1 ) : 0,
- is_null_mac(peer->mac_addr) ? "" : macaddr_str(mac_buf, peer->mac_addr),
- sock_to_cstr(sockbuf, &(peer->sock)),
- sn_selection_criterion_str(sel_buf, peer),
- peer->last_seen,
- peer->uptime);
-
- sendto(eee->udp_mgmt_sock, udp_buf, msg_len, 0,
- (struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
- }
-}
-
-static void handleMgmtJson_peer (n2n_edge_t *eee, char *udp_buf, struct sockaddr_in sender_sock, enum n2n_mgmt_type type, char *tag, char *argv0, char *argv) {
- size_t msg_len;
- struct peer_info *peer, *tmpPeer;
- macstr_t mac_buf;
- n2n_sock_str_t sockbuf;
- in_addr_t net;
-
- if(type!=N2N_MGMT_READ) {
- handleMgmtJson_error(eee, udp_buf, sender_sock, tag, "readonly");
- return;
- }
-
- /* FIXME:
- * dont repeat yourself - the body of these two loops is identical
- */
-
- // dump nodes with forwarding through supernodes
- HASH_ITER(hh, eee->pending_peers, peer, tmpPeer) {
- net = htonl(peer->dev_addr.net_addr);
- msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE,
- "{"
- "\"_tag\":\"%s\","
- "\"_type\":\"row\","
- "\"mode\":\"pSp\","
- "\"ip4addr\":\"%s\","
- "\"macaddr\":\"%s\","
- "\"sockaddr\":\"%s\","
- "\"desc\":\"%s\","
- "\"lastseen\":%li}\n",
- tag,
- (peer->dev_addr.net_addr == 0) ? "" : inet_ntoa(*(struct in_addr *) &net),
- (is_null_mac(peer->mac_addr)) ? "" : macaddr_str(mac_buf, peer->mac_addr),
- sock_to_cstr(sockbuf, &(peer->sock)),
- peer->dev_desc,
- peer->last_seen);
-
- sendto(eee->udp_mgmt_sock, udp_buf, msg_len, 0/*flags*/,
- (struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
- }
-
- // dump peer-to-peer nodes
- HASH_ITER(hh, eee->known_peers, peer, tmpPeer) {
- net = htonl(peer->dev_addr.net_addr);
- msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE,
- "{"
- "\"_tag\":\"%s\","
- "\"_type\":\"row\","
- "\"mode\":\"p2p\","
- "\"ip4addr\":\"%s\","
- "\"macaddr\":\"%s\","
- "\"sockaddr\":\"%s\","
- "\"desc\":\"%s\","
- "\"lastseen\":%li}\n",
- tag,
- (peer->dev_addr.net_addr == 0) ? "" : inet_ntoa(*(struct in_addr *) &net),
- (is_null_mac(peer->mac_addr)) ? "" : macaddr_str(mac_buf, peer->mac_addr),
- sock_to_cstr(sockbuf, &(peer->sock)),
- peer->dev_desc,
- peer->last_seen);
- sendto(eee->udp_mgmt_sock, udp_buf, msg_len, 0/*flags*/,
- (struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
- }
-}
-
-static void handleMgmtJson_help (n2n_edge_t *eee, char *udp_buf, struct sockaddr_in sender_sock, enum n2n_mgmt_type type, char *tag, char *argv0, char *argv);
-
-n2n_mgmt_handler_t mgmt_handlers[] = {
- { .cmd = "peer", .help = "List current peers", .func = handleMgmtJson_peer},
- { .cmd = "super", .help = "List current supernodes", .func = handleMgmtJson_super},
- { .cmd = "help", .help = "Show JSON commands", .func = handleMgmtJson_help},
- { .cmd = NULL },
-};
-
-static void handleMgmtJson_help (n2n_edge_t *eee, char *udp_buf, struct sockaddr_in sender_sock, enum n2n_mgmt_type type, char *tag, char *argv0, char *argv) {
- size_t msg_len;
- n2n_mgmt_handler_t *handler;
-
- /*
- * Even though this command is readonly, we deliberately do not check
- * the type - allowing help replys to both read and write requests
- */
-
- for( handler=mgmt_handlers; handler->cmd; handler++ ) {
- msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE,
- "{"
- "\"_tag\":\"%s\","
- "\"_type\":\"row\","
- "\"cmd\":\"%s\","
- "\"help\":\"%s\"}\n",
- tag,
- handler->cmd,
- handler->help);
-
- sendto(eee->udp_mgmt_sock, udp_buf, msg_len, 0,
- (struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
- }
-}
-
-/*
- * Check if the user is authorised for this command.
- * - this should be more configurable!
- * - for the moment we use some simple heuristics:
- * Reads are not dangerous, so they are simply allowed
- * Writes are possibly dangerous, so they need a fake password
- */
-int handleMgmtJson_auth(struct sockaddr_in sender_sock, enum n2n_mgmt_type type, char *auth, char *argv0, char *argv) {
- if(auth) {
- /* If we have an auth key, it must match */
- if(0 == strcmp(auth,"CHANGEME")) {
- return 1;
- }
- return 0;
- }
- /* if we dont have an auth key, we can still read */
- if(type==N2N_MGMT_READ) {
- return 1;
- }
- return 0;
-}
-
-static void handleMgmtJson (n2n_edge_t *eee, char *udp_buf, struct sockaddr_in sender_sock) {
-
- char cmdlinebuf[80];
- enum n2n_mgmt_type type;
- char *typechar;
- char *options;
- char *argv0;
- char *argv;
- char *tag;
- char *flagstr;
- int flags;
- char *auth;
- n2n_mgmt_handler_t *handler;
- size_t msg_len;
-
- /* save a copy of the commandline before we reuse the udp_buf */
- strncpy(cmdlinebuf, udp_buf, sizeof(cmdlinebuf)-1);
- cmdlinebuf[sizeof(cmdlinebuf)-1] = 0;
-
- traceEvent(TRACE_DEBUG, "mgmt json %s", cmdlinebuf);
-
- typechar = strtok(cmdlinebuf, " \r\n");
- if(!typechar) {
- /* should not happen */
- handleMgmtJson_error(eee, udp_buf, sender_sock, "-1", "notype");
- return;
- }
- if(*typechar == 'r') {
- type=N2N_MGMT_READ;
- } else if(*typechar == 'w') {
- type=N2N_MGMT_WRITE;
- } else {
- /* dunno how we got here */
- handleMgmtJson_error(eee, udp_buf, sender_sock, "-1", "badtype");
- return;
- }
-
- /* Extract the tag to use in all reply packets */
- options = strtok(NULL, " \r\n");
- if(!options) {
- handleMgmtJson_error(eee, udp_buf, sender_sock, "-1", "nooptions");
- return;
- }
-
- argv0 = strtok(NULL, " \r\n");
- if(!argv0) {
- handleMgmtJson_error(eee, udp_buf, sender_sock, "-1", "nocmd");
- return;
- }
-
- /*
- * The entire rest of the line is the argv. We apply no processing
- * or arg separation so that the cmd can use it however it needs.
- */
- argv = strtok(NULL, "\r\n");
-
- /*
- * There might be an auth token mixed in with the tag
- */
- tag = strtok(options, ":");
- flagstr = strtok(NULL, ":");
- if (flagstr) {
- flags = strtoul(flagstr, NULL, 16);
- } else {
- flags = 0;
- }
-
- /* Only 1 flag bit defined at the moment - "auth option present" */
- if (flags & 1) {
- auth = strtok(NULL, ":");
- } else {
- auth = NULL;
- }
-
- if(!handleMgmtJson_auth(sender_sock, type, auth, argv0, argv)) {
- handleMgmtJson_error(eee, udp_buf, sender_sock, tag, "badauth");
- return;
- }
-
- for( handler=mgmt_handlers; handler->cmd; handler++ ) {
- if(0 == strcmp(handler->cmd, argv0)) {
- break;
- }
- }
- if(!handler->cmd) {
- handleMgmtJson_error(eee, udp_buf, sender_sock, tag, "unknowncmd");
- return;
- }
-
- /*
- * TODO:
- * The tag provided by the requester could contain chars
- * that make our JSON invalid.
- * - do we care?
- */
- msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE,
- "{\"_tag\":\"%s\",\"_type\":\"begin\",\"cmd\":\"%s\"}\n", tag, argv0);
- sendto(eee->udp_mgmt_sock, udp_buf, msg_len, 0,
- (struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
-
- handler->func(eee, udp_buf, sender_sock, type, tag, argv0, argv);
-
- msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE,
- "{\"_tag\":\"%s\",\"_type\":\"end\"}\n", tag);
- sendto(eee->udp_mgmt_sock, udp_buf, msg_len, 0,
- (struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
- return;
-}
-
/** Read a datagram from the management UDP socket and take appropriate
* action. */
-static void readFromMgmtSocket (n2n_edge_t *eee, int *keep_running) {
+static void readFromMgmtSocket (n2n_edge_t *eee) {
char udp_buf[N2N_PKT_BUF_SIZE]; /* Compete UDP packet */
ssize_t recvlen;
@@ -2155,7 +1865,7 @@ static void readFromMgmtSocket (n2n_edge_t *eee, int *keep_running) {
if(0 == memcmp(udp_buf, "stop", 4)) {
traceEvent(TRACE_NORMAL, "stop command received");
- *keep_running = 0;
+ *eee->keep_running = 0;
return;
}
@@ -2194,7 +1904,7 @@ static void readFromMgmtSocket (n2n_edge_t *eee, int *keep_running) {
if((udp_buf[0] == 'r' || udp_buf[0] == 'w') && (udp_buf[1] == ' ')) {
/* this is a JSON request */
- handleMgmtJson(eee, (char *)udp_buf, sender_sock);
+ handleMgmtJson(eee, udp_buf, sender_sock);
return;
}
@@ -2216,7 +1926,7 @@ static void readFromMgmtSocket (n2n_edge_t *eee, int *keep_running) {
HASH_ITER(hh, eee->pending_peers, peer, tmpPeer) {
++num_pending_peers;
net = htonl(peer->dev_addr.net_addr);
- snprintf (time_buf, sizeof(time_buf), "%9u", (unsigned int)(now - peer->last_seen));
+ snprintf(time_buf, sizeof(time_buf), "%9u", (unsigned int)(now - peer->last_seen));
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_len),
"%4u | %-15s | %-17s | %-21s | %-15s | %9s |\n",
++num,
@@ -2240,7 +1950,7 @@ static void readFromMgmtSocket (n2n_edge_t *eee, int *keep_running) {
HASH_ITER(hh, eee->known_peers, peer, tmpPeer) {
++num_known_peers;
net = htonl(peer->dev_addr.net_addr);
- snprintf (time_buf, sizeof(time_buf), "%9u", (unsigned int)(now - peer->last_seen));
+ snprintf(time_buf, sizeof(time_buf), "%9u", (unsigned int)(now - peer->last_seen));
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_len),
"%4u | %-15s | %-17s | %-21s | %-15s | %9s |\n",
++num,
@@ -2263,8 +1973,8 @@ static void readFromMgmtSocket (n2n_edge_t *eee, int *keep_running) {
"SUPERNODES\n");
HASH_ITER(hh, eee->conf.supernodes, peer, tmpPeer) {
net = htonl(peer->dev_addr.net_addr);
- snprintf (time_buf, sizeof(time_buf), "%9u", (unsigned int)(now - peer->last_seen));
- snprintf (uptime_buf, sizeof(uptime_buf), "%10u", (unsigned int)(peer->uptime));
+ snprintf(time_buf, sizeof(time_buf), "%9u", (unsigned int)(now - peer->last_seen));
+ snprintf(uptime_buf, sizeof(uptime_buf), "%10u", (unsigned int)(peer->uptime));
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,
@@ -2293,9 +2003,9 @@ static void readFromMgmtSocket (n2n_edge_t *eee, int *keep_running) {
"pend_peers %u | ",
num_pending_peers);
- msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_len),
- "known_peers %u | ",
- num_known_peers);
+ msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_len),
+ "known_peers %u | ",
+ num_known_peers);
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_len),
"transop %u,%u\n",
@@ -2519,9 +2229,9 @@ void edge_send_packet2net (n2n_edge_t * eee,
uint8_t * compression_buffer = NULL;
int32_t compression_len;
- switch (eee->conf.compression) {
+ switch(eee->conf.compression) {
case N2N_COMPRESSION_ID_LZO:
- compression_buffer = malloc (len + len / 16 + 64 + 3);
+ compression_buffer = malloc(len + len / 16 + 64 + 3);
if(lzo1x_1_compress(tap_pkt, len, compression_buffer, (lzo_uint*)&compression_len, wrkmem) == LZO_E_OK) {
if(compression_len < len) {
pkt.compression = N2N_COMPRESSION_ID_LZO;
@@ -2531,7 +2241,7 @@ void edge_send_packet2net (n2n_edge_t * eee,
#ifdef N2N_HAVE_ZSTD
case N2N_COMPRESSION_ID_ZSTD:
compression_len = N2N_PKT_BUF_SIZE + 128;
- compression_buffer = malloc (compression_len); // leaves enough room, for exact size call compression_len = ZSTD_compressBound (len); (slower)
+ compression_buffer = malloc(compression_len); // leaves enough room, for exact size call compression_len = ZSTD_compressBound (len); (slower)
compression_len = (int32_t)ZSTD_compress(compression_buffer, compression_len, tap_pkt, len, ZSTD_COMPRESSION_LEVEL);
if(!ZSTD_isError(compression_len)) {
if(compression_len < len) {
@@ -2767,7 +2477,7 @@ void process_udp (n2n_edge_t *eee, const struct sockaddr_in *sender_sock, const
from_supernode = cmn.flags & N2N_FLAGS_FROM_SUPERNODE;
if(from_supernode) {
skip_add = SN_ADD_SKIP;
- sn = add_sn_to_list_by_mac_or_sock (&(eee->conf.supernodes), &sender, null_mac, &skip_add);
+ sn = add_sn_to_list_by_mac_or_sock(&(eee->conf.supernodes), &sender, null_mac, &skip_add);
if(!sn) {
traceEvent(TRACE_DEBUG, "dropped incoming data from unknown supernode");
return;
@@ -3300,7 +3010,7 @@ void print_edge_stats (const n2n_edge_t *eee) {
/* ************************************** */
-int run_edge_loop (n2n_edge_t *eee, int *keep_running) {
+int run_edge_loop (n2n_edge_t *eee) {
size_t numPurged;
time_t lastIfaceCheck = 0;
@@ -3315,11 +3025,10 @@ int run_edge_loop (n2n_edge_t *eee, int *keep_running) {
#ifdef WIN32
struct tunread_arg arg;
arg.eee = eee;
- arg.keep_running = keep_running;
HANDLE tun_read_thread = startTunReadThread(&arg);
#endif
- *keep_running = 1;
+ *eee->keep_running = 1;
update_supernode_reg(eee, time(NULL));
/* Main loop
@@ -3329,7 +3038,7 @@ int run_edge_loop (n2n_edge_t *eee, int *keep_running) {
* readFromIPSocket() or edge_read_from_tap()
*/
- while(*keep_running) {
+ while(*eee->keep_running) {
int rc, max_sock = 0;
fd_set socket_mask;
@@ -3375,10 +3084,10 @@ int run_edge_loop (n2n_edge_t *eee, int *keep_running) {
// external
if(FD_ISSET(eee->sock, &socket_mask)) {
- if (0 != fetch_and_eventually_process_data(eee, eee->sock,
- pktbuf, &expected, &position,
- now)) {
- *keep_running = 0;
+ if(0 != fetch_and_eventually_process_data(eee, eee->sock,
+ pktbuf, &expected, &position,
+ now)) {
+ *eee->keep_running = 0;
break;
}
if(eee->conf.connect_tcp) {
@@ -3396,10 +3105,10 @@ int run_edge_loop (n2n_edge_t *eee, int *keep_running) {
#ifndef SKIP_MULTICAST_PEERS_DISCOVERY
if(FD_ISSET(eee->udp_multicast_sock, &socket_mask)) {
- if (0 != fetch_and_eventually_process_data (eee, eee->udp_multicast_sock,
- pktbuf, &expected, &position,
- now)) {
- *keep_running = 0;
+ if(0 != fetch_and_eventually_process_data(eee, eee->udp_multicast_sock,
+ pktbuf, &expected, &position,
+ now)) {
+ *eee->keep_running = 0;
break;
}
}
@@ -3407,9 +3116,9 @@ int run_edge_loop (n2n_edge_t *eee, int *keep_running) {
if(FD_ISSET(eee->udp_mgmt_sock, &socket_mask)) {
// read from the management port socket
- readFromMgmtSocket(eee, keep_running);
+ readFromMgmtSocket(eee);
- if(!(*keep_running))
+ if(!(*eee->keep_running))
break;
}
@@ -3556,7 +3265,7 @@ static int edge_init_sockets (n2n_edge_t *eee) {
#ifdef __linux__
-static uint32_t get_gateway_ip() {
+static uint32_t get_gateway_ip () {
FILE *fd;
char *token = NULL;
@@ -3780,7 +3489,7 @@ static int routectl (int cmd, int flags, n2n_route_t *route, int if_idx) {
traceEvent(TRACE_DEBUG, route_cmd_to_str(cmd, route, route_buf, sizeof(route_buf)));
rv = 0;
- out:
+out:
close(nl_sock);
return(rv);
@@ -4096,11 +3805,12 @@ int quick_edge_init (char *device_name, char *community_name,
if((eee = edge_init(&conf, &rv)) == NULL)
goto quick_edge_init_end;
- rv = run_edge_loop(eee, keep_on_running);
+ eee->keep_running = keep_on_running;
+ rv = run_edge_loop(eee);
edge_term(eee);
edge_term_conf(&conf);
- quick_edge_init_end:
+quick_edge_init_end:
tuntap_close(&tuntap);
return(rv);
}
diff --git a/src/edge_utils_win32.c b/src/edge_utils_win32.c
index 52361aa..452a897 100644
--- a/src/edge_utils_win32.c
+++ b/src/edge_utils_win32.c
@@ -26,7 +26,7 @@ static DWORD* tunReadThread (LPVOID lpArg) {
struct tunread_arg *arg = (struct tunread_arg*)lpArg;
- while(*arg->keep_running) {
+ while(*arg->eee->keep_running) {
edge_read_from_tap(arg->eee);
}
diff --git a/src/example_edge_embed.c b/src/example_edge_embed.c
index f6dfde8..3c926ce 100644
--- a/src/example_edge_embed.c
+++ b/src/example_edge_embed.c
@@ -68,7 +68,8 @@ int main() {
}
keep_running = 1;
- rc = run_edge_loop(eee, &keep_running);
+ eee->keep_running = &keep_running;
+ rc = run_edge_loop(eee);
edge_term(eee);
tuntap_close(&tuntap);
diff --git a/src/example_sn_embed.c b/src/example_sn_embed.c
index a714f98..0f5b679 100644
--- a/src/example_sn_embed.c
+++ b/src/example_sn_embed.c
@@ -42,7 +42,8 @@ int main () {
sn_init(&sss_node);
keep_running = 1;
- rc = run_sn_loop(&sss_node, &keep_running);
+ sss_node.keep_running = &keep_running;
+ rc = run_sn_loop(&sss_node);
sn_term(&sss_node);
diff --git a/src/sn_management.c b/src/sn_management.c
new file mode 100644
index 0000000..bde2daf
--- /dev/null
+++ b/src/sn_management.c
@@ -0,0 +1,439 @@
+/**
+ * (C) 2007-21 - 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
+ *
+ */
+
+/*
+ * This file has a large amount of duplication with the edge_management.c
+ * code. In the fullness of time, they should both be merged
+ */
+
+#include "n2n.h"
+#include "edge_utils_win32.h"
+
+int load_allowed_sn_community (n2n_sn_t *sss); /* defined in sn_utils.c */
+
+#define FLAG_WROK 1
+typedef struct n2n_mgmt_handler {
+ int flags;
+ char *cmd;
+ char *help;
+ void (*func)(n2n_sn_t *sss, char *udp_buf, struct sockaddr_in sender_sock, enum n2n_mgmt_type type, char *tag, char *argv0, char *argv);
+} n2n_mgmt_handler_t;
+
+static void mgmt_error (n2n_sn_t *sss, char *udp_buf, const struct sockaddr_in sender_sock, char *tag, char *msg) {
+ size_t msg_len;
+ msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE,
+ "{"
+ "\"_tag\":\"%s\","
+ "\"_type\":\"error\","
+ "\"error\":\"%s\"}\n",
+ tag,
+ msg);
+ sendto(sss->mgmt_sock, udp_buf, msg_len, 0,
+ (struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
+}
+
+static void mgmt_stop (n2n_sn_t *sss, char *udp_buf, const struct sockaddr_in sender_sock, enum n2n_mgmt_type type, char *tag, char *argv0, char *argv) {
+ size_t msg_len;
+
+ if(type==N2N_MGMT_WRITE) {
+ *sss->keep_running = 0;
+ }
+
+ msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE,
+ "{"
+ "\"_tag\":\"%s\","
+ "\"_type\":\"row\","
+ "\"keep_running\":%u}\n",
+ tag,
+ *sss->keep_running);
+
+ sendto(sss->mgmt_sock, udp_buf, msg_len, 0,
+ (struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
+}
+
+static void mgmt_verbose (n2n_sn_t *sss, char *udp_buf, const struct sockaddr_in sender_sock, enum n2n_mgmt_type type, char *tag, char *argv0, char *argv) {
+ size_t msg_len;
+
+ if(type==N2N_MGMT_WRITE) {
+ if(argv) {
+ setTraceLevel(strtoul(argv, NULL, 0));
+ }
+ }
+
+ msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE,
+ "{"
+ "\"_tag\":\"%s\","
+ "\"_type\":\"row\","
+ "\"traceLevel\":%u}\n",
+ tag,
+ getTraceLevel());
+
+ sendto(sss->mgmt_sock, udp_buf, msg_len, 0,
+ (struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
+}
+
+static void mgmt_reload_communities (n2n_sn_t *sss, char *udp_buf, const struct sockaddr_in sender_sock, enum n2n_mgmt_type type, char *tag, char *argv0, char *argv) {
+ size_t msg_len;
+
+ if(type!=N2N_MGMT_WRITE) {
+ mgmt_error(sss, udp_buf, sender_sock, tag, "writeonly");
+ return;
+ }
+
+ if(!sss->community_file) {
+ mgmt_error(sss, udp_buf, sender_sock, tag, "nofile");
+ return;
+ }
+
+ int ok = load_allowed_sn_community(sss);
+
+ msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE,
+ "{"
+ "\"_tag\":\"%s\","
+ "\"_type\":\"row\","
+ "\"ok\":%i}\n",
+ tag,
+ ok);
+
+ sendto(sss->mgmt_sock, udp_buf, msg_len, 0,
+ (struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
+}
+
+static void mgmt_timestamps (n2n_sn_t *sss, char *udp_buf, const struct sockaddr_in sender_sock, enum n2n_mgmt_type type, char *tag, char *argv0, char *argv) {
+ size_t msg_len;
+
+ msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE,
+ "{"
+ "\"_tag\":\"%s\","
+ "\"_type\":\"row\","
+ "\"start_time\":%lu,"
+ "\"last_fwd\":%ld,"
+ "\"last_reg_super\":%ld}\n",
+ tag,
+ sss->start_time,
+ sss->stats.last_fwd,
+ sss->stats.last_reg_super);
+
+ sendto(sss->mgmt_sock, udp_buf, msg_len, 0,
+ (struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
+}
+
+static void mgmt_packetstats (n2n_sn_t *sss, char *udp_buf, const struct sockaddr_in sender_sock, enum n2n_mgmt_type type, char *tag, char *argv0, char *argv) {
+ size_t msg_len;
+
+ msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE,
+ "{"
+ "\"_tag\":\"%s\","
+ "\"_type\":\"row\","
+ "\"type\":\"forward\","
+ "\"tx_pkt\":%lu}\n",
+ tag,
+ sss->stats.fwd);
+
+ sendto(sss->mgmt_sock, udp_buf, msg_len, 0,
+ (struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
+
+ msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE,
+ "{"
+ "\"_tag\":\"%s\","
+ "\"_type\":\"row\","
+ "\"type\":\"broadcast\","
+ "\"tx_pkt\":%lu}\n",
+ tag,
+ sss->stats.broadcast);
+
+ sendto(sss->mgmt_sock, udp_buf, msg_len, 0,
+ (struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
+
+ msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE,
+ "{"
+ "\"_tag\":\"%s\","
+ "\"_type\":\"row\","
+ "\"type\":\"reg_super\","
+ "\"rx_pkt\":%lu,"
+ "\"nak\":%lu}\n",
+ tag,
+ sss->stats.reg_super,
+ sss->stats.reg_super_nak);
+
+ /* Note: reg_super_nak is not currently incremented anywhere */
+
+ sendto(sss->mgmt_sock, udp_buf, msg_len, 0,
+ (struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
+
+ /* Generic errors when trying to sendto() */
+ msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE,
+ "{"
+ "\"_tag\":\"%s\","
+ "\"_type\":\"row\","
+ "\"type\":\"errors\","
+ "\"tx_pkt\":%lu}\n",
+ tag,
+ sss->stats.errors);
+
+ sendto(sss->mgmt_sock, udp_buf, msg_len, 0,
+ (struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
+
+}
+
+static void mgmt_communities (n2n_sn_t *sss, char *udp_buf, const struct sockaddr_in sender_sock, enum n2n_mgmt_type type, char *tag, char *argv0, char *argv) {
+ size_t msg_len;
+ struct sn_community *community, *tmp;
+ dec_ip_bit_str_t ip_bit_str = {'\0'};
+
+ HASH_ITER(hh, sss->communities, community, tmp) {
+
+ msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE,
+ "{"
+ "\"_tag\":\"%s\","
+ "\"_type\":\"row\","
+ "\"community\":\"%s\","
+ "\"purgeable\":%i,"
+ "\"is_federation\":%i,"
+ "\"ip4addr\":\"%s\"}\n",
+ tag,
+ (community->is_federation) ? "-/-" : community->community,
+ community->purgeable,
+ community->is_federation,
+ (community->auto_ip_net.net_addr == 0) ? "" : ip_subnet_to_str(ip_bit_str, &community->auto_ip_net));
+
+
+ sendto(sss->mgmt_sock, udp_buf, msg_len, 0,
+ (struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
+ }
+}
+
+static void mgmt_edges (n2n_sn_t *sss, char *udp_buf, const struct sockaddr_in sender_sock, enum n2n_mgmt_type type, char *tag, char *argv0, char *argv) {
+ size_t msg_len;
+ struct sn_community *community, *tmp;
+ struct peer_info *peer, *tmpPeer;
+ macstr_t mac_buf;
+ n2n_sock_str_t sockbuf;
+ dec_ip_bit_str_t ip_bit_str = {'\0'};
+
+ HASH_ITER(hh, sss->communities, community, tmp) {
+ HASH_ITER(hh, community->edges, peer, tmpPeer) {
+
+ msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE,
+ "{"
+ "\"_tag\":\"%s\","
+ "\"_type\":\"row\","
+ "\"community\":\"%s\","
+ "\"ip4addr\":\"%s\","
+ "\"purgeable\":%i,"
+ "\"macaddr\":\"%s\","
+ "\"sockaddr\":\"%s\","
+ "\"proto\":\"%s\","
+ "\"desc\":\"%s\","
+ "\"last_seen\":%li}\n",
+ tag,
+ (community->is_federation) ? "-/-" : community->community,
+ (peer->dev_addr.net_addr == 0) ? "" : ip_subnet_to_str(ip_bit_str, &peer->dev_addr),
+ peer->purgeable,
+ (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" : "UDP",
+ peer->dev_desc,
+ peer->last_seen);
+
+ sendto(sss->mgmt_sock, udp_buf, msg_len, 0,
+ (struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
+ }
+ }
+}
+
+static void mgmt_unimplemented (n2n_sn_t *sss, char *udp_buf, const struct sockaddr_in sender_sock, enum n2n_mgmt_type type, char *tag, char *argv0, char *argv) {
+ mgmt_error(sss, udp_buf, sender_sock, tag, "unimplemented");
+}
+
+static void mgmt_help (n2n_sn_t *sss, char *udp_buf, const struct sockaddr_in sender_sock, enum n2n_mgmt_type type, char *tag, char *argv0, char *argv);
+
+n2n_mgmt_handler_t mgmt_handlers[] = {
+ { .cmd = "supernodes", .help = "Reserved for edge", .func = mgmt_unimplemented},
+
+ { .cmd = "stop", .flags = FLAG_WROK, .help = "Gracefully exit edge", .func = mgmt_stop},
+ { .cmd = "verbose", .flags = FLAG_WROK, .help = "Manage verbosity level", .func = mgmt_verbose},
+ { .cmd = "reload_communities", .flags = FLAG_WROK, .help = "Reloads communities and user's public keys", .func = mgmt_reload_communities},
+ { .cmd = "communities", .help = "List current communities", .func = mgmt_communities},
+ { .cmd = "edges", .help = "List current edges/peers", .func = mgmt_edges},
+ { .cmd = "timestamps", .help = "Event timestamps", .func = mgmt_timestamps},
+ { .cmd = "packetstats", .help = "Traffic statistics", .func = mgmt_packetstats},
+ { .cmd = "help", .flags = FLAG_WROK, .help = "Show JSON commands", .func = mgmt_help},
+ { .cmd = NULL },
+};
+
+static void mgmt_help (n2n_sn_t *sss, char *udp_buf, const struct sockaddr_in sender_sock, enum n2n_mgmt_type type, char *tag, char *argv0, char *argv) {
+ size_t msg_len;
+ n2n_mgmt_handler_t *handler;
+
+ /*
+ * Even though this command is readonly, we deliberately do not check
+ * the type - allowing help replies to both read and write requests
+ */
+
+ for( handler=mgmt_handlers; handler->cmd; handler++ ) {
+ msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE,
+ "{"
+ "\"_tag\":\"%s\","
+ "\"_type\":\"row\","
+ "\"cmd\":\"%s\","
+ "\"help\":\"%s\"}\n",
+ tag,
+ handler->cmd,
+ handler->help);
+
+ sendto(sss->mgmt_sock, udp_buf, msg_len, 0,
+ (struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
+ }
+}
+
+/*
+ * Check if the user is authorised for this command.
+ * - this should be more configurable!
+ * - for the moment we use some simple heuristics:
+ * Reads are not dangerous, so they are simply allowed
+ * Writes are possibly dangerous, so they need a fake password
+ */
+static int mgmt_auth (const struct sockaddr_in sender_sock, enum n2n_mgmt_type type, char *auth, char *argv0, char *argv) {
+ if(auth) {
+ /* If we have an auth key, it must match */
+ if(0 == strcmp(auth,"CHANGEME")) {
+ return 1;
+ }
+ return 0;
+ }
+ /* if we dont have an auth key, we can still read */
+ if(type==N2N_MGMT_READ) {
+ return 1;
+ }
+ return 0;
+}
+
+void handleMgmtJson_sn (n2n_sn_t *sss, char *udp_buf, const struct sockaddr_in sender_sock) {
+
+ char cmdlinebuf[80];
+ enum n2n_mgmt_type type;
+ char *typechar;
+ char *options;
+ char *argv0;
+ char *argv;
+ char *tag;
+ char *flagstr;
+ int flags;
+ char *auth;
+ n2n_mgmt_handler_t *handler;
+ size_t msg_len;
+
+ /* save a copy of the commandline before we reuse the udp_buf */
+ strncpy(cmdlinebuf, udp_buf, sizeof(cmdlinebuf)-1);
+ cmdlinebuf[sizeof(cmdlinebuf)-1] = 0;
+
+ traceEvent(TRACE_DEBUG, "mgmt json %s", cmdlinebuf);
+
+ typechar = strtok(cmdlinebuf, " \r\n");
+ if(!typechar) {
+ /* should not happen */
+ mgmt_error(sss, udp_buf, sender_sock, "-1", "notype");
+ return;
+ }
+ if(*typechar == 'r') {
+ type=N2N_MGMT_READ;
+ } else if(*typechar == 'w') {
+ type=N2N_MGMT_WRITE;
+ } else {
+ /* dunno how we got here */
+ mgmt_error(sss, udp_buf, sender_sock, "-1", "badtype");
+ return;
+ }
+
+ /* Extract the tag to use in all reply packets */
+ options = strtok(NULL, " \r\n");
+ if(!options) {
+ mgmt_error(sss, udp_buf, sender_sock, "-1", "nooptions");
+ return;
+ }
+
+ argv0 = strtok(NULL, " \r\n");
+ if(!argv0) {
+ mgmt_error(sss, udp_buf, sender_sock, "-1", "nocmd");
+ return;
+ }
+
+ /*
+ * The entire rest of the line is the argv. We apply no processing
+ * or arg separation so that the cmd can use it however it needs.
+ */
+ argv = strtok(NULL, "\r\n");
+
+ /*
+ * There might be an auth token mixed in with the tag
+ */
+ tag = strtok(options, ":");
+ flagstr = strtok(NULL, ":");
+ if(flagstr) {
+ flags = strtoul(flagstr, NULL, 16);
+ } else {
+ flags = 0;
+ }
+
+ /* Only 1 flag bit defined at the moment - "auth option present" */
+ if(flags & 1) {
+ auth = strtok(NULL, ":");
+ } else {
+ auth = NULL;
+ }
+
+ if(!mgmt_auth(sender_sock, type, auth, argv0, argv)) {
+ mgmt_error(sss, udp_buf, sender_sock, tag, "badauth");
+ return;
+ }
+
+ for( handler=mgmt_handlers; handler->cmd; handler++ ) {
+ if(0 == strcmp(handler->cmd, argv0)) {
+ break;
+ }
+ }
+ if(!handler->cmd) {
+ mgmt_error(sss, udp_buf, sender_sock, tag, "unknowncmd");
+ return;
+ }
+
+ if((type==N2N_MGMT_WRITE) && !(handler->flags & FLAG_WROK)) {
+ mgmt_error(sss, udp_buf, sender_sock, tag, "readonly");
+ return;
+ }
+
+ /*
+ * TODO:
+ * The tag provided by the requester could contain chars
+ * that make our JSON invalid.
+ * - do we care?
+ */
+ msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE,
+ "{\"_tag\":\"%s\",\"_type\":\"begin\",\"cmd\":\"%s\"}\n", tag, argv0);
+ sendto(sss->mgmt_sock, udp_buf, msg_len, 0,
+ (struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
+
+ handler->func(sss, udp_buf, sender_sock, type, tag, argv0, argv);
+
+ msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE,
+ "{\"_tag\":\"%s\",\"_type\":\"end\"}\n", tag);
+ sendto(sss->mgmt_sock, udp_buf, msg_len, 0,
+ (struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
+ return;
+}
diff --git a/src/sn_utils.c b/src/sn_utils.c
index 984423a..2bb65d6 100644
--- a/src/sn_utils.c
+++ b/src/sn_utils.c
@@ -63,7 +63,7 @@ static int sort_communities (n2n_sn_t *sss,
static int process_mgmt (n2n_sn_t *sss,
const struct sockaddr_in *sender_sock,
- const uint8_t *mgmt_buf,
+ char *mgmt_buf,
size_t mgmt_size,
time_t now);
@@ -1493,7 +1493,7 @@ static int sort_communities (n2n_sn_t *sss,
static int process_mgmt (n2n_sn_t *sss,
const struct sockaddr_in *sender_sock,
- const uint8_t *mgmt_buf,
+ char *mgmt_buf,
size_t mgmt_size,
time_t now) {
@@ -1511,6 +1511,9 @@ static int process_mgmt (n2n_sn_t *sss,
traceEvent(TRACE_DEBUG, "process_mgmt");
+ /* avoid parsing any uninitialized junk from the stack */
+ mgmt_buf[mgmt_size] = 0;
+
// process input, if any
if((0 == memcmp(mgmt_buf, "help", 4)) || (0 == memcmp(mgmt_buf, "?", 1))) {
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize,
@@ -1543,6 +1546,12 @@ static int process_mgmt (n2n_sn_t *sss,
return 0; /* no status output afterwards */
}
+ if((mgmt_buf[0] == 'r' || mgmt_buf[0] == 'w') && (mgmt_buf[1] == ' ')) {
+ /* this is a JSON request */
+ handleMgmtJson_sn(sss, mgmt_buf, *sender_sock);
+ return 0;
+ }
+
// output current status
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize,
@@ -2690,7 +2699,7 @@ static int process_udp (n2n_sn_t * sss,
/** Long lived processing entry point. Split out from main to simply
* daemonisation on some platforms. */
-int run_sn_loop (n2n_sn_t *sss, int *keep_running) {
+int run_sn_loop (n2n_sn_t *sss) {
uint8_t pktbuf[N2N_SN_PKTBUF_SIZE];
time_t last_purge_edges = 0;
@@ -2699,7 +2708,7 @@ int run_sn_loop (n2n_sn_t *sss, int *keep_running) {
sss->start_time = time(NULL);
- while(*keep_running) {
+ while(*sss->keep_running) {
int rc;
ssize_t bread;
int max_sock;
@@ -2764,7 +2773,7 @@ int run_sn_loop (n2n_sn_t *sss, int *keep_running) {
#ifdef WIN32
traceEvent(TRACE_ERROR, "WSAGetLastError(): %u", WSAGetLastError());
#endif
- *keep_running = 0;
+ *sss->keep_running = 0;
break;
}
@@ -2880,16 +2889,16 @@ int run_sn_loop (n2n_sn_t *sss, int *keep_running) {
if(bread <= 0) {
traceEvent(TRACE_ERROR, "recvfrom() failed %d errno %d (%s)", bread, errno, strerror(errno));
- *keep_running = 0;
+ *sss->keep_running = 0;
break;
}
// we have a datagram to process
- process_mgmt(sss, &sender_sock, pktbuf, bread, now);
+ process_mgmt(sss, &sender_sock, (char *)pktbuf, bread, now);
}
} else {
- if(((now - before) < wait_time.tv_sec) && (*keep_running)){
+ if(((now - before) < wait_time.tv_sec) && (*sss->keep_running)){
// this is no real timeout, something went wrong with one of the tcp connections (probably)
// close them all, edges will re-open if they detect closure
traceEvent(TRACE_DEBUG, "falsly claimed timeout, assuming issue with tcp connection, closing them all");
diff --git a/src/supernode.c b/src/supernode.c
index c6df9fd..31158ce 100644
--- a/src/supernode.c
+++ b/src/supernode.c
@@ -658,5 +658,6 @@ int main (int argc, char * const argv[]) {
#endif
keep_running = 1;
- return run_sn_loop(&sss_node, &keep_running);
+ sss_node.keep_running = &keep_running;
+ return run_sn_loop(&sss_node);
}