Merge pull request #947 from hamishcoleman/json_pubsub

Add JSON pubsub framework for edge
This commit is contained in:
Hamish Coleman 2022-02-12 10:59:52 +00:00 committed by GitHub
commit 4b8aaa7f6e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 1309 additions and 736 deletions

View File

@ -66,3 +66,17 @@ jobs:
# See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more
# detail
run: ctest -C $BUILD_TYPE
- if: ${{ failure() }}
name: Upload test data
uses: actions/upload-artifact@v2
with:
name: tests-out
path: tests
- if: ${{ failure() }}
name: Upload cmake test output
uses: actions/upload-artifact@v2
with:
name: tests-out
path: build/Testing

View File

@ -91,18 +91,18 @@ jobs:
./configure
shell: bash
- name: Run embedded tests
run: make test
- name: Run embedded unit tests
run: make test.units
shell: bash
- if: ${{ always() }}
- if: ${{ failure() }}
name: Move test outputs to an arch specific location
shell: bash
run: |
mkdir -p tests/${{ matrix.os }}
mv tests/*.out tests/${{ matrix.os }}
- if: ${{ always() }}
- if: ${{ failure() }}
name: Upload tests output
uses: actions/upload-artifact@v2
with:
@ -162,18 +162,18 @@ jobs:
./configure
shell: bash
- name: Run embedded tests
run: make test
- name: Run embedded unit tests
run: make test.units
shell: bash
- if: ${{ always() }}
- if: ${{ failure() }}
name: Move test outputs to an arch specific location
shell: bash
run: |
mkdir -p tests/${{ matrix.os }}
mv tests/*.out tests/${{ matrix.os }}
- if: ${{ always() }}
- if: ${{ failure() }}
name: Upload tests output
uses: actions/upload-artifact@v2
with:
@ -237,18 +237,18 @@ jobs:
./scripts/hack_fakeautoconf.sh
shell: bash
- name: Run embedded tests
run: make test
- name: Run embedded unit tests
run: make test.units
shell: bash
- if: ${{ always() }}
- if: ${{ failure() }}
name: Move test outputs to an arch specific location
shell: bash
run: |
mkdir -p tests/${{ matrix.os }}
mv tests/*.out tests/${{ matrix.os }}
- if: ${{ always() }}
- if: ${{ failure() }}
name: Upload tests output
uses: actions/upload-artifact@v2
with:

View File

@ -309,10 +309,21 @@ install(FILES ${PROJECT_BINARY_DIR}/doc/supernode.1.gz
DESTINATION /usr/share/man/man1)
install(FILES ${PROJECT_BINARY_DIR}/doc/n2n.7.gz
DESTINATION /usr/share/man/man7)
endif(DEFINED UNIX)
if (CMAKE_SYSTEM_NAME STREQUAL Linux)
# TODO:
# - Add the right dependancy so that the tests binaries get built first
enable_testing()
add_test(tests ${PROJECT_SOURCE_DIR}/scripts/test_harness.sh ${PROJECT_BINARY_DIR} ${PROJECT_SOURCE_DIR}/tests)
endif(DEFINED UNIX)
add_test(NAME unit
COMMAND ${CMAKE_COMMAND} -E env
TOPDIR=${PROJECT_SOURCE_DIR} BINDIR=${PROJECT_BINARY_DIR}
${PROJECT_SOURCE_DIR}/scripts/test_harness.sh ${PROJECT_SOURCE_DIR}/tests/tests_units.list
)
add_test(NAME integration
COMMAND ${CMAKE_COMMAND} -E env
TOPDIR=${PROJECT_SOURCE_DIR} BINDIR=${PROJECT_BINARY_DIR}
${PROJECT_SOURCE_DIR}/scripts/test_harness.sh ${PROJECT_SOURCE_DIR}/tests/tests_integration.list
)
endif()

View File

@ -84,16 +84,30 @@ N2N_DEPS=$(wildcard include/*.h) $(wildcard src/*.c) Makefile
# As source files pass the linter, they can be added here (If all the source
# is passing the linter tests, this can be refactored)
LINT_CCODE=\
include/curve25519.h \
include/edge_utils_win32.h \
include/header_encryption.h \
include/hexdump.h \
include/n2n_define.h \
include/n2n_wire.h \
include/network_traffic_filter.h \
include/pearson.h \
include/random_numbers.h \
include/sn_selection.h \
include/speck.h \
include/tf.h \
src/edge_management.c \
src/edge_utils_win32.c \
src/example_edge_embed_quick_edge_init.c \
src/header_encryption.c \
src/sn_management.c \
src/sn_selection.c \
src/transform_cc20.c \
src/transform_null.c \
src/tuntap_freebsd.c \
src/tuntap_linux.c \
src/tuntap_netbsd.c \
src/tuntap_osx.c \
src/wire.c \
tools/tests-auth.c \
tools/tests-compress.c \
@ -146,9 +160,8 @@ SUBDIRS+=tools
COVERAGEDIR?=coverage
.PHONY: $(SUBDIRS)
.PHONY: steps build push all clean distclean install test cover gcov build-dep
.PHONY: lint lint.python lint.ccode lint.shell lint.yaml
.PHONY: all
all: version $(APPS) $(DOCS) $(SUBDIRS)
# This allows breaking the build if the version.sh script discovers
@ -188,9 +201,16 @@ $(N2N_LIB): $(N2N_OBJS)
win32/n2n_win32.a: win32
test: tools
scripts/test_harness.sh
.PHONY: test test.units test.integration
test: test.units test.integration
test.units: tools
scripts/test_harness.sh tests/tests_units.list
test.integration: $(APPS)
scripts/test_harness.sh tests/tests_integration.list
.PHONY: lint lint.python lint.ccode lint.shell lint.yaml
lint: lint.python lint.ccode lint.shell lint.yaml
lint.python:
@ -209,6 +229,7 @@ lint.yaml:
# CFLAGS="-fprofile-arcs -ftest-coverage" LDFLAGS="--coverage"
# and run the desired tests. Ensure that package gcovr is installed
# and then run "make cover"
.PHONY: cover
cover:
mkdir -p $(COVERAGEDIR)
gcovr -s --html --html-details --output=$(COVERAGEDIR)/index.html
@ -217,11 +238,13 @@ cover:
# Unfortunately, these end up in the wrong directory due to the
# makefile layout
# The steps to use this are similar to the "make cover" above
.PHONY: gcov
gcov:
gcov $(N2N_OBJS)
$(MAKE) -C tools gcov
# This is a convinent target to use during development or from a CI/CD system
.PHONY: build-dep
build-dep:
ifeq ($(CONFIG_TARGET),generic)
sudo apt install $(BUILD_DEP)
@ -231,11 +254,13 @@ else
echo Not attempting to install dependancies for system $(CONFIG_TARGET)
endif
.PHONY: clean
clean:
rm -rf $(N2N_OBJS) $(N2N_LIB) $(APPS) $(DOCS) $(COVERAGEDIR)/ *.dSYM *~
rm -f tests/*.out src/*.gcno src/*.gcda
for dir in $(SUBDIRS); do $(MAKE) -C $$dir clean; done
.PHONY: distclean
distclean:
rm -f tests/*.out src/*.gcno src/*.gcda src/*.indent src/*.unc-backup*
rm -rf autom4te.cache/
@ -246,6 +271,7 @@ distclean:
rm -f packages/rpm/config.log packages/rpm/config.status
rm -f $(addprefix src/,$(APPS))
.PHONY: install
install: edge supernode edge.8.gz supernode.1.gz n2n.7.gz
echo "MANDIR=$(MANDIR)"
$(MKDIR) $(SBINDIR) $(MAN1DIR) $(MAN7DIR) $(MAN8DIR)
@ -261,6 +287,7 @@ DOCKER_IMAGE_NAME=ntop/supernode
DOCKER_IMAGE_VERSION=$N2N_VERSION_SHORT
N2N_COMMIT_HASH=$(shell scripts/version.sh hash)
.PHONY: default steps build push
default: steps
steps:

View File

@ -1,6 +1,6 @@
# Management API
This document is focused on the machine readable API interfaces.
This document is focused on the machine readable API interfaces.
Both the edge and the supernode provide a management interface UDP port.
These interfaces have some documentation on their non machine readable
@ -48,7 +48,7 @@ but this is intended for debugging.
The request is a single UDP packet containing one line of text with at least
three space separated fields. Any text after the third field is available for
the API method to use for additional parameters
the API method to use for additional parameters
Fields:
- Message Type
@ -58,15 +58,23 @@ Fields:
The maximum length of the entire line of text is 80 octets.
All request packets should generate a reply. However, this reply may simply
be an error.
### Message Type
This is a single octet that is either "r" for a read (or query) method
call or "w" for a write (or change) method call.
This is a single octet specifying the type:
- "r" for a read-only method (or one that does not need change permissions)
- "w" for a write method (or one that makes changes)
- "s" for a subscribe method to request this socket receive some events
To simplify the interface, the reply from both read and write calls to the
same method is expected to contain the same data. In the case of a write
call, the reply will contain the new state after making the requested change.
The subscribe and events message flow works with a different set of messages.
### Options
The options field is a colon separated set of options for this request. Only
@ -83,14 +91,16 @@ SubFields:
Each request provides a tag value. Any non error reply associated with this
request will include this tag value, allowing all related messages to be
collected within the client.
collected within the client. The tag will be truncated if needed by the
daemon, but there will be at least 8 octets of space available.
Where possible, the error replies will also include this tag, however some
errors occur before the tag is parsed.
The tag is not interpreted by the daemon, it is simply echoed back in all
the replies. It is expected to be a short string that the client chooses
to be unique amongst all recent or still outstanding requests.
the replies. It is expected to be a short string that the client knows
will be unique amongst all recent, still outstanding or subscription requests
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.
@ -127,11 +137,14 @@ e.g:
Each UDP packet in the reply is a complete and valid JSON dictionary
containing a fragment of information related to the entire reply.
Reply packets are generated both in response to requests and whenever
an event is published to a subscribed channel.
### Common metadata
There are two keys in each dictionary containing metadata. First
is the `_tag`, containing the Message Tag from the original request.
Second is the `_type` whic identifies the expected contents of this
Second is the `_type` which identifies the expected contents of this
packet.
### `_type: error`
@ -176,6 +189,47 @@ packets will be required.
e.g:
`{"_tag":"108","_type":"row","mode":"p2p","ip4addr":"10.135.98.84","macaddr":"86:56:21:E4:AA:39","sockaddr":"192.168.7.191:41701","desc":"client4","lastseen":1584682200}`
### `_type: subscribed`
Signals that the subscription request has been successfully completed.
Any future events on the requested channel will be asynchronously sent
as `event` packets using the same tag as the subscribe request.
### `_type: unsubscribed`
Only one management client can be subscribed to any given event topic, so if
another subscribe request arrives, the older client will be sent this message
to let them know that they have been replaced.
(In the future, this may also be sent as a reply to a explicit unsubscribe
request)
### `_type: replacing`
If a new subscription request will replace an existing one, this message is
sent to the new client to inform them that they have replaced an older
connection.
### `_type: event`
Asynchronous events will arrive with this message type, using the same tag as
the original subscribe request. Just like with the `row` packets, the non
metadata contents are entirely defined by the topic and the specific n2n
version.
## Subscribe API
A client can subscribe to events using a request with the type of "s".
Once a subscribe has been successfully completed, any events published
on that channel will be forwarded to the client.
Only one management client can be subscribed to any given event topic,
with newer subscriptions replacing older ones.
The special channel "debug" will receive copies of all events published.
Note that this is for debugging of events and the packets may not have
the same tag as the debug subscription.
## Authentication
Some API requests will make global changes to the running daemon and may

View File

@ -51,10 +51,6 @@ This shell script is a wrapper for the `uncrustify` C code style checker
which checks or applies a set of rules to the code. It is used during
the automated lint checks.
### `test_harness.sh`
This shell script is used to run automated tests during development.
### `n2n-gateway.sh`
A sample script to route all the host traffic towards a remote gateway,
@ -96,3 +92,26 @@ Manually test fetching and config:
/etc/munin/plugins/n2n_supernode_pkts
/etc/munin/plugins/n2n_supernode_pkts config
```
## Testing scripts
### `test_harness.sh`
This shell script is used to run automated tests during development. It is
run with a testlist filename - pointing at a file containing the list of
tests to run.
Each test needs a file containing the expected output `${TESTNAME}.expected`
which is expected to exist in the same directory as the testlist (this dir is
referred to as `${listdir}` below).
Each test is a program, searched for in several locations, including the
`${listdir}/../scripts` dir.
Each test is run with its output being sent to `*.out` files in the `listdir`
and compared with the expected output.
### `scripts/test_integration_supernode.sh`
This starts a supernode and runs an integration test on the Json API using
the `n2n-ctl` command.

View File

@ -285,6 +285,7 @@ int assign_one_ip_subnet (n2n_sn_t *sss, struct sn_community *comm);
const char* compression_str (uint8_t cmpr);
const char* transop_str (enum n2n_transform tr);
void handleMgmtJson (n2n_edge_t *eee, char *udp_buf, const struct sockaddr_in sender_sock);
void handleMgmtJson_sn (n2n_sn_t *sss, char *udp_buf, const struct sockaddr_in sender_sock);
void readFromMgmtSocket (n2n_edge_t *eee);
void mgmt_event_post (enum n2n_event_topic topic, int data0, void *data1);
#endif /* _N2N_H_ */

View File

@ -121,11 +121,17 @@ enum sn_purge {SN_PURGEABLE = 0, SN_UNPURGEABLE = 1};
#define N2N_EDGE_MGMT_PORT 5644
#define N2N_SN_MGMT_PORT 5645
enum n2n_mgmt_type {
N2N_MGMT_READ = 0,
N2N_MGMT_WRITE = 1,
enum n2n_event_topic {
N2N_EVENT_DEBUG = 0,
N2N_EVENT_TEST = 1,
N2N_EVENT_PEER = 2,
};
#define N2N_EVENT_PEER_PURGE 1
#define N2N_EVENT_PEER_CLEAR 2
#define N2N_EVENT_PEER_DEL_P2P 3
#define N2N_EVENT_PEER_ADD_P2P 4
#define N2N_MGMT_PASSWORD "n2n" /* default password for management port access (so far, json only) */

View File

@ -13,6 +13,7 @@ help() {
[ -z "$1" ] && help
[ "$1" = "-h" ] && help
[ "$1" = "--help" ] && help
INPLACE=0
if [ "$1" = "-i" ]; then

View File

@ -41,23 +41,37 @@ class JsonUDP():
def _rx(self, tagstr):
"""Wait for rx packets"""
# TODO: there are no timeouts with any of the recv calls
data, _ = self.sock.recvfrom(1024)
data = json.loads(data.decode('utf8'))
seen_begin = False
while not seen_begin:
# TODO: there are no timeouts with any of the recv calls
data, _ = self.sock.recvfrom(1024)
data = json.loads(data.decode('utf8'))
# TODO: We assume the first packet we get will be tagged for us
# and be either an "error" or a "begin"
assert(data['_tag'] == tagstr)
# TODO: We assume the first packet we get will be tagged for us
assert(data['_tag'] == tagstr)
if data['_type'] == 'error':
raise ValueError('Error: {}'.format(data['error']))
if data['_type'] == 'error':
raise ValueError('Error: {}'.format(data['error']))
assert(data['_type'] == 'begin')
if data['_type'] == 'replacing':
# a signal that we have evicted an earlier subscribe
continue
# Ideally, we would confirm that this is our "begin", but that
# would need the cmd passed into this method, and that would
# probably require parsing the cmdline passed to us :-(
# assert(data['cmd'] == cmd)
if data['_type'] == 'subscribe':
return True
if data['_type'] == 'begin':
seen_begin = True
# Ideally, we would confirm that this is our "begin", but that
# would need the cmd passed into this method, and that would
# probably require parsing the cmdline passed to us :-(
# assert(data['cmd'] == cmd)
continue
raise ValueError('Unknown data type {} from '
'edge'.format(data['_type']))
result = list()
error = None
@ -105,6 +119,21 @@ class JsonUDP():
def write(self, cmdline):
return self._call('w', cmdline)
def sub(self, cmdline):
return self._call('s', cmdline)
def readevent(self):
self.sock.settimeout(3600)
data, _ = self.sock.recvfrom(1024)
data = json.loads(data.decode('utf8'))
# assert(data['_tag'] == tagstr)
assert(data['_type'] == 'event')
del data['_tag']
del data['_type']
return data
def str_table(rows, columns, orderby):
"""Given an array of dicts, do a simple table print"""
@ -203,8 +232,17 @@ def subcmd_default(rpc, args):
cmdline = ' '.join([args.cmd] + args.args)
if args.write:
rows = rpc.write(cmdline)
else:
elif args.read:
rows = rpc.read(cmdline)
elif args.sub:
if not rpc.sub(cmdline):
raise ValueError('Could not subscribe')
while True:
event = rpc.readevent()
# FIXME: violates layering..
print(json.dumps(event, sort_keys=True, indent=4))
else:
raise ValueError('Unknown request type')
return json.dumps(rows, sort_keys=True, indent=4)
@ -228,6 +266,8 @@ def main():
group.add_argument('--write', action='store_true',
help='Make a write request (only to non pretty'
'printed cmds)')
group.add_argument('--sub', action='store_true',
help='Make a subscribe request')
ap.add_argument('cmd', action='store',
help='Command to run (try "help" for list)')
@ -236,6 +276,9 @@ def main():
args = ap.parse_args()
if not args.read and not args.write and not args.sub:
args.read = True
if args.raw or (args.cmd not in subcmds):
func = subcmd_default
else:

View File

@ -23,102 +23,31 @@ import base64
from http import HTTPStatus
import os
import sys
import importlib.machinery
import importlib.util
class JsonUDP():
"""encapsulate communication with the edge"""
def __init__(self, port):
self.address = "127.0.0.1"
self.port = port
self.tag = 0
self.key = None
self.debug = False
self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.sock.settimeout(1)
def import_filename(modulename, filename):
# look in the same dir as this script
pathname = os.path.join(os.path.dirname(os.path.abspath(__file__)),
filename)
loader = importlib.machinery.SourceFileLoader(modulename, pathname)
spec = importlib.util.spec_from_loader(modulename, loader)
module = importlib.util.module_from_spec(spec)
def _next_tag(self):
tagstr = str(self.tag)
self.tag = (self.tag + 1) % 1000
return tagstr
try:
loader.exec_module(module)
except FileNotFoundError:
print("Script {} not found".format(pathname), file=sys.stderr)
sys.exit(1)
return module
def _cmdstr(self, msgtype, cmdline):
"""Create the full command string to send"""
tagstr = self._next_tag()
options = [tagstr]
if self.key is not None:
options += ['1'] # Flags set for auth key field
options += [self.key]
optionsstr = ':'.join(options)
return tagstr, ' '.join((msgtype, optionsstr, cmdline))
def _rx(self, tagstr):
"""Wait for rx packets"""
# TODO: there are no timeouts with any of the recv calls
data, _ = self.sock.recvfrom(1024)
data = json.loads(data.decode('utf8'))
# TODO: We assume the first packet we get will be tagged for us
# and be either an "error" or a "begin"
assert(data['_tag'] == tagstr)
if data['_type'] == 'error':
raise ValueError('Error: {}'.format(data['error']))
assert(data['_type'] == 'begin')
# Ideally, we would confirm that this is our "begin", but that
# would need the cmd passed into this method, and that would
# probably require parsing the cmdline passed to us :-(
# assert(data['cmd'] == cmd)
result = list()
error = None
while True:
data, _ = self.sock.recvfrom(1024)
data = json.loads(data.decode('utf8'))
if data['_tag'] != tagstr:
# this packet is not for us, ignore it
continue
if data['_type'] == 'error':
# we still expect an end packet, so save the error
error = ValueError('Error: {}'.format(data['error']))
continue
if data['_type'] == 'end':
if error:
raise error
return result
if data['_type'] != 'row':
raise ValueError('Unknown data type {} from '
'edge'.format(data['_type']))
# remove our boring metadata
del data['_tag']
del data['_type']
if self.debug:
print(data)
result.append(data)
def _call(self, msgtype, cmdline):
"""Perform a rpc call"""
tagstr, msgstr = self._cmdstr(msgtype, cmdline)
self.sock.sendto(msgstr.encode('utf8'), (self.address, self.port))
return self._rx(tagstr)
def read(self, cmdline):
return self._call('r', cmdline)
def write(self, cmdline):
return self._call('w', cmdline)
# We share the implementation of the RPC class with the n2n-ctl script. We
# cannot just import the module as 'n2n-ctl' has a dash in its name :-(
JsonUDP = import_filename('n2nctl', 'n2n-ctl').JsonUDP
pages = {

View File

@ -1,40 +1,47 @@
#!/bin/sh
#
# This expects to find the tests in the tools dir and the expected results
# in the tests dir.
# Run with the name of a test list file.
#
# This expects to find the tests in the tools dir or scripts dir and the
# expected results in the tests dir.
TESTS="
tests-auth
tests-compress
tests-elliptic
tests-hashing
tests-transform
tests-wire
"
# boilerplate so we can support whaky cmake dirs
[ -z "$TOPDIR" ] && TOPDIR="."
[ -z "$BINDIR" ] && BINDIR="."
export TOPDIR
export BINDIR
TOOLSDIR=tools
TESTDATA=tests
if [ -z "$1" ]; then
echo need test list filename
exit 1
fi
TESTLIST="$1"
LISTDIR=$(dirname "$TESTLIST")
# Allow both dirs be overidden
[ -n "$1" ] && TOOLSDIR="$1"
[ -n "$2" ] && TESTDATA="$2"
# Confirm we have all the tools and data
for i in $TESTS; do
if [ ! -e "$TOOLSDIR/$i" ]; then
echo "Could not find test $TOOLSDIR/$i"
exit 1
fi
if [ ! -e "$TESTDATA/$i.expected" ]; then
echo "Could not find testdata $TESTDATA/$i.expected"
exit 1
fi
done
TESTS=$(sed -e "s/#.*//" "$TESTLIST")
# Actually run the tests
set -e
for i in $TESTS; do
echo "$TOOLSDIR/$i >$TESTDATA/$i.out"
"$TOOLSDIR/$i" >"$TESTDATA/$i.out"
cmp "$TESTDATA/$i.expected" "$TESTDATA/$i.out"
# Look in several places for the test program
if [ -e "$BINDIR/$i" ]; then
TEST="$BINDIR/$i"
elif [ -e "$BINDIR/tools/$i" ]; then
TEST="$BINDIR/tools/$i"
elif [ -e "$LISTDIR/../scripts/$i" ]; then
TEST="$LISTDIR/../scripts/$i"
else
echo "Could not find test $i"
exit 1
fi
if [ ! -e "$LISTDIR/$i.expected" ]; then
echo "Could not find testdata $LISTDIR/$i.expected"
exit 1
fi
echo "$TEST >$LISTDIR/$i.out"
set -e
"$TEST" >"$LISTDIR/$i.out"
cmp "$LISTDIR/$i.expected" "$LISTDIR/$i.out"
set +e
done

View File

@ -0,0 +1,49 @@
#!/bin/sh
#
# Do some quick tests via the Json API against the edge
#
AUTH=n2n
# boilerplate so we can support whaky cmake dirs
[ -z "$TOPDIR" ] && TOPDIR=.
[ -z "$BINDIR" ] && BINDIR=.
docmd() {
echo "###"
"$@"
echo
}
# start a supernode
docmd ${BINDIR}/supernode -v
# Start the edge in the background
docmd sudo ${BINDIR}/edge -l localhost:7654 -c test >/dev/null
# TODO:
# - send edge messages to stderr?
docmd ${TOPDIR}/scripts/n2n-ctl communities
docmd ${TOPDIR}/scripts/n2n-ctl packetstats
docmd ${TOPDIR}/scripts/n2n-ctl edges --raw
# TODO:
# docmd ${TOPDIR}/scripts/n2n-ctl supernodes --raw
# - need fixed mac address
# - need to mask out:
# - version string
# - last_seen timestamp
# - uptime
docmd ${TOPDIR}/scripts/n2n-ctl verbose
docmd ${TOPDIR}/scripts/n2n-ctl --write verbose 1 2>/dev/null
echo $?
docmd ${TOPDIR}/scripts/n2n-ctl -k $AUTH --write verbose 1
# looks strange, but we are querying the state of the "stop" verb
docmd ${TOPDIR}/scripts/n2n-ctl stop
# stop them both
docmd ${TOPDIR}/scripts/n2n-ctl -k $AUTH --write stop
docmd ${TOPDIR}/scripts/n2n-ctl -t 5645 -k $AUTH --write stop

View File

@ -0,0 +1,33 @@
#!/bin/sh
#
# Do some quick tests via the Json API against the supernode
#
AUTH=n2n
# boilerplate so we can support whaky cmake dirs
[ -z "$TOPDIR" ] && TOPDIR=.
[ -z "$BINDIR" ] && BINDIR=.
docmd() {
echo "###"
"$@"
echo
}
# start it running in the background
docmd ${BINDIR}/supernode -v
docmd ${TOPDIR}/scripts/n2n-ctl -t 5645 communities
docmd ${TOPDIR}/scripts/n2n-ctl -t 5645 packetstats
docmd ${TOPDIR}/scripts/n2n-ctl -t 5645 edges --raw
docmd ${TOPDIR}/scripts/n2n-ctl -t 5645 verbose
docmd ${TOPDIR}/scripts/n2n-ctl -t 5645 -k $AUTH --write verbose 1
# looks strange, but we are querying the state of the "stop" verb
docmd ${TOPDIR}/scripts/n2n-ctl -t 5645 stop
# stop it
docmd ${TOPDIR}/scripts/n2n-ctl -t 5645 -k $AUTH --write stop

View File

@ -19,95 +19,184 @@
#include "n2n.h"
#include "edge_utils_win32.h"
typedef struct strbuf {
size_t size;
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 n2n_mgmt_handler {
typedef struct mgmt_handler {
int flags;
char *cmd;
char *help;
void (*func)(n2n_edge_t *eee, char *udp_buf, struct sockaddr_in sender_sock, enum n2n_mgmt_type type, char *tag, char *argv0, char *argv);
} n2n_mgmt_handler_t;
void (*func)(mgmt_req_t *req, strbuf_t *buf, char *argv0, char *argv);
} mgmt_handler_t;
static void mgmt_error (n2n_edge_t *eee, char *udp_buf, const struct sockaddr_in sender_sock, char *tag, char *msg) {
size_t msg_len;
msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE,
"{"
"\"_tag\":\"%s\","
"\"_type\":\"error\","
"\"error\":\"%s\"}\n",
tag,
msg);
sendto(eee->udp_mgmt_sock, udp_buf, msg_len, 0,
(struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
/*
* 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));
}
static void mgmt_stop (n2n_edge_t *eee, char *udp_buf, const struct sockaddr_in sender_sock, enum n2n_mgmt_type type, char *tag, char *argv0, char *argv) {
size_t msg_len;
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);
}
if(type==N2N_MGMT_WRITE) {
*eee->keep_running = 0;
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) {
traceEvent(TRACE_DEBUG, "Unexpected call to event_debug");
return 0;
}
size_t event_test (strbuf_t *buf, char *tag, int data0, void *data1) {
size_t msg_len = gen_json_1str(buf, tag, "event", "test", (char *)data1);
return msg_len;
}
size_t event_peer (strbuf_t *buf, char *tag, int data0, void *data1) {
int action = data0;
struct peer_info *peer = (struct peer_info *)data1;
macstr_t mac_buf;
n2n_sock_str_t sockbuf;
/*
* Just the peer_info bits that are needed for lookup (maccaddr) or
* firewall and routing (sockaddr)
* If needed, other details can be fetched via the edges method call.
*/
return snprintf(buf->str, buf->size,
"{"
"\"_tag\":\"%s\","
"\"_type\":\"event\","
"\"action\":%i,"
"\"macaddr\":\"%s\","
"\"sockaddr\":\"%s\"}\n",
tag,
action,
(is_null_mac(peer->mac_addr)) ? "" : macaddr_str(mac_buf, peer->mac_addr),
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;
}
msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE,
"{"
"\"_tag\":\"%s\","
"\"_type\":\"row\","
"\"keep_running\":%u}\n",
tag,
*eee->keep_running);
sendto(eee->udp_mgmt_sock, udp_buf, msg_len, 0,
(struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
send_json_1uint(req, buf, "row", "keep_running", *req->eee->keep_running);
}
static void mgmt_verbose (n2n_edge_t *eee, char *udp_buf, const struct sockaddr_in sender_sock, enum n2n_mgmt_type type, char *tag, char *argv0, char *argv) {
size_t msg_len;
static void mgmt_verbose (mgmt_req_t *req, strbuf_t *buf, char *argv0, char *argv) {
if(type==N2N_MGMT_WRITE) {
if(req->type==N2N_MGMT_WRITE) {
if(argv) {
setTraceLevel(strtoul(argv, NULL, 0));
}
}
msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE,
"{"
"\"_tag\":\"%s\","
"\"_type\":\"row\","
"\"traceLevel\":%u}\n",
tag,
getTraceLevel());
sendto(eee->udp_mgmt_sock, udp_buf, msg_len, 0,
(struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
send_json_1uint(req, buf, "row", "traceLevel", getTraceLevel());
}
static void mgmt_communities (n2n_edge_t *eee, char *udp_buf, const struct sockaddr_in sender_sock, enum n2n_mgmt_type type, char *tag, char *argv0, char *argv) {
size_t msg_len;
static void mgmt_communities (mgmt_req_t *req, strbuf_t *buf, char *argv0, char *argv) {
if(eee->conf.header_encryption != HEADER_ENCRYPTION_NONE) {
mgmt_error(eee, udp_buf, sender_sock, tag, "noaccess");
if(req->eee->conf.header_encryption != HEADER_ENCRYPTION_NONE) {
mgmt_error(req, buf, "noaccess");
return;
}
msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE,
"{"
"\"_tag\":\"%s\","
"\"_type\":\"row\","
"\"community\":\"%s\"}",
tag,
eee->conf.community_name);
sendto(eee->udp_mgmt_sock, udp_buf, msg_len, 0,
(struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
send_json_1str(req, buf, "row", "community", (char *)req->eee->conf.community_name);
}
static void mgmt_supernodes (n2n_edge_t *eee, char *udp_buf, const struct sockaddr_in sender_sock, enum n2n_mgmt_type type, char *tag, char *argv0, char *argv) {
static void mgmt_supernodes (mgmt_req_t *req, strbuf_t *buf, char *argv0, char *argv) {
size_t msg_len;
struct peer_info *peer, *tmpPeer;
macstr_t mac_buf;
n2n_sock_str_t sockbuf;
selection_criterion_str_t sel_buf;
HASH_ITER(hh, eee->conf.supernodes, peer, tmpPeer) {
HASH_ITER(hh, req->eee->conf.supernodes, peer, tmpPeer) {
/*
* TODO:
@ -116,7 +205,7 @@ static void mgmt_supernodes (n2n_edge_t *eee, char *udp_buf, const struct sockad
* - do we care?
*/
msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE,
msg_len = snprintf(buf->str, buf->size,
"{"
"\"_tag\":\"%s\","
"\"_type\":\"row\","
@ -128,28 +217,27 @@ static void mgmt_supernodes (n2n_edge_t *eee, char *udp_buf, const struct sockad
"\"selection\":\"%s\","
"\"last_seen\":%li,"
"\"uptime\":%li}\n",
tag,
req->tag,
peer->version,
peer->purgeable,
(peer == eee->curr_sn) ? (eee->sn_wait ? 2 : 1 ) : 0,
(peer == req->eee->curr_sn) ? (req->eee->sn_wait ? 2 : 1 ) : 0,
is_null_mac(peer->mac_addr) ? "" : macaddr_str(mac_buf, peer->mac_addr),
sock_to_cstr(sockbuf, &(peer->sock)),
sn_selection_criterion_str(eee, sel_buf, peer),
sn_selection_criterion_str(req->eee, sel_buf, peer),
peer->last_seen,
peer->uptime);
sendto(eee->udp_mgmt_sock, udp_buf, msg_len, 0,
(struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
send_reply(req, buf, msg_len);
}
}
static void mgmt_edges_row (n2n_edge_t *eee, char *udp_buf, const struct sockaddr_in sender_sock, char *tag, struct peer_info *peer, char *mode) {
static void mgmt_edges_row (mgmt_req_t *req, strbuf_t *buf, struct peer_info *peer, char *mode) {
size_t msg_len;
macstr_t mac_buf;
n2n_sock_str_t sockbuf;
dec_ip_bit_str_t ip_bit_str = {'\0'};
msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE,
msg_len = snprintf(buf->str, buf->size,
"{"
"\"_tag\":\"%s\","
"\"_type\":\"row\","
@ -163,7 +251,7 @@ static void mgmt_edges_row (n2n_edge_t *eee, char *udp_buf, const struct sockadd
"\"last_p2p\":%li,\n"
"\"last_sent_query\":%li,\n"
"\"last_seen\":%li}\n",
tag,
req->tag,
mode,
(peer->dev_addr.net_addr == 0) ? "" : ip_subnet_to_str(ip_bit_str, &peer->dev_addr),
peer->purgeable,
@ -175,111 +263,113 @@ static void mgmt_edges_row (n2n_edge_t *eee, char *udp_buf, const struct sockadd
peer->last_sent_query,
peer->last_seen);
sendto(eee->udp_mgmt_sock, udp_buf, msg_len, 0 /*flags*/,
(struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
send_reply(req, buf, msg_len);
}
static void mgmt_edges (n2n_edge_t *eee, char *udp_buf, const struct sockaddr_in sender_sock, enum n2n_mgmt_type type, char *tag, char *argv0, char *argv) {
static void mgmt_edges (mgmt_req_t *req, strbuf_t *buf, char *argv0, char *argv) {
struct peer_info *peer, *tmpPeer;
// dump nodes with forwarding through supernodes
HASH_ITER(hh, eee->pending_peers, peer, tmpPeer) {
mgmt_edges_row(eee, udp_buf, sender_sock, tag, peer, "pSp");
HASH_ITER(hh, req->eee->pending_peers, peer, tmpPeer) {
mgmt_edges_row(req, buf, peer, "pSp");
}
// dump peer-to-peer nodes
HASH_ITER(hh, eee->known_peers, peer, tmpPeer) {
mgmt_edges_row(eee, udp_buf, sender_sock, tag, peer, "p2p");
HASH_ITER(hh, req->eee->known_peers, peer, tmpPeer) {
mgmt_edges_row(req, buf, peer, "p2p");
}
}
static void mgmt_timestamps (n2n_edge_t *eee, char *udp_buf, const struct sockaddr_in sender_sock, enum n2n_mgmt_type type, char *tag, char *argv0, char *argv) {
static void mgmt_timestamps (mgmt_req_t *req, strbuf_t *buf, char *argv0, char *argv) {
size_t msg_len;
msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE,
msg_len = snprintf(buf->str, buf->size,
"{"
"\"_tag\":\"%s\","
"\"_type\":\"row\","
"\"start_time\":%lu,"
"\"last_super\":%ld,"
"\"last_p2p\":%ld}\n",
tag,
eee->start_time,
eee->last_sup,
eee->last_p2p);
req->tag,
req->eee->start_time,
req->eee->last_sup,
req->eee->last_p2p);
sendto(eee->udp_mgmt_sock, udp_buf, msg_len, 0,
(struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
send_reply(req, buf, msg_len);
}
static void mgmt_packetstats (n2n_edge_t *eee, char *udp_buf, const struct sockaddr_in sender_sock, enum n2n_mgmt_type type, char *tag, char *argv0, char *argv) {
static void mgmt_packetstats (mgmt_req_t *req, strbuf_t *buf, char *argv0, char *argv) {
size_t msg_len;
msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE,
msg_len = snprintf(buf->str, buf->size,
"{"
"\"_tag\":\"%s\","
"\"_type\":\"row\","
"\"type\":\"transop\","
"\"tx_pkt\":%lu,"
"\"rx_pkt\":%lu}\n",
tag,
eee->transop.tx_cnt,
eee->transop.rx_cnt);
req->tag,
req->eee->transop.tx_cnt,
req->eee->transop.rx_cnt);
sendto(eee->udp_mgmt_sock, udp_buf, msg_len, 0,
(struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
send_reply(req, buf, msg_len);
msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE,
msg_len = snprintf(buf->str, buf->size,
"{"
"\"_tag\":\"%s\","
"\"_type\":\"row\","
"\"type\":\"p2p\","
"\"tx_pkt\":%u,"
"\"rx_pkt\":%u}\n",
tag,
eee->stats.tx_p2p,
eee->stats.rx_p2p);
req->tag,
req->eee->stats.tx_p2p,
req->eee->stats.rx_p2p);
sendto(eee->udp_mgmt_sock, udp_buf, msg_len, 0,
(struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
send_reply(req, buf, msg_len);
msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE,
msg_len = snprintf(buf->str, buf->size,
"{"
"\"_tag\":\"%s\","
"\"_type\":\"row\","
"\"type\":\"super\","
"\"tx_pkt\":%u,"
"\"rx_pkt\":%u}\n",
tag,
eee->stats.tx_sup,
eee->stats.rx_sup);
req->tag,
req->eee->stats.tx_sup,
req->eee->stats.rx_sup);
sendto(eee->udp_mgmt_sock, udp_buf, msg_len, 0,
(struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
send_reply(req, buf, msg_len);
msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE,
msg_len = snprintf(buf->str, buf->size,
"{"
"\"_tag\":\"%s\","
"\"_type\":\"row\","
"\"type\":\"super_broadcast\","
"\"tx_pkt\":%u,"
"\"rx_pkt\":%u}\n",
tag,
eee->stats.tx_sup_broadcast,
eee->stats.rx_sup_broadcast);
req->tag,
req->eee->stats.tx_sup_broadcast,
req->eee->stats.rx_sup_broadcast);
sendto(eee->udp_mgmt_sock, udp_buf, msg_len, 0,
(struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
send_reply(req, buf, msg_len);
}
static void mgmt_unimplemented (n2n_edge_t *eee, char *udp_buf, const struct sockaddr_in sender_sock, enum n2n_mgmt_type type, char *tag, char *argv0, char *argv) {
static void mgmt_post_test (mgmt_req_t *req, strbuf_t *buf, char *argv0, char *argv) {
mgmt_error(eee, udp_buf, sender_sock, tag, "unimplemented");
send_json_1str(req, buf, "row", "sending", "test");
mgmt_event_post(N2N_EVENT_TEST, -1, argv);
}
static void mgmt_help (n2n_edge_t *eee, char *udp_buf, const struct sockaddr_in sender_sock, enum n2n_mgmt_type type, char *tag, char *argv0, char *argv);
static void mgmt_unimplemented (mgmt_req_t *req, strbuf_t *buf, char *argv0, char *argv) {
n2n_mgmt_handler_t mgmt_handlers[] = {
mgmt_error(req, buf, "unimplemented");
}
// 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 const mgmt_handler_t mgmt_handlers[] = {
{ .cmd = "reload_communities", .flags = FLAG_WROK, .help = "Reserved for supernode", .func = mgmt_unimplemented},
{ .cmd = "stop", .flags = FLAG_WROK, .help = "Gracefully exit edge", .func = mgmt_stop},
@ -289,32 +379,129 @@ n2n_mgmt_handler_t mgmt_handlers[] = {
{ .cmd = "supernodes", .help = "List current supernodes", .func = mgmt_supernodes},
{ .cmd = "timestamps", .help = "Event timestamps", .func = mgmt_timestamps},
{ .cmd = "packetstats", .help = "traffic counters", .func = mgmt_packetstats},
{ .cmd = "post.test", .help = "send a test event", .func = mgmt_post_test},
{ .cmd = "help", .flags = FLAG_WROK, .help = "Show JSON commands", .func = mgmt_help},
{ .cmd = NULL },
{ .cmd = "help.events", .help = "Show available Subscribe topics", .func = mgmt_help_events},
};
static void mgmt_help (n2n_edge_t *eee, char *udp_buf, const struct sockaddr_in sender_sock, enum n2n_mgmt_type type, char *tag, char *argv0, char *argv) {
/* Current subscriber for each event topic */
static mgmt_req_t mgmt_event_subscribers[] = {
[N2N_EVENT_DEBUG] = { .eee = NULL, .type = N2N_MGMT_UNKNOWN, .tag = "\0" },
[N2N_EVENT_TEST] = { .eee = NULL, .type = N2N_MGMT_UNKNOWN, .tag = "\0" },
[N2N_EVENT_PEER] = { .eee = NULL, .type = N2N_MGMT_UNKNOWN, .tag = "\0" },
};
/* Map topic number to function */
static const size_t (*mgmt_events[])(strbuf_t *buf, char *tag, int data0, void *data1) = {
[N2N_EVENT_DEBUG] = event_debug,
[N2N_EVENT_TEST] = event_test,
[N2N_EVENT_PEER] = event_peer,
};
/* Allow help and subscriptions to use topic name */
static const mgmt_events_t mgmt_event_names[] = {
{ .cmd = "debug", .topic = N2N_EVENT_DEBUG, .help = "All events - for event debugging"},
{ .cmd = "test", .topic = N2N_EVENT_TEST, .help = "Used only by post.test"},
{ .cmd = "peer", .topic = N2N_EVENT_PEER, .help = "Changes to peer list"},
};
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];
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 = 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;
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);
}
}
static void mgmt_help (mgmt_req_t *req, strbuf_t *buf, char *argv0, char *argv) {
size_t msg_len;
n2n_mgmt_handler_t *handler;
/*
* Even though this command is readonly, we deliberately do not check
* the type - allowing help replies to both read and write requests
*/
for( handler=mgmt_handlers; handler->cmd; handler++ ) {
msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE,
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",
tag,
handler->cmd,
handler->help);
req->tag,
mgmt_handlers[i].cmd,
mgmt_handlers[i].help);
sendto(eee->udp_mgmt_sock, udp_buf, msg_len, 0,
(struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
send_reply(req, buf, msg_len);
}
}
@ -325,37 +512,39 @@ static void mgmt_help (n2n_edge_t *eee, char *udp_buf, const struct sockaddr_in
* Reads are not dangerous, so they are simply allowed
* Writes are possibly dangerous, so they need a fake password
*/
static int mgmt_auth (n2n_edge_t *eee, const struct sockaddr_in sender_sock, enum n2n_mgmt_type type, char *auth, char *argv0, char *argv) {
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(eee->conf.mgmt_password_hash == pearson_hash_64((uint8_t*)auth, strlen(auth))) {
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(type == N2N_MGMT_READ) {
if(req->type == N2N_MGMT_READ) {
return 1;
}
return 0;
}
void handleMgmtJson (n2n_edge_t *eee, char *udp_buf, const struct sockaddr_in sender_sock) {
static void handleMgmtJson (mgmt_req_t *req, char *udp_buf, const int recvlen) {
strbuf_t *buf;
char cmdlinebuf[80];
enum n2n_mgmt_type type;
char *typechar;
char *options;
char *argv0;
char *argv;
char *tag;
char *flagstr;
int flags;
char *auth;
n2n_mgmt_handler_t *handler;
size_t msg_len;
/* 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);
@ -363,32 +552,36 @@ void handleMgmtJson (n2n_edge_t *eee, char *udp_buf, const struct sockaddr_in se
traceEvent(TRACE_DEBUG, "mgmt json %s", cmdlinebuf);
/* 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(eee, udp_buf, sender_sock, "-1", "notype");
mgmt_error(req, buf, "notype");
return;
}
if(*typechar == 'r') {
type=N2N_MGMT_READ;
req->type=N2N_MGMT_READ;
} else if(*typechar == 'w') {
type=N2N_MGMT_WRITE;
req->type=N2N_MGMT_WRITE;
} else if(*typechar == 's') {
req->type=N2N_MGMT_SUB;
} else {
/* dunno how we got here */
mgmt_error(eee, udp_buf, sender_sock, "-1", "badtype");
mgmt_error(req, buf, "badtype");
return;
}
/* Extract the tag to use in all reply packets */
options = strtok(NULL, " \r\n");
if(!options) {
mgmt_error(eee, udp_buf, sender_sock, "-1", "nooptions");
mgmt_error(req, buf, "nooptions");
return;
}
argv0 = strtok(NULL, " \r\n");
if(!argv0) {
mgmt_error(eee, udp_buf, sender_sock, "-1", "nocmd");
mgmt_error(req, buf, "nocmd");
return;
}
@ -401,7 +594,10 @@ void handleMgmtJson (n2n_edge_t *eee, char *udp_buf, const struct sockaddr_in se
/*
* There might be an auth token mixed in with the tag
*/
tag = strtok(options, ":");
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);
@ -416,23 +612,41 @@ void handleMgmtJson (n2n_edge_t *eee, char *udp_buf, const struct sockaddr_in se
auth = NULL;
}
if(!mgmt_auth(eee, sender_sock, type, auth, argv0, argv)) {
mgmt_error(eee, udp_buf, sender_sock, tag, "badauth");
if(!mgmt_auth(req, auth, argv0, argv)) {
mgmt_error(req, buf, "badauth");
return;
}
for( handler=mgmt_handlers; handler->cmd; handler++ ) {
if(0 == strcmp(handler->cmd, argv0)) {
break;
if(req->type == N2N_MGMT_SUB) {
int handler;
lookup_handler(handler, mgmt_event_names, argv0);
if(handler == -1) {
mgmt_error(req, buf, "unknowntopic");
return;
}
}
if(!handler->cmd) {
mgmt_error(eee, udp_buf, sender_sock, tag, "unknowncmd");
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);
}
memcpy(&mgmt_event_subscribers[topic], req, sizeof(*req));
send_json_1str(req, buf, "subscribe", "topic", argv0);
return;
}
if((type==N2N_MGMT_WRITE) && !(handler->flags & FLAG_WROK)) {
mgmt_error(eee, udp_buf, sender_sock, tag, "readonly");
int handler;
lookup_handler(handler, mgmt_handlers, argv0);
if(handler == -1) {
mgmt_error(req, buf, "unknowncmd");
return;
}
if((req->type==N2N_MGMT_WRITE) && !(mgmt_handlers[handler].flags & FLAG_WROK)) {
mgmt_error(req, buf, "readonly");
return;
}
@ -442,16 +656,243 @@ void handleMgmtJson (n2n_edge_t *eee, char *udp_buf, const struct sockaddr_in se
* that make our JSON invalid.
* - do we care?
*/
msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE,
"{\"_tag\":\"%s\",\"_type\":\"begin\",\"cmd\":\"%s\"}\n", tag, argv0);
sendto(eee->udp_mgmt_sock, udp_buf, msg_len, 0,
(struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
send_json_1str(req, buf, "begin", "cmd", argv0);
handler->func(eee, udp_buf, sender_sock, type, tag, argv0, argv);
mgmt_handlers[handler].func(req, buf, argv0, argv);
msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE,
"{\"_tag\":\"%s\",\"_type\":\"end\"}\n", tag);
sendto(eee->udp_mgmt_sock, udp_buf, msg_len, 0,
(struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
send_json_1str(req, buf, "end", "cmd", argv0);
return;
}
/** Read a datagram from the management UDP socket and take appropriate
* action. */
void readFromMgmtSocket (n2n_edge_t *eee) {
char udp_buf[N2N_PKT_BUF_SIZE]; /* Compete UDP packet */
ssize_t recvlen;
/* ssize_t sendlen; */
mgmt_req_t req;
socklen_t i;
size_t msg_len;
time_t now;
struct peer_info *peer, *tmpPeer;
macstr_t mac_buf;
char time_buf[10]; /* 9 digits + 1 terminating zero */
char uptime_buf[11]; /* 10 digits + 1 terminating zero */
/* dec_ip_bit_str_t ip_bit_str = {'\0'}; */
/* dec_ip_str_t ip_str = {'\0'}; */
in_addr_t net;
n2n_sock_str_t sockbuf;
uint32_t num_pending_peers = 0;
uint32_t num_known_peers = 0;
uint32_t num = 0;
selection_criterion_str_t sel_buf;
req.eee = eee;
now = time(NULL);
i = sizeof(req.sender_sock);
recvlen = recvfrom(eee->udp_mgmt_sock, udp_buf, N2N_PKT_BUF_SIZE, 0 /*flags*/,
(struct sockaddr *) &req.sender_sock, (socklen_t *) &i);
if(recvlen < 0) {
traceEvent(TRACE_WARNING, "mgmt recvfrom failed: %d - %s", errno, strerror(errno));
return; /* failed to receive data from UDP */
}
/* avoid parsing any uninitialized junk from the stack */
udp_buf[recvlen] = 0;
if((0 == memcmp(udp_buf, "help", 4)) || (0 == memcmp(udp_buf, "?", 1))) {
strbuf_t *buf;
STRBUF_INIT(buf, &udp_buf);
msg_len = snprintf(buf->str, buf->size,
"Help for edge management console:\n"
"\tstop | Gracefully exit edge\n"
"\thelp | This help message\n"
"\t+verb | Increase verbosity of logging\n"
"\t-verb | Decrease verbosity of logging\n"
"\tr ... | start query with JSON reply\n"
"\tw ... | start update with JSON reply\n"
"\ts ... | subscribe to event channel JSON reply\n"
"\t<enter> | Display statistics\n\n");
send_reply(&req, buf, msg_len);
return;
}
if(0 == memcmp(udp_buf, "stop", 4)) {
traceEvent(TRACE_NORMAL, "stop command received");
*eee->keep_running = 0;
return;
}
if(0 == memcmp(udp_buf, "+verb", 5)) {
setTraceLevel(getTraceLevel() + 1);
traceEvent(TRACE_NORMAL, "+verb traceLevel=%u", (unsigned int) getTraceLevel());
strbuf_t *buf;
STRBUF_INIT(buf, &udp_buf);
msg_len = snprintf(buf->str, buf->size,
"> +OK traceLevel=%u\n", (unsigned int) getTraceLevel());
send_reply(&req, buf, msg_len);
return;
}
if(0 == memcmp(udp_buf, "-verb", 5)) {
strbuf_t *buf;
STRBUF_INIT(buf, &udp_buf);
if(getTraceLevel() > 0) {
setTraceLevel(getTraceLevel() - 1);
msg_len = snprintf(buf->str, buf->size,
"> -OK traceLevel=%u\n", getTraceLevel());
} else {
msg_len = snprintf(buf->str, buf->size,
"> -NOK traceLevel=%u\n", getTraceLevel());
}
traceEvent(TRACE_NORMAL, "-verb traceLevel=%u", (unsigned int) getTraceLevel());
send_reply(&req, buf, msg_len);
return;
}
if((udp_buf[0] >= 'a' && udp_buf[0] <= 'z') && (udp_buf[1] == ' ')) {
/* this is a JSON request */
handleMgmtJson(&req, udp_buf, recvlen);
return;
}
traceEvent(TRACE_DEBUG, "mgmt status requested");
msg_len = 0;
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_len),
"COMMUNITY '%s'\n\n",
(eee->conf.header_encryption == HEADER_ENCRYPTION_NONE) ? (char*)eee->conf.community_name : "-- header encrypted --");
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_len),
" ### | TAP | MAC | EDGE | HINT | LAST SEEN | UPTIME\n");
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_len),
"=============================================================================================================\n");
// dump nodes with forwarding through supernodes
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_len),
"SUPERNODE FORWARD\n");
num = 0;
HASH_ITER(hh, eee->pending_peers, peer, tmpPeer) {
++num_pending_peers;
net = htonl(peer->dev_addr.net_addr);
snprintf(time_buf, sizeof(time_buf), "%9u", (unsigned int)(now - peer->last_seen));
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_len),
"%4u | %-15s | %-17s | %-21s | %-15s | %9s |\n",
++num,
(peer->dev_addr.net_addr == 0) ? "" : inet_ntoa(*(struct in_addr *) &net),
(is_null_mac(peer->mac_addr)) ? "" : macaddr_str(mac_buf, peer->mac_addr),
sock_to_cstr(sockbuf, &(peer->sock)),
peer->dev_desc,
(peer->last_seen) ? time_buf : "");
sendto(eee->udp_mgmt_sock, udp_buf, msg_len, 0,
(struct sockaddr *) &req.sender_sock, sizeof(struct sockaddr_in));
msg_len = 0;
}
// dump peer-to-peer nodes
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_len),
"-------------------------------------------------------------------------------------------------------------\n");
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_len),
"PEER TO PEER\n");
num = 0;
HASH_ITER(hh, eee->known_peers, peer, tmpPeer) {
++num_known_peers;
net = htonl(peer->dev_addr.net_addr);
snprintf(time_buf, sizeof(time_buf), "%9u", (unsigned int)(now - peer->last_seen));
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_len),
"%4u | %-15s | %-17s | %-21s | %-15s | %9s |\n",
++num,
(peer->dev_addr.net_addr == 0) ? "" : inet_ntoa(*(struct in_addr *) &net),
(is_null_mac(peer->mac_addr)) ? "" : macaddr_str(mac_buf, peer->mac_addr),
sock_to_cstr(sockbuf, &(peer->sock)),
peer->dev_desc,
(peer->last_seen) ? time_buf : "");
sendto(eee->udp_mgmt_sock, udp_buf, msg_len, 0,
(struct sockaddr *) &req.sender_sock, sizeof(struct sockaddr_in));
msg_len = 0;
}
// dump supernodes
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_len),
"-------------------------------------------------------------------------------------------------------------\n");
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_len),
"SUPERNODES\n");
HASH_ITER(hh, eee->conf.supernodes, peer, tmpPeer) {
net = htonl(peer->dev_addr.net_addr);
snprintf(time_buf, sizeof(time_buf), "%9u", (unsigned int)(now - peer->last_seen));
snprintf(uptime_buf, sizeof(uptime_buf), "%10u", (unsigned int)(peer->uptime));
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_len),
"%-19s %1s%1s | %-17s | %-21s | %-15s | %9s | %10s\n",
peer->version,
(peer->purgeable == SN_UNPURGEABLE) ? "l" : "",
(peer == eee->curr_sn) ? (eee->sn_wait ? "." : "*" ) : "",
is_null_mac(peer->mac_addr) ? "" : macaddr_str(mac_buf, peer->mac_addr),
sock_to_cstr(sockbuf, &(peer->sock)),
sn_selection_criterion_str(eee, sel_buf, peer),
(peer->last_seen) ? time_buf : "",
(peer->uptime) ? uptime_buf : "");
sendto(eee->udp_mgmt_sock, udp_buf, msg_len, 0,
(struct sockaddr *) &req.sender_sock, sizeof(struct sockaddr_in));
msg_len = 0;
}
// further stats
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_len),
"=============================================================================================================\n");
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_len),
"uptime %lu | ",
time(NULL) - eee->start_time);
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_len),
"pend_peers %u | ",
num_pending_peers);
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_len),
"known_peers %u | ",
num_known_peers);
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_len),
"transop %u,%u\n",
(unsigned int) eee->transop.tx_cnt,
(unsigned int) eee->transop.rx_cnt);
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_len),
"super %u,%u | ",
(unsigned int) eee->stats.tx_sup,
(unsigned int) eee->stats.rx_sup);
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_len),
"p2p %u,%u\n",
(unsigned int) eee->stats.tx_p2p,
(unsigned int) eee->stats.rx_p2p);
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_len),
"last_super %ld sec ago | ",
(now - eee->last_sup));
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_len),
"last_p2p %ld sec ago\n",
(now - eee->last_p2p));
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_len),
"\nType \"help\" to see more commands.\n\n");
sendto(eee->udp_mgmt_sock, udp_buf, msg_len, 0,
(struct sockaddr *) &req.sender_sock, sizeof(struct sockaddr_in));
}

View File

@ -811,6 +811,7 @@ static void peer_set_p2p_confirmed (n2n_edge_t * eee,
scan_tmp = find_peer_by_sock(peer, eee->known_peers);
if(scan_tmp != NULL) {
HASH_DEL(eee->known_peers, scan_tmp);
mgmt_event_post(N2N_EVENT_PEER,N2N_EVENT_PEER_DEL_P2P,scan);
free(scan);
scan = scan_tmp;
memcpy(scan->mac_addr, mac, sizeof(n2n_mac_t));
@ -828,6 +829,7 @@ static void peer_set_p2p_confirmed (n2n_edge_t * eee,
HASH_ADD_PEER(eee->known_peers, scan);
scan->last_p2p = now;
mgmt_event_post(N2N_EVENT_PEER,N2N_EVENT_PEER_ADD_P2P,scan);
traceEvent(TRACE_DEBUG, "p2p connection established: %s [%s]",
macaddr_str(mac_buf, mac),
@ -994,6 +996,7 @@ static void check_known_peer_sock_change (n2n_edge_t *eee,
sock_to_cstr(sockbuf2, peer));
/* The peer has changed public socket. It can no longer be assumed to be reachable. */
HASH_DEL(eee->known_peers, scan);
mgmt_event_post(N2N_EVENT_PEER,N2N_EVENT_PEER_DEL_P2P,scan);
free(scan);
register_with_new_peer(eee, from_supernode, via_multicast, mac, dev_addr, dev_desc, peer);
@ -1821,238 +1824,6 @@ static char *get_ip_from_arp (dec_ip_str_t buf, const n2n_mac_t req_mac) {
#endif
#endif
/** Read a datagram from the management UDP socket and take appropriate
* action. */
static void readFromMgmtSocket (n2n_edge_t *eee) {
char udp_buf[N2N_PKT_BUF_SIZE]; /* Compete UDP packet */
ssize_t recvlen;
/* ssize_t sendlen; */
struct sockaddr_in sender_sock;
socklen_t i;
size_t msg_len;
time_t now;
struct peer_info *peer, *tmpPeer;
macstr_t mac_buf;
char time_buf[10]; /* 9 digits + 1 terminating zero */
char uptime_buf[11]; /* 10 digits + 1 terminating zero */
/* dec_ip_bit_str_t ip_bit_str = {'\0'}; */
/* dec_ip_str_t ip_str = {'\0'}; */
in_addr_t net;
n2n_sock_str_t sockbuf;
uint32_t num_pending_peers = 0;
uint32_t num_known_peers = 0;
uint32_t num = 0;
selection_criterion_str_t sel_buf;
now = time(NULL);
i = sizeof(sender_sock);
recvlen = recvfrom(eee->udp_mgmt_sock, udp_buf, N2N_PKT_BUF_SIZE, 0/*flags*/,
(struct sockaddr *) &sender_sock, (socklen_t *) &i);
if(recvlen < 0) {
traceEvent(TRACE_WARNING, "mgmt recvfrom failed: %d - %s", errno, strerror(errno));
return; /* failed to receive data from UDP */
}
/* avoid parsing any uninitialized junk from the stack */
udp_buf[recvlen] = 0;
if((0 == memcmp(udp_buf, "help", 4)) || (0 == memcmp(udp_buf, "?", 1))) {
msg_len = 0;
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_len),
"Help for edge management console:\n"
"\tstop | Gracefully exit edge\n"
"\thelp | This help message\n"
"\t+verb | Increase verbosity of logging\n"
"\t-verb | Decrease verbosity of logging\n"
"\tr ... | start query with JSON reply\n"
"\tw ... | start update with JSON reply\n"
"\t<enter> | Display statistics\n\n");
sendto(eee->udp_mgmt_sock, udp_buf, msg_len, 0/*flags*/,
(struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
return;
}
if(0 == memcmp(udp_buf, "stop", 4)) {
traceEvent(TRACE_NORMAL, "stop command received");
*eee->keep_running = 0;
return;
}
if(0 == memcmp(udp_buf, "+verb", 5)) {
msg_len = 0;
setTraceLevel(getTraceLevel() + 1);
traceEvent(TRACE_NORMAL, "+verb traceLevel=%u", (unsigned int) getTraceLevel());
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_len),
"> +OK traceLevel=%u\n", (unsigned int) getTraceLevel());
sendto(eee->udp_mgmt_sock, udp_buf, msg_len, 0/*flags*/,
(struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
return;
}
if(0 == memcmp(udp_buf, "-verb", 5)) {
msg_len = 0;
if(getTraceLevel() > 0) {
setTraceLevel(getTraceLevel() - 1);
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_len),
"> -OK traceLevel=%u\n", getTraceLevel());
} else {
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_len),
"> -NOK traceLevel=%u\n", getTraceLevel());
}
traceEvent(TRACE_NORMAL, "-verb traceLevel=%u", (unsigned int) getTraceLevel());
sendto(eee->udp_mgmt_sock, udp_buf, msg_len, 0/*flags*/,
(struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
return;
}
if((udp_buf[0] == 'r' || udp_buf[0] == 'w') && (udp_buf[1] == ' ')) {
/* this is a JSON request */
handleMgmtJson(eee, udp_buf, sender_sock);
return;
}
traceEvent(TRACE_DEBUG, "mgmt status requested");
msg_len = 0;
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_len),
"COMMUNITY '%s'\n\n",
(eee->conf.header_encryption == HEADER_ENCRYPTION_NONE) ? (char*)eee->conf.community_name : "-- header encrypted --");
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_len),
" ### | TAP | MAC | EDGE | HINT | LAST SEEN | UPTIME\n");
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_len),
"=============================================================================================================\n");
// dump nodes with forwarding through supernodes
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_len),
"SUPERNODE FORWARD\n");
num = 0;
HASH_ITER(hh, eee->pending_peers, peer, tmpPeer) {
++num_pending_peers;
net = htonl(peer->dev_addr.net_addr);
snprintf(time_buf, sizeof(time_buf), "%9u", (unsigned int)(now - peer->last_seen));
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_len),
"%4u | %-15s | %-17s | %-21s | %-15s | %9s |\n",
++num,
(peer->dev_addr.net_addr == 0) ? "" : inet_ntoa(*(struct in_addr *) &net),
(is_null_mac(peer->mac_addr)) ? "" : macaddr_str(mac_buf, peer->mac_addr),
sock_to_cstr(sockbuf, &(peer->sock)),
peer->dev_desc,
(peer->last_seen) ? time_buf : "");
sendto(eee->udp_mgmt_sock, udp_buf, msg_len, 0/*flags*/,
(struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
msg_len = 0;
}
// dump peer-to-peer nodes
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_len),
"-------------------------------------------------------------------------------------------------------------\n");
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_len),
"PEER TO PEER\n");
num = 0;
HASH_ITER(hh, eee->known_peers, peer, tmpPeer) {
++num_known_peers;
net = htonl(peer->dev_addr.net_addr);
snprintf(time_buf, sizeof(time_buf), "%9u", (unsigned int)(now - peer->last_seen));
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_len),
"%4u | %-15s | %-17s | %-21s | %-15s | %9s |\n",
++num,
(peer->dev_addr.net_addr == 0) ? "" : inet_ntoa(*(struct in_addr *) &net),
(is_null_mac(peer->mac_addr)) ? "" : macaddr_str(mac_buf, peer->mac_addr),
sock_to_cstr(sockbuf, &(peer->sock)),
peer->dev_desc,
(peer->last_seen) ? time_buf : "");
sendto(eee->udp_mgmt_sock, udp_buf, msg_len, 0/*flags*/,
(struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
msg_len = 0;
}
// dump supernodes
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_len),
"-------------------------------------------------------------------------------------------------------------\n");
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_len),
"SUPERNODES\n");
HASH_ITER(hh, eee->conf.supernodes, peer, tmpPeer) {
net = htonl(peer->dev_addr.net_addr);
snprintf(time_buf, sizeof(time_buf), "%9u", (unsigned int)(now - peer->last_seen));
snprintf(uptime_buf, sizeof(uptime_buf), "%10u", (unsigned int)(peer->uptime));
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_len),
"%-19s %1s%1s | %-17s | %-21s | %-15s | %9s | %10s\n",
peer->version,
(peer->purgeable == SN_UNPURGEABLE) ? "l" : "",
(peer == eee->curr_sn) ? (eee->sn_wait ? "." : "*" ) : "",
is_null_mac(peer->mac_addr) ? "" : macaddr_str(mac_buf, peer->mac_addr),
sock_to_cstr(sockbuf, &(peer->sock)),
sn_selection_criterion_str(eee, sel_buf, peer),
(peer->last_seen) ? time_buf : "",
(peer->uptime) ? uptime_buf : "");
sendto(eee->udp_mgmt_sock, udp_buf, msg_len, 0,
(struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
msg_len = 0;
}
// further stats
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_len),
"=============================================================================================================\n");
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_len),
"uptime %lu | ",
time(NULL) - eee->start_time);
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_len),
"pend_peers %u | ",
num_pending_peers);
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_len),
"known_peers %u | ",
num_known_peers);
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_len),
"transop %u,%u\n",
(unsigned int) eee->transop.tx_cnt,
(unsigned int) eee->transop.rx_cnt);
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_len),
"super %u,%u | ",
(unsigned int) eee->stats.tx_sup,
(unsigned int) eee->stats.rx_sup);
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_len),
"p2p %u,%u\n",
(unsigned int) eee->stats.tx_p2p,
(unsigned int) eee->stats.rx_p2p);
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_len),
"last_super %ld sec ago | ",
(now - eee->last_sup));
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_len),
"last_p2p %ld sec ago\n",
(now - eee->last_p2p));
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_len),
"\nType \"help\" to see more commands.\n\n");
/* sendlen = */ sendto(eee->udp_mgmt_sock, udp_buf, msg_len, 0/*flags*/,
(struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
}
/* ************************************** */
static int check_query_peer_info (n2n_edge_t *eee, time_t now, n2n_mac_t mac) {
@ -2112,6 +1883,7 @@ static int find_peer_destination (n2n_edge_t * eee,
* since the peer address may have changed. */
traceEvent(TRACE_DEBUG, "refreshing idle known peer");
HASH_DEL(eee->known_peers, scan);
mgmt_event_post(N2N_EVENT_PEER,N2N_EVENT_PEER_DEL_P2P,scan);
free(scan);
/* NOTE: registration will be performed upon the receival of the next response packet */
} else {

View File

@ -636,6 +636,8 @@ size_t purge_peer_list (struct peer_info **peer_list,
}
}
HASH_DEL(*peer_list, scan);
mgmt_event_post(N2N_EVENT_PEER,N2N_EVENT_PEER_PURGE,scan);
/* FIXME: generates events for more than just p2p */
retval++;
free(scan);
}
@ -652,6 +654,8 @@ size_t clear_peer_list (struct peer_info ** peer_list) {
HASH_ITER(hh, *peer_list, scan, tmp) {
HASH_DEL(*peer_list, scan);
mgmt_event_post(N2N_EVENT_PEER,N2N_EVENT_PEER_CLEAR,scan);
/* FIXME: generates events for more than just p2p */
retval++;
free(scan);
}

View File

@ -26,13 +26,18 @@
int load_allowed_sn_community (n2n_sn_t *sss); /* defined in sn_utils.c */
enum n2n_mgmt_type {
N2N_MGMT_READ = 0,
N2N_MGMT_WRITE = 1,
};
#define FLAG_WROK 1
typedef struct n2n_mgmt_handler {
typedef struct mgmt_handler {
int flags;
char *cmd;
char *help;
void (*func)(n2n_sn_t *sss, char *udp_buf, struct sockaddr_in sender_sock, enum n2n_mgmt_type type, char *tag, char *argv0, char *argv);
} n2n_mgmt_handler_t;
} 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;
@ -263,7 +268,7 @@ static void mgmt_unimplemented (n2n_sn_t *sss, char *udp_buf, const struct socka
static void mgmt_help (n2n_sn_t *sss, char *udp_buf, const struct sockaddr_in sender_sock, enum n2n_mgmt_type type, char *tag, char *argv0, char *argv);
n2n_mgmt_handler_t mgmt_handlers[] = {
mgmt_handler_t mgmt_handlers[] = {
{ .cmd = "supernodes", .help = "Reserved for edge", .func = mgmt_unimplemented},
{ .cmd = "stop", .flags = FLAG_WROK, .help = "Gracefully exit edge", .func = mgmt_stop},
@ -279,7 +284,7 @@ n2n_mgmt_handler_t mgmt_handlers[] = {
static void mgmt_help (n2n_sn_t *sss, char *udp_buf, const struct sockaddr_in sender_sock, enum n2n_mgmt_type type, char *tag, char *argv0, char *argv) {
size_t msg_len;
n2n_mgmt_handler_t *handler;
mgmt_handler_t *handler;
/*
* Even though this command is readonly, we deliberately do not check
@ -326,7 +331,7 @@ static int mgmt_auth (n2n_sn_t *sss, const struct sockaddr_in sender_sock, enum
return 0;
}
void handleMgmtJson_sn (n2n_sn_t *sss, char *udp_buf, const struct sockaddr_in sender_sock) {
static void handleMgmtJson (n2n_sn_t *sss, char *udp_buf, const struct sockaddr_in sender_sock) {
char cmdlinebuf[80];
enum n2n_mgmt_type type;
@ -338,7 +343,7 @@ void handleMgmtJson_sn (n2n_sn_t *sss, char *udp_buf, const struct sockaddr_in s
char *flagstr;
int flags;
char *auth;
n2n_mgmt_handler_t *handler;
mgmt_handler_t *handler;
size_t msg_len;
/* save a copy of the commandline before we reuse the udp_buf */
@ -439,3 +444,164 @@ void handleMgmtJson_sn (n2n_sn_t *sss, char *udp_buf, const struct sockaddr_in s
(struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
return;
}
static int sendto_mgmt (n2n_sn_t *sss,
const struct sockaddr_in *sender_sock,
const uint8_t *mgmt_buf,
size_t mgmt_size) {
ssize_t r = sendto(sss->mgmt_sock, (void *)mgmt_buf, mgmt_size, 0 /*flags*/,
(struct sockaddr *)sender_sock, sizeof (struct sockaddr_in));
if(r <= 0) {
++(sss->stats.errors);
traceEvent(TRACE_ERROR, "sendto_mgmt : sendto failed. %s", strerror(errno));
return -1;
}
return 0;
}
int process_mgmt (n2n_sn_t *sss,
const struct sockaddr_in *sender_sock,
char *mgmt_buf,
size_t mgmt_size,
time_t now) {
char resbuf[N2N_SN_PKTBUF_SIZE];
size_t ressize = 0;
uint32_t num_edges = 0;
uint32_t num_comm = 0;
uint32_t num = 0;
struct sn_community *community, *tmp;
struct peer_info *peer, *tmpPeer;
macstr_t mac_buf;
n2n_sock_str_t sockbuf;
char time_buf[10]; /* 9 digits + 1 terminating zero */
dec_ip_bit_str_t ip_bit_str = {'\0'};
traceEvent(TRACE_DEBUG, "process_mgmt");
/* avoid parsing any uninitialized junk from the stack */
mgmt_buf[mgmt_size] = 0;
// process input, if any
if((0 == memcmp(mgmt_buf, "help", 4)) || (0 == memcmp(mgmt_buf, "?", 1))) {
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize,
"Help for supernode management console:\n"
"\thelp | This help message\n"
"\treload_communities | Reloads communities and user's public keys\n"
"\t<enter> | Display status and statistics\n");
sendto_mgmt(sss, sender_sock, (const uint8_t *) resbuf, ressize);
return 0; /* no status output afterwards */
}
if(0 == memcmp(mgmt_buf, "reload_communities", 18)) {
if(!sss->community_file) {
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize,
"No community file provided (-c command line option)\n");
sendto_mgmt(sss, sender_sock, (const uint8_t *) resbuf, ressize);
return 0; /* no status output afterwards */
}
traceEvent(TRACE_NORMAL, "'reload_communities' command");
if(load_allowed_sn_community(sss)) {
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize,
"Error while re-loading community file (not found or no valid content)\n");
sendto_mgmt(sss, sender_sock, (const uint8_t *) resbuf, ressize);
return 0; /* no status output afterwards */
}
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize,
"OK.\n");
sendto_mgmt(sss, sender_sock, (const uint8_t *) resbuf, ressize);
return 0; /* no status output afterwards */
}
if((mgmt_buf[0] == 'r' || mgmt_buf[0] == 'w') && (mgmt_buf[1] == ' ')) {
/* this is a JSON request */
handleMgmtJson(sss, mgmt_buf, *sender_sock);
return 0;
}
// output current status
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize,
" ### | TAP | MAC | EDGE | HINT | LAST SEEN\n");
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize,
"========================================================================================================\n");
HASH_ITER(hh, sss->communities, community, tmp) {
if(num_comm)
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize,
"--------------------------------------------------------------------------------------------------------\n");
num_comm++;
num_edges += HASH_COUNT(community->edges);
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize,
"%s '%s'\n",
(community->is_federation) ? "FEDERATION" : ((community->purgeable == COMMUNITY_UNPURGEABLE) ? "FIXED NAME COMMUNITY" : "COMMUNITY"),
(community->is_federation) ? "-/-" : community->community);
sendto_mgmt(sss, sender_sock, (const uint8_t *) resbuf, ressize);
ressize = 0;
num = 0;
HASH_ITER(hh, community->edges, peer, tmpPeer) {
sprintf(time_buf, "%9u", (unsigned int)(now - peer->last_seen));
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize,
"%4u | %-19s | %-17s | %-21s %-3s | %-15s | %9s\n",
++num,
(peer->dev_addr.net_addr == 0) ? ((peer->purgeable == SN_UNPURGEABLE) ? "-l" : "") : ip_subnet_to_str(ip_bit_str, &peer->dev_addr),
(is_null_mac(peer->mac_addr)) ? "" : macaddr_str(mac_buf, peer->mac_addr),
sock_to_cstr(sockbuf, &(peer->sock)),
((peer->socket_fd >= 0) && (peer->socket_fd != sss->sock)) ? "TCP" : "",
peer->dev_desc,
(peer->last_seen) ? time_buf : "");
sendto_mgmt(sss, sender_sock, (const uint8_t *) resbuf, ressize);
ressize = 0;
}
}
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize,
"========================================================================================================\n");
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize,
"uptime %lu | ", (now - sss->start_time));
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize,
"edges %u | ",
num_edges);
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize,
"reg_sup %u | ",
(unsigned int) sss->stats.reg_super);
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize,
"reg_nak %u | ",
(unsigned int) sss->stats.reg_super_nak);
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize,
"errors %u \n",
(unsigned int) sss->stats.errors);
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize,
"fwd %u | ",
(unsigned int) sss->stats.fwd);
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize,
"broadcast %u | ",
(unsigned int) sss->stats.broadcast);
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize,
"cur_cmnts %u\n", HASH_COUNT(sss->communities));
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize,
"last_fwd %lu sec ago | ",
(long unsigned int) (now - sss->stats.last_fwd));
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize,
"last reg %lu sec ago\n\n",
(long unsigned int) (now - sss->stats.last_reg_super));
sendto_mgmt(sss, sender_sock, (const uint8_t *) resbuf, ressize);
return 0;
}

View File

@ -30,11 +30,6 @@ static ssize_t sendto_peer (n2n_sn_t *sss,
const uint8_t *pktbuf,
size_t pktsize);
static int sendto_mgmt (n2n_sn_t *sss,
const struct sockaddr_in *sender_sock,
const uint8_t *mgmt_buf,
size_t mgmt_size);
static uint16_t reg_lifetime (n2n_sn_t *sss);
static int update_edge (n2n_sn_t *sss,
@ -61,7 +56,7 @@ static int sort_communities (n2n_sn_t *sss,
time_t* p_last_sort,
time_t now);
static int process_mgmt (n2n_sn_t *sss,
int process_mgmt (n2n_sn_t *sss,
const struct sockaddr_in *sender_sock,
char *mgmt_buf,
size_t mgmt_size,
@ -1507,170 +1502,6 @@ static int sort_communities (n2n_sn_t *sss,
}
static int process_mgmt (n2n_sn_t *sss,
const struct sockaddr_in *sender_sock,
char *mgmt_buf,
size_t mgmt_size,
time_t now) {
char resbuf[N2N_SN_PKTBUF_SIZE];
size_t ressize = 0;
uint32_t num_edges = 0;
uint32_t num_comm = 0;
uint32_t num = 0;
struct sn_community *community, *tmp;
struct peer_info *peer, *tmpPeer;
macstr_t mac_buf;
n2n_sock_str_t sockbuf;
char time_buf[10]; /* 9 digits + 1 terminating zero */
dec_ip_bit_str_t ip_bit_str = {'\0'};
traceEvent(TRACE_DEBUG, "process_mgmt");
/* avoid parsing any uninitialized junk from the stack */
mgmt_buf[mgmt_size] = 0;
// process input, if any
if((0 == memcmp(mgmt_buf, "help", 4)) || (0 == memcmp(mgmt_buf, "?", 1))) {
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize,
"Help for supernode management console:\n"
"\thelp | This help message\n"
"\treload_communities | Reloads communities and user's public keys\n"
"\t<enter> | Display status and statistics\n");
sendto_mgmt(sss, sender_sock, (const uint8_t *) resbuf, ressize);
return 0; /* no status output afterwards */
}
if(0 == memcmp(mgmt_buf, "reload_communities", 18)) {
if(!sss->community_file) {
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize,
"No community file provided (-c command line option)\n");
sendto_mgmt(sss, sender_sock, (const uint8_t *) resbuf, ressize);
return 0; /* no status output afterwards */
}
traceEvent(TRACE_NORMAL, "'reload_communities' command");
if(load_allowed_sn_community(sss)) {
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize,
"Error while re-loading community file (not found or no valid content)\n");
sendto_mgmt(sss, sender_sock, (const uint8_t *) resbuf, ressize);
return 0; /* no status output afterwards */
}
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize,
"OK.\n");
sendto_mgmt(sss, sender_sock, (const uint8_t *) resbuf, ressize);
return 0; /* no status output afterwards */
}
if((mgmt_buf[0] == 'r' || mgmt_buf[0] == 'w') && (mgmt_buf[1] == ' ')) {
/* this is a JSON request */
handleMgmtJson_sn(sss, mgmt_buf, *sender_sock);
return 0;
}
// output current status
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize,
" ### | TAP | MAC | EDGE | HINT | LAST SEEN\n");
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize,
"========================================================================================================\n");
HASH_ITER(hh, sss->communities, community, tmp) {
if(num_comm)
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize,
"--------------------------------------------------------------------------------------------------------\n");
num_comm++;
num_edges += HASH_COUNT(community->edges);
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize,
"%s '%s'\n",
(community->is_federation) ? "FEDERATION" :
((community->purgeable == COMMUNITY_UNPURGEABLE) ? "FIXED NAME COMMUNITY" : "COMMUNITY"),
(community->is_federation) ? "-/-" : community->community);
sendto_mgmt(sss, sender_sock, (const uint8_t *) resbuf, ressize);
ressize = 0;
num = 0;
HASH_ITER(hh, community->edges, peer, tmpPeer) {
sprintf (time_buf, "%9u", (unsigned int)(now - peer->last_seen));
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize,
"%4u | %-19s | %-17s | %-21s %-3s | %-15s | %9s\n",
++num,
(peer->dev_addr.net_addr == 0) ? ((peer->purgeable == SN_UNPURGEABLE) ? "-l" : "") :
ip_subnet_to_str(ip_bit_str, &peer->dev_addr),
(is_null_mac(peer->mac_addr)) ? "" : macaddr_str(mac_buf, peer->mac_addr),
sock_to_cstr(sockbuf, &(peer->sock)),
((peer->socket_fd >= 0) && (peer->socket_fd != sss->sock)) ? "TCP" : "",
peer->dev_desc,
(peer->last_seen) ? time_buf : "");
sendto_mgmt(sss, sender_sock, (const uint8_t *) resbuf, ressize);
ressize = 0;
}
}
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize,
"========================================================================================================\n");
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize,
"uptime %lu | ", (now - sss->start_time));
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize,
"edges %u | ",
num_edges);
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize,
"reg_sup %u | ",
(unsigned int) sss->stats.reg_super);
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize,
"reg_nak %u | ",
(unsigned int) sss->stats.reg_super_nak);
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize,
"errors %u \n",
(unsigned int) sss->stats.errors);
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize,
"fwd %u | ",
(unsigned int) sss->stats.fwd);
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize,
"broadcast %u | ",
(unsigned int) sss->stats.broadcast);
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize,
"cur_cmnts %u\n", HASH_COUNT(sss->communities));
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize,
"last_fwd %lu sec ago | ",
(long unsigned int) (now - sss->stats.last_fwd));
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize,
"last reg %lu sec ago\n\n",
(long unsigned int) (now - sss->stats.last_reg_super));
sendto_mgmt(sss, sender_sock, (const uint8_t *) resbuf, ressize);
return 0;
}
static int sendto_mgmt (n2n_sn_t *sss,
const struct sockaddr_in *sender_sock,
const uint8_t *mgmt_buf,
size_t mgmt_size) {
ssize_t r = sendto(sss->mgmt_sock, (void *)mgmt_buf, mgmt_size, 0 /*flags*/,
(struct sockaddr *)sender_sock, sizeof (struct sockaddr_in));
if(r <= 0) {
++(sss->stats.errors);
traceEvent (TRACE_ERROR, "sendto_mgmt : sendto failed. %s", strerror (errno));
return -1;
}
return 0;
}
/** Examine a datagram and determine what to do with it.
*
*/

View File

@ -639,10 +639,24 @@ int main (int argc, char * const argv[]) {
scan->socket_fd = sss_node.sock;
#ifndef WIN32
/*
* If no uid/gid is specified on the commandline, use the uid/gid of the
* first found out of user "n2n" or "nobody"
*/
if(((pw = getpwnam ("n2n")) != NULL) || ((pw = getpwnam ("nobody")) != NULL)) {
/*
* If the uid/gid is not set from the CLI, set it from getpwnam
* otherwise reset it to zero
* (TODO: this looks wrong)
*/
sss_node.userid = sss_node.userid == 0 ? pw->pw_uid : 0;
sss_node.groupid = sss_node.groupid == 0 ? pw->pw_gid : 0;
}
/*
* If we have a non-zero requested uid/gid, attempt to switch to use
* those
*/
if((sss_node.userid != 0) || (sss_node.groupid != 0)) {
traceEvent(TRACE_NORMAL, "dropping privileges to uid=%d, gid=%d",
(signed int)sss_node.userid, (signed int)sss_node.groupid);
@ -651,7 +665,6 @@ int main (int argc, char * const argv[]) {
if((setgid(sss_node.groupid) != 0)
|| (setuid(sss_node.userid) != 0)) {
traceEvent(TRACE_ERROR, "unable to drop privileges [%u/%s]", errno, strerror(errno));
exit(1);
}
}

View File

@ -0,0 +1,74 @@
###
###
[
{
"community": "test"
}
]
###
[
{
"rx_pkt": 0,
"tx_pkt": 2,
"type": "transop"
},
{
"rx_pkt": 0,
"tx_pkt": 0,
"type": "p2p"
},
{
"rx_pkt": 0,
"tx_pkt": 2,
"type": "super"
},
{
"rx_pkt": 0,
"tx_pkt": 2,
"type": "super_broadcast"
}
]
###
[]
###
[
{
"traceLevel": 2
}
]
###
0
###
[
{
"traceLevel": 1
}
]
###
[
{
"keep_running": 1
}
]
###
[
{
"keep_running": 0
}
]
###
[
{
"keep_running": 0
}
]

View File

@ -0,0 +1,64 @@
###
###
[
{
"community": "-/-",
"ip4addr": "",
"is_federation": 1,
"purgeable": 0
}
]
###
[
{
"tx_pkt": 0,
"type": "forward"
},
{
"tx_pkt": 0,
"type": "broadcast"
},
{
"nak": 0,
"rx_pkt": 0,
"type": "reg_super"
},
{
"tx_pkt": 0,
"type": "errors"
}
]
###
[]
###
[
{
"traceLevel": 3
}
]
###
[
{
"traceLevel": 1
}
]
###
[
{
"keep_running": 1
}
]
###
[
{
"keep_running": 0
}
]

View File

@ -0,0 +1,5 @@
#
# The integration tests
test_integration_supernode.sh
test_integration_edge.sh

9
tests/tests_units.list Normal file
View File

@ -0,0 +1,9 @@
#
# The unit tests
tests-auth
tests-compress
tests-elliptic
tests-hashing
tests-transform
tests-wire