Merge pull request #983 from hamishcoleman/json_pubsub

Refactoring management code to increase sharing
This commit is contained in:
Hamish Coleman 2022-04-30 18:21:36 +01:00 committed by GitHub
commit 3fa0fa1bbe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 570 additions and 615 deletions

View File

@ -190,6 +190,7 @@ add_library(n2n STATIC
src/aes.c src/aes.c
src/speck.c src/speck.c
src/random_numbers.c src/random_numbers.c
src/management.c
src/pearson.c src/pearson.c
src/header_encryption.c src/header_encryption.c
src/tuntap_freebsd.c src/tuntap_freebsd.c

View File

@ -100,8 +100,11 @@ LINT_CCODE=\
src/edge_utils_win32.c \ src/edge_utils_win32.c \
src/example_edge_embed_quick_edge_init.c \ src/example_edge_embed_quick_edge_init.c \
src/header_encryption.c \ src/header_encryption.c \
src/management.c \
src/management.h \
src/sn_management.c \ src/sn_management.c \
src/sn_selection.c \ src/sn_selection.c \
src/strbuf.h \
src/transform_cc20.c \ src/transform_cc20.c \
src/transform_null.c \ src/transform_null.c \
src/tuntap_freebsd.c \ src/tuntap_freebsd.c \

View File

@ -1,6 +1,8 @@
# Management API # Management API
This document is focused on the machine readable API interfaces. This document is focused on the machine readable API interfaces. It is a
generic document describing the protocol framing, but not the specific
messages and replies.
Both the edge and the supernode provide a management interface UDP port. Both the edge and the supernode provide a management interface UDP port.
These interfaces have some documentation on their non machine readable These interfaces have some documentation on their non machine readable
@ -30,8 +32,7 @@ where this flexibility is easy to provide.
Since the API is over UDP, the replies are structured so that each part of Since the API is over UDP, the replies are structured so that each part of
the reply is clearly tagged as belonging to one request and there is a the reply is clearly tagged as belonging to one request and there is a
clear begin and end marker for the reply. This is expected to support the clear begin and end marker for the reply. This is expected to support the
future possibilities of pipelined and overlapping transactions as well as future possibilities of pipelined and overlapping transactions.
pub/sub asynchronous event channels.
The replies will also handle some small amount of re-ordering of the The replies will also handle some small amount of re-ordering of the
packets, but that is not an specific goal of the protocol. packets, but that is not an specific goal of the protocol.
@ -105,6 +106,11 @@ on a given socket.
One possible client implementation is a number between 0 and 999, incremented One possible client implementation is a number between 0 and 999, incremented
for each request and wrapping around to zero when it is greater than 999. for each request and wrapping around to zero when it is greater than 999.
The client is also expected to use the tag to determine the format of the
data in the reply - since the client requested that data, it also knows what
should be in the matching reply. This is especially important with
asynchronous pub/sub events.
#### Message Flags #### Message Flags
This subfield is a set of bit flags that are hex-encoded and describe any This subfield is a set of bit flags that are hex-encoded and describe any

View File

@ -16,6 +16,9 @@
* *
*/ */
#ifndef _N2N_DEFINE_H_
#define _N2N_DEFINE_H_
/* N2N packet header indicators. */ /* N2N packet header indicators. */
#define MSG_TYPE_REGISTER 1 #define MSG_TYPE_REGISTER 1
#define MSG_TYPE_DEREGISTER 2 #define MSG_TYPE_DEREGISTER 2
@ -219,3 +222,5 @@ enum skip_add {SN_ADD = 0, SN_ADD_SKIP = 1, SN_ADD_ADDED = 2};
#ifndef min #ifndef min
#define min(a, b) (((a) >(b)) ? (b) : (a)) #define min(a, b) (((a) >(b)) ? (b) : (a))
#endif #endif
#endif

View File

@ -19,6 +19,12 @@
#ifndef _N2N_TYPEDEFS_H_ #ifndef _N2N_TYPEDEFS_H_
#define _N2N_TYPEDEFS_H_ #define _N2N_TYPEDEFS_H_
#include <stdint.h> // for uint8_t and friends
#ifndef WIN32
#include <arpa/inet.h> // for in_addr_t
#endif
#include <uthash.h>
#include <n2n_define.h>
typedef uint8_t n2n_community_t[N2N_COMMUNITY_SIZE]; typedef uint8_t n2n_community_t[N2N_COMMUNITY_SIZE];
typedef uint8_t n2n_private_public_key_t[N2N_PRIVATE_PUBLIC_KEY_SIZE]; typedef uint8_t n2n_private_public_key_t[N2N_PRIVATE_PUBLIC_KEY_SIZE];
@ -38,11 +44,6 @@ typedef unsigned int uint32_t;
typedef unsigned short uint16_t; typedef unsigned short uint16_t;
typedef unsigned char uint8_t; typedef unsigned char uint8_t;
/* sys/types.h typedefs (not present in Visual Studio) */
typedef unsigned int u_int32_t;
typedef unsigned short u_int16_t;
typedef unsigned char u_int8_t;
#ifndef __MINGW32__ #ifndef __MINGW32__
typedef int ssize_t; typedef int ssize_t;
#endif #endif
@ -139,45 +140,45 @@ typedef struct ether_hdr ether_hdr_t;
struct n2n_iphdr { struct n2n_iphdr {
#if defined(__LITTLE_ENDIAN__) #if defined(__LITTLE_ENDIAN__)
u_int8_t ihl:4, version:4; uint8_t ihl:4, version:4;
#elif defined(__BIG_ENDIAN__) #elif defined(__BIG_ENDIAN__)
u_int8_t version:4, ihl:4; uint8_t version:4, ihl:4;
#else #else
# error "Byte order must be defined" # error "Byte order must be defined"
#endif #endif
u_int8_t tos; uint8_t tos;
u_int16_t tot_len; uint16_t tot_len;
u_int16_t id; uint16_t id;
u_int16_t frag_off; uint16_t frag_off;
u_int8_t ttl; uint8_t ttl;
u_int8_t protocol; uint8_t protocol;
u_int16_t check; uint16_t check;
u_int32_t saddr; uint32_t saddr;
u_int32_t daddr; uint32_t daddr;
} PACK_STRUCT; } PACK_STRUCT;
struct n2n_tcphdr { struct n2n_tcphdr {
u_int16_t source; uint16_t source;
u_int16_t dest; uint16_t dest;
u_int32_t seq; uint32_t seq;
u_int32_t ack_seq; uint32_t ack_seq;
#if defined(__LITTLE_ENDIAN__) #if defined(__LITTLE_ENDIAN__)
u_int16_t res1:4, doff:4, fin:1, syn:1, rst:1, psh:1, ack:1, urg:1, ece:1, cwr:1; uint16_t res1:4, doff:4, fin:1, syn:1, rst:1, psh:1, ack:1, urg:1, ece:1, cwr:1;
#elif defined(__BIG_ENDIAN__) #elif defined(__BIG_ENDIAN__)
u_int16_t doff:4, res1:4, cwr:1, ece:1, urg:1, ack:1, psh:1, rst:1, syn:1, fin:1; uint16_t doff:4, res1:4, cwr:1, ece:1, urg:1, ack:1, psh:1, rst:1, syn:1, fin:1;
#else #else
# error "Byte order must be defined" # error "Byte order must be defined"
#endif #endif
u_int16_t window; uint16_t window;
u_int16_t check; uint16_t check;
u_int16_t urg_ptr; uint16_t urg_ptr;
} PACK_STRUCT; } PACK_STRUCT;
struct n2n_udphdr { struct n2n_udphdr {
u_int16_t source; uint16_t source;
u_int16_t dest; uint16_t dest;
u_int16_t len; uint16_t len;
u_int16_t check; uint16_t check;
} PACK_STRUCT; } PACK_STRUCT;
#if defined(_MSC_VER) || defined(__MINGW32__) #if defined(_MSC_VER) || defined(__MINGW32__)

View File

@ -19,106 +19,8 @@
#include "n2n.h" #include "n2n.h"
#include "edge_utils_win32.h" #include "edge_utils_win32.h"
typedef struct strbuf { #include "strbuf.h"
size_t size; #include "management.h"
char str[];
} strbuf_t;
#define STRBUF_INIT(buf,p) do { \
buf = (void *)p; \
buf->size = sizeof(*p) - sizeof(size_t); \
} while(0)
enum n2n_mgmt_type {
N2N_MGMT_UNKNOWN = 0,
N2N_MGMT_READ = 1,
N2N_MGMT_WRITE = 2,
N2N_MGMT_SUB = 3,
};
/*
* Everything needed to reply to a request
*/
typedef struct mgmt_req {
n2n_edge_t *eee;
enum n2n_mgmt_type type;
char tag[10];
struct sockaddr_in sender_sock;
} mgmt_req_t;
/*
* Read/Write handlers are defined in this structure
*/
#define FLAG_WROK 1
typedef struct mgmt_handler {
int flags;
char *cmd;
char *help;
void (*func)(mgmt_req_t *req, strbuf_t *buf, char *argv0, char *argv);
} mgmt_handler_t;
/*
* Event topic names are defined in this structure
*/
typedef struct mgmt_events {
enum n2n_event_topic topic;
char *cmd;
char *help;
} mgmt_events_t;
// Lookup the index of matching argv0 in a cmd list
// store index in "Result", or -1 for not found
#define lookup_handler(Result, list, argv0) do { \
int nr_max = sizeof(list) / sizeof(list[0]); \
for( Result=0; Result < nr_max; Result++ ) { \
if(0 == strcmp(list[Result].cmd, argv0)) { \
break; \
} \
} \
if( Result >= nr_max ) { \
Result = -1; \
} \
} while(0)
ssize_t send_reply (mgmt_req_t *req, strbuf_t *buf, size_t msg_len) {
// TODO: better error handling (counters?)
return sendto(req->eee->udp_mgmt_sock, buf->str, msg_len, 0,
(struct sockaddr *) &req->sender_sock, sizeof(struct sockaddr_in));
}
size_t gen_json_1str (strbuf_t *buf, char *tag, char *_type, char *key, char *val) {
return snprintf(buf->str, buf->size,
"{"
"\"_tag\":\"%s\","
"\"_type\":\"%s\","
"\"%s\":\"%s\"}\n",
tag,
_type,
key,
val);
}
size_t gen_json_1uint (strbuf_t *buf, char *tag, char *_type, char *key, unsigned int val) {
return snprintf(buf->str, buf->size,
"{"
"\"_tag\":\"%s\","
"\"_type\":\"%s\","
"\"%s\":%u}\n",
tag,
_type,
key,
val);
}
static void send_json_1str (mgmt_req_t *req, strbuf_t *buf, char *_type, char *key, char *val) {
size_t msg_len = gen_json_1str(buf, req->tag, _type, key, val);
send_reply(req, buf, msg_len);
}
static void send_json_1uint (mgmt_req_t *req, strbuf_t *buf, char *_type, char *key, unsigned int val) {
size_t msg_len = gen_json_1uint(buf, req->tag, _type, key, val);
send_reply(req, buf, msg_len);
}
size_t event_debug (strbuf_t *buf, char *tag, int data0, void *data1) { size_t event_debug (strbuf_t *buf, char *tag, int data0, void *data1) {
traceEvent(TRACE_DEBUG, "Unexpected call to event_debug"); traceEvent(TRACE_DEBUG, "Unexpected call to event_debug");
@ -155,31 +57,9 @@ size_t event_peer (strbuf_t *buf, char *tag, int data0, void *data1) {
sock_to_cstr(sockbuf, &(peer->sock))); sock_to_cstr(sockbuf, &(peer->sock)));
} }
static void mgmt_error (mgmt_req_t *req, strbuf_t *buf, char *msg) {
send_json_1str(req, buf, "error", "error", msg);
}
static void mgmt_stop (mgmt_req_t *req, strbuf_t *buf, char *argv0, char *argv) {
if(req->type==N2N_MGMT_WRITE) { static void mgmt_communities (mgmt_req_t *req, strbuf_t *buf) {
*req->eee->keep_running = 0;
}
send_json_1uint(req, buf, "row", "keep_running", *req->eee->keep_running);
}
static void mgmt_verbose (mgmt_req_t *req, strbuf_t *buf, char *argv0, char *argv) {
if(req->type==N2N_MGMT_WRITE) {
if(argv) {
setTraceLevel(strtoul(argv, NULL, 0));
}
}
send_json_1uint(req, buf, "row", "traceLevel", getTraceLevel());
}
static void mgmt_communities (mgmt_req_t *req, strbuf_t *buf, char *argv0, char *argv) {
if(req->eee->conf.header_encryption != HEADER_ENCRYPTION_NONE) { if(req->eee->conf.header_encryption != HEADER_ENCRYPTION_NONE) {
mgmt_error(req, buf, "noaccess"); mgmt_error(req, buf, "noaccess");
@ -189,7 +69,7 @@ static void mgmt_communities (mgmt_req_t *req, strbuf_t *buf, char *argv0, char
send_json_1str(req, buf, "row", "community", (char *)req->eee->conf.community_name); send_json_1str(req, buf, "row", "community", (char *)req->eee->conf.community_name);
} }
static void mgmt_supernodes (mgmt_req_t *req, strbuf_t *buf, char *argv0, char *argv) { static void mgmt_supernodes (mgmt_req_t *req, strbuf_t *buf) {
size_t msg_len; size_t msg_len;
struct peer_info *peer, *tmpPeer; struct peer_info *peer, *tmpPeer;
macstr_t mac_buf; macstr_t mac_buf;
@ -266,7 +146,7 @@ static void mgmt_edges_row (mgmt_req_t *req, strbuf_t *buf, struct peer_info *pe
send_reply(req, buf, msg_len); send_reply(req, buf, msg_len);
} }
static void mgmt_edges (mgmt_req_t *req, strbuf_t *buf, char *argv0, char *argv) { static void mgmt_edges (mgmt_req_t *req, strbuf_t *buf) {
struct peer_info *peer, *tmpPeer; struct peer_info *peer, *tmpPeer;
// dump nodes with forwarding through supernodes // dump nodes with forwarding through supernodes
@ -280,7 +160,7 @@ static void mgmt_edges (mgmt_req_t *req, strbuf_t *buf, char *argv0, char *argv)
} }
} }
static void mgmt_timestamps (mgmt_req_t *req, strbuf_t *buf, char *argv0, char *argv) { static void mgmt_timestamps (mgmt_req_t *req, strbuf_t *buf) {
size_t msg_len; size_t msg_len;
msg_len = snprintf(buf->str, buf->size, msg_len = snprintf(buf->str, buf->size,
@ -298,7 +178,7 @@ static void mgmt_timestamps (mgmt_req_t *req, strbuf_t *buf, char *argv0, char *
send_reply(req, buf, msg_len); send_reply(req, buf, msg_len);
} }
static void mgmt_packetstats (mgmt_req_t *req, strbuf_t *buf, char *argv0, char *argv) { static void mgmt_packetstats (mgmt_req_t *req, strbuf_t *buf) {
size_t msg_len; size_t msg_len;
msg_len = snprintf(buf->str, buf->size, msg_len = snprintf(buf->str, buf->size,
@ -354,20 +234,15 @@ static void mgmt_packetstats (mgmt_req_t *req, strbuf_t *buf, char *argv0, char
send_reply(req, buf, msg_len); send_reply(req, buf, msg_len);
} }
static void mgmt_post_test (mgmt_req_t *req, strbuf_t *buf, char *argv0, char *argv) { static void mgmt_post_test (mgmt_req_t *req, strbuf_t *buf) {
send_json_1str(req, buf, "row", "sending", "test"); send_json_1str(req, buf, "row", "sending", "test");
mgmt_event_post(N2N_EVENT_TEST, -1, argv); mgmt_event_post(N2N_EVENT_TEST, -1, req->argv);
}
static void mgmt_unimplemented (mgmt_req_t *req, strbuf_t *buf, char *argv0, char *argv) {
mgmt_error(req, buf, "unimplemented");
} }
// Forward define so we can include this in the mgmt_handlers[] table // Forward define so we can include this in the mgmt_handlers[] table
static void mgmt_help (mgmt_req_t *req, strbuf_t *buf, char *argv0, char *argv); static void mgmt_help (mgmt_req_t *req, strbuf_t *buf);
static void mgmt_help_events (mgmt_req_t *req, strbuf_t *buf, char *argv0, char *argv); static void mgmt_help_events (mgmt_req_t *req, strbuf_t *buf);
static const mgmt_handler_t mgmt_handlers[] = { static const mgmt_handler_t mgmt_handlers[] = {
{ .cmd = "reload_communities", .flags = FLAG_WROK, .help = "Reserved for supernode", .func = mgmt_unimplemented}, { .cmd = "reload_communities", .flags = FLAG_WROK, .help = "Reserved for supernode", .func = mgmt_unimplemented},
@ -392,7 +267,8 @@ static mgmt_req_t mgmt_event_subscribers[] = {
}; };
/* Map topic number to function */ /* Map topic number to function */
static const size_t (*mgmt_events[])(strbuf_t *buf, char *tag, int data0, void *data1) = { // TODO: want this to be const
static mgmt_event_handler_t *mgmt_events[] = {
[N2N_EVENT_DEBUG] = event_debug, [N2N_EVENT_DEBUG] = event_debug,
[N2N_EVENT_TEST] = event_test, [N2N_EVENT_TEST] = event_test,
[N2N_EVENT_PEER] = event_peer, [N2N_EVENT_PEER] = event_peer,
@ -408,81 +284,25 @@ static const mgmt_events_t mgmt_event_names[] = {
void mgmt_event_post (enum n2n_event_topic topic, int data0, void *data1) { void mgmt_event_post (enum n2n_event_topic topic, int data0, void *data1) {
mgmt_req_t *debug = &mgmt_event_subscribers[N2N_EVENT_DEBUG]; mgmt_req_t *debug = &mgmt_event_subscribers[N2N_EVENT_DEBUG];
mgmt_req_t *sub = &mgmt_event_subscribers[topic]; mgmt_req_t *sub = &mgmt_event_subscribers[topic];
mgmt_event_handler_t *fn = mgmt_events[topic];
traceEvent(TRACE_DEBUG, "post topic=%i data0=%i", topic, data0); mgmt_event_post2(topic, data0, data1, debug, sub, fn);
if( sub->type != N2N_MGMT_SUB && debug->type != N2N_MGMT_SUB) {
// If neither of this topic or the debug topic have a subscriber
// then we dont need to do any work
return;
}
char buf_space[100];
strbuf_t *buf;
STRBUF_INIT(buf, buf_space);
char *tag;
if(sub->type == N2N_MGMT_SUB) {
tag = sub->tag;
} else {
tag = debug->tag;
}
size_t msg_len = mgmt_events[topic](buf, tag, data0, data1);
if(sub->type == N2N_MGMT_SUB) {
send_reply(sub, buf, msg_len);
}
if(debug->type == N2N_MGMT_SUB) {
send_reply(debug, buf, msg_len);
}
} }
static void mgmt_help_events (mgmt_req_t *req, strbuf_t *buf, char *argv0, char *argv) { static void mgmt_help_events (mgmt_req_t *req, strbuf_t *buf) {
size_t msg_len;
int i; int i;
int nr_handlers = sizeof(mgmt_event_names) / sizeof(mgmt_events_t); int nr_handlers = sizeof(mgmt_event_names) / sizeof(mgmt_events_t);
for( i=0; i < nr_handlers; i++ ) { for( i=0; i < nr_handlers; i++ ) {
int topic = mgmt_event_names[i].topic; int topic = mgmt_event_names[i].topic;
mgmt_req_t *sub = &mgmt_event_subscribers[topic]; mgmt_req_t *sub = &mgmt_event_subscribers[topic];
char host[40];
char serv[6];
if((sub->type != N2N_MGMT_SUB) || mgmt_help_events_row(req, buf, sub, mgmt_event_names[i].cmd, mgmt_event_names[i].help);
getnameinfo((struct sockaddr *)&sub->sender_sock, sizeof(sub->sender_sock),
host, sizeof(host),
serv, sizeof(serv),
NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
host[0] = '?';
host[1] = 0;
serv[0] = '?';
serv[1] = 0;
}
// TODO: handle a topic with no subscribers more cleanly
msg_len = snprintf(buf->str, buf->size,
"{"
"\"_tag\":\"%s\","
"\"_type\":\"row\","
"\"topic\":\"%s\","
"\"tag\":\"%s\","
"\"sockaddr\":\"%s:%s\","
"\"help\":\"%s\"}\n",
req->tag,
mgmt_event_names[i].cmd,
sub->tag,
host, serv,
mgmt_event_names[i].help);
send_reply(req, buf, msg_len);
} }
} }
static void mgmt_help (mgmt_req_t *req, strbuf_t *buf, char *argv0, char *argv) { // TODO: want to keep the mgmt_handlers defintion const static, otherwise
size_t msg_len; // this whole function could be shared
static void mgmt_help (mgmt_req_t *req, strbuf_t *buf) {
/* /*
* Even though this command is readonly, we deliberately do not check * Even though this command is readonly, we deliberately do not check
* the type - allowing help replies to both read and write requests * the type - allowing help replies to both read and write requests
@ -491,60 +311,14 @@ static void mgmt_help (mgmt_req_t *req, strbuf_t *buf, char *argv0, char *argv)
int i; int i;
int nr_handlers = sizeof(mgmt_handlers) / sizeof(mgmt_handler_t); int nr_handlers = sizeof(mgmt_handlers) / sizeof(mgmt_handler_t);
for( i=0; i < nr_handlers; i++ ) { for( i=0; i < nr_handlers; i++ ) {
msg_len = snprintf(buf->str, buf->size, mgmt_help_row(req, buf, mgmt_handlers[i].cmd, mgmt_handlers[i].help);
"{"
"\"_tag\":\"%s\","
"\"_type\":\"row\","
"\"cmd\":\"%s\","
"\"help\":\"%s\"}\n",
req->tag,
mgmt_handlers[i].cmd,
mgmt_handlers[i].help);
send_reply(req, buf, msg_len);
} }
} }
/*
* 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 (mgmt_req_t *req, char *auth, char *argv0, char *argv) {
if(auth) {
/* If we have an auth key, it must match */
if(req->eee->conf.mgmt_password_hash == pearson_hash_64((uint8_t*)auth, strlen(auth))) {
return 1;
}
return 0;
}
/* if we dont have an auth key, we can still read */
if(req->type == N2N_MGMT_READ) {
return 1;
}
return 0;
}
static void handleMgmtJson (mgmt_req_t *req, char *udp_buf, const int recvlen) { static void handleMgmtJson (mgmt_req_t *req, char *udp_buf, const int recvlen) {
strbuf_t *buf; strbuf_t *buf;
char cmdlinebuf[80]; char cmdlinebuf[80];
char *typechar;
char *options;
char *argv0;
char *argv;
char *flagstr;
int flags;
char *auth;
/* Initialise the tag field until we extract it from the cmdline */
req->tag[0] = '-';
req->tag[1] = '1';
req->tag[2] = '\0';
/* save a copy of the commandline before we reuse the udp_buf */ /* save a copy of the commandline before we reuse the udp_buf */
strncpy(cmdlinebuf, udp_buf, sizeof(cmdlinebuf)-1); strncpy(cmdlinebuf, udp_buf, sizeof(cmdlinebuf)-1);
@ -555,71 +329,11 @@ static void handleMgmtJson (mgmt_req_t *req, char *udp_buf, const int recvlen) {
/* we reuse the buffer already on the stack for all our strings */ /* we reuse the buffer already on the stack for all our strings */
STRBUF_INIT(buf, udp_buf); STRBUF_INIT(buf, udp_buf);
typechar = strtok(cmdlinebuf, " \r\n"); mgmt_req_init2(req, buf, (char *)&cmdlinebuf);
if(!typechar) {
/* should not happen */
mgmt_error(req, buf, "notype");
return;
}
if(*typechar == 'r') {
req->type=N2N_MGMT_READ;
} else if(*typechar == 'w') {
req->type=N2N_MGMT_WRITE;
} else if(*typechar == 's') {
req->type=N2N_MGMT_SUB;
} else {
mgmt_error(req, buf, "badtype");
return;
}
/* Extract the tag to use in all reply packets */
options = strtok(NULL, " \r\n");
if(!options) {
mgmt_error(req, buf, "nooptions");
return;
}
argv0 = strtok(NULL, " \r\n");
if(!argv0) {
mgmt_error(req, buf, "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
*/
char *tagp = strtok(options, ":");
strncpy(req->tag, tagp, sizeof(req->tag)-1);
req->tag[sizeof(req->tag)-1] = '\0';
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(req, auth, argv0, argv)) {
mgmt_error(req, buf, "badauth");
return;
}
if(req->type == N2N_MGMT_SUB) { if(req->type == N2N_MGMT_SUB) {
int handler; int handler;
lookup_handler(handler, mgmt_event_names, argv0); lookup_handler(handler, mgmt_event_names, req->argv0);
if(handler == -1) { if(handler == -1) {
mgmt_error(req, buf, "unknowntopic"); mgmt_error(req, buf, "unknowntopic");
return; return;
@ -628,18 +342,18 @@ static void handleMgmtJson (mgmt_req_t *req, char *udp_buf, const int recvlen) {
int topic = mgmt_event_names[handler].topic; int topic = mgmt_event_names[handler].topic;
if(mgmt_event_subscribers[topic].type == N2N_MGMT_SUB) { if(mgmt_event_subscribers[topic].type == N2N_MGMT_SUB) {
send_json_1str(&mgmt_event_subscribers[topic], buf, send_json_1str(&mgmt_event_subscribers[topic], buf,
"unsubscribed", "topic", argv0); "unsubscribed", "topic", req->argv0);
send_json_1str(req, buf, "replacing", "topic", argv0); send_json_1str(req, buf, "replacing", "topic", req->argv0);
} }
memcpy(&mgmt_event_subscribers[topic], req, sizeof(*req)); memcpy(&mgmt_event_subscribers[topic], req, sizeof(*req));
send_json_1str(req, buf, "subscribe", "topic", argv0); send_json_1str(req, buf, "subscribe", "topic", req->argv0);
return; return;
} }
int handler; int handler;
lookup_handler(handler, mgmt_handlers, argv0); lookup_handler(handler, mgmt_handlers, req->argv0);
if(handler == -1) { if(handler == -1) {
mgmt_error(req, buf, "unknowncmd"); mgmt_error(req, buf, "unknowncmd");
return; return;
@ -656,11 +370,11 @@ static void handleMgmtJson (mgmt_req_t *req, char *udp_buf, const int recvlen) {
* that make our JSON invalid. * that make our JSON invalid.
* - do we care? * - do we care?
*/ */
send_json_1str(req, buf, "begin", "cmd", argv0); send_json_1str(req, buf, "begin", "cmd", req->argv0);
mgmt_handlers[handler].func(req, buf, argv0, argv); mgmt_handlers[handler].func(req, buf);
send_json_1str(req, buf, "end", "cmd", argv0); send_json_1str(req, buf, "end", "cmd", req->argv0);
return; return;
} }
@ -688,7 +402,11 @@ void readFromMgmtSocket (n2n_edge_t *eee) {
uint32_t num = 0; uint32_t num = 0;
selection_criterion_str_t sel_buf; selection_criterion_str_t sel_buf;
req.sss = NULL;
req.eee = eee; req.eee = eee;
req.mgmt_sock = eee->udp_mgmt_sock;
req.keep_running = eee->keep_running;
req.mgmt_password_hash = eee->conf.mgmt_password_hash;
now = time(NULL); now = time(NULL);
i = sizeof(req.sender_sock); i = sizeof(req.sender_sock);

280
src/management.c Normal file
View File

@ -0,0 +1,280 @@
/*
* Common routines shared between the management interfaces
*
*/
#include <stdio.h>
#ifndef WIN32
#include <sys/socket.h>
#include <netdb.h>
#endif
// TODO: move logging defs in their own header and include that
void setTraceLevel (int level);
int getTraceLevel ();
#include <pearson.h>
#include "management.h"
#include "n2n.h" // for traceEvent and friends
ssize_t send_reply (mgmt_req_t *req, strbuf_t *buf, size_t msg_len) {
// TODO: better error handling (counters?)
return sendto(req->mgmt_sock, buf->str, msg_len, 0,
(struct sockaddr *) &req->sender_sock, sizeof(struct sockaddr_in));
}
size_t gen_json_1str (strbuf_t *buf, char *tag, char *_type, char *key, char *val) {
return snprintf(buf->str, buf->size,
"{"
"\"_tag\":\"%s\","
"\"_type\":\"%s\","
"\"%s\":\"%s\"}\n",
tag,
_type,
key,
val);
}
size_t gen_json_1uint (strbuf_t *buf, char *tag, char *_type, char *key, unsigned int val) {
return snprintf(buf->str, buf->size,
"{"
"\"_tag\":\"%s\","
"\"_type\":\"%s\","
"\"%s\":%u}\n",
tag,
_type,
key,
val);
}
void send_json_1str (mgmt_req_t *req, strbuf_t *buf, char *_type, char *key, char *val) {
size_t msg_len = gen_json_1str(buf, req->tag, _type, key, val);
send_reply(req, buf, msg_len);
}
void send_json_1uint (mgmt_req_t *req, strbuf_t *buf, char *_type, char *key, unsigned int val) {
size_t msg_len = gen_json_1uint(buf, req->tag, _type, key, val);
send_reply(req, buf, msg_len);
}
void mgmt_error (mgmt_req_t *req, strbuf_t *buf, char *msg) {
send_json_1str(req, buf, "error", "error", msg);
}
void mgmt_stop (mgmt_req_t *req, strbuf_t *buf) {
if(req->type==N2N_MGMT_WRITE) {
*req->keep_running = 0;
}
send_json_1uint(req, buf, "row", "keep_running", *req->keep_running);
}
void mgmt_verbose (mgmt_req_t *req, strbuf_t *buf) {
if(req->type==N2N_MGMT_WRITE) {
if(req->argv) {
setTraceLevel(strtoul(req->argv, NULL, 0));
}
}
send_json_1uint(req, buf, "row", "traceLevel", getTraceLevel());
}
void mgmt_unimplemented (mgmt_req_t *req, strbuf_t *buf) {
mgmt_error(req, buf, "unimplemented");
}
void mgmt_event_post2 (enum n2n_event_topic topic, int data0, void *data1, mgmt_req_t *debug, mgmt_req_t *sub, mgmt_event_handler_t fn) {
traceEvent(TRACE_DEBUG, "post topic=%i data0=%i", topic, data0);
if( sub->type != N2N_MGMT_SUB && debug->type != N2N_MGMT_SUB) {
// If neither of this topic or the debug topic have a subscriber
// then we dont need to do any work
return;
}
char buf_space[100];
strbuf_t *buf;
STRBUF_INIT(buf, buf_space);
char *tag;
if(sub->type == N2N_MGMT_SUB) {
tag = sub->tag;
} else {
tag = debug->tag;
}
size_t msg_len = fn(buf, tag, data0, data1);
if(sub->type == N2N_MGMT_SUB) {
send_reply(sub, buf, msg_len);
}
if(debug->type == N2N_MGMT_SUB) {
send_reply(debug, buf, msg_len);
}
// TODO:
// - ideally, we would detect that the far end has gone away and
// set the ->type back to N2N_MGMT_UNKNOWN, but we are not using
// a connected socket, so that is difficult
// - failing that, we should require the client to send an unsubscribe
// and provide a manual unsubscribe
}
void mgmt_help_row (mgmt_req_t *req, strbuf_t *buf, char *cmd, char *help) {
size_t msg_len;
msg_len = snprintf(buf->str, buf->size,
"{"
"\"_tag\":\"%s\","
"\"_type\":\"row\","
"\"cmd\":\"%s\","
"\"help\":\"%s\"}\n",
req->tag,
cmd,
help);
send_reply(req, buf, msg_len);
}
void mgmt_help_events_row (mgmt_req_t *req, strbuf_t *buf, mgmt_req_t *sub, char *cmd, char *help) {
size_t msg_len;
char host[40];
char serv[6];
if((sub->type != N2N_MGMT_SUB) ||
getnameinfo((struct sockaddr *)&sub->sender_sock, sizeof(sub->sender_sock),
host, sizeof(host),
serv, sizeof(serv),
NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
host[0] = '?';
host[1] = 0;
serv[0] = '?';
serv[1] = 0;
}
// TODO: handle a topic with no subscribers more cleanly
msg_len = snprintf(buf->str, buf->size,
"{"
"\"_tag\":\"%s\","
"\"_type\":\"row\","
"\"topic\":\"%s\","
"\"tag\":\"%s\","
"\"sockaddr\":\"%s:%s\","
"\"help\":\"%s\"}\n",
req->tag,
cmd,
sub->tag,
host, serv,
help);
send_reply(req, buf, msg_len);
}
// TODO: work out a method to keep the mgmt_handlers defintion const static,
// and then import the shared mgmt_help () definition to this file
/*
* 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 mgmt_auth (mgmt_req_t *req, char *auth) {
if(auth) {
/* If we have an auth key, it must match */
if(req->mgmt_password_hash == pearson_hash_64((uint8_t*)auth, strlen(auth))) {
return 1;
}
return 0;
}
/* if we dont have an auth key, we can still read */
if(req->type == N2N_MGMT_READ) {
return 1;
}
return 0;
}
/*
* Handle the common and shred parts of the mgmt_req_t initialisation
*/
void mgmt_req_init2 (mgmt_req_t *req, strbuf_t *buf, char *cmdline) {
char *typechar;
char *options;
char *flagstr;
int flags;
char *auth;
/* Initialise the tag field until we extract it from the cmdline */
req->tag[0] = '-';
req->tag[1] = '1';
req->tag[2] = '\0';
typechar = strtok(cmdline, " \r\n");
if(!typechar) {
/* should not happen */
mgmt_error(req, buf, "notype");
return;
}
if(*typechar == 'r') {
req->type=N2N_MGMT_READ;
} else if(*typechar == 'w') {
req->type=N2N_MGMT_WRITE;
} else if(*typechar == 's') {
req->type=N2N_MGMT_SUB;
} else {
mgmt_error(req, buf, "badtype");
return;
}
/* Extract the tag to use in all reply packets */
options = strtok(NULL, " \r\n");
if(!options) {
mgmt_error(req, buf, "nooptions");
return;
}
req->argv0 = strtok(NULL, " \r\n");
if(!req->argv0) {
mgmt_error(req, buf, "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.
*/
req->argv = strtok(NULL, "\r\n");
/*
* There might be an auth token mixed in with the tag
*/
char *tagp = strtok(options, ":");
strncpy(req->tag, tagp, sizeof(req->tag)-1);
req->tag[sizeof(req->tag)-1] = '\0';
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(req, auth)) {
mgmt_error(req, buf, "badauth");
return;
}
}

100
src/management.h Normal file
View File

@ -0,0 +1,100 @@
/*
* Internal interface definitions for the management interfaces
*
* This header is not part of the public library API and is thus not in
* the public include folder
*/
#ifndef MANAGEMENT_H
#define MANAGEMENT_H 1
#include <n2n_typedefs.h> // For the n2n_edge_t and n2n_sn_t defs
#include "strbuf.h"
enum n2n_mgmt_type {
N2N_MGMT_UNKNOWN = 0,
N2N_MGMT_READ = 1,
N2N_MGMT_WRITE = 2,
N2N_MGMT_SUB = 3,
};
/*
* Everything needed to reply to a request
*
* TODO:
* - one day, we might be able to merge the sss and eee members
* - once eee and sss are merged, some fields should migrate back into it:
* - mgmt_sock
* - keep_running
* - mgmt_password_hash
*/
typedef struct mgmt_req {
n2n_sn_t *sss;
n2n_edge_t *eee;
int mgmt_sock; // socket replies come from
int *keep_running;
uint64_t mgmt_password_hash;
enum n2n_mgmt_type type;
char *argv0;
char *argv;
char tag[10];
struct sockaddr_in sender_sock;
} mgmt_req_t;
/*
* Read/Write handlers are defined in this structure
* TODO: DRY
*/
#define FLAG_WROK 1
typedef struct mgmt_handler {
int flags;
char *cmd;
char *help;
void (*func)(mgmt_req_t *req, strbuf_t *buf);
} mgmt_handler_t;
/*
* Event topic names are defined in this structure
*/
typedef struct mgmt_events {
enum n2n_event_topic topic;
char *cmd;
char *help;
} mgmt_events_t;
typedef size_t (mgmt_event_handler_t)(strbuf_t *buf, char *tag, int data0, void *data1);
// Lookup the index of matching argv0 in a cmd list
// store index in "Result", or -1 for not found
#define lookup_handler(Result, list, argv0) do { \
int nr_max = sizeof(list) / sizeof(list[0]); \
for( Result=0; Result < nr_max; Result++ ) { \
if(0 == strcmp(list[Result].cmd, argv0)) { \
break; \
} \
} \
if( Result >= nr_max ) { \
Result = -1; \
} \
} while(0)
ssize_t send_reply (mgmt_req_t *req, strbuf_t *buf, size_t msg_len);
size_t gen_json_1str (strbuf_t *buf, char *tag, char *_type, char *key, char *val);
size_t gen_json_1uint (strbuf_t *buf, char *tag, char *_type, char *key, unsigned int val);
void send_json_1str (mgmt_req_t *req, strbuf_t *buf, char *_type, char *key, char *val);
void send_json_1uint (mgmt_req_t *req, strbuf_t *buf, char *_type, char *key, unsigned int val);
void mgmt_error (mgmt_req_t *req, strbuf_t *buf, char *msg);
void mgmt_stop (mgmt_req_t *req, strbuf_t *buf);
void mgmt_verbose (mgmt_req_t *req, strbuf_t *buf);
void mgmt_unimplemented (mgmt_req_t *req, strbuf_t *buf);
void mgmt_event_post2 (enum n2n_event_topic topic, int data0, void *data1, mgmt_req_t *debug, mgmt_req_t *sub, mgmt_event_handler_t fn);
void mgmt_help_row (mgmt_req_t *req, strbuf_t *buf, char *cmd, char *help);
void mgmt_help_events_row (mgmt_req_t *req, strbuf_t *buf, mgmt_req_t *sub, char *cmd, char *help);
int mgmt_auth (mgmt_req_t *req, char *auth);
void mgmt_req_init2 (mgmt_req_t *req, strbuf_t *buf, char *cmdline);
#endif

View File

@ -24,186 +24,106 @@
#include "n2n.h" #include "n2n.h"
#include "edge_utils_win32.h" #include "edge_utils_win32.h"
#include "strbuf.h"
#include "management.h"
int load_allowed_sn_community (n2n_sn_t *sss); /* defined in sn_utils.c */ int load_allowed_sn_community (n2n_sn_t *sss); /* defined in sn_utils.c */
enum n2n_mgmt_type { static void mgmt_reload_communities (mgmt_req_t *req, strbuf_t *buf) {
N2N_MGMT_READ = 0,
N2N_MGMT_WRITE = 1,
};
#define FLAG_WROK 1 if(req->type!=N2N_MGMT_WRITE) {
typedef struct mgmt_handler { mgmt_error(req, buf, "writeonly");
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);
} 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; return;
} }
if(!sss->community_file) { if(!req->sss->community_file) {
mgmt_error(sss, udp_buf, sender_sock, tag, "nofile"); mgmt_error(req, buf, "nofile");
return; return;
} }
int ok = load_allowed_sn_community(sss); int ok = load_allowed_sn_community(req->sss);
send_json_1uint(req, buf, "row", "ok", ok);
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) { static void mgmt_timestamps (mgmt_req_t *req, strbuf_t *buf) {
size_t msg_len; size_t msg_len;
msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE, msg_len = snprintf(buf->str, buf->size,
"{" "{"
"\"_tag\":\"%s\"," "\"_tag\":\"%s\","
"\"_type\":\"row\"," "\"_type\":\"row\","
"\"start_time\":%lu," "\"start_time\":%lu,"
"\"last_fwd\":%ld," "\"last_fwd\":%ld,"
"\"last_reg_super\":%ld}\n", "\"last_reg_super\":%ld}\n",
tag, req->tag,
sss->start_time, req->sss->start_time,
sss->stats.last_fwd, req->sss->stats.last_fwd,
sss->stats.last_reg_super); req->sss->stats.last_reg_super);
sendto(sss->mgmt_sock, udp_buf, msg_len, 0, send_reply(req, buf, msg_len);
(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) { static void mgmt_packetstats (mgmt_req_t *req, strbuf_t *buf) {
size_t msg_len; size_t msg_len;
msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE, msg_len = snprintf(buf->str, buf->size,
"{" "{"
"\"_tag\":\"%s\"," "\"_tag\":\"%s\","
"\"_type\":\"row\"," "\"_type\":\"row\","
"\"type\":\"forward\"," "\"type\":\"forward\","
"\"tx_pkt\":%lu}\n", "\"tx_pkt\":%lu}\n",
tag, req->tag,
sss->stats.fwd); req->sss->stats.fwd);
sendto(sss->mgmt_sock, udp_buf, msg_len, 0, send_reply(req, buf, msg_len);
(struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE, msg_len = snprintf(buf->str, buf->size,
"{" "{"
"\"_tag\":\"%s\"," "\"_tag\":\"%s\","
"\"_type\":\"row\"," "\"_type\":\"row\","
"\"type\":\"broadcast\"," "\"type\":\"broadcast\","
"\"tx_pkt\":%lu}\n", "\"tx_pkt\":%lu}\n",
tag, req->tag,
sss->stats.broadcast); req->sss->stats.broadcast);
sendto(sss->mgmt_sock, udp_buf, msg_len, 0, send_reply(req, buf, msg_len);
(struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE, msg_len = snprintf(buf->str, buf->size,
"{" "{"
"\"_tag\":\"%s\"," "\"_tag\":\"%s\","
"\"_type\":\"row\"," "\"_type\":\"row\","
"\"type\":\"reg_super\"," "\"type\":\"reg_super\","
"\"rx_pkt\":%lu," "\"rx_pkt\":%lu,"
"\"nak\":%lu}\n", "\"nak\":%lu}\n",
tag, req->tag,
sss->stats.reg_super, req->sss->stats.reg_super,
sss->stats.reg_super_nak); req->sss->stats.reg_super_nak);
/* Note: reg_super_nak is not currently incremented anywhere */ /* Note: reg_super_nak is not currently incremented anywhere */
sendto(sss->mgmt_sock, udp_buf, msg_len, 0, send_reply(req, buf, msg_len);
(struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
/* Generic errors when trying to sendto() */ /* Generic errors when trying to sendto() */
msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE, msg_len = snprintf(buf->str, buf->size,
"{" "{"
"\"_tag\":\"%s\"," "\"_tag\":\"%s\","
"\"_type\":\"row\"," "\"_type\":\"row\","
"\"type\":\"errors\"," "\"type\":\"errors\","
"\"tx_pkt\":%lu}\n", "\"tx_pkt\":%lu}\n",
tag, req->tag,
sss->stats.errors); req->sss->stats.errors);
sendto(sss->mgmt_sock, udp_buf, msg_len, 0,
(struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
send_reply(req, buf, msg_len);
} }
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) { static void mgmt_communities (mgmt_req_t *req, strbuf_t *buf) {
size_t msg_len; size_t msg_len;
struct sn_community *community, *tmp; struct sn_community *community, *tmp;
dec_ip_bit_str_t ip_bit_str = {'\0'}; dec_ip_bit_str_t ip_bit_str = {'\0'};
HASH_ITER(hh, sss->communities, community, tmp) { HASH_ITER(hh, req->sss->communities, community, tmp) {
msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE, msg_len = snprintf(buf->str, buf->size,
"{" "{"
"\"_tag\":\"%s\"," "\"_tag\":\"%s\","
"\"_type\":\"row\"," "\"_type\":\"row\","
@ -211,19 +131,17 @@ static void mgmt_communities (n2n_sn_t *sss, char *udp_buf, const struct sockadd
"\"purgeable\":%i," "\"purgeable\":%i,"
"\"is_federation\":%i," "\"is_federation\":%i,"
"\"ip4addr\":\"%s\"}\n", "\"ip4addr\":\"%s\"}\n",
tag, req->tag,
(community->is_federation) ? "-/-" : community->community, (community->is_federation) ? "-/-" : community->community,
community->purgeable, community->purgeable,
community->is_federation, community->is_federation,
(community->auto_ip_net.net_addr == 0) ? "" : ip_subnet_to_str(ip_bit_str, &community->auto_ip_net)); (community->auto_ip_net.net_addr == 0) ? "" : ip_subnet_to_str(ip_bit_str, &community->auto_ip_net));
send_reply(req, buf, msg_len);
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) { static void mgmt_edges (mgmt_req_t *req, strbuf_t *buf) {
size_t msg_len; size_t msg_len;
struct sn_community *community, *tmp; struct sn_community *community, *tmp;
struct peer_info *peer, *tmpPeer; struct peer_info *peer, *tmpPeer;
@ -231,10 +149,10 @@ static void mgmt_edges (n2n_sn_t *sss, char *udp_buf, const struct sockaddr_in s
n2n_sock_str_t sockbuf; n2n_sock_str_t sockbuf;
dec_ip_bit_str_t ip_bit_str = {'\0'}; dec_ip_bit_str_t ip_bit_str = {'\0'};
HASH_ITER(hh, sss->communities, community, tmp) { HASH_ITER(hh, req->sss->communities, community, tmp) {
HASH_ITER(hh, community->edges, peer, tmpPeer) { HASH_ITER(hh, community->edges, peer, tmpPeer) {
msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE, msg_len = snprintf(buf->str, buf->size,
"{" "{"
"\"_tag\":\"%s\"," "\"_tag\":\"%s\","
"\"_type\":\"row\"," "\"_type\":\"row\","
@ -246,29 +164,25 @@ static void mgmt_edges (n2n_sn_t *sss, char *udp_buf, const struct sockaddr_in s
"\"proto\":\"%s\"," "\"proto\":\"%s\","
"\"desc\":\"%s\"," "\"desc\":\"%s\","
"\"last_seen\":%li}\n", "\"last_seen\":%li}\n",
tag, req->tag,
(community->is_federation) ? "-/-" : community->community, (community->is_federation) ? "-/-" : community->community,
(peer->dev_addr.net_addr == 0) ? "" : ip_subnet_to_str(ip_bit_str, &peer->dev_addr), (peer->dev_addr.net_addr == 0) ? "" : ip_subnet_to_str(ip_bit_str, &peer->dev_addr),
peer->purgeable, peer->purgeable,
(is_null_mac(peer->mac_addr)) ? "" : macaddr_str(mac_buf, peer->mac_addr), (is_null_mac(peer->mac_addr)) ? "" : macaddr_str(mac_buf, peer->mac_addr),
sock_to_cstr(sockbuf, &(peer->sock)), sock_to_cstr(sockbuf, &(peer->sock)),
((peer->socket_fd >= 0) && (peer->socket_fd != sss->sock)) ? "TCP" : "UDP", ((peer->socket_fd >= 0) && (peer->socket_fd != req->sss->sock)) ? "TCP" : "UDP",
peer->dev_desc, peer->dev_desc,
peer->last_seen); peer->last_seen);
sendto(sss->mgmt_sock, udp_buf, msg_len, 0, send_reply(req, buf, msg_len);
(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) { // Forward define so we can include this in the mgmt_handlers[] table
mgmt_error(sss, udp_buf, sender_sock, tag, "unimplemented"); static void mgmt_help (mgmt_req_t *req, strbuf_t *buf);
}
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); static const mgmt_handler_t mgmt_handlers[] = {
mgmt_handler_t mgmt_handlers[] = {
{ .cmd = "supernodes", .help = "Reserved for edge", .func = mgmt_unimplemented}, { .cmd = "supernodes", .help = "Reserved for edge", .func = mgmt_unimplemented},
{ .cmd = "stop", .flags = FLAG_WROK, .help = "Gracefully exit edge", .func = mgmt_stop}, { .cmd = "stop", .flags = FLAG_WROK, .help = "Gracefully exit edge", .func = mgmt_stop},
@ -279,72 +193,28 @@ mgmt_handler_t mgmt_handlers[] = {
{ .cmd = "timestamps", .help = "Event timestamps", .func = mgmt_timestamps}, { .cmd = "timestamps", .help = "Event timestamps", .func = mgmt_timestamps},
{ .cmd = "packetstats", .help = "Traffic statistics", .func = mgmt_packetstats}, { .cmd = "packetstats", .help = "Traffic statistics", .func = mgmt_packetstats},
{ .cmd = "help", .flags = FLAG_WROK, .help = "Show JSON commands", .func = mgmt_help}, { .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) { // TODO: want to keep the mgmt_handlers defintion const static, otherwise
size_t msg_len; // this whole function could be shared
mgmt_handler_t *handler; static void mgmt_help (mgmt_req_t *req, strbuf_t *buf) {
/* /*
* Even though this command is readonly, we deliberately do not check * Even though this command is readonly, we deliberately do not check
* the type - allowing help replies to both read and write requests * the type - allowing help replies to both read and write requests
*/ */
for( handler=mgmt_handlers; handler->cmd; handler++ ) { int i;
msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE, int nr_handlers = sizeof(mgmt_handlers) / sizeof(mgmt_handler_t);
"{" for( i=0; i < nr_handlers; i++ ) {
"\"_tag\":\"%s\"," mgmt_help_row(req, buf, mgmt_handlers[i].cmd, mgmt_handlers[i].help);
"\"_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));
} }
} }
/* // TODO: DRY
* Check if the user is authorised for this command. static void handleMgmtJson (mgmt_req_t *req, char *udp_buf, const int recvlen) {
* - 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 (n2n_sn_t *sss, 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(sss->mgmt_password_hash == pearson_hash_64((uint8_t*)auth, strlen(auth))) {
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_sn_t *sss, char *udp_buf, const struct sockaddr_in sender_sock) {
strbuf_t *buf;
char cmdlinebuf[80]; char cmdlinebuf[80];
enum n2n_mgmt_type type;
char *typechar;
char *options;
char *argv0;
char *argv;
char *tag;
char *flagstr;
int flags;
char *auth;
mgmt_handler_t *handler;
size_t msg_len;
/* save a copy of the commandline before we reuse the udp_buf */ /* save a copy of the commandline before we reuse the udp_buf */
strncpy(cmdlinebuf, udp_buf, sizeof(cmdlinebuf)-1); strncpy(cmdlinebuf, udp_buf, sizeof(cmdlinebuf)-1);
@ -352,76 +222,21 @@ static void handleMgmtJson (n2n_sn_t *sss, char *udp_buf, const struct sockaddr_
traceEvent(TRACE_DEBUG, "mgmt json %s", cmdlinebuf); traceEvent(TRACE_DEBUG, "mgmt json %s", cmdlinebuf);
typechar = strtok(cmdlinebuf, " \r\n"); /* we reuse the buffer already on the stack for all our strings */
if(!typechar) { // xx
/* should not happen */ STRBUF_INIT(buf, udp_buf);
mgmt_error(sss, udp_buf, sender_sock, "-1", "notype");
return; mgmt_req_init2(req, buf, (char *)&cmdlinebuf);
}
if(*typechar == 'r') { int handler;
type=N2N_MGMT_READ; lookup_handler(handler, mgmt_handlers, req->argv0);
} else if(*typechar == 'w') { if(handler == -1) {
type=N2N_MGMT_WRITE; mgmt_error(req, buf, "unknowncmd");
} else {
/* dunno how we got here */
mgmt_error(sss, udp_buf, sender_sock, "-1", "badtype");
return; return;
} }
/* Extract the tag to use in all reply packets */ if((req->type==N2N_MGMT_WRITE) && !(mgmt_handlers[handler].flags & FLAG_WROK)) {
options = strtok(NULL, " \r\n"); mgmt_error(req, buf, "readonly");
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(sss, 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; return;
} }
@ -431,17 +246,11 @@ static void handleMgmtJson (n2n_sn_t *sss, char *udp_buf, const struct sockaddr_
* that make our JSON invalid. * that make our JSON invalid.
* - do we care? * - do we care?
*/ */
msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE, send_json_1str(req, buf, "begin", "cmd", req->argv0);
"{\"_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); mgmt_handlers[handler].func(req, buf);
msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE, send_json_1str(req, buf, "end", "cmd", req->argv0);
"{\"_tag\":\"%s\",\"_type\":\"end\"}\n", tag);
sendto(sss->mgmt_sock, udp_buf, msg_len, 0,
(struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
return; return;
} }
@ -470,6 +279,7 @@ int process_mgmt (n2n_sn_t *sss,
char resbuf[N2N_SN_PKTBUF_SIZE]; char resbuf[N2N_SN_PKTBUF_SIZE];
size_t ressize = 0; size_t ressize = 0;
mgmt_req_t req;
uint32_t num_edges = 0; uint32_t num_edges = 0;
uint32_t num_comm = 0; uint32_t num_comm = 0;
uint32_t num = 0; uint32_t num = 0;
@ -482,6 +292,13 @@ int process_mgmt (n2n_sn_t *sss,
traceEvent(TRACE_DEBUG, "process_mgmt"); traceEvent(TRACE_DEBUG, "process_mgmt");
req.eee = NULL;
req.sss = sss;
req.mgmt_sock = sss->mgmt_sock;
req.keep_running = sss->keep_running;
req.mgmt_password_hash = sss->mgmt_password_hash;
memcpy(&req.sender_sock, sender_sock, sizeof(req.sender_sock));
/* avoid parsing any uninitialized junk from the stack */ /* avoid parsing any uninitialized junk from the stack */
mgmt_buf[mgmt_size] = 0; mgmt_buf[mgmt_size] = 0;
@ -517,9 +334,9 @@ int process_mgmt (n2n_sn_t *sss,
return 0; /* no status output afterwards */ return 0; /* no status output afterwards */
} }
if((mgmt_buf[0] == 'r' || mgmt_buf[0] == 'w') && (mgmt_buf[1] == ' ')) { if((mgmt_buf[0] >= 'a' || mgmt_buf[0] <= 'z') && (mgmt_buf[1] == ' ')) {
/* this is a JSON request */ /* this is a JSON request */
handleMgmtJson(sss, mgmt_buf, *sender_sock); handleMgmtJson(&req, mgmt_buf, mgmt_size);
return 0; return 0;
} }

24
src/strbuf.h Normal file
View File

@ -0,0 +1,24 @@
/*
* Internal interface definitions for the strbuf abstrction
*
* This header is not part of the public library API and is thus not in
* the public include folder
*/
#ifndef STRBUF_H
#define STRBUF_H 1
typedef struct strbuf {
size_t size;
char str[];
} strbuf_t;
// Initialise the strbuf pointer buf to point at the storage area p
// p must be a known sized object
#define STRBUF_INIT(buf,p) do { \
buf = (void *)p; \
buf->size = sizeof(*p) - sizeof(size_t); \
} while(0)
#endif