From 6e945e18f3f3e9b7f9ee614eac6d3bf681f768d9 Mon Sep 17 00:00:00 2001 From: Garrett D'Amore Date: Mon, 2 Oct 2017 13:34:55 -0700 Subject: Added more complete tests, and changes to property handling. We allow some properties to be set on endpoints after they are started; transports now responsible for checking that. (The new values will only apply to new connections of course!) We added short-hand functions for pipe properties, and also added uint64_t shorthands across the board. The zerotier documentation got some updates (corrections). We have also added a separate header now for the ZT stuff. Also, dialers and listeners do not intermix anymore -- we test that only a dialer can be used with setting dialer options, and likewise for listeners. --- src/CMakeLists.txt | 7 +- src/core/endpt.c | 12 +-- src/core/endpt.h | 1 + src/nng.c | 118 ++++++++++++++++---- src/nng.h | 11 ++ src/transport/zerotier/zerotier.adoc | 4 +- src/transport/zerotier/zerotier.c | 71 +++++------- src/transport/zerotier/zerotier.h | 110 +++++++++++++++++++ tests/sock.c | 27 ----- tests/zt.c | 203 +++++++++++++++++++++++++---------- 10 files changed, 407 insertions(+), 157 deletions(-) create mode 100644 src/transport/zerotier/zerotier.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ffcb2b7b..2f2f54ff 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -144,7 +144,12 @@ if (NNG_PLATFORM_WINDOWS) endif() if (NNG_ENABLE_ZEROTIER) - set (NNG_SOURCES ${NNG_SOURCES} transport/zerotier/zerotier.c) + set (NNG_SOURCES ${NNG_SOURCES} + transport/zerotier/zerotier.c + transport/zerotier/zerotier.h + ) + install(FILES transport/zerotier/zerotier.h + DESTINATION include/nng/transport/zerotier) endif() include_directories(AFTER SYSTEM ${PROJECT_SOURCE_DIR}/src diff --git a/src/core/endpt.c b/src/core/endpt.c index e6216ba3..fa30bf77 100644 --- a/src/core/endpt.c +++ b/src/core/endpt.c @@ -605,20 +605,20 @@ nni_ep_setopt(nni_ep *ep, const char *name, const void *val, size_t sz) return (NNG_EREADONLY); } nni_mtx_lock(&ep->ep_mtx); - // XXX: Consider removing this test. - if (ep->ep_started) { - nni_mtx_unlock(&ep->ep_mtx); - return (NNG_ESTATE); - } rv = eo->eo_setopt(ep->ep_data, val, sz); nni_mtx_unlock(&ep->ep_mtx); return (rv); } - // XXX: socket fallback return (NNG_ENOTSUP); } +int +nni_ep_mode(nni_ep *ep) +{ + return (ep->ep_mode); +} + int nni_ep_getopt(nni_ep *ep, const char *name, void *valp, size_t *szp) { diff --git a/src/core/endpt.h b/src/core/endpt.h index d12d661f..f5ad09ee 100644 --- a/src/core/endpt.h +++ b/src/core/endpt.h @@ -32,6 +32,7 @@ extern int nni_ep_getopt(nni_ep *, const char *, void *, size_t *); extern int nni_ep_pipe_add(nni_ep *ep, nni_pipe *); extern void nni_ep_pipe_remove(nni_ep *, nni_pipe *); extern const char *nni_ep_url(nni_ep *); +extern int nni_ep_mode(nni_ep *); // Endpoint modes. Currently used by transports. Remove this when we make // transport dialers and listeners explicit. diff --git a/src/nng.c b/src/nng.c index 7a78357e..85be6b26 100644 --- a/src/nng.c +++ b/src/nng.c @@ -328,7 +328,8 @@ nng_dialer_start(nng_dialer id, int flags) } static int -nng_ep_setopt(uint32_t id, const char *name, const void *val, size_t sz) +nng_ep_setopt( + uint32_t id, const char *name, const void *val, size_t sz, int mode) { nni_ep *ep; int rv; @@ -339,13 +340,17 @@ nng_ep_setopt(uint32_t id, const char *name, const void *val, size_t sz) if ((rv = nni_ep_find(&ep, id)) != 0) { return (rv); } - rv = nni_ep_setopt(ep, name, val, sz); + if (nni_ep_mode(ep) == mode) { + rv = nni_ep_setopt(ep, name, val, sz); + } else { + rv = NNG_ENOENT; + } nni_ep_rele(ep); return (rv); } static int -nng_ep_getopt(uint32_t id, const char *name, void *val, size_t *szp) +nng_ep_getopt(uint32_t id, const char *name, void *val, size_t *szp, int mode) { nni_ep *ep; int rv; @@ -356,7 +361,11 @@ nng_ep_getopt(uint32_t id, const char *name, void *val, size_t *szp) if ((rv = nni_ep_find(&ep, id)) != 0) { return (rv); } - rv = nni_ep_getopt(ep, name, val, szp); + if (nni_ep_mode(ep) == mode) { + rv = nni_ep_getopt(ep, name, val, szp); + } else { + rv = NNG_ENOENT; + } nni_ep_rele(ep); return (rv); } @@ -364,104 +373,128 @@ nng_ep_getopt(uint32_t id, const char *name, void *val, size_t *szp) int nng_dialer_setopt(nng_dialer id, const char *name, const void *v, size_t sz) { - return (nng_ep_setopt(id, name, v, sz)); + return (nng_ep_setopt(id, name, v, sz, NNI_EP_MODE_DIAL)); } int nng_dialer_setopt_int(nng_dialer id, const char *name, int val) { - return (nng_ep_setopt(id, name, &val, sizeof(val))); + return (nng_dialer_setopt(id, name, &val, sizeof(val))); } int nng_dialer_setopt_size(nng_dialer id, const char *name, size_t val) { - return (nng_ep_setopt(id, name, &val, sizeof(val))); + return (nng_dialer_setopt(id, name, &val, sizeof(val))); } int nng_dialer_setopt_usec(nng_dialer id, const char *name, uint64_t val) { - return (nng_ep_setopt(id, name, &val, sizeof(val))); + return (nng_dialer_setopt(id, name, &val, sizeof(val))); +} + +int +nng_dialer_setopt_uint64(nng_dialer id, const char *name, uint64_t val) +{ + return (nng_dialer_setopt(id, name, &val, sizeof(val))); } int nng_dialer_getopt(nng_dialer id, const char *name, void *val, size_t *szp) { - return (nng_ep_getopt(id, name, val, szp)); + return (nng_ep_getopt(id, name, val, szp, NNI_EP_MODE_DIAL)); } int nng_dialer_getopt_int(nng_dialer id, const char *name, int *valp) { size_t sz = sizeof(*valp); - return (nng_ep_getopt(id, name, valp, &sz)); + return (nng_dialer_getopt(id, name, valp, &sz)); } int nng_dialer_getopt_size(nng_dialer id, const char *name, size_t *valp) { size_t sz = sizeof(*valp); - return (nng_ep_getopt(id, name, valp, &sz)); + return (nng_dialer_getopt(id, name, valp, &sz)); } int -nng_dialer_getopt_usec(nng_dialer id, const char *name, uint64_t *valp) +nng_dialer_getopt_uint64(nng_dialer id, const char *name, uint64_t *valp) { size_t sz = sizeof(*valp); - return (nng_ep_getopt(id, name, valp, &sz)); + return (nng_dialer_getopt(id, name, valp, &sz)); +} + +int +nng_dialer_getopt_usec(nng_dialer id, const char *name, uint64_t *valp) +{ + return (nng_dialer_getopt_uint64(id, name, valp)); } int nng_listener_setopt( nng_listener id, const char *name, const void *v, size_t sz) { - return (nng_ep_setopt(id, name, v, sz)); + return (nng_ep_setopt(id, name, v, sz, NNI_EP_MODE_LISTEN)); } int nng_listener_setopt_int(nng_listener id, const char *name, int val) { - return (nng_ep_setopt(id, name, &val, sizeof(val))); + return (nng_listener_setopt(id, name, &val, sizeof(val))); } int nng_listener_setopt_size(nng_listener id, const char *name, size_t val) { - return (nng_ep_setopt(id, name, &val, sizeof(val))); + return (nng_listener_setopt(id, name, &val, sizeof(val))); } int nng_listener_setopt_usec(nng_listener id, const char *name, uint64_t val) { - return (nng_ep_setopt(id, name, &val, sizeof(val))); + return (nng_listener_setopt(id, name, &val, sizeof(val))); +} + +int +nng_listener_setopt_uint64(nng_listener id, const char *name, uint64_t val) +{ + return (nng_listener_setopt(id, name, &val, sizeof(val))); } int nng_listener_getopt(nng_listener id, const char *name, void *val, size_t *szp) { - return (nng_ep_getopt(id, name, val, szp)); + return (nng_ep_getopt(id, name, val, szp, NNI_EP_MODE_LISTEN)); } int nng_listener_getopt_int(nng_listener id, const char *name, int *valp) { size_t sz = sizeof(*valp); - return (nng_ep_getopt(id, name, valp, &sz)); + return (nng_listener_getopt(id, name, valp, &sz)); } int nng_listener_getopt_size(nng_listener id, const char *name, size_t *valp) { size_t sz = sizeof(*valp); - return (nng_ep_getopt(id, name, valp, &sz)); + return (nng_listener_getopt(id, name, valp, &sz)); } int -nng_listener_getopt_usec(nng_listener id, const char *name, uint64_t *valp) +nng_listener_getopt_uint64(nng_listener id, const char *name, uint64_t *valp) { size_t sz = sizeof(*valp); - return (nng_ep_getopt(id, name, valp, &sz)); + return (nng_listener_getopt(id, name, valp, &sz)); +} + +int +nng_listener_getopt_usec(nng_listener id, const char *name, uint64_t *valp) +{ + return (nng_listener_getopt_uint64(id, name, valp)); } static int @@ -542,6 +575,12 @@ nng_setopt_usec(nng_socket sid, const char *name, uint64_t val) return (nng_setopt(sid, name, &val, sizeof(val))); } +int +nng_setopt_uint64(nng_socket sid, const char *name, uint64_t val) +{ + return (nng_setopt(sid, name, &val, sizeof(val))); +} + int nng_getopt_int(nng_socket sid, const char *name, int *valp) { @@ -557,12 +596,18 @@ nng_getopt_size(nng_socket sid, const char *name, size_t *valp) } int -nng_getopt_usec(nng_socket sid, const char *name, uint64_t *valp) +nng_getopt_uint64(nng_socket sid, const char *name, uint64_t *valp) { size_t sz = sizeof(*valp); return (nng_getopt(sid, name, valp, &sz)); } +int +nng_getopt_usec(nng_socket sid, const char *name, uint64_t *valp) +{ + return (nng_getopt_uint64(sid, name, valp)); +} + nng_notify * nng_setnotify(nng_socket sid, int mask, nng_notify_func fn, void *arg) { @@ -708,6 +753,33 @@ nng_pipe_getopt(nng_pipe id, const char *name, void *val, size_t *sizep) return (rv); } +int +nng_pipe_getopt_int(nng_pipe id, const char *name, int *valp) +{ + size_t sz = sizeof(*valp); + return (nng_pipe_getopt(id, name, valp, &sz)); +} + +int +nng_pipe_getopt_size(nng_pipe id, const char *name, size_t *valp) +{ + size_t sz = sizeof(*valp); + return (nng_pipe_getopt(id, name, valp, &sz)); +} + +int +nng_pipe_getopt_uint64(nng_pipe id, const char *name, uint64_t *valp) +{ + size_t sz = sizeof(*valp); + return (nng_pipe_getopt(id, name, valp, &sz)); +} + +int +nni_pipe_getopt_usec(nng_pipe id, const char *name, uint64_t *valp) +{ + return (nng_pipe_getopt_uint64(id, name, valp)); +} + int nng_pipe_close(nng_pipe id) { diff --git a/src/nng.h b/src/nng.h index 362e324b..f9ed54b0 100644 --- a/src/nng.h +++ b/src/nng.h @@ -90,12 +90,14 @@ NNG_DECL int nng_setopt(nng_socket, const char *, const void *, size_t); NNG_DECL int nng_setopt_int(nng_socket, const char *, int); NNG_DECL int nng_setopt_usec(nng_socket, const char *, uint64_t); NNG_DECL int nng_setopt_size(nng_socket, const char *, size_t); +NNG_DECL int nng_setopt_uint64(nng_socket, const char *, uint64_t); // nng_socket_getopt obtains the option for a socket. NNG_DECL int nng_getopt(nng_socket, const char *, void *, size_t *); NNG_DECL int nng_getopt_int(nng_socket, const char *, int *); NNG_DECL int nng_getopt_usec(nng_socket, const char *, uint64_t *); NNG_DECL int nng_getopt_size(nng_socket, const char *, size_t *); +NNG_DECL int nng_getopt_uint64(nng_socket, const char *, uint64_t *); // nng_notify_func is a user function that is executed upon certain // events. See below. @@ -203,6 +205,7 @@ NNG_DECL int nng_dialer_setopt(nng_dialer, const char *, const void *, size_t); NNG_DECL int nng_dialer_setopt_int(nng_dialer, const char *, int); NNG_DECL int nng_dialer_setopt_usec(nng_dialer, const char *, uint64_t); NNG_DECL int nng_dialer_setopt_size(nng_dialer, const char *, size_t); +NNG_DECL int nng_dialer_setopt_uint64(nng_dialer, const char *, uint64_t); // nng_dialer_getopt obtains the option for a dialer. This will // fail for options that a particular dialer is not interested in, @@ -211,6 +214,7 @@ NNG_DECL int nng_dialer_getopt(nng_dialer, const char *, void *, size_t *); NNG_DECL int nng_dialer_getopt_int(nng_dialer, const char *, int *); NNG_DECL int nng_dialer_getopt_usec(nng_dialer, const char *, uint64_t *); NNG_DECL int nng_dialer_getopt_size(nng_dialer, const char *, size_t *); +NNG_DECL int nng_dialer_getopt_uint64(nng_dialer, const char *, uint64_t *); // nng_listener_setopt sets an option for a dialer. This value is // not stored in the socket. Subsequent setopts on the socket may @@ -221,6 +225,7 @@ NNG_DECL int nng_listener_setopt( NNG_DECL int nng_listener_setopt_int(nng_listener, const char *, int); NNG_DECL int nng_listener_setopt_usec(nng_listener, const char *, uint64_t); NNG_DECL int nng_listener_setopt_size(nng_listener, const char *, size_t); +NNG_DECL int nng_listener_setopt_uint64(nng_listener, const char *, uint64_t); // nng_listener_getopt obtains the option for a listener. This will // fail for options that a particular listener is not interested in, @@ -229,6 +234,8 @@ NNG_DECL int nng_listener_getopt(nng_listener, const char *, void *, size_t *); NNG_DECL int nng_listener_getopt_int(nng_listener, const char *, int *); NNG_DECL int nng_listener_getopt_usec(nng_listener, const char *, uint64_t *); NNG_DECL int nng_listener_getopt_size(nng_listener, const char *, size_t *); +NNG_DECL int nng_listener_getopt_uint64( + nng_listener, const char *, uint64_t *); // nng_strerror returns a human readable string associated with the error // code supplied. @@ -322,6 +329,10 @@ NNG_DECL const char *nng_option_name(int); // example during a connection notification, to disconnect a pipe that // is associated with an invalid or untrusted remote peer. NNG_DECL int nng_pipe_getopt(nng_pipe, const char *, void *, size_t *); +NNG_DECL int nng_pipe_getopt_int(nng_pipe, const char *, int *); +NNG_DECL int nng_pipe_getopt_usec(nng_pipe, const char *, uint64_t *); +NNG_DECL int nng_pipe_getopt_size(nng_pipe, const char *, size_t *); +NNG_DECL int nng_pipe_getopt_uint64(nng_pipe, const char *, uint64_t *); NNG_DECL int nng_pipe_close(nng_pipe); // Flags. diff --git a/src/transport/zerotier/zerotier.adoc b/src/transport/zerotier/zerotier.adoc index dc838420..84501807 100644 --- a/src/transport/zerotier/zerotier.adoc +++ b/src/transport/zerotier/zerotier.adoc @@ -357,10 +357,10 @@ ZeroTier IDs, ZeroTier network IDs, and our own 24-bit ports. The format shall be `zt:///:`, where the `` component represents the 64-bit hexadecimal ZeroTier network ID, the `` represents the 40-bit hexadecimal ZeroTier Device ID, -and the `` is the 24-bit port number previously described. +and the `` is the 24-bit port number (decimal) previously described. A responder may elide the `/` portion, to just bind to itself, -in which case the format will be `zt:///:`. +in which case the format will be `zt://:`. A port number of 0 may be used when listening to indicate that a random ephemeral port should be chosen. diff --git a/src/transport/zerotier/zerotier.c b/src/transport/zerotier/zerotier.c index 46423b80..56acce03 100644 --- a/src/transport/zerotier/zerotier.c +++ b/src/transport/zerotier/zerotier.c @@ -15,6 +15,7 @@ #include #include "core/nng_impl.h" +#include "zerotier.h" #ifndef _WIN32 #include @@ -22,31 +23,16 @@ #include -#define NNG_ZT_OPT_HOME "zt:home" -#define NNG_ZT_OPT_NWID "zt:nwid" -#define NNG_ZT_OPT_NODE "zt:node" -#define NNG_ZT_OPT_STATUS "zt:status" -#define NNG_ZT_OPT_NETWORK_NAME "zt:network-name" -#define NNG_ZT_OPT_PING_TIME "zt:ping-time" -#define NNG_ZT_OPT_PING_COUNT "zt:ping-count" -#define NNG_ZT_OPT_MTU "zt:mtu" - -const char *nng_opt_zt_home = NNG_ZT_OPT_HOME; -const char *nng_opt_zt_nwid = NNG_ZT_OPT_NWID; -const char *nng_opt_zt_node = NNG_ZT_OPT_NODE; -const char *nng_opt_zt_status = NNG_ZT_OPT_STATUS; -const char *nng_opt_zt_network_name = NNG_ZT_OPT_NETWORK_NAME; -const char *nng_opt_zt_ping_time = NNG_ZT_OPT_PING_TIME; -const char *nng_opt_zt_ping_count = NNG_ZT_OPT_PING_COUNT; - // These values are supplied to help folks checking status. They are the -// return values from zt_opt_status. -int nng_zt_status_configuring = ZT_NETWORK_STATUS_REQUESTING_CONFIGURATION; -int nng_zt_status_ok = ZT_NETWORK_STATUS_OK; -int nng_zt_status_denied = ZT_NETWORK_STATUS_ACCESS_DENIED; -int nng_zt_status_notfound = ZT_NETWORK_STATUS_NOT_FOUND; -int nng_zt_status_error = ZT_NETWORK_STATUS_PORT_ERROR; -int nng_zt_status_obsolete = ZT_NETWORK_STATUS_CLIENT_TOO_OLD; +// return values from zt_opt_status. It's important that the status values +// here match what the underlying ZeroTier core gives us. +int nng_zt_network_status_configuring = + ZT_NETWORK_STATUS_REQUESTING_CONFIGURATION; +int nng_zt_network_status_ok = ZT_NETWORK_STATUS_OK; +int nng_zt_network_status_denied = ZT_NETWORK_STATUS_ACCESS_DENIED; +int nng_zt_network_status_notfound = ZT_NETWORK_STATUS_NOT_FOUND; +int nng_zt_network_status_error = ZT_NETWORK_STATUS_PORT_ERROR; +int nng_zt_network_status_obsolete = ZT_NETWORK_STATUS_CLIENT_TOO_OLD; // ZeroTier Transport. This sits on the ZeroTier L2 network, which itself // is implemented on top of UDP. This requires the 3rd party @@ -248,6 +234,7 @@ struct zt_ep { zt_node * ze_ztn; uint64_t ze_nwid; int ze_mode; + int ze_running; nni_sockaddr ze_addr; uint64_t ze_raddr; // remote node address uint64_t ze_laddr; // local node address @@ -1932,7 +1919,7 @@ zt_pipe_peer(void *arg) } static int -zt_getopt_status(zt_node *ztn, uint64_t nwid, void *buf, size_t *szp) +zt_getopt_network_status(zt_node *ztn, uint64_t nwid, void *buf, size_t *szp) { ZT_VirtualNetworkConfig *vcfg; int status; @@ -1990,13 +1977,6 @@ zt_pipe_get_node(void *arg, void *buf, size_t *szp) return (nni_getopt_u64(p->zp_laddr >> 24, buf, szp)); } -static int -zt_pipe_get_status(void *arg, void *buf, size_t *szp) -{ - zt_pipe *p = arg; - return (zt_getopt_status(p->zp_ztn, p->zp_nwid, buf, szp)); -} - static void zt_pipe_cancel_ping(nni_aio *aio, int rv) { @@ -2308,6 +2288,7 @@ zt_ep_bind_locked(zt_ep *ep) ep->ze_laddr = ztn->zn_self; ep->ze_laddr <<= 24; ep->ze_laddr |= port; + ep->ze_running = 1; if ((rv = nni_idhash_insert(ztn->zn_eps, ep->ze_laddr, ep)) != 0) { nni_idhash_remove(ztn->zn_ports, port); @@ -2498,6 +2479,7 @@ zt_ep_connect(void *arg, nni_aio *aio) nni_aio_list_append(&ep->ze_aios, aio); ep->ze_creq_try = 1; + ep->ze_running = 1; nni_aio_set_timeout(ep->ze_creq_aio, now + zt_conn_interval); // This can't fail -- the only way the ze_creq_aio gets @@ -2543,6 +2525,9 @@ zt_ep_setopt_home(void *arg, const void *data, size_t sz) return (NNG_EINVAL); } if (ep != NULL) { + if (ep->ze_running) { + return (NNG_ESTATE); + } nni_mtx_lock(&zt_lk); nni_strlcpy(ep->ze_home, data, sizeof(ep->ze_home)); if ((rv = zt_node_find(ep)) != 0) { @@ -2584,10 +2569,10 @@ zt_ep_getopt_network_name(void *arg, void *buf, size_t *szp) } static int -zt_ep_getopt_status(void *arg, void *buf, size_t *szp) +zt_ep_getopt_network_status(void *arg, void *buf, size_t *szp) { zt_ep *ep = arg; - return (zt_getopt_status(ep->ze_ztn, ep->ze_nwid, buf, szp)); + return (zt_getopt_network_status(ep->ze_ztn, ep->ze_nwid, buf, szp)); } static int @@ -2658,7 +2643,7 @@ zt_pipe_getopt_mtu(void *arg, void *data, size_t *szp) static nni_tran_pipe_option zt_pipe_options[] = { { NNG_OPT_LOCADDR, zt_pipe_getopt_locaddr }, { NNG_OPT_REMADDR, zt_pipe_getopt_remaddr }, - { NNG_ZT_OPT_MTU, zt_pipe_getopt_mtu }, + { NNG_OPT_ZT_MTU, zt_pipe_getopt_mtu }, // terminate list { NULL, NULL }, }; @@ -2680,37 +2665,37 @@ static nni_tran_ep_option zt_ep_options[] = { .eo_setopt = zt_ep_setopt_recvmaxsz, }, { - .eo_name = NNG_ZT_OPT_HOME, + .eo_name = NNG_OPT_ZT_HOME, .eo_getopt = zt_ep_getopt_home, .eo_setopt = zt_ep_setopt_home, }, { - .eo_name = NNG_ZT_OPT_NODE, + .eo_name = NNG_OPT_ZT_NODE, .eo_getopt = zt_ep_getopt_node, .eo_setopt = NULL, }, { - .eo_name = NNG_ZT_OPT_NWID, + .eo_name = NNG_OPT_ZT_NWID, .eo_getopt = zt_ep_getopt_nwid, .eo_setopt = NULL, }, { - .eo_name = NNG_ZT_OPT_STATUS, - .eo_getopt = zt_ep_getopt_status, + .eo_name = NNG_OPT_ZT_NETWORK_STATUS, + .eo_getopt = zt_ep_getopt_network_status, .eo_setopt = NULL, }, { - .eo_name = NNG_ZT_OPT_NETWORK_NAME, + .eo_name = NNG_OPT_ZT_NETWORK_NAME, .eo_getopt = zt_ep_getopt_network_name, .eo_setopt = NULL, }, { - .eo_name = NNG_ZT_OPT_PING_TIME, + .eo_name = NNG_OPT_ZT_PING_TIME, .eo_getopt = zt_ep_getopt_ping_time, .eo_setopt = zt_ep_setopt_ping_time, }, { - .eo_name = NNG_ZT_OPT_PING_COUNT, + .eo_name = NNG_OPT_ZT_PING_COUNT, .eo_getopt = zt_ep_getopt_ping_count, .eo_setopt = zt_ep_setopt_ping_count, }, diff --git a/src/transport/zerotier/zerotier.h b/src/transport/zerotier/zerotier.h new file mode 100644 index 00000000..ff33609e --- /dev/null +++ b/src/transport/zerotier/zerotier.h @@ -0,0 +1,110 @@ +// +// Copyright 2017 Garrett D'Amore +// Copyright 2017 Capitar IT Group BV +// +// This software is supplied under the terms of the MIT License, a +// copy of which should be located in the distribution where this +// file was obtained (LICENSE.txt). A copy of the license may also be +// found online at https://opensource.org/licenses/MIT. +// + +#ifndef NNG_TRANSPORT_ZEROTIER_ZEROTIER_H +#define NNG_TRANSPORT_ZEROTIER_ZEROTIER_H + +// ZeroTier Transport. This sits on the ZeroTier L2 network, which itself +// is implemented on top of UDP. This requires the 3rd party +// libzerotiercore library (which is GPLv3!) and platform specific UDP +// functionality to be built in. Note that care must be taken to link +// dynamically if one wishes to avoid making your entire application GPL3. +// (Alternatively ZeroTier offers commercial licenses which may prevent +// this particular problem.) This implementation does not make use of +// certain advanced capabilities in ZeroTier such as more sophisticated +// route management and TCP fallback. You need to have connectivity +// to the Internet to use this. (Or at least to your Planetary root.) +// +// The ZeroTier URL format we support is zt:///: where +// the component represents the 64-bit hexadecimal ZeroTier +// network ID,the represents the 40-bit hexadecimal ZeroTier +// node (device) ID, and the is a 24-bit (decimal) port number. +// +// A listener may elide the / portion, to just bind to itself, +// in which case the format will be zt://: +// +// A listener may also use either 0 or * for the to indicate that +// a random local ephemeral port should be used. +// +// Because ZeroTier takes a while to establish connectivity, it is even +// more important that applications using the ZeroTier transport not +// assume that a connection will be immediately available. It can take +// quite a few seconds for peer-to-peer connectivity to be established. +// +// The ZeroTier transport was funded by Capitar IT Group, BV. +// +// This transport is highly experimental. + +// ZeroTier transport-specific options. + +// NNG_OPT_ZT_HOME is a string containing a directory, where persistent +// state (key files, etc.) will be stored. It should be protected from +// unauthorized viewing and modification. This option must be set on an +// endpoint or socket before the endpoint(s) are started. If the unset, +// or an empty string, then no persistence is used and an ephemeral node +// will be created instead. Note that different endpoints may use different +// values for this option, and that will lead to each endpoint having a +// different ZeroTier identity -- however only one ephemeral node will +// be created for the application. +#define NNG_OPT_ZT_HOME "zt:home" + +// NNG_OPT_ZT_NWID is the 64-bit network ID, represented using a uint64_t in +// native byte order. This is a read-only option; it is derived automatically +// from the URL. +#define NNG_OPT_ZT_NWID "zt:nwid" + +// NNG_OPT_ZT_NODE is the 40-bit node ID, stored in native order in the low +// 40-bits of a uint64_t, of the node. This is a read-only option. +#define NNG_OPT_ZT_NODE "zt:node" + +// NNG_OPT_ZT_NETWORK_STATUS represents the status of the ZeroTier virtual +// network. The option is a read-only value, stored as an integer, which +// takes of the nng_zt_network_status_xxx values listed below. +#define NNG_OPT_ZT_NETWORK_STATUS "zt:network-status" + +// NNG_OPT_ZT_NETWORK_NAME is a human-readable name for the ZeroTier virtual +// network. This will only be set once the ZeroTier network has come up +// as the name comes from the network controller. This is read-only, and +// is presented as an ASCIIZ string. +#define NNG_OPT_ZT_NETWORK_NAME "zt:network-name" + +// NNG_OPT_ZT_PING_TIME and NNG_OPT_ZT_PING_COUNT are used to send ping +// requests when a connection appears to be idled. If a logical session +// has not received traffic from it's peer for ping-time, then a ping packet +// is sent. This will be done up to ping-count times. If no traffic from +// the remote peer is seen after all ping requests are sent, then the peer +// is assumed to be dead or offline, and the session is closed. The +// NNG_OPT_ZT_PING_TIME is a duration (usec, stored as an nng_duration, and +// NNG_OPT_ZT_PING_COUNT is an integer.) This ping process can be disabled +// by setting either ping-time or ping-count to zero. +#define NNG_OPT_ZT_PING_TIME "zt:ping-time" +#define NNG_OPT_ZT_PING_COUNT "zt:ping-count" + +// NNG_OPT_ZT_MTU is a read-only size_t and contains the ZeroTier virtual +// network MTU (i.e. the L2 payload MTU). Messages that are larger than this +// (including our 20-byte header data) will be fragmented into multiple +// virtual L2 frames. +#define NNG_OPT_ZT_MTU "zt:mtu" + +// Network status values. +// These values are supplied to help folks checking status. They are the +// return values from zt_opt_status. We avoid hard coding them as defines, +// to keep applications from baking in values that may change if the +// underlying ZeroTier transport changes. +extern int nng_zt_network_status_configuring; +extern int nng_zt_network_status_ok; +extern int nng_zt_network_status_denied; +extern int nng_zt_network_status_notfound; +extern int nng_zt_network_status_error; +extern int nng_zt_network_status_obsolete; + +extern int nng_zt_register(void); + +#endif // NNG_TRANSPORT_ZEROTIER_ZEROTIER_H diff --git a/tests/sock.c b/tests/sock.c index ca125214..f2afde4c 100644 --- a/tests/sock.c +++ b/tests/sock.c @@ -426,33 +426,6 @@ TestMain("Socket Operations", { }); - Convey("Cannot set dialer opts when running", { - nng_dialer ep; - char addr[NNG_MAXADDRLEN]; - - trantest_next_address(addr, "ipc:///tmp/sock_test_%u"); - So(nng_dialer_create(&ep, s1, addr) == 0); - So(nng_dialer_start(ep, NNG_FLAG_NONBLOCK) == 0); - So(nng_dialer_setopt_size(ep, NNG_OPT_RECVMAXSZ, 10) == - NNG_ESTATE); - So(nng_dialer_close(ep) == 0); - So(nng_dialer_close(ep) == NNG_ENOENT); - }); - - Convey("Cannot set listener opts when running", { - nng_listener ep; - char addr[NNG_MAXADDRLEN]; - - trantest_next_address(addr, "ipc:///tmp/sock_test_%u"); - - So(nng_listener_create(&ep, s1, addr) == 0); - So(nng_listener_start(ep, 0) == 0); - So(nng_listener_setopt_size( - ep, NNG_OPT_RECVMAXSZ, 10) == NNG_ESTATE); - So(nng_listener_close(ep) == 0); - So(nng_listener_close(ep) == NNG_ENOENT); - }); - Convey("We can send and receive messages", { nng_socket s2; int len; diff --git a/tests/zt.c b/tests/zt.c index b57d0f5b..1e6992b4 100644 --- a/tests/zt.c +++ b/tests/zt.c @@ -11,12 +11,7 @@ #include "convey.h" #include "trantest.h" -extern int nng_zt_register(void); -extern const char *nng_opt_zt_home; -extern const char *nng_opt_zt_node; -extern const char *nng_opt_zt_status; -extern const char *nng_opt_zt_network_name; -extern int nng_zt_status_ok; +#include "transport/zerotier/zerotier.h" // zerotier tests. @@ -24,6 +19,10 @@ extern int nng_zt_status_ok; // Do not attach to it in production. #define NWID "a09acf02337b057b" +// This network is a closed network, which nothing can join. We use it for +// testing permission denied. +#define CLOSED_NWID "17d709436ce162a3" + #ifdef _WIN32 int @@ -42,49 +41,143 @@ check_props(nng_msg *msg, nng_listener l, nng_dialer d) nng_sockaddr la, ra; nng_pipe p; size_t z; - size_t mtu; - uint64_t nwid; p = nng_msg_get_pipe(msg); So(p > 0); // Check local address. - z = sizeof(nng_sockaddr); - So(nng_pipe_getopt(p, NNG_OPT_LOCADDR, &la, &z) == 0); - So(z == sizeof(la)); - So(la.s_un.s_family == NNG_AF_ZT); - So(la.s_un.s_zt.sa_port == (trantest_port - 1)); - So(la.s_un.s_zt.sa_nwid == 0xa09acf02337b057bull); - So(la.s_un.s_zt.sa_nodeid != 0); - - // Check remote address. - z = sizeof(nng_sockaddr); - So(nng_pipe_getopt(p, NNG_OPT_REMADDR, &ra, &z) == 0); - So(z == sizeof(ra)); - So(ra.s_un.s_family == NNG_AF_ZT); - So(ra.s_un.s_zt.sa_port != 0); - So(ra.s_un.s_zt.sa_nwid == 0xa09acf02337b057bull); - So(ra.s_un.s_zt.sa_nodeid == la.s_un.s_zt.sa_nodeid); - - // Check network ID. - z = sizeof(nwid); - nwid = 0; - So(nng_pipe_getopt(p, "zt:nwid", &nwid, &z) == 0); - So(nwid = 0xa09acf02337b057bull); - - z = sizeof(nwid); - nwid = 0; - So(nng_dialer_getopt(d, "zt:nwid", &nwid, &z) == 0); - So(nwid = 0xa09acf02337b057bull); - - z = sizeof(nwid); - nwid = 0; - So(nng_listener_getopt(l, "zt:nwid", &nwid, &z) == 0); - So(nwid = 0xa09acf02337b057bull); - - // Check MTU - z = sizeof(mtu); - So(nng_pipe_getopt(p, "zt:mtu", &mtu, &z) == 0); - So(mtu >= 1000 && mtu <= 10000); + Convey("Local address property works", { + z = sizeof(nng_sockaddr); + So(nng_pipe_getopt(p, NNG_OPT_LOCADDR, &la, &z) == 0); + So(z == sizeof(la)); + So(la.s_un.s_family == NNG_AF_ZT); + So(la.s_un.s_zt.sa_port == (trantest_port - 1)); + So(la.s_un.s_zt.sa_nwid == 0xa09acf02337b057bull); + So(la.s_un.s_zt.sa_nodeid != 0); + }); + + Convey("Remote address property works", { + // Check remote address. + uint64_t mynode; + + z = sizeof(nng_sockaddr); + So(nng_pipe_getopt(p, NNG_OPT_REMADDR, &ra, &z) == 0); + So(z == sizeof(ra)); + So(ra.s_un.s_family == NNG_AF_ZT); + So(ra.s_un.s_zt.sa_port != 0); + So(ra.s_un.s_zt.sa_nwid == 0xa09acf02337b057bull); + + z = sizeof(mynode); + So(nng_pipe_getopt(p, NNG_OPT_ZT_NODE, &mynode, &z) == 0); + So(mynode != 0); + So(ra.s_un.s_zt.sa_nodeid == mynode); + + So(nng_dialer_getopt(d, NNG_OPT_REMADDR, &ra, &z) != 0); + }); + + Convey("NWID property works", { + uint64_t nwid; + + z = sizeof(nwid); + nwid = 0; + So(nng_pipe_getopt(p, NNG_OPT_ZT_NWID, &nwid, &z) == 0); + So(nwid = 0xa09acf02337b057bull); + + z = sizeof(nwid); + nwid = 0; + So(nng_dialer_getopt(d, NNG_OPT_ZT_NWID, &nwid, &z) == 0); + So(nwid = 0xa09acf02337b057bull); + + z = sizeof(nwid); + nwid = 0; + So(nng_listener_getopt(l, NNG_OPT_ZT_NWID, &nwid, &z) == 0); + So(nwid = 0xa09acf02337b057bull); + }); + + Convey("Network status property works", { + int s; + z = sizeof(s); + s = 0; + So(nng_pipe_getopt(p, NNG_OPT_ZT_NETWORK_STATUS, &s, &z) == 0); + So(s == nng_zt_network_status_ok); + + z = sizeof(s); + s = 0; + So(nng_dialer_getopt(d, NNG_OPT_ZT_NETWORK_STATUS, &s, &z) == + 0); + So(s == nng_zt_network_status_ok); + + z = sizeof(s); + s = 0; + So(nng_listener_getopt(l, NNG_OPT_ZT_NETWORK_STATUS, &s, &z) == + 0); + So(s == nng_zt_network_status_ok); + + So(nng_dialer_setopt(d, NNG_OPT_ZT_NETWORK_STATUS, &s, z) == + NNG_EREADONLY); + So(nng_listener_setopt(l, NNG_OPT_ZT_NETWORK_STATUS, &s, z) == + NNG_EREADONLY); + }); + + Convey("Ping properties work", { + int c; + uint64_t u; + z = sizeof(c); + c = 0; + So(nng_pipe_getopt(p, NNG_OPT_ZT_PING_COUNT, &c, &z) == 0); + So(c > 0 && c < 10); // actually 5... + + z = sizeof(u); + u = 0; + So(nng_pipe_getopt(p, NNG_OPT_ZT_PING_TIME, &u, &z) == 0); + So(u > 1000000 && u < 3600000000ull); // 1 sec - 1 hour + + c = 0; + So(nng_dialer_getopt_int(d, NNG_OPT_ZT_PING_COUNT, &c) == 0); + So(c > 0 && c < 10); // actually 5... + + z = sizeof(u); + u = 0; + So(nng_dialer_getopt_usec(d, NNG_OPT_ZT_PING_TIME, &u) == 0); + So(u > 1000000 && u < 3600000000ull); // 1 sec - 1 hour + + int rv = nng_dialer_setopt_int(d, NNG_OPT_ZT_PING_COUNT, 20); + + So(nng_dialer_setopt_int(d, NNG_OPT_ZT_PING_COUNT, 20) == 0); + So(nng_dialer_setopt_usec(d, NNG_OPT_ZT_PING_TIME, 2000000) == + 0); + So(nng_listener_setopt_int(l, NNG_OPT_ZT_PING_COUNT, 0) == 0); + So(nng_listener_setopt_usec(l, NNG_OPT_ZT_PING_TIME, 0) == 0); + }); + + Convey("Home property works", { + char v[256]; + z = sizeof(v); + So(nng_pipe_getopt(p, NNG_OPT_ZT_HOME, v, &z) == 0); + So(strlen(v) < sizeof(v)); + + z = sizeof(v); + So(nng_dialer_getopt(d, NNG_OPT_ZT_HOME, v, &z) == 0); + So(strlen(v) < sizeof(v)); + + z = sizeof(v); + So(nng_listener_getopt(l, NNG_OPT_ZT_HOME, v, &z) == 0); + So(strlen(v) < sizeof(v)); + + z = strlen("/tmp/bogus") + 1; + So(nng_dialer_setopt(d, NNG_OPT_ZT_HOME, "/tmp/bogus", z) == + NNG_ESTATE); + So(nng_listener_setopt(l, NNG_OPT_ZT_HOME, "/tmp/bogus", z) == + NNG_ESTATE); + }); + + Convey("MTU property works", { + size_t mtu; + + // Check MTU + z = sizeof(mtu); + So(nng_pipe_getopt(p, NNG_OPT_ZT_MTU, &mtu, &z) == 0); + So(mtu >= 1000 && mtu <= 10000); + }); return (0); } @@ -117,7 +210,7 @@ TestMain("ZeroTier Transport", { mkdir(path1, 0700); - So(nng_listener_setopt(l, nng_opt_zt_home, path1, + So(nng_listener_setopt(l, NNG_OPT_ZT_HOME, path1, strlen(path1) + 1) == 0); So(nng_listener_start(l, 0) == 0); @@ -157,7 +250,7 @@ TestMain("ZeroTier Transport", { So(nng_listener_create(&l, s, addr) == 0); - So(nng_listener_getopt_usec(l, nng_opt_zt_node, &node1) == 0); + So(nng_listener_getopt_usec(l, NNG_OPT_ZT_NODE, &node1) == 0); So(node1 != 0); Convey("Network name & status options work", { @@ -167,19 +260,19 @@ TestMain("ZeroTier Transport", { namesz = sizeof(name); nng_usleep(10000000); - So(nng_listener_getopt(l, nng_opt_zt_network_name, + So(nng_listener_getopt(l, NNG_OPT_ZT_NETWORK_NAME, name, &namesz) == 0); So(strcmp(name, "nng_test_open") == 0); So(nng_listener_getopt_int( - l, nng_opt_zt_status, &status) == 0); - So(status == nng_zt_status_ok); + l, NNG_OPT_ZT_NETWORK_STATUS, &status) == 0); + So(status == nng_zt_network_status_ok); }); Convey("Connection refused works", { snprintf(addr, sizeof(addr), "zt://" NWID "/%llx:%u", (unsigned long long) node1, 42u); So(nng_dialer_create(&d, s, addr) == 0); So(nng_dialer_getopt_usec( - d, nng_opt_zt_node, &node2) == 0); + d, NNG_OPT_ZT_NODE, &node2) == 0); So(node2 == node1); So(nng_dialer_start(d, 0) == NNG_ECONNREFUSED); }); @@ -206,24 +299,24 @@ TestMain("ZeroTier Transport", { nng_close(s1); // This sleep allows us to ensure disconnect // messages work. - nng_usleep(1000000); + nng_usleep(500000); nng_close(s2); }); So(nng_listener_create(&l, s1, addr1) == 0); So(nng_listener_setopt( - l, nng_opt_zt_home, path1, strlen(path1) + 1) == 0); + l, NNG_OPT_ZT_HOME, path1, strlen(path1) + 1) == 0); So(nng_listener_start(l, 0) == 0); node = 0; - So(nng_listener_getopt_usec(l, nng_opt_zt_node, &node) == 0); + So(nng_listener_getopt_usec(l, NNG_OPT_ZT_NODE, &node) == 0); So(node != 0); snprintf(addr2, sizeof(addr2), "zt://" NWID "/%llx:%u", (unsigned long long) node, port); So(nng_dialer_create(&d, s2, addr2) == 0); So(nng_dialer_setopt( - d, nng_opt_zt_home, path2, strlen(path2) + 1) == 0); + d, NNG_OPT_ZT_HOME, path2, strlen(path2) + 1) == 0); So(nng_dialer_start(d, 0) == 0); }); -- cgit v1.2.3-70-g09d2