Refactor as much as possible of sn_management to use shared code

This commit is contained in:
Hamish Coleman 2022-04-17 00:21:24 +01:00
parent d81908d6a2
commit 71dbaf0bdc

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;
} }