Move as much as simply possible from edge_management into shared code

This commit is contained in:
Hamish Coleman 2022-04-17 00:19:08 +01:00
parent 9728a36a56
commit d81908d6a2
7 changed files with 450 additions and 310 deletions

View File

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

View File

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

View File

@ -1,6 +1,8 @@
# 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.
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
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
future possibilities of pipelined and overlapping transactions as well as
pub/sub asynchronous event channels.
future possibilities of pipelined and overlapping transactions.
The replies will also handle some small amount of re-ordering of the
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
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
This subfield is a set of bit flags that are hex-encoded and describe any

View File

@ -20,97 +20,7 @@
#include "edge_utils_win32.h"
#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
*/
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);
}
#include "management.h"
size_t event_debug (strbuf_t *buf, char *tag, int data0, void *data1) {
traceEvent(TRACE_DEBUG, "Unexpected call to event_debug");
@ -147,31 +57,11 @@ size_t event_peer (strbuf_t *buf, char *tag, int data0, void *data1) {
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) {
*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) {
static void mgmt_communities (mgmt_req_t *req, strbuf_t *buf) {
if(req->eee->conf.header_encryption != HEADER_ENCRYPTION_NONE) {
mgmt_error(req, buf, "noaccess");
@ -181,7 +71,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);
}
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;
struct peer_info *peer, *tmpPeer;
macstr_t mac_buf;
@ -258,7 +148,7 @@ static void mgmt_edges_row (mgmt_req_t *req, strbuf_t *buf, struct peer_info *pe
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;
// dump nodes with forwarding through supernodes
@ -272,7 +162,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;
msg_len = snprintf(buf->str, buf->size,
@ -290,7 +180,7 @@ static void mgmt_timestamps (mgmt_req_t *req, strbuf_t *buf, char *argv0, char *
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;
msg_len = snprintf(buf->str, buf->size,
@ -346,20 +236,15 @@ static void mgmt_packetstats (mgmt_req_t *req, strbuf_t *buf, char *argv0, char
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");
mgmt_event_post(N2N_EVENT_TEST, -1, argv);
}
static void mgmt_unimplemented (mgmt_req_t *req, strbuf_t *buf, char *argv0, char *argv) {
mgmt_error(req, buf, "unimplemented");
mgmt_event_post(N2N_EVENT_TEST, -1, req->argv);
}
// 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_events (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);
static const mgmt_handler_t mgmt_handlers[] = {
{ .cmd = "reload_communities", .flags = FLAG_WROK, .help = "Reserved for supernode", .func = mgmt_unimplemented},
@ -384,7 +269,8 @@ static mgmt_req_t mgmt_event_subscribers[] = {
};
/* 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_TEST] = event_test,
[N2N_EVENT_PEER] = event_peer,
@ -400,81 +286,25 @@ static const mgmt_events_t mgmt_event_names[] = {
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 *sub = &mgmt_event_subscribers[topic];
mgmt_event_handler_t *fn = mgmt_events[topic];
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;
mgmt_event_post2 (topic, data0, data1, debug, sub, fn);
}
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) {
size_t msg_len;
static void mgmt_help_events (mgmt_req_t *req, strbuf_t *buf) {
int i;
int nr_handlers = sizeof(mgmt_event_names) / sizeof(mgmt_events_t);
for( i=0; i < nr_handlers; i++ ) {
int topic = mgmt_event_names[i].topic;
mgmt_req_t *sub = &mgmt_event_subscribers[topic];
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,
mgmt_event_names[i].cmd,
sub->tag,
host, serv,
mgmt_event_names[i].help);
send_reply(req, buf, msg_len);
mgmt_help_events_row(req, buf, sub, mgmt_event_names[i].cmd, mgmt_event_names[i].help);
}
}
static void mgmt_help (mgmt_req_t *req, strbuf_t *buf, char *argv0, char *argv) {
size_t msg_len;
// TODO: want to keep the mgmt_handlers defintion const static, otherwise
// 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
* the type - allowing help replies to both read and write requests
@ -483,60 +313,14 @@ static void mgmt_help (mgmt_req_t *req, strbuf_t *buf, char *argv0, char *argv)
int i;
int nr_handlers = sizeof(mgmt_handlers) / sizeof(mgmt_handler_t);
for( i=0; i < nr_handlers; i++ ) {
msg_len = snprintf(buf->str, buf->size,
"{"
"\"_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);
mgmt_help_row(req, buf, mgmt_handlers[i].cmd, mgmt_handlers[i].help);
}
}
/*
* 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) {
strbuf_t *buf;
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 */
strncpy(cmdlinebuf, udp_buf, sizeof(cmdlinebuf)-1);
@ -547,71 +331,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 */
STRBUF_INIT(buf, udp_buf);
typechar = strtok(cmdlinebuf, " \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;
}
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;
}
mgmt_req_init2(req, buf, (char *)&cmdlinebuf);
if(req->type == N2N_MGMT_SUB) {
int handler;
lookup_handler(handler, mgmt_event_names, argv0);
lookup_handler(handler, mgmt_event_names, req->argv0);
if(handler == -1) {
mgmt_error(req, buf, "unknowntopic");
return;
@ -620,18 +344,18 @@ static void handleMgmtJson (mgmt_req_t *req, char *udp_buf, const int recvlen) {
int topic = mgmt_event_names[handler].topic;
if(mgmt_event_subscribers[topic].type == N2N_MGMT_SUB) {
send_json_1str(&mgmt_event_subscribers[topic], buf,
"unsubscribed", "topic", argv0);
send_json_1str(req, buf, "replacing", "topic", argv0);
"unsubscribed", "topic", req->argv0);
send_json_1str(req, buf, "replacing", "topic", req->argv0);
}
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;
}
int handler;
lookup_handler(handler, mgmt_handlers, argv0);
lookup_handler(handler, mgmt_handlers, req->argv0);
if(handler == -1) {
mgmt_error(req, buf, "unknowncmd");
return;
@ -648,11 +372,11 @@ static void handleMgmtJson (mgmt_req_t *req, char *udp_buf, const int recvlen) {
* that make our JSON invalid.
* - 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;
}
@ -680,7 +404,11 @@ void readFromMgmtSocket (n2n_edge_t *eee) {
uint32_t num = 0;
selection_criterion_str_t sel_buf;
req.sss = NULL;
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);
i = sizeof(req.sender_sock);

278
src/management.c Normal file
View File

@ -0,0 +1,278 @@
/*
* Common routines shared between the management interfaces
*
*/
#include <stdio.h>
#include <sys/socket.h>
#include <netdb.h>
// 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

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