From b8e9a79d009411525c99b886b7af41bb4c650669 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Sat, 20 Jul 2013 18:24:56 -0400 Subject: docs --- node/Packet.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'node/Packet.hpp') diff --git a/node/Packet.hpp b/node/Packet.hpp index a5c450fb..5ccfae45 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -449,7 +449,7 @@ public: * <[2] 16-bit length of payload> * <[2] 16-bit length of signature> * <[...] ethernet payload> - * <[...] ECDSA signature> + * <[...] ECDSA signature of SHA-256 hash (see below)> * * The signature is made using the key of the original submitter, and * can be used to authenticate the submitter for security and rate -- cgit v1.2.3 From 668c428051706e33a1d5a411a1b446ca865a4854 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 23 Jul 2013 22:46:04 -0700 Subject: Basic RPC stuff in Packet and PacketDecoder for RPC service support. --- Makefile.mac | 5 +---- node/NodeConfig.cpp | 2 +- node/Packet.cpp | 1 + node/Packet.hpp | 21 ++++++++++++++++++++- node/PacketDecoder.cpp | 6 ++++++ node/PacketDecoder.hpp | 1 + 6 files changed, 30 insertions(+), 6 deletions(-) (limited to 'node/Packet.hpp') diff --git a/Makefile.mac b/Makefile.mac index bace24b2..5f6283ba 100644 --- a/Makefile.mac +++ b/Makefile.mac @@ -13,10 +13,7 @@ STRIP=strip #STRIP=echo CXXFLAGS=$(CFLAGS) -fno-rtti - -# We statically link against libcrypto since Apple has apparently decided -# to deprecate it and may remove it in future OS releases. -LIBS=ext/bin/libcrypto/mac-x86_combined/libcrypto.a +LIBS=-lcrypto include objects.mk diff --git a/node/NodeConfig.cpp b/node/NodeConfig.cpp index 925b056b..1a84b6b7 100644 --- a/node/NodeConfig.cpp +++ b/node/NodeConfig.cpp @@ -237,7 +237,7 @@ void NodeConfig::_CBcontrolPacketHandler(UdpSocket *sock,void *arg,const InetAdd sock->send(remoteAddr,p->data(),p->size(),-1); } } catch ( ... ) { - TRACE("exception handling control bus packet from %s",remoteAddr.toString.c_str()); + TRACE("exception handling control bus packet from %s",remoteAddr.toString().c_str()); } } diff --git a/node/Packet.cpp b/node/Packet.cpp index d12f396d..bce80bf1 100644 --- a/node/Packet.cpp +++ b/node/Packet.cpp @@ -42,6 +42,7 @@ const char *Packet::verbString(Verb v) case VERB_FRAME: return "FRAME"; case VERB_MULTICAST_FRAME: return "MULTICAST_FRAME"; case VERB_MULTICAST_LIKE: return "MULTICAST_LIKE"; + case VERB_RPC: return "RPC"; } return "(unknown)"; } diff --git a/node/Packet.hpp b/node/Packet.hpp index 5ccfae45..13361497 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -463,7 +463,26 @@ public: * * No OK or ERROR is generated. */ - VERB_MULTICAST_FRAME = 9 + VERB_MULTICAST_FRAME = 9, + + /* Call a plugin via RPC: + * <[1] length of function name> + * <[...] function name> + * [<[2] length of argument>] + * [<[...] argument>] + * [... additional length/argument pairs ...] + * + * This generates ERROR_NOT_FOUND if the specified function was not + * found. Otherwise it generates an OK message. The OK message has + * the same format as the request, except arguments are replaced + * by results. + * + * This can be used to implement simple RPC. No retransmission or + * other guarantees are made, so it behaves like UDP-based RPC + * protocols. Plugins are typically run by supernodes and are used + * to implement network lookup and other services. + */ + VERB_RPC = 10 }; /** diff --git a/node/PacketDecoder.cpp b/node/PacketDecoder.cpp index 810e30a7..e081fa8e 100644 --- a/node/PacketDecoder.cpp +++ b/node/PacketDecoder.cpp @@ -102,6 +102,8 @@ bool PacketDecoder::tryDecode(const RuntimeEnvironment *_r) return _doMULTICAST_LIKE(_r,peer); case Packet::VERB_MULTICAST_FRAME: return _doMULTICAST_FRAME(_r,peer); + case Packet::VERB_RPC: + return _doRPC(_r,peer); default: // This might be something from a new or old version of the protocol. // Technically it passed HMAC so the packet is still valid, but we @@ -538,4 +540,8 @@ bool PacketDecoder::_doMULTICAST_FRAME(const RuntimeEnvironment *_r,const Shared return true; } +bool PacketDecoder::_doRPC(const RuntimeEnvironment *_r,const SharedPtr &peer) +{ +} + } // namespace ZeroTier diff --git a/node/PacketDecoder.hpp b/node/PacketDecoder.hpp index e595d326..82bf9aee 100644 --- a/node/PacketDecoder.hpp +++ b/node/PacketDecoder.hpp @@ -122,6 +122,7 @@ private: bool _doFRAME(const RuntimeEnvironment *_r,const SharedPtr &peer); bool _doMULTICAST_LIKE(const RuntimeEnvironment *_r,const SharedPtr &peer); bool _doMULTICAST_FRAME(const RuntimeEnvironment *_r,const SharedPtr &peer); + bool _doRPC(const RuntimeEnvironment *_r,const SharedPtr &peer); uint64_t _receiveTime; Demarc::Port _localPort; -- cgit v1.2.3 From af8fcac0fcfd64600a442dc4d633601e29e611ea Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Thu, 25 Jul 2013 15:19:35 -0400 Subject: RPC infrastructure work in progress. --- Makefile.linux | 2 +- Makefile.mac | 2 +- node/Constants.hpp | 5 ++ node/Packet.hpp | 2 +- node/RPC.cpp | 159 +++++++++++++++++++++++++++++++++++++++++++++++++++++ node/RPC.hpp | 21 +++---- objects.mk | 1 + 7 files changed, 179 insertions(+), 13 deletions(-) (limited to 'node/Packet.hpp') diff --git a/Makefile.linux b/Makefile.linux index 77327282..21fc615c 100644 --- a/Makefile.linux +++ b/Makefile.linux @@ -20,7 +20,7 @@ CXXFLAGS=$(CFLAGS) -fno-rtti # separate binaries for the RedHat and Debian universes to distribute via # auto-update. This way we get one Linux binary for all systems of a given # architecture. -LIBS=ext/bin/libcrypto/linux-$(ARCH)/libcrypto.a +LIBS=ext/bin/libcrypto/linux-$(ARCH)/libcrypto.a -ldl include objects.mk diff --git a/Makefile.mac b/Makefile.mac index 5f6283ba..4f85cc5a 100644 --- a/Makefile.mac +++ b/Makefile.mac @@ -13,7 +13,7 @@ STRIP=strip #STRIP=echo CXXFLAGS=$(CFLAGS) -fno-rtti -LIBS=-lcrypto +LIBS=-lcrypto -ldl include objects.mk diff --git a/node/Constants.hpp b/node/Constants.hpp index b8dc9ebf..9f856948 100644 --- a/node/Constants.hpp +++ b/node/Constants.hpp @@ -324,4 +324,9 @@ error_no_ZT_ARCH_defined; */ #define ZT_RENDEZVOUS_NAT_T_DELAY 500 +/** + * Timeout for remote RPC calls + */ +#define ZT_RPC_TIMEOUT 10000 + #endif diff --git a/node/Packet.hpp b/node/Packet.hpp index 13361497..8b06d1a5 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -466,7 +466,7 @@ public: VERB_MULTICAST_FRAME = 9, /* Call a plugin via RPC: - * <[1] length of function name> + * <[2] length of function name> * <[...] function name> * [<[2] length of argument>] * [<[...] argument>] diff --git a/node/RPC.cpp b/node/RPC.cpp index 2c69033e..e6591d7d 100644 --- a/node/RPC.cpp +++ b/node/RPC.cpp @@ -25,7 +25,166 @@ * LLC. Start here: http://www.zerotier.com/ */ +#ifndef __WINDOWS__ +#include +#endif + +#include "Utils.hpp" #include "RuntimeEnvironment.hpp" #include "RPC.hpp" #include "Switch.hpp" #include "Topology.hpp" + +namespace ZeroTier { + +RPC::LocalService::LocalService(const char *dllPath) + throw(std::invalid_argument) : + _handle((void *)0), + _init((void *)0), + _do((void *)0), + _free((void *)0), + _destroy((void *)0) +{ + _handle = dlopen(dllPath,RTLD_LAZY|RTLD_LOCAL); + if (!_handle) + throw std::invalid_argument("Unable to load DLL: dlopen() failed"); + + _init = dlsym(_handle,"ZeroTierPluginInit"); + if (!_init) { + dlclose(_handle); + throw std::invalid_argument("Unable to resolve symbol ZeroTierPluginInit in DLL"); + } + _do = dlsym(_handle,"ZeroTierPluginDo"); + if (!_do) { + dlclose(_handle); + throw std::invalid_argument("Unable to resolve symbol ZeroTierPluginDo in DLL"); + } + _free = dlsym(_handle,"ZeroTierPluginFree"); + if (!_free) { + dlclose(_handle); + throw std::invalid_argument("Unable to resolve symbol ZeroTierPluginFree in DLL"); + } + _destroy = dlsym(_handle,"ZeroTierPluginDestroy"); + if (!_destroy) { + dlclose(_handle); + throw std::invalid_argument("Unable to resolve symbol ZeroTierPluginDestroy in DLL"); + } + + if (((int (*)())_init)() < 0) { + dlclose(_handle); + throw std::invalid_argument("ZeroTierPluginInit() returned error"); + } +} + +RPC::LocalService::~LocalService() +{ + if (_handle) { + if (_destroy) + ((void (*)())_destroy)(); + dlclose(_handle); + } +} + +std::pair< int,std::vector > RPC::LocalService::operator()(const std::vector &args) +{ + unsigned int alengths[4096]; + const void *argptrs[4096]; + const unsigned int *rlengths = (const unsigned int *)0; + const void **resultptrs = (const void **)0; + std::vector results; + + if (args.size() > 4096) + throw std::runtime_error("args[] too long"); + + for(unsigned int i=0;i >(rcount,results); +} + +RPC::RPC(const RuntimeEnvironment *renv) : + _r(renv) +{ +} + +RPC::~RPC() +{ + for(std::map::iterator co(_remoteCallsOutstanding.begin());co!=_remoteCallsOutstanding.end();++co) { + if (co->second.handler) + co->second.handler(co->second.arg,co->first,co->second.peer,ZT_RPC_ERROR_CANCELLED,std::vector()); + } + + for(std::map::iterator s(_rpcServices.begin());s!=_rpcServices.end();++s) + delete s->second; +} + +std::pair< int,std::vector > RPC::callLocal(const std::string &name,const std::vector &args) +{ + Mutex::Lock _l(_rpcServices_m); + std::map::iterator s(_rpcServices.find(name)); + if (s == _rpcServices.end()) + return std::pair< int,std::vector >(ZT_RPC_ERROR_NOT_FOUND,std::vector()); + return ((*(s->second))(args)); +} + +uint64_t RPC::callRemote( + const Address &peer, + const std::string &name, + const std::vector &args, + void (*handler)(void *,uint64_t,const Address &,int,const std::vector &), + void *arg) + throw(std::invalid_argument,std::out_of_range) +{ + Packet outp(peer,_r->identity.address(),Packet::VERB_RPC); + + if (name.length() > 0xffff) + throw std::invalid_argument("function name too long"); + outp.append((uint16_t)name.length()); + outp.append(name); + for(std::vector::const_iterator a(args.begin());a!=args.end();++a) { + if (a->length() > 0xffff) + throw std::invalid_argument("argument too long"); + outp.append((uint16_t)a->length()); + outp.append(*a); + } + outp.compress(); + + uint64_t id = outp.packetId(); + + { + Mutex::Lock _l(_remoteCallsOutstanding_m); + RemoteCallOutstanding &rc = _remoteCallsOutstanding[id]; + rc.callTime = Utils::now(); + rc.peer = peer; + rc.handler = handler; + rc.arg = arg; + } + + _r->sw->send(outp,true); + + return id; +} + +void RPC::clean() +{ + Mutex::Lock _l(_remoteCallsOutstanding_m); + uint64_t now = Utils::now(); + for(std::map::iterator co(_remoteCallsOutstanding.begin());co!=_remoteCallsOutstanding.end();) { + if ((now - co->second.callTime) >= ZT_RPC_TIMEOUT) { + if (co->second.handler) + co->second.handler(co->second.arg,co->first,co->second.peer,ZT_RPC_ERROR_EXPIRED_NO_RESPONSE,std::vector()); + _remoteCallsOutstanding.erase(co++); + } else ++co; + } +} + +} // namespace ZeroTier diff --git a/node/RPC.hpp b/node/RPC.hpp index ac41dd6a..e31b5f6d 100644 --- a/node/RPC.hpp +++ b/node/RPC.hpp @@ -35,6 +35,7 @@ #include #include +#include "Constants.hpp" #include "NonCopyable.hpp" #include "Mutex.hpp" #include "Address.hpp" @@ -69,13 +70,13 @@ class RuntimeEnvironment; class RPC : NonCopyable { public: -#ifndef _WIN32 +#ifndef __WINDOWS__ /** * A local service accessible by RPC, non-Windows only for now * * Each service DLL must export these functions: * - * void ZeroTierPluginInit(); + * int ZeroTierPluginInit(); * int ZeroTierPluginDo(unsigned int,const unsigned int *,const void **,const unsigned int **,const void ***); * void ZeroTierPluginFree(int,const unsigned int *,const void **); * void ZeroTierPluginDestroy(); @@ -115,11 +116,14 @@ public: * @return Results from DLL * @throws std::runtime_error Error calling DLL */ - std::vector operator()(const std::vector &args) - throw(std::runtime_error); + std::pair< int,std::vector > operator()(const std::vector &args); private: - void *_dlhandle; + void *_handle; + void *_init; + void *_do; + void *_free; + void *_destroy; }; #endif @@ -133,8 +137,7 @@ public: * @param args Arguments to method * @return Return value of method, and results (negative first item and empty vector means error) */ - std::pair< int,std::vector > callLocal(const std::string &name,const std::vector &args) - throw(); + std::pair< int,std::vector > callLocal(const std::string &name,const std::vector &args); /** * Call a remote service @@ -152,7 +155,7 @@ public: const std::vector &args, void (*handler)(void *,uint64_t,const Address &,int,const std::vector &), void *arg) - throw(std::invalid_argument); + throw(std::invalid_argument,std::out_of_range); /** * Periodically called to clean up, such as by expiring remote calls @@ -169,8 +172,6 @@ private: { uint64_t callTime; Address peer; - std::string name; - std::vector &args; void (*handler)(void *,uint64_t,const Address &,int,const std::vector &); void *arg; }; diff --git a/objects.mk b/objects.mk index f5b88719..d7811a2e 100644 --- a/objects.mk +++ b/objects.mk @@ -21,6 +21,7 @@ OBJS=\ node/PacketDecoder.o \ node/Pack.o \ node/Peer.o \ + node/RPC.o \ node/Salsa20.o \ node/Switch.o \ node/SysEnv.o \ -- cgit v1.2.3 From b0a83093ce698390a4e2d266ba1c63ee241cd7ad Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Sat, 27 Jul 2013 13:36:27 -0400 Subject: Back out of RPC... blech. Have a better idea. --- node/Constants.hpp | 5 -- node/Network.hpp | 8 -- node/Node.cpp | 22 ----- node/Packet.cpp | 1 - node/Packet.hpp | 21 +---- node/PacketDecoder.cpp | 6 -- node/PacketDecoder.hpp | 1 - node/RPC.cpp | 212 -------------------------------------------- node/RPC.hpp | 196 ---------------------------------------- node/RuntimeEnvironment.hpp | 5 +- objects.mk | 1 - 11 files changed, 2 insertions(+), 476 deletions(-) delete mode 100644 node/RPC.cpp delete mode 100644 node/RPC.hpp (limited to 'node/Packet.hpp') diff --git a/node/Constants.hpp b/node/Constants.hpp index 9f856948..b8dc9ebf 100644 --- a/node/Constants.hpp +++ b/node/Constants.hpp @@ -324,9 +324,4 @@ error_no_ZT_ARCH_defined; */ #define ZT_RENDEZVOUS_NAT_T_DELAY 500 -/** - * Timeout for remote RPC calls - */ -#define ZT_RPC_TIMEOUT 10000 - #endif diff --git a/node/Network.hpp b/node/Network.hpp index 8429cf84..a95ae869 100644 --- a/node/Network.hpp +++ b/node/Network.hpp @@ -50,14 +50,6 @@ class NodeConfig; /** * Local membership to a network - * - * Networks configure themselves via RPC by accessing the function - * com.zerotier.one.Network.bootstrap at any supernode. This returns - * a series of key/value pairs that includes the IP address - * information for this node on the network and -- for closed - * networks -- a URL to retreive the network's membership list. - * A SHA-256 hash is also included to verify the return from this - * URL query. */ class Network : NonCopyable { diff --git a/node/Node.cpp b/node/Node.cpp index b5c23ed9..08b08fc1 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -68,7 +68,6 @@ #include "Mutex.hpp" #include "Multicaster.hpp" #include "CMWC4096.hpp" -#include "RPC.hpp" #include "../version.h" @@ -211,7 +210,6 @@ Node::~Node() { _NodeImpl *impl = (_NodeImpl *)_impl; - delete impl->renv.rpc; delete impl->renv.sysEnv; delete impl->renv.topology; delete impl->renv.sw; @@ -317,7 +315,6 @@ Node::ReasonForTermination Node::run() _r->sw = new Switch(_r); _r->topology = new Topology(_r,(_r->homePath + ZT_PATH_SEPARATOR_S + "peer.db").c_str()); _r->sysEnv = new SysEnv(_r); - _r->rpc = new RPC(_r); // TODO: make configurable bool boundPort = false; @@ -341,25 +338,6 @@ Node::ReasonForTermination Node::run() return impl->terminateBecause(Node::NODE_UNRECOVERABLE_ERROR,"unknown exception during initialization"); } - try { - std::map pluginsd(Utils::listDirectory((_r->homePath + ZT_PATH_SEPARATOR_S + "plugins.d").c_str())); - for(std::map::iterator ppath(pluginsd.begin());ppath!=pluginsd.end();++ppath) { - if (!ppath->second) { - try { - std::string funcName(ppath->first.substr(0,ppath->first.rfind('.'))); - LOG("loading plugins.d/%s as RPC function %s",ppath->first.c_str(),funcName.c_str()); - _r->rpc->loadLocal(funcName.c_str(),(_r->homePath + ZT_PATH_SEPARATOR_S + "plugins.d" + ZT_PATH_SEPARATOR_S + ppath->first).c_str()); - } catch (std::exception &exc) { - LOG("failed to load plugin plugins.d/%s: %s",ppath->first.c_str(),exc.what()); - } catch ( ... ) { - LOG("failed to load plugin plugins.d/%s: (unknown exception)",ppath->first.c_str()); - } - } - } - } catch ( ... ) { - TRACE("unknown exception attempting to enumerate and load plugins"); - } - try { uint64_t lastPingCheck = 0; uint64_t lastTopologyClean = Utils::now(); // don't need to do this immediately diff --git a/node/Packet.cpp b/node/Packet.cpp index bce80bf1..d12f396d 100644 --- a/node/Packet.cpp +++ b/node/Packet.cpp @@ -42,7 +42,6 @@ const char *Packet::verbString(Verb v) case VERB_FRAME: return "FRAME"; case VERB_MULTICAST_FRAME: return "MULTICAST_FRAME"; case VERB_MULTICAST_LIKE: return "MULTICAST_LIKE"; - case VERB_RPC: return "RPC"; } return "(unknown)"; } diff --git a/node/Packet.hpp b/node/Packet.hpp index 8b06d1a5..5ccfae45 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -463,26 +463,7 @@ public: * * No OK or ERROR is generated. */ - VERB_MULTICAST_FRAME = 9, - - /* Call a plugin via RPC: - * <[2] length of function name> - * <[...] function name> - * [<[2] length of argument>] - * [<[...] argument>] - * [... additional length/argument pairs ...] - * - * This generates ERROR_NOT_FOUND if the specified function was not - * found. Otherwise it generates an OK message. The OK message has - * the same format as the request, except arguments are replaced - * by results. - * - * This can be used to implement simple RPC. No retransmission or - * other guarantees are made, so it behaves like UDP-based RPC - * protocols. Plugins are typically run by supernodes and are used - * to implement network lookup and other services. - */ - VERB_RPC = 10 + VERB_MULTICAST_FRAME = 9 }; /** diff --git a/node/PacketDecoder.cpp b/node/PacketDecoder.cpp index e081fa8e..810e30a7 100644 --- a/node/PacketDecoder.cpp +++ b/node/PacketDecoder.cpp @@ -102,8 +102,6 @@ bool PacketDecoder::tryDecode(const RuntimeEnvironment *_r) return _doMULTICAST_LIKE(_r,peer); case Packet::VERB_MULTICAST_FRAME: return _doMULTICAST_FRAME(_r,peer); - case Packet::VERB_RPC: - return _doRPC(_r,peer); default: // This might be something from a new or old version of the protocol. // Technically it passed HMAC so the packet is still valid, but we @@ -540,8 +538,4 @@ bool PacketDecoder::_doMULTICAST_FRAME(const RuntimeEnvironment *_r,const Shared return true; } -bool PacketDecoder::_doRPC(const RuntimeEnvironment *_r,const SharedPtr &peer) -{ -} - } // namespace ZeroTier diff --git a/node/PacketDecoder.hpp b/node/PacketDecoder.hpp index 82bf9aee..e595d326 100644 --- a/node/PacketDecoder.hpp +++ b/node/PacketDecoder.hpp @@ -122,7 +122,6 @@ private: bool _doFRAME(const RuntimeEnvironment *_r,const SharedPtr &peer); bool _doMULTICAST_LIKE(const RuntimeEnvironment *_r,const SharedPtr &peer); bool _doMULTICAST_FRAME(const RuntimeEnvironment *_r,const SharedPtr &peer); - bool _doRPC(const RuntimeEnvironment *_r,const SharedPtr &peer); uint64_t _receiveTime; Demarc::Port _localPort; diff --git a/node/RPC.cpp b/node/RPC.cpp deleted file mode 100644 index 5a9cd719..00000000 --- a/node/RPC.cpp +++ /dev/null @@ -1,212 +0,0 @@ -/* - * ZeroTier One - Global Peer to Peer Ethernet - * Copyright (C) 2012-2013 ZeroTier Networks LLC - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * -- - * - * ZeroTier may be used and distributed under the terms of the GPLv3, which - * are available at: http://www.gnu.org/licenses/gpl-3.0.html - * - * If you would like to embed ZeroTier into a commercial application or - * redistribute it in a modified binary form, please contact ZeroTier Networks - * LLC. Start here: http://www.zerotier.com/ - */ - -#ifndef __WINDOWS__ -#include -#endif - -#include "Utils.hpp" -#include "RuntimeEnvironment.hpp" -#include "RPC.hpp" -#include "Switch.hpp" -#include "Topology.hpp" - -namespace ZeroTier { - -#ifndef __WINDOWS__ - -RPC::LocalService::LocalService(const char *dllPath) - throw(std::invalid_argument) : - _handle((void *)0), - _init((void *)0), - _do((void *)0), - _free((void *)0), - _destroy((void *)0) -{ - _handle = dlopen(dllPath,RTLD_LAZY|RTLD_LOCAL); - if (!_handle) - throw std::invalid_argument("Unable to load DLL: dlopen() failed"); - - _init = dlsym(_handle,"ZeroTierPluginInit"); - if (!_init) { - dlclose(_handle); - throw std::invalid_argument("Unable to resolve symbol ZeroTierPluginInit in DLL"); - } - _do = dlsym(_handle,"ZeroTierPluginDo"); - if (!_do) { - dlclose(_handle); - throw std::invalid_argument("Unable to resolve symbol ZeroTierPluginDo in DLL"); - } - _free = dlsym(_handle,"ZeroTierPluginFree"); - if (!_free) { - dlclose(_handle); - throw std::invalid_argument("Unable to resolve symbol ZeroTierPluginFree in DLL"); - } - _destroy = dlsym(_handle,"ZeroTierPluginDestroy"); - if (!_destroy) { - dlclose(_handle); - throw std::invalid_argument("Unable to resolve symbol ZeroTierPluginDestroy in DLL"); - } - - if (((int (*)())_init)() < 0) { - dlclose(_handle); - throw std::invalid_argument("ZeroTierPluginInit() returned error"); - } -} - -RPC::LocalService::~LocalService() -{ - if (_handle) { - if (_destroy) - ((void (*)())_destroy)(); - dlclose(_handle); - } -} - -std::pair< int,std::vector > RPC::LocalService::operator()(const std::vector &args) -{ - unsigned int alengths[4096]; - const void *argptrs[4096]; - const unsigned int *rlengths = (const unsigned int *)0; - const void **resultptrs = (const void **)0; - std::vector results; - - if (args.size() > 4096) - throw std::runtime_error("args[] too long"); - - for(unsigned int i=0;i >(rcount,results); -} - -#endif // __WINDOWS__ - -RPC::RPC(const RuntimeEnvironment *renv) : - _r(renv) -{ -} - -RPC::~RPC() -{ - for(std::map::iterator co(_remoteCallsOutstanding.begin());co!=_remoteCallsOutstanding.end();++co) { - if (co->second.handler) - co->second.handler(co->second.arg,co->first,co->second.peer,ZT_RPC_ERROR_CANCELLED,std::vector()); - } - -#ifndef __WINDOWS__ - for(std::map::iterator s(_rpcServices.begin());s!=_rpcServices.end();++s) - delete s->second; -#endif -} - -std::pair< int,std::vector > RPC::callLocal(const std::string &name,const std::vector &args) -{ -#ifdef __WINDOWS__ - return std::pair< int,std::vector >(ZT_RPC_ERROR_NOT_FOUND,std::vector()); -#else - Mutex::Lock _l(_rpcServices_m); - std::map::iterator s(_rpcServices.find(name)); - if (s == _rpcServices.end()) - return std::pair< int,std::vector >(ZT_RPC_ERROR_NOT_FOUND,std::vector()); - return ((*(s->second))(args)); -#endif -} - -void RPC::loadLocal(const char *name,const char *path) - throw(std::invalid_argument) -{ -#ifdef __WINDOWS__ - throw std::invalid_argument("RPC plugins not supported on Windows (yet?)"); -#else - LocalService *s = new LocalService(path); - Mutex::Lock _l(_rpcServices_m); - _rpcServices[std::string(name)] = s; -#endif -} - -uint64_t RPC::callRemote( - const Address &peer, - const std::string &name, - const std::vector &args, - void (*handler)(void *,uint64_t,const Address &,int,const std::vector &), - void *arg) - throw(std::invalid_argument,std::out_of_range) -{ - Packet outp(peer,_r->identity.address(),Packet::VERB_RPC); - - if (name.length() > 0xffff) - throw std::invalid_argument("function name too long"); - outp.append((uint16_t)name.length()); - outp.append(name); - for(std::vector::const_iterator a(args.begin());a!=args.end();++a) { - if (a->length() > 0xffff) - throw std::invalid_argument("argument too long"); - outp.append((uint16_t)a->length()); - outp.append(*a); - } - outp.compress(); - - uint64_t id = outp.packetId(); - - { - Mutex::Lock _l(_remoteCallsOutstanding_m); - RemoteCallOutstanding &rc = _remoteCallsOutstanding[id]; - rc.callTime = Utils::now(); - rc.peer = peer; - rc.handler = handler; - rc.arg = arg; - } - - _r->sw->send(outp,true); - - return id; -} - -void RPC::clean() -{ - Mutex::Lock _l(_remoteCallsOutstanding_m); - uint64_t now = Utils::now(); - for(std::map::iterator co(_remoteCallsOutstanding.begin());co!=_remoteCallsOutstanding.end();) { - if ((now - co->second.callTime) >= ZT_RPC_TIMEOUT) { - if (co->second.handler) - co->second.handler(co->second.arg,co->first,co->second.peer,ZT_RPC_ERROR_EXPIRED_NO_RESPONSE,std::vector()); - _remoteCallsOutstanding.erase(co++); - } else ++co; - } -} - -} // namespace ZeroTier diff --git a/node/RPC.hpp b/node/RPC.hpp deleted file mode 100644 index 669bf5ae..00000000 --- a/node/RPC.hpp +++ /dev/null @@ -1,196 +0,0 @@ -/* - * ZeroTier One - Global Peer to Peer Ethernet - * Copyright (C) 2012-2013 ZeroTier Networks LLC - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * -- - * - * ZeroTier may be used and distributed under the terms of the GPLv3, which - * are available at: http://www.gnu.org/licenses/gpl-3.0.html - * - * If you would like to embed ZeroTier into a commercial application or - * redistribute it in a modified binary form, please contact ZeroTier Networks - * LLC. Start here: http://www.zerotier.com/ - */ - -#ifndef _ZT_RPC_HPP -#define _ZT_RPC_HPP - -#include - -#include -#include -#include -#include - -#include "Constants.hpp" -#include "NonCopyable.hpp" -#include "Mutex.hpp" -#include "Address.hpp" - -namespace ZeroTier { - -class RuntimeEnvironment; - -/** - * Peer or method not found - */ -#define ZT_RPC_ERROR_NOT_FOUND -1 - -/** - * A runtime error occurred - */ -#define ZT_RPC_ERROR_RUNTIME -2 - -/** - * Call was expired without response from target - */ -#define ZT_RPC_ERROR_EXPIRED_NO_RESPONSE -3 - -/** - * Call was cancelled (or RPC is shutting down) - */ -#define ZT_RPC_ERROR_CANCELLED -4 - -/** - * RPC request and result handler - */ -class RPC : NonCopyable -{ -public: -#ifndef __WINDOWS__ - /** - * A local service accessible by RPC, non-Windows only for now - * - * Each service DLL must export these functions: - * - * int ZeroTierPluginInit(); - * int ZeroTierPluginDo(unsigned int,const unsigned int *,const void **,const unsigned int **,const void ***); - * void ZeroTierPluginFree(int,const unsigned int *,const void **); - * void ZeroTierPluginDestroy(); - * - * Init is called on library load, Destroy on unload. Do() may - * be called from multiple threads concurrently, so any locking - * is the responsibility of the library. These must have C - * function signatures (extern "C" in C++). - * - * Do's arguments are: the number of paramters, the size of each parameter in bytes, - * and each parameter's contents. The last two arguments are result parameters. The - * first result parameter must be set to an array of integers describing the size of - * each result. The second is set to an array of pointers to actual results. The number - * of results (size of both arrays) is returned. If Do() returns zero or negative, - * these result paremeters are not used by the caller and don't need to be set. - * - * After the caller is done with Do()'s results, it calls ZeroTierPluginFree() to - * free them. This may also be called concurrently. Free() takes the number of - * results, the array of result sizes, and the result array. - */ - class LocalService : NonCopyable - { - public: - /** - * @param dllPath Path to DLL/shared object - * @throws std::invalid_argument Unable to properly load or resolve symbol(s) in DLL - */ - LocalService(const char *dllPath) - throw(std::invalid_argument); - - ~LocalService(); - - /** - * Call the DLL, return result - * - * @param args Input arguments - * @return Results from DLL - * @throws std::runtime_error Error calling DLL - */ - std::pair< int,std::vector > operator()(const std::vector &args); - - private: - void *_handle; - void *_init; - void *_do; - void *_free; - void *_destroy; - }; -#endif - - RPC(const RuntimeEnvironment *renv); - ~RPC(); - - /** - * Used by PacketDecoder to call local RPC methods - * - * @param name Name of locally loaded method to call - * @param args Arguments to method - * @return Return value of method, and results (negative first item and empty vector means error) - */ - std::pair< int,std::vector > callLocal(const std::string &name,const std::vector &args); - - /** - * Load a plugin - * - * @param name Name of RPC function - * @param path Path to plugin DLL - * @throws std::invalid_argument Unable to properly load or resolve symbol(s) in DLL - */ - void loadLocal(const char *name,const char *path) - throw(std::invalid_argument); - - /** - * Call a remote service - * - * @param peer Peer to call on - * @param name Name of remote function - * @param args Arguments to remote function - * @param handler Handler to call on result - * @param arg First argument to handler - * @return Call ID (packet ID of sent packet) - */ - uint64_t callRemote( - const Address &peer, - const std::string &name, - const std::vector &args, - void (*handler)(void *,uint64_t,const Address &,int,const std::vector &), - void *arg) - throw(std::invalid_argument,std::out_of_range); - - /** - * Periodically called to clean up, such as by expiring remote calls - */ - void clean(); - -private: - const RuntimeEnvironment *_r; - -#ifndef __WINDOWS__ - std::map _rpcServices; - Mutex _rpcServices_m; -#endif - - struct RemoteCallOutstanding - { - uint64_t callTime; - Address peer; - void (*handler)(void *,uint64_t,const Address &,int,const std::vector &); - void *arg; - }; - std::map _remoteCallsOutstanding; - Mutex _remoteCallsOutstanding_m; -}; - -} // namespace ZeroTier - -#endif diff --git a/node/RuntimeEnvironment.hpp b/node/RuntimeEnvironment.hpp index 35c2de3f..7797e651 100644 --- a/node/RuntimeEnvironment.hpp +++ b/node/RuntimeEnvironment.hpp @@ -42,7 +42,6 @@ class Topology; class SysEnv; class Multicaster; class CMWC4096; -class RPC; /** * Holds global state for an instance of ZeroTier::Node @@ -67,8 +66,7 @@ public: multicaster((Multicaster *)0), sw((Switch *)0), topology((Topology *)0), - sysEnv((SysEnv *)0), - rpc((RPC *)0) + sysEnv((SysEnv *)0) { } @@ -90,7 +88,6 @@ public: Switch *sw; Topology *topology; SysEnv *sysEnv; - RPC *rpc; }; } // namespace ZeroTier diff --git a/objects.mk b/objects.mk index d7811a2e..f5b88719 100644 --- a/objects.mk +++ b/objects.mk @@ -21,7 +21,6 @@ OBJS=\ node/PacketDecoder.o \ node/Pack.o \ node/Peer.o \ - node/RPC.o \ node/Salsa20.o \ node/Switch.o \ node/SysEnv.o \ -- cgit v1.2.3 From 7a17f6ca80e3df9e1509dc99d0acdd00f12686e0 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Sat, 27 Jul 2013 16:20:08 -0400 Subject: Add skeleton of certificate-based private network authentication. Also remove some old code. --- node/BlobArray.hpp | 94 ------------------------------- node/Network.hpp | 15 ++++- node/Node.cpp | 1 - node/Pack.cpp | 159 ----------------------------------------------------- node/Pack.hpp | 141 ----------------------------------------------- node/Packet.cpp | 3 + node/Packet.hpp | 25 ++++++++- objects.mk | 1 - 8 files changed, 40 insertions(+), 399 deletions(-) delete mode 100644 node/BlobArray.hpp delete mode 100644 node/Pack.cpp delete mode 100644 node/Pack.hpp (limited to 'node/Packet.hpp') diff --git a/node/BlobArray.hpp b/node/BlobArray.hpp deleted file mode 100644 index d78bcffa..00000000 --- a/node/BlobArray.hpp +++ /dev/null @@ -1,94 +0,0 @@ -/* - * ZeroTier One - Global Peer to Peer Ethernet - * Copyright (C) 2012-2013 ZeroTier Networks LLC - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * -- - * - * ZeroTier may be used and distributed under the terms of the GPLv3, which - * are available at: http://www.gnu.org/licenses/gpl-3.0.html - * - * If you would like to embed ZeroTier into a commercial application or - * redistribute it in a modified binary form, please contact ZeroTier Networks - * LLC. Start here: http://www.zerotier.com/ - */ - -#ifndef _ZT_BLOBARRAY_HPP -#define _ZT_BLOBARRAY_HPP - -#include -#include -#include - -namespace ZeroTier { - -/** - * A vector of binary strings serializable in a packed format - * - * The format uses variable-length integers to indicate the length of each - * field. Each byte of the length has another byte with seven more significant - * bits if its 8th bit is set. Fields can be up to 2^28 in length. - */ -class BlobArray : public std::vector -{ -public: - inline std::string serialize() const - { - std::string r; - for(BlobArray::const_iterator i=begin();i!=end();++i) { - unsigned int flen = (unsigned int)i->length(); - do { - unsigned char flenb = (unsigned char)(flen & 0x7f); - flen >>= 7; - flenb |= (flen) ? 0x80 : 0; - r.push_back((char)flenb); - } while (flen); - r.append(*i); - } - return r; - } - - /** - * Deserialize, replacing the current contents of this array - * - * @param data Serialized binary data - * @param len Length of serialized data - */ - inline void deserialize(const void *data,unsigned int len) - { - clear(); - for(unsigned int i=0;i. - * - * -- - * - * ZeroTier may be used and distributed under the terms of the GPLv3, which - * are available at: http://www.gnu.org/licenses/gpl-3.0.html - * - * If you would like to embed ZeroTier into a commercial application or - * redistribute it in a modified binary form, please contact ZeroTier Networks - * LLC. Start here: http://www.zerotier.com/ - */ - -#include -#include -#include -#include "Pack.hpp" -#include "BlobArray.hpp" -#include "Utils.hpp" - -#include - -namespace ZeroTier { - -std::vector Pack::getAll() const -{ - std::vector v; - for(std::map::const_iterator e=_entries.begin();e!=_entries.end();++e) - v.push_back(&(e->second)); - return v; -} - -const Pack::Entry *Pack::get(const std::string &name) const -{ - std::map::const_iterator e(_entries.find(name)); - return ((e == _entries.end()) ? (const Entry *)0 : &(e->second)); -} - -const Pack::Entry *Pack::put(const std::string &name,const std::string &content) -{ - SHA256_CTX sha; - - Pack::Entry &e = _entries[name]; - e.name = name; - e.content = content; - - SHA256_Init(&sha); - SHA256_Update(&sha,content.data(),content.length()); - SHA256_Final(e.sha256,&sha); - - e.signedBy = 0; - e.signature.assign((const char *)0,0); - - return &e; -} - -void Pack::clear() -{ - _entries.clear(); -} - -std::string Pack::serialize() const -{ - BlobArray archive; - for(std::map::const_iterator e=_entries.begin();e!=_entries.end();++e) { - BlobArray entry; - entry.push_back(e->second.name); - entry.push_back(e->second.content); - entry.push_back(std::string((const char *)e->second.sha256,sizeof(e->second.sha256))); - entry.push_back(e->second.signedBy.toBinaryString()); - entry.push_back(e->second.signature); - archive.push_back(entry.serialize()); - } - - std::string ser(archive.serialize()); - std::string comp; - Utils::compress(ser.begin(),ser.end(),Utils::StringAppendOutput(comp)); - return comp; -} - -bool Pack::deserialize(const void *sd,unsigned int sdlen) -{ - unsigned char dig[32]; - SHA256_CTX sha; - - std::string decomp; - if (!Utils::decompress(((const char *)sd),((const char *)sd) + sdlen,Utils::StringAppendOutput(decomp))) - return false; - - BlobArray archive; - archive.deserialize(decomp.data(),decomp.length()); - clear(); - for(BlobArray::const_iterator i=archive.begin();i!=archive.end();++i) { - BlobArray entry; - entry.deserialize(i->data(),i->length()); - - if (entry.size() != 5) return false; - if (entry[2].length() != 32) return false; // SHA-256 - if (entry[3].length() != ZT_ADDRESS_LENGTH) return false; // Address - - Pack::Entry &e = _entries[entry[0]]; - e.name = entry[0]; - e.content = entry[1]; - - SHA256_Init(&sha); - SHA256_Update(&sha,e.content.data(),e.content.length()); - SHA256_Final(dig,&sha); - if (memcmp(dig,entry[2].data(),32)) return false; // integrity check failed - memcpy(e.sha256,dig,32); - - if (entry[3].length() == ZT_ADDRESS_LENGTH) - e.signedBy.setTo(entry[3].data()); - else e.signedBy = 0; - e.signature = entry[4]; - } - return true; -} - -bool Pack::signAll(const Identity &id) -{ - for(std::map::iterator e=_entries.begin();e!=_entries.end();++e) { - e->second.signedBy = id.address(); - e->second.signature = id.sign(e->second.sha256); - if (!e->second.signature.length()) - return false; - } - return true; -} - -std::vector Pack::verifyAll(const Identity &id,bool mandatory) const -{ - std::vector bad; - for(std::map::const_iterator e=_entries.begin();e!=_entries.end();++e) { - if ((e->second.signedBy)&&(e->second.signature.length())) { - if (id.address() != e->second.signedBy) - bad.push_back(&(e->second)); - else if (!id.verifySignature(e->second.sha256,e->second.signature.data(),e->second.signature.length())) - bad.push_back(&(e->second)); - } else if (mandatory) - bad.push_back(&(e->second)); - } - return bad; -} - -} // namespace ZeroTier diff --git a/node/Pack.hpp b/node/Pack.hpp deleted file mode 100644 index a0aecd6e..00000000 --- a/node/Pack.hpp +++ /dev/null @@ -1,141 +0,0 @@ -/* - * ZeroTier One - Global Peer to Peer Ethernet - * Copyright (C) 2012-2013 ZeroTier Networks LLC - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * -- - * - * ZeroTier may be used and distributed under the terms of the GPLv3, which - * are available at: http://www.gnu.org/licenses/gpl-3.0.html - * - * If you would like to embed ZeroTier into a commercial application or - * redistribute it in a modified binary form, please contact ZeroTier Networks - * LLC. Start here: http://www.zerotier.com/ - */ - -#ifndef _ZT_PACK_HPP -#define _ZT_PACK_HPP - -#include -#include -#include -#include -#include "Address.hpp" -#include "Identity.hpp" - -namespace ZeroTier { - -/** - * A very simple archive format for distributing packs of files or resources - * - * This is used for things like the auto-updater. It's not suitable for huge - * files, since at present it must work in memory. Packs support signing with - * identities and signature verification. - */ -class Pack -{ -public: - /** - * Pack entry structure for looking up deserialized entries - */ - struct Entry - { - std::string name; - std::string content; - unsigned char sha256[32]; - Address signedBy; - std::string signature; - }; - - Pack() {} - ~Pack() {} - - /** - * @return Vector of all entries - */ - std::vector getAll() const; - - /** - * Look up an entry - * - * @param name Name to look up - * @return Pointer to entry if it exists or NULL if not found - */ - const Entry *get(const std::string &name) const; - - /** - * Add an entry to this pack - * - * @param name Entry to add - * @param content Entry's contents - * @return The new entry - */ - const Entry *put(const std::string &name,const std::string &content); - - /** - * Remove all entries - */ - void clear(); - - /** - * @return Number of entries in pack - */ - inline unsigned int numEntries() const { return (unsigned int)_entries.size(); } - - /** - * Serialize this pack - * - * @return Serialized form (compressed with LZ4) - */ - std::string serialize() const; - - /** - * Deserialize this pack - * - * Any current contents are lost. This does not verify signatures, - * but does check SHA256 hashes for entry integrity. If the return - * value is false, the pack's contents are undefined. - * - * @param sd Serialized data - * @param sdlen Length of serialized data - * @return True on success, false on deserialization error - */ - bool deserialize(const void *sd,unsigned int sdlen); - inline bool deserialize(const std::string &sd) { return deserialize(sd.data(),sd.length()); } - - /** - * Sign all entries in this pack with a given identity - * - * @param id Identity to sign with - * @return True on signature success, false if error - */ - bool signAll(const Identity &id); - - /** - * Verify all signed entries - * - * @param id Identity to verify against - * @param mandatory If true, require that all entries be signed and fail if no signature - * @return Vector of entries that failed verification or empty vector if all passed - */ - std::vector verifyAll(const Identity &id,bool mandatory) const; - -private: - std::map _entries; -}; - -} // namespace ZeroTier - -#endif diff --git a/node/Packet.cpp b/node/Packet.cpp index d12f396d..4728609d 100644 --- a/node/Packet.cpp +++ b/node/Packet.cpp @@ -42,6 +42,7 @@ const char *Packet::verbString(Verb v) case VERB_FRAME: return "FRAME"; case VERB_MULTICAST_FRAME: return "MULTICAST_FRAME"; case VERB_MULTICAST_LIKE: return "MULTICAST_LIKE"; + case VERB_NETWORK_PERMISSION_CERTIFICATE: return "NETWORK_PERMISSION_CERTIFICATE"; } return "(unknown)"; } @@ -57,6 +58,8 @@ const char *Packet::errorString(ErrorCode e) case ERROR_IDENTITY_COLLISION: return "IDENTITY_COLLISION"; case ERROR_IDENTITY_INVALID: return "IDENTITY_INVALID"; case ERROR_UNSUPPORTED_OPERATION: return "UNSUPPORTED_OPERATION"; + case ERROR_NO_NETWORK_CERTIFICATE_ON_FILE: return "NO_NETWORK_CERTIFICATE_ON_FILE"; + case ERROR_OBJECT_EXPIRED: return "OBJECT_EXPIRED"; } return "(unknown)"; } diff --git a/node/Packet.hpp b/node/Packet.hpp index 5ccfae45..86d94e1d 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -463,7 +463,22 @@ public: * * No OK or ERROR is generated. */ - VERB_MULTICAST_FRAME = 9 + VERB_MULTICAST_FRAME = 9, + + /* Network permission certificate: + * <[8] 64-bit network ID> + * <[1] flags (currently unused, must be 0)> + * <[8] certificate timestamp> + * <[8] 16-bit length of signature> + * <[...] ECDSA signature of my binary serialized identity and timestamp> + * + * This message is used to send ahead of time a certificate proving + * this node has permission to communicate on a private network. + * + * OK is generated on acceptance. ERROR is returned on failure. In both + * cases the payload is the network ID. + */ + VERB_NETWORK_PERMISSION_CERTIFICATE = 10 }; /** @@ -490,7 +505,13 @@ public: ERROR_IDENTITY_INVALID = 5, /* Verb or use case not supported/enabled by this node */ - ERROR_UNSUPPORTED_OPERATION = 6 + ERROR_UNSUPPORTED_OPERATION = 6, + + /* Message to private network rejected -- no unexpired certificate on file */ + ERROR_NO_NETWORK_CERTIFICATE_ON_FILE = 7, + + /* Object is expired (e.g. network certificate) */ + ERROR_OBJECT_EXPIRED = 8 }; /** diff --git a/objects.mk b/objects.mk index 49312994..c6fab0b6 100644 --- a/objects.mk +++ b/objects.mk @@ -18,7 +18,6 @@ OBJS=\ node/NodeConfig.o \ node/Packet.o \ node/PacketDecoder.o \ - node/Pack.o \ node/Peer.o \ node/Salsa20.o \ node/Switch.o \ -- cgit v1.2.3 From a53cfc909638ea9eeb2bd477cee20d106f79bf6d Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 29 Jul 2013 13:56:20 -0400 Subject: Network membership certificate work in progress... does not build yet. --- Makefile.linux | 2 +- Makefile.mac | 2 +- node/Address.hpp | 34 ++++++- node/Dictionary.hpp | 73 +++++++++++++- node/Identity.cpp | 6 +- node/Network.cpp | 87 +++++++++++++++- node/Network.hpp | 262 ++++++++++++++++++++++++++++++++++++++++--------- node/NodeConfig.cpp | 2 +- node/Packet.cpp | 3 +- node/Packet.hpp | 49 +++++++-- node/PacketDecoder.cpp | 18 ++++ node/PacketDecoder.hpp | 3 + 12 files changed, 467 insertions(+), 74 deletions(-) (limited to 'node/Packet.hpp') diff --git a/Makefile.linux b/Makefile.linux index 9cb86305..bc77db4e 100644 --- a/Makefile.linux +++ b/Makefile.linux @@ -20,7 +20,7 @@ CXXFLAGS=$(CFLAGS) -fno-rtti # separate binaries for the RedHat and Debian universes to distribute via # auto-update. This way we get one Linux binary for all systems of a given # architecture. -LIBS=ext/bin/libcrypto/linux-$(ARCH)/libcrypto.a -ldl +LIBS=ext/bin/libcrypto/linux-$(ARCH)/libcrypto.a -lm include objects.mk diff --git a/Makefile.mac b/Makefile.mac index d3e3c27a..d47d18cc 100644 --- a/Makefile.mac +++ b/Makefile.mac @@ -13,7 +13,7 @@ STRIP=strip #STRIP=echo CXXFLAGS=$(CFLAGS) -fno-rtti -LIBS=-lcrypto -ldl +LIBS=-lcrypto -lm include objects.mk diff --git a/node/Address.hpp b/node/Address.hpp index 0e3f7b8e..8573a268 100644 --- a/node/Address.hpp +++ b/node/Address.hpp @@ -64,13 +64,28 @@ public: { } + Address(const char *s) + throw() + { + unsigned char foo[ZT_ADDRESS_LENGTH]; + setTo(foo,Utils::unhex(s,foo,ZT_ADDRESS_LENGTH)); + } + + Address(const std::string &s) + throw() + { + unsigned char foo[ZT_ADDRESS_LENGTH]; + setTo(foo,Utils::unhex(s.c_str(),foo,ZT_ADDRESS_LENGTH)); + } + /** * @param bits Raw address -- 5 bytes, big-endian byte order + * @param len Length of array */ - Address(const void *bits) + Address(const void *bits,unsigned int len) throw() { - setTo(bits); + setTo(bits,len); } inline Address &operator=(const Address &a) @@ -89,10 +104,15 @@ public: /** * @param bits Raw address -- 5 bytes, big-endian byte order + * @param len Length of array */ - inline void setTo(const void *bits) + inline void setTo(const void *bits,unsigned int len) throw() { + if (len < ZT_ADDRESS_LENGTH) { + _a = 0; + return; + } const unsigned char *b = (const unsigned char *)bits; uint64_t a = ((uint64_t)*b++) << 32; a |= ((uint64_t)*b++) << 24; @@ -104,10 +124,13 @@ public: /** * @param bits Buffer to hold 5-byte address in big-endian byte order + * @param len Length of array */ - inline void copyTo(void *bits) const + inline void copyTo(void *bits,unsigned int len) const throw() { + if (len < ZT_ADDRESS_LENGTH) + return; unsigned char *b = (unsigned char *)bits; *(b++) = (unsigned char)((_a >> 32) & 0xff); *(b++) = (unsigned char)((_a >> 24) & 0xff); @@ -164,7 +187,8 @@ public: throw() { MAC m; - copyTo(m.data); + m.data[0] = ZT_MAC_FIRST_OCTET; + copyTo(m.data + 1,ZT_ADDRESS_LENGTH); return m; } diff --git a/node/Dictionary.hpp b/node/Dictionary.hpp index fff600eb..f706168f 100644 --- a/node/Dictionary.hpp +++ b/node/Dictionary.hpp @@ -30,6 +30,7 @@ #include #include +#include #include "Constants.hpp" namespace ZeroTier { @@ -38,11 +39,76 @@ namespace ZeroTier { * Simple key/value dictionary with string serialization * * The serialization format is a flat key=value with backslash escape. - * It does not support comments or other syntactic complexities. + * It does not support comments or other syntactic complexities. It is + * human-readable if the keys and values in the dictionary are also + * human-readable. Otherwise it might contain unprintable characters. */ class Dictionary : public std::map { public: + Dictionary() + { + } + + /** + * @param s String-serialized dictionary + */ + Dictionary(const char *s) + { + fromString(s); + } + + /** + * @param s String-serialized dictionary + */ + Dictionary(const std::string &s) + { + fromString(s.c_str()); + } + + /** + * Get a key, throwing an exception if it is not present + * + * @param key Key to look up + * @return Reference to value + * @throws std::invalid_argument Key not found + */ + inline const std::string &get(const std::string &key) const + throw(std::invalid_argument) + { + const_iterator e(find(key)); + if (e == end()) + throw std::invalid_argument(std::string("missing required field: ")+key); + return e->second; + } + + /** + * Get a key, returning a default if not present + * + * @param key Key to look up + * @param dfl Default if not present + * @return Value or default + */ + inline const std::string &get(const std::string &key,const std::string &dfl) const + { + const_iterator e(find(key)); + if (e == end()) + return dfl; + return e->second; + } + + /** + * @param key Key to check + * @return True if dictionary contains key + */ + inline bool contains(const std::string &key) const + { + return (find(key) != end()); + } + + /** + * @return String-serialized dictionary + */ inline std::string toString() const { std::string s; @@ -57,6 +123,11 @@ public: return s; } + /** + * Clear and initialize from a string + * + * @param s String-serialized dictionary + */ inline void fromString(const char *s) { clear(); diff --git a/node/Identity.cpp b/node/Identity.cpp index a7ef403d..fdfdcd99 100644 --- a/node/Identity.cpp +++ b/node/Identity.cpp @@ -58,7 +58,7 @@ void Identity::generate() // invalid. Of course, deep verification of address/key relationship is // required to cover the more elaborate address claim jump attempt case. unsigned char atmp[ZT_ADDRESS_LENGTH]; - _address.copyTo(atmp); + _address.copyTo(atmp,ZT_ADDRESS_LENGTH); SHA256_CTX sha; unsigned char dig[32]; unsigned char idtype = IDENTITY_TYPE_NIST_P_521,zero = 0; @@ -76,7 +76,7 @@ void Identity::generate() bool Identity::locallyValidate(bool doAddressDerivationCheck) const { unsigned char atmp[ZT_ADDRESS_LENGTH]; - _address.copyTo(atmp); + _address.copyTo(atmp,ZT_ADDRESS_LENGTH); SHA256_CTX sha; unsigned char dig[32]; unsigned char idtype = IDENTITY_TYPE_NIST_P_521,zero = 0; @@ -222,7 +222,7 @@ Address Identity::deriveAddress(const void *keyBytes,unsigned int keyLen) delete [] ram; - return Address(dig); // first 5 bytes of dig[] + return Address(dig,ZT_ADDRESS_LENGTH); // first 5 bytes of dig[] } std::string Identity::encrypt(const Identity &to,const void *data,unsigned int len) const diff --git a/node/Network.cpp b/node/Network.cpp index f34e07e0..34a9a85b 100644 --- a/node/Network.cpp +++ b/node/Network.cpp @@ -25,19 +25,90 @@ * LLC. Start here: http://www.zerotier.com/ */ +#include +#include + +#include + +#include "RuntimeEnvironment.hpp" +#include "NodeConfig.hpp" #include "Network.hpp" #include "Switch.hpp" namespace ZeroTier { +void Network::Certificate::sign(const Identity &with) +{ + unsigned char dig[32]; + SHA256_CTX sha; + SHA256_Init(&sha); + unsigned char zero = 0; + for(const_iterator i(begin());i!=end();++i) { + if (i->first != "sig") { + SHA256_Update(&sha,&zero,1); + SHA256_Update(&sha,(const unsigned char *)i->first.data(),i->first.length()); + SHA256_Update(&sha,&zero,1); + SHA256_Update(&sha,(const unsigned char *)i->second.data(),i->second.length()); + SHA256_Update(&sha,&zero,1); + } + } + SHA256_Final(dig,&sha); + (*this)["sig"] = with.sign(dig); +} + +static const std::string _DELTA_PREFIX("~"); +bool Network::Certificate::qualifyMembership(const Network::Certificate &mc) const +{ + // Note: optimization probably needed here, probably via some kind of + // memoization / dynamic programming. + + for(const_iterator myField(begin());myField!=end();++myField) { + if (!((myField->first.length() > 1)&&(myField->first[0] == '~'))) { // ~fields are max delta range specs + // If they lack the same field, comparison fails. + const_iterator theirField(mc.find(myField->first)); + if (theirField == mc.end()) + return false; + + const_iterator deltaField(find(_DELTA_PREFIX + myField->first)); + if (deltaField == end()) { + // If there is no delta, compare for equality (e.g. node, nwid) + if (myField->second != theirField->second) + return false; + } else { + // Otherwise compare range with max delta. Presence of a dot in delta + // indicates a floating point comparison. Otherwise an integer + // comparison occurs. + if (deltaField->second.find('.') != std::string::npos) { + double my = strtod(myField->second.c_str(),(char **)0); + double their = strtod(theirField->second.c_str(),(char **)0); + double delta = strtod(deltaField->second.c_str(),(char **)0); + if (fabs(my - their) > delta) + return false; + } else { + int64_t my = strtoll(myField->second.c_str(),(char **)0,10); + int64_t their = strtoll(theirField->second.c_str(),(char **)0,10); + int64_t delta = strtoll(deltaField->second.c_str(),(char **)0,10); + if (my > their) { + if ((my - their) > delta) + return false; + } else { + if ((their - my) > delta) + return false; + } + } + } + } + } + + return true; +} + Network::Network(const RuntimeEnvironment *renv,uint64_t id) throw(std::runtime_error) : _r(renv), - _id(id), _tap(renv,renv->identity.address().toMAC(),ZT_IF_MTU,&_CBhandleTapData,this), - _members(), - _open(false), - _lock() + _id(id), + _isOpen(false) { } @@ -45,6 +116,14 @@ Network::~Network() { } +void Network::setConfiguration(const Network::Config &conf) +{ +} + +void Network::requestConfiguration() +{ +} + void Network::_CBhandleTapData(void *arg,const MAC &from,const MAC &to,unsigned int etherType,const Buffer<4096> &data) { const RuntimeEnvironment *_r = ((Network *)arg)->_r; diff --git a/node/Network.hpp b/node/Network.hpp index 6263aa9b..359e2ce5 100644 --- a/node/Network.hpp +++ b/node/Network.hpp @@ -30,28 +30,37 @@ #include #include +#include #include #include + +#include "Constants.hpp" +#include "Utils.hpp" #include "EthernetTap.hpp" #include "Address.hpp" #include "Mutex.hpp" -#include "InetAddress.hpp" -#include "Constants.hpp" #include "SharedPtr.hpp" #include "AtomicCounter.hpp" -#include "RuntimeEnvironment.hpp" #include "MulticastGroup.hpp" #include "NonCopyable.hpp" #include "MAC.hpp" +#include "Dictionary.hpp" +#include "Identity.hpp" +#include "InetAddress.hpp" namespace ZeroTier { +class RuntimeEnvironment; class NodeConfig; /** * A virtual LAN * - * Networks can be open or closed. + * Networks can be open or closed. Each network has an ID whose most + * significant 40 bits are the ZeroTier address of the node that should + * be contacted for network configuration. The least significant 24 + * bits are arbitrary, allowing up to 2^24 networks per managing + * node. * * Open networks do not track membership. Anyone is allowed to communicate * over them. @@ -69,7 +78,183 @@ class Network : NonCopyable friend class SharedPtr; friend class NodeConfig; +public: + /** + * A certificate of network membership + */ + class Certificate : private Dictionary + { + public: + Certificate() + { + } + + Certificate(const char *s) : + Dictionary(s) + { + } + + Certificate(const std::string &s) : + Dictionary(s) + { + } + + /** + * @return Read-only underlying dictionary + */ + inline const Dictionary &dictionary() const { return *this; } + + inline void setNetworkId(uint64_t id) + { + char buf[32]; + sprintf(buf,"%llu",id); + (*this)["nwid"] = buf; + } + + inline uint64_t networkId() const + throw(std::invalid_argument) + { + return strtoull(get("nwid").c_str(),(char **)0,10); + } + + inline void setPeerAddress(Address &a) + { + (*this)["peer"] = a.toString(); + } + + inline Address peerAddress() const + throw(std::invalid_argument) + { + return Address(get("peer")); + } + + /** + * Set the timestamp and max-delta + * + * @param ts Timestamp in ms since epoch + * @param maxDelta Maximum difference between two peers on the same network + */ + inline void setTimestamp(uint64_t ts,uint64_t maxDelta) + { + char foo[32]; + sprintf(foo,"%llu",ts); + (*this)["ts"] = foo; + sprintf(foo,"%llu",maxDelta); + (*this)["~ts"] = foo; + } + + /** + * Set or update the sig field to contain a signature + * + * @param with Signing identity -- the identity of this network's controller + */ + void sign(const Identity &with); + + /** + * Check if another peer is indeed a current member of this network + * + * Fields with companion ~fields are compared with the defined maximum + * delta in this certificate. Fields without ~fields are compared for + * equality. + * + * This does not verify the certificate's signature! The signature + * must be verified first. + * + * @param mc Peer membership certificate + * @return True if mc's membership in this network is current + */ + bool qualifyMembership(const Certificate &mc) const; + }; + + /** + * A network configuration for a given node + */ + class Config : private Dictionary + { + public: + Config() + { + } + + Config(const char *s) : + Dictionary(s) + { + } + + Config(const std::string &s) : + Dictionary(s) + { + } + + /** + * @return Certificate of membership for this network, or empty cert if none + */ + inline Certificate certificateOfMembership() const + { + return Certificate(get("com","")); + } + + /** + * @return True if this is an open non-access-controlled network + */ + inline bool isOpen() const + { + return (get("isOpen","0") == "1"); + } + + /** + * @return All static addresses / netmasks, IPv4 or IPv6 + */ + inline std::set staticAddresses() const + { + std::set sa; + std::vector ips(Utils::split(get("ipv4Static","").c_str(),",","","")); + for(std::vector::const_iterator i(ips.begin());i!=ips.end();++i) + sa.insert(InetAddress(*i)); + ips = Utils::split(get("ipv6Static","").c_str(),",","",""); + for(std::vector::const_iterator i(ips.begin());i!=ips.end();++i) + sa.insert(InetAddress(*i)); + return sa; + } + + /** + * Set static IPv4 and IPv6 addresses + * + * This sets the ipv4Static and ipv6Static fields to comma-delimited + * lists of assignments. The port field in InetAddress must be the + * number of bits in the netmask. + * + * @param begin Start of container or array of addresses (InetAddress) + * @param end End of container or array of addresses (InetAddress) + * @tparam I Type of container or array + */ + template + inline void setStaticInetAddresses(const I &begin,const I &end) + { + std::string v4; + std::string v6; + for(I i(begin);i!=end;++i) { + if (i->isV4()) { + if (v4.length()) + v4.push_back(','); + v4.append(i->toString()); + } else if (i->isV6()) { + if (v6.length()) + v6.push_back(','); + v6.append(i->toString()); + } + } + if (v4.length()) + (*this)["ipv4Static"] = v4; + else erase("ipv4Static"); + if (v6.length()) + (*this)["ipv6Static"] = v6; + else erase("ipv6Static"); + } + }; + private: + // Only NodeConfig can create, only SharedPtr can delete Network(const RuntimeEnvironment *renv,uint64_t id) throw(std::runtime_error); @@ -87,56 +272,22 @@ public: inline EthernetTap &tap() throw() { return _tap; } /** - * Get this network's members - * - * If this is an open network, membership isn't relevant and this doesn't - * mean much. If it's a closed network, frames will only be exchanged to/from - * members. - * - * @return Members of this network + * @return Address of network's controlling node */ - inline std::set
members() const - { - Mutex::Lock _l(_lock); - return _members; - } - - /** - * @param addr Address to check - * @return True if address is a member - */ - inline bool isMember(const Address &addr) const - throw() - { - Mutex::Lock _l(_lock); - return (_members.count(addr) > 0); - } - - /** - * Shortcut to check open() and then isMember() - * - * @param addr Address to check - * @return True if network is open or if address is a member - */ - inline bool isAllowed(const Address &addr) const - throw() - { - Mutex::Lock _l(_lock); - return ((_open)||(_members.count(addr) > 0)); - } + inline Address controller() throw() { return Address(_id >> 24); } /** * @return True if network is open (no membership required) */ - inline bool open() const + inline bool isOpen() const throw() { Mutex::Lock _l(_lock); - return _open; + return _isOpen; } /** - * Update internal multicast group set and return true if changed + * Update multicast groups for this network's tap * * @return True if internal multicast group set has changed */ @@ -147,7 +298,7 @@ public: } /** - * @return Latest set of multicast groups + * @return Latest set of multicast groups for this network's tap */ inline std::set multicastGroups() const { @@ -155,15 +306,32 @@ public: return _multicastGroups; } + /** + * Set or update this network's configuration + * + * This is called by PacketDecoder when an update comes over the wire, or + * internally when an old config is reloaded from disk. + * + * @param conf Configuration in key/value dictionary form + */ + void setConfiguration(const Config &conf); + + /** + * Causes this network to request an updated configuration from its master node now + */ + void requestConfiguration(); + private: static void _CBhandleTapData(void *arg,const MAC &from,const MAC &to,unsigned int etherType,const Buffer<4096> &data); const RuntimeEnvironment *_r; - uint64_t _id; + EthernetTap _tap; - std::set
_members; std::set _multicastGroups; - bool _open; + + uint64_t _id; + bool _isOpen; + Mutex _lock; AtomicCounter __refCount; diff --git a/node/NodeConfig.cpp b/node/NodeConfig.cpp index 1a84b6b7..214c06b7 100644 --- a/node/NodeConfig.cpp +++ b/node/NodeConfig.cpp @@ -126,7 +126,7 @@ std::vector NodeConfig::execute(const char *command) _P("200 listnetworks %llu %s %s", nw->first, nw->second->tap().deviceName().c_str(), - (nw->second->open() ? "public" : "private")); + (nw->second->isOpen() ? "public" : "private")); } } else if (cmd[0] == "join") { _P("404 join Not implemented yet."); diff --git a/node/Packet.cpp b/node/Packet.cpp index 4728609d..d3c0a3af 100644 --- a/node/Packet.cpp +++ b/node/Packet.cpp @@ -43,6 +43,8 @@ const char *Packet::verbString(Verb v) case VERB_MULTICAST_FRAME: return "MULTICAST_FRAME"; case VERB_MULTICAST_LIKE: return "MULTICAST_LIKE"; case VERB_NETWORK_PERMISSION_CERTIFICATE: return "NETWORK_PERMISSION_CERTIFICATE"; + case VERB_NETWORK_CONFIG_REQUEST: return "NETWORK_CONFIG_REQUEST"; + case VERB_NETWORK_CONFIG_REFRESH: return "NETWORK_CONFIG_REFRESH"; } return "(unknown)"; } @@ -59,7 +61,6 @@ const char *Packet::errorString(ErrorCode e) case ERROR_IDENTITY_INVALID: return "IDENTITY_INVALID"; case ERROR_UNSUPPORTED_OPERATION: return "UNSUPPORTED_OPERATION"; case ERROR_NO_NETWORK_CERTIFICATE_ON_FILE: return "NO_NETWORK_CERTIFICATE_ON_FILE"; - case ERROR_OBJECT_EXPIRED: return "OBJECT_EXPIRED"; } return "(unknown)"; } diff --git a/node/Packet.hpp b/node/Packet.hpp index 86d94e1d..2f88ac37 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -287,7 +287,7 @@ public: * * @return Destination ZT address */ - inline Address destination() const { return Address(field(ZT_PACKET_FRAGMENT_IDX_DEST,ZT_ADDRESS_LENGTH)); } + inline Address destination() const { return Address(field(ZT_PACKET_FRAGMENT_IDX_DEST,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); } /** * @return True if fragment is of a valid length @@ -468,8 +468,9 @@ public: /* Network permission certificate: * <[8] 64-bit network ID> * <[1] flags (currently unused, must be 0)> - * <[8] certificate timestamp> - * <[8] 16-bit length of signature> + * <[2] 16-bit length of qualifying fields> + * <[...] string-serialized dictionary of qualifying fields> + * <[2] 16-bit length of signature> * <[...] ECDSA signature of my binary serialized identity and timestamp> * * This message is used to send ahead of time a certificate proving @@ -478,7 +479,38 @@ public: * OK is generated on acceptance. ERROR is returned on failure. In both * cases the payload is the network ID. */ - VERB_NETWORK_PERMISSION_CERTIFICATE = 10 + VERB_NETWORK_PERMISSION_CERTIFICATE = 10, + + /* Network configuration request: + * <[8] 64-bit network ID> + * + * This message requests network configuration from a node capable of + * providing it. Such nodes run the netconf service, which must be + * installed into the ZeroTier home directory. + * + * OK response payload: + * <[8] 64-bit network ID> + * <[...] network configuration dictionary> + * + * OK returns a Dictionary (string serialized) containing the network's + * configuration and IP address assignment information for the querying + * node. + * + * ERROR may be NOT_FOUND if no such network is known, or + * UNSUPPORTED_OPERATION if the netconf service isn't available. The + * payload will be the network ID. + */ + VERB_NETWORK_CONFIG_REQUEST = 11, + + /* Network configuration refresh request: + * <[8] 64-bit network ID> + * + * This message can be sent by the network configuration master node + * to request that nodes refresh their network configuration. + * + * It is only a hint and does not presently elicit a response. + */ + VERB_NETWORK_CONFIG_REFRESH = 12 }; /** @@ -508,10 +540,7 @@ public: ERROR_UNSUPPORTED_OPERATION = 6, /* Message to private network rejected -- no unexpired certificate on file */ - ERROR_NO_NETWORK_CERTIFICATE_ON_FILE = 7, - - /* Object is expired (e.g. network certificate) */ - ERROR_OBJECT_EXPIRED = 8 + ERROR_NO_NETWORK_CERTIFICATE_ON_FILE = 7 }; /** @@ -624,14 +653,14 @@ public: * * @return Destination ZT address */ - inline Address destination() const { return Address(field(ZT_PACKET_IDX_DEST,ZT_ADDRESS_LENGTH)); } + inline Address destination() const { return Address(field(ZT_PACKET_IDX_DEST,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); } /** * Get this packet's source * * @return Source ZT address */ - inline Address source() const { return Address(field(ZT_PACKET_IDX_SOURCE,ZT_ADDRESS_LENGTH)); } + inline Address source() const { return Address(field(ZT_PACKET_IDX_SOURCE,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); } /** * @return True if packet is of valid length diff --git a/node/PacketDecoder.cpp b/node/PacketDecoder.cpp index 810e30a7..cd9985c3 100644 --- a/node/PacketDecoder.cpp +++ b/node/PacketDecoder.cpp @@ -102,6 +102,12 @@ bool PacketDecoder::tryDecode(const RuntimeEnvironment *_r) return _doMULTICAST_LIKE(_r,peer); case Packet::VERB_MULTICAST_FRAME: return _doMULTICAST_FRAME(_r,peer); + case Packet::VERB_NETWORK_PERMISSION_CERTIFICATE: + return _doNETWORK_PERMISSION_CERTIFICATE(_r,peer); + case Packet::VERB_NETWORK_CONFIG_REQUEST: + return _doNETWORK_CONFIG_REQUEST(_r,peer); + case Packet::VERB_NETWORK_CONFIG_REFRESH: + return _doNETWORK_CONFIG_REFRESH(_r,peer); default: // This might be something from a new or old version of the protocol. // Technically it passed HMAC so the packet is still valid, but we @@ -538,4 +544,16 @@ bool PacketDecoder::_doMULTICAST_FRAME(const RuntimeEnvironment *_r,const Shared return true; } +bool PacketDecoder::_doNETWORK_PERMISSION_CERTIFICATE(const RuntimeEnvironment *_r,const SharedPtr &peer) +{ +} + +bool PacketDecoder::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *_r,const SharedPtr &peer) +{ +} + +bool PacketDecoder::_doNETWORK_CONFIG_REFRESH(const RuntimeEnvironment *_r,const SharedPtr &peer) +{ +} + } // namespace ZeroTier diff --git a/node/PacketDecoder.hpp b/node/PacketDecoder.hpp index e595d326..1cd1dca7 100644 --- a/node/PacketDecoder.hpp +++ b/node/PacketDecoder.hpp @@ -122,6 +122,9 @@ private: bool _doFRAME(const RuntimeEnvironment *_r,const SharedPtr &peer); bool _doMULTICAST_LIKE(const RuntimeEnvironment *_r,const SharedPtr &peer); bool _doMULTICAST_FRAME(const RuntimeEnvironment *_r,const SharedPtr &peer); + bool _doNETWORK_PERMISSION_CERTIFICATE(const RuntimeEnvironment *_r,const SharedPtr &peer); + bool _doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *_r,const SharedPtr &peer); + bool _doNETWORK_CONFIG_REFRESH(const RuntimeEnvironment *_r,const SharedPtr &peer); uint64_t _receiveTime; Demarc::Port _localPort; -- cgit v1.2.3 From 439e602d5a5712d1b33fb19d558d0e9fdf784703 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 29 Jul 2013 16:18:29 -0400 Subject: Fix a bunch of errors due to minor method signature changes, still a work in progress. --- node/Identity.hpp | 2 +- node/Network.cpp | 18 +++++++----------- node/Network.hpp | 32 +++++++++++++++++++++++++++----- node/Packet.cpp | 5 +++-- node/Packet.hpp | 22 +++++++++++----------- node/PacketDecoder.cpp | 16 ++++++++-------- node/PacketDecoder.hpp | 2 +- 7 files changed, 58 insertions(+), 39 deletions(-) (limited to 'node/Packet.hpp') diff --git a/node/Identity.hpp b/node/Identity.hpp index e5a11d6a..a9f78c8a 100644 --- a/node/Identity.hpp +++ b/node/Identity.hpp @@ -340,7 +340,7 @@ public: unsigned int p = startAt; - _address = b.field(p,ZT_ADDRESS_LENGTH); + _address.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); p += ZT_ADDRESS_LENGTH; if (b[p++] != IDENTITY_TYPE_NIST_P_521) diff --git a/node/Network.cpp b/node/Network.cpp index 34a9a85b..696426e4 100644 --- a/node/Network.cpp +++ b/node/Network.cpp @@ -37,23 +37,19 @@ namespace ZeroTier { -void Network::Certificate::sign(const Identity &with) +void Network::Certificate::_shaForSignature(unsigned char *dig) const { - unsigned char dig[32]; SHA256_CTX sha; SHA256_Init(&sha); unsigned char zero = 0; for(const_iterator i(begin());i!=end();++i) { - if (i->first != "sig") { - SHA256_Update(&sha,&zero,1); - SHA256_Update(&sha,(const unsigned char *)i->first.data(),i->first.length()); - SHA256_Update(&sha,&zero,1); - SHA256_Update(&sha,(const unsigned char *)i->second.data(),i->second.length()); - SHA256_Update(&sha,&zero,1); - } + SHA256_Update(&sha,&zero,1); + SHA256_Update(&sha,(const unsigned char *)i->first.data(),i->first.length()); + SHA256_Update(&sha,&zero,1); + SHA256_Update(&sha,(const unsigned char *)i->second.data(),i->second.length()); + SHA256_Update(&sha,&zero,1); } SHA256_Final(dig,&sha); - (*this)["sig"] = with.sign(dig); } static const std::string _DELTA_PREFIX("~"); @@ -71,7 +67,7 @@ bool Network::Certificate::qualifyMembership(const Network::Certificate &mc) con const_iterator deltaField(find(_DELTA_PREFIX + myField->first)); if (deltaField == end()) { - // If there is no delta, compare for equality (e.g. node, nwid) + // If there is no delta, compare on simple equality if (myField->second != theirField->second) return false; } else { diff --git a/node/Network.hpp b/node/Network.hpp index 359e2ce5..c13f00a4 100644 --- a/node/Network.hpp +++ b/node/Network.hpp @@ -129,7 +129,7 @@ public: } /** - * Set the timestamp and max-delta + * Set the timestamp and timestamp max-delta * * @param ts Timestamp in ms since epoch * @param maxDelta Maximum difference between two peers on the same network @@ -144,11 +144,31 @@ public: } /** - * Set or update the sig field to contain a signature + * Sign this certificate * * @param with Signing identity -- the identity of this network's controller + * @return Signature or empty string on failure */ - void sign(const Identity &with); + inline std::string sign(const Identity &with) const + { + unsigned char dig[32]; + _shaForSignature(dig); + return with.sign(dig); + } + + /** + * Verify this certificate's signature + * + * @param with Signing identity -- the identity of this network's controller + * @param sig Signature + * @param siglen Length of signature in bytes + */ + inline bool verify(const Identity &with,const void *sig,unsigned int siglen) const + { + unsigned char dig[32]; + _shaForSignature(dig); + return with.verifySignature(dig,sig,siglen); + } /** * Check if another peer is indeed a current member of this network @@ -157,13 +177,15 @@ public: * delta in this certificate. Fields without ~fields are compared for * equality. * - * This does not verify the certificate's signature! The signature - * must be verified first. + * This does not verify the certificate's signature! * * @param mc Peer membership certificate * @return True if mc's membership in this network is current */ bool qualifyMembership(const Certificate &mc) const; + + private: + void _shaForSignature(unsigned char *dig) const; }; /** diff --git a/node/Packet.cpp b/node/Packet.cpp index d3c0a3af..0aae1e2d 100644 --- a/node/Packet.cpp +++ b/node/Packet.cpp @@ -42,7 +42,7 @@ const char *Packet::verbString(Verb v) case VERB_FRAME: return "FRAME"; case VERB_MULTICAST_FRAME: return "MULTICAST_FRAME"; case VERB_MULTICAST_LIKE: return "MULTICAST_LIKE"; - case VERB_NETWORK_PERMISSION_CERTIFICATE: return "NETWORK_PERMISSION_CERTIFICATE"; + case VERB_NETWORK_MEMBERSHIP_CERTIFICATE: return "NETWORK_MEMBERSHIP_CERTIFICATE"; case VERB_NETWORK_CONFIG_REQUEST: return "NETWORK_CONFIG_REQUEST"; case VERB_NETWORK_CONFIG_REFRESH: return "NETWORK_CONFIG_REFRESH"; } @@ -60,7 +60,8 @@ const char *Packet::errorString(ErrorCode e) case ERROR_IDENTITY_COLLISION: return "IDENTITY_COLLISION"; case ERROR_IDENTITY_INVALID: return "IDENTITY_INVALID"; case ERROR_UNSUPPORTED_OPERATION: return "UNSUPPORTED_OPERATION"; - case ERROR_NO_NETWORK_CERTIFICATE_ON_FILE: return "NO_NETWORK_CERTIFICATE_ON_FILE"; + case ERROR_NO_MEMBER_CERTIFICATE_ON_FILE: return "NO_MEMBER_CERTIFICATE_ON_FILE"; + case ERROR_MEMBER_CERTIFICATE_UNQUALIFIED: return "MEMBER_CERTIFICATE_UNQUALIFIED"; } return "(unknown)"; } diff --git a/node/Packet.hpp b/node/Packet.hpp index 2f88ac37..85ccb466 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -465,21 +465,17 @@ public: */ VERB_MULTICAST_FRAME = 9, - /* Network permission certificate: + /* Network member certificate for sending peer: * <[8] 64-bit network ID> - * <[1] flags (currently unused, must be 0)> - * <[2] 16-bit length of qualifying fields> - * <[...] string-serialized dictionary of qualifying fields> + * <[2] 16-bit length of certificate> + * <[...] string-serialized certificate dictionary> * <[2] 16-bit length of signature> - * <[...] ECDSA signature of my binary serialized identity and timestamp> - * - * This message is used to send ahead of time a certificate proving - * this node has permission to communicate on a private network. + * <[...] ECDSA signature of certificate> * * OK is generated on acceptance. ERROR is returned on failure. In both * cases the payload is the network ID. */ - VERB_NETWORK_PERMISSION_CERTIFICATE = 10, + VERB_NETWORK_MEMBERSHIP_CERTIFICATE = 10, /* Network configuration request: * <[8] 64-bit network ID> @@ -506,7 +502,8 @@ public: * <[8] 64-bit network ID> * * This message can be sent by the network configuration master node - * to request that nodes refresh their network configuration. + * to request that nodes refresh their network configuration. It can + * thus be used to "push" updates. * * It is only a hint and does not presently elicit a response. */ @@ -540,7 +537,10 @@ public: ERROR_UNSUPPORTED_OPERATION = 6, /* Message to private network rejected -- no unexpired certificate on file */ - ERROR_NO_NETWORK_CERTIFICATE_ON_FILE = 7 + ERROR_NO_MEMBER_CERTIFICATE_ON_FILE = 7, + + /* Membership certificate no longer qualified for membership in network */ + ERROR_MEMBER_CERTIFICATE_UNQUALIFIED = 8 }; /** diff --git a/node/PacketDecoder.cpp b/node/PacketDecoder.cpp index cd9985c3..0353625c 100644 --- a/node/PacketDecoder.cpp +++ b/node/PacketDecoder.cpp @@ -102,8 +102,8 @@ bool PacketDecoder::tryDecode(const RuntimeEnvironment *_r) return _doMULTICAST_LIKE(_r,peer); case Packet::VERB_MULTICAST_FRAME: return _doMULTICAST_FRAME(_r,peer); - case Packet::VERB_NETWORK_PERMISSION_CERTIFICATE: - return _doNETWORK_PERMISSION_CERTIFICATE(_r,peer); + case Packet::VERB_NETWORK_MEMBERSHIP_CERTIFICATE: + return _doNETWORK_MEMBERSHIP_CERTIFICATE(_r,peer); case Packet::VERB_NETWORK_CONFIG_REQUEST: return _doNETWORK_CONFIG_REQUEST(_r,peer); case Packet::VERB_NETWORK_CONFIG_REFRESH: @@ -311,7 +311,7 @@ bool PacketDecoder::_doOK(const RuntimeEnvironment *_r,const SharedPtr &pe bool PacketDecoder::_doWHOIS(const RuntimeEnvironment *_r,const SharedPtr &peer) { if (payloadLength() == ZT_ADDRESS_LENGTH) { - SharedPtr p(_r->topology->getPeer(Address(payload()))); + SharedPtr p(_r->topology->getPeer(Address(payload(),ZT_ADDRESS_LENGTH))); if (p) { Packet outp(source(),_r->identity.address(),Packet::VERB_OK); outp.append((unsigned char)Packet::VERB_WHOIS); @@ -320,7 +320,7 @@ bool PacketDecoder::_doWHOIS(const RuntimeEnvironment *_r,const SharedPtr outp.encrypt(peer->cryptKey()); outp.hmacSet(peer->macKey()); _r->demarc->send(_localPort,_remoteAddress,outp.data(),outp.size(),-1); - TRACE("sent WHOIS response to %s for %s",source().toString().c_str(),Address(payload()).toString().c_str()); + TRACE("sent WHOIS response to %s for %s",source().toString().c_str(),Address(payload(),ZT_ADDRESS_LENGTH).toString().c_str()); } else { Packet outp(source(),_r->identity.address(),Packet::VERB_ERROR); outp.append((unsigned char)Packet::VERB_WHOIS); @@ -330,7 +330,7 @@ bool PacketDecoder::_doWHOIS(const RuntimeEnvironment *_r,const SharedPtr outp.encrypt(peer->cryptKey()); outp.hmacSet(peer->macKey()); _r->demarc->send(_localPort,_remoteAddress,outp.data(),outp.size(),-1); - TRACE("sent WHOIS ERROR to %s for %s (not found)",source().toString().c_str(),Address(payload()).toString().c_str()); + TRACE("sent WHOIS ERROR to %s for %s (not found)",source().toString().c_str(),Address(payload(),ZT_ADDRESS_LENGTH).toString().c_str()); } } else { TRACE("dropped WHOIS from %s(%s): missing or invalid address",source().toString().c_str(),_remoteAddress.toString().c_str()); @@ -341,7 +341,7 @@ bool PacketDecoder::_doWHOIS(const RuntimeEnvironment *_r,const SharedPtr bool PacketDecoder::_doRENDEZVOUS(const RuntimeEnvironment *_r,const SharedPtr &peer) { try { - Address with(field(ZT_PROTO_VERB_RENDEZVOUS_IDX_ZTADDRESS,ZT_ADDRESS_LENGTH)); + Address with(field(ZT_PROTO_VERB_RENDEZVOUS_IDX_ZTADDRESS,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); SharedPtr withPeer(_r->topology->getPeer(with)); if (withPeer) { unsigned int port = at(ZT_PROTO_VERB_RENDEZVOUS_IDX_PORT); @@ -439,7 +439,7 @@ bool PacketDecoder::_doMULTICAST_FRAME(const RuntimeEnvironment *_r,const Shared if (network->isAllowed(source())) { if (size() > ZT_PROTO_VERB_MULTICAST_FRAME_IDX_PAYLOAD) { - Address originalSubmitterAddress(field(ZT_PROTO_VERB_MULTICAST_FRAME_IDX_SUBMITTER_ADDRESS,ZT_ADDRESS_LENGTH)); + Address originalSubmitterAddress(field(ZT_PROTO_VERB_MULTICAST_FRAME_IDX_SUBMITTER_ADDRESS,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); MAC fromMac(field(ZT_PROTO_VERB_MULTICAST_FRAME_IDX_SOURCE_MAC,6)); MulticastGroup mg(MAC(field(ZT_PROTO_VERB_MULTICAST_FRAME_IDX_DESTINATION_MAC,6)),at(ZT_PROTO_VERB_MULTICAST_FRAME_IDX_ADI)); unsigned int hops = (*this)[ZT_PROTO_VERB_MULTICAST_FRAME_IDX_HOP_COUNT]; @@ -544,7 +544,7 @@ bool PacketDecoder::_doMULTICAST_FRAME(const RuntimeEnvironment *_r,const Shared return true; } -bool PacketDecoder::_doNETWORK_PERMISSION_CERTIFICATE(const RuntimeEnvironment *_r,const SharedPtr &peer) +bool PacketDecoder::_doNETWORK_MEMBERSHIP_CERTIFICATE(const RuntimeEnvironment *_r,const SharedPtr &peer) { } diff --git a/node/PacketDecoder.hpp b/node/PacketDecoder.hpp index 1cd1dca7..51408ba5 100644 --- a/node/PacketDecoder.hpp +++ b/node/PacketDecoder.hpp @@ -122,7 +122,7 @@ private: bool _doFRAME(const RuntimeEnvironment *_r,const SharedPtr &peer); bool _doMULTICAST_LIKE(const RuntimeEnvironment *_r,const SharedPtr &peer); bool _doMULTICAST_FRAME(const RuntimeEnvironment *_r,const SharedPtr &peer); - bool _doNETWORK_PERMISSION_CERTIFICATE(const RuntimeEnvironment *_r,const SharedPtr &peer); + bool _doNETWORK_MEMBERSHIP_CERTIFICATE(const RuntimeEnvironment *_r,const SharedPtr &peer); bool _doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *_r,const SharedPtr &peer); bool _doNETWORK_CONFIG_REFRESH(const RuntimeEnvironment *_r,const SharedPtr &peer); -- cgit v1.2.3 From e4c5ad9f43f37f3c5cd9feb1035d3b3091820e43 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 29 Jul 2013 17:11:00 -0400 Subject: More work on network membership certs, and it builds now. Still in heavy development. --- node/Network.cpp | 35 +++++++++++++++++++++++++++++-- node/Network.hpp | 62 +++++++++++++++++++++++++++++++++++++++++++++++++------ node/Packet.cpp | 3 +-- node/Packet.hpp | 5 +---- node/Switch.cpp | 2 +- node/Topology.cpp | 6 +++--- 6 files changed, 95 insertions(+), 18 deletions(-) (limited to 'node/Packet.hpp') diff --git a/node/Network.cpp b/node/Network.cpp index 696426e4..5878a281 100644 --- a/node/Network.cpp +++ b/node/Network.cpp @@ -103,8 +103,7 @@ Network::Network(const RuntimeEnvironment *renv,uint64_t id) throw(std::runtime_error) : _r(renv), _tap(renv,renv->identity.address().toMAC(),ZT_IF_MTU,&_CBhandleTapData,this), - _id(id), - _isOpen(false) + _id(id) { } @@ -114,12 +113,44 @@ Network::~Network() void Network::setConfiguration(const Network::Config &conf) { + Mutex::Lock _l(_lock); + _configuration = conf; + _myCertificate = conf.certificateOfMembership(); } void Network::requestConfiguration() { } +bool Network::isAllowed(const Address &peer) const +{ + try { + Mutex::Lock _l(_lock); + if (_configuration.isOpen()) + return true; + std::map::const_iterator pc(_membershipCertificates.find(peer)); + if (pc == _membershipCertificates.end()) + return false; + return _myCertificate.qualifyMembership(pc->second); + } catch (std::exception &exc) { + TRACE("isAllowed() check failed for peer %s: unexpected exception: %s",peer.toString().c_str(),exc.what()); + return false; + } catch ( ... ) { + TRACE("isAllowed() check failed for peer %s: unexpected exception: unknown exception",peer.toString().c_str()); + return false; + } +} + +void Network::clean() +{ + Mutex::Lock _l(_lock); + for(std::map::iterator i=(_membershipCertificates.begin());i!=_membershipCertificates.end();) { + if (_myCertificate.qualifyMembership(i->second)) + ++i; + else _membershipCertificates.erase(i++); + } +} + void Network::_CBhandleTapData(void *arg,const MAC &from,const MAC &to,unsigned int etherType,const Buffer<4096> &data) { const RuntimeEnvironment *_r = ((Network *)arg)->_r; diff --git a/node/Network.hpp b/node/Network.hpp index c13f00a4..e553cd3a 100644 --- a/node/Network.hpp +++ b/node/Network.hpp @@ -208,6 +208,30 @@ public: { } + inline void setNetworkId(uint64_t id) + { + char buf[32]; + sprintf(buf,"%llu",id); + (*this)["nwid"] = buf; + } + + inline uint64_t networkId() const + throw(std::invalid_argument) + { + return strtoull(get("nwid").c_str(),(char **)0,10); + } + + inline void setPeerAddress(Address &a) + { + (*this)["peer"] = a.toString(); + } + + inline Address peerAddress() const + throw(std::invalid_argument) + { + return Address(get("peer")); + } + /** * @return Certificate of membership for this network, or empty cert if none */ @@ -221,7 +245,7 @@ public: */ inline bool isOpen() const { - return (get("isOpen","0") == "1"); + return (get("isOpen") == "1"); } /** @@ -304,8 +328,12 @@ public: inline bool isOpen() const throw() { - Mutex::Lock _l(_lock); - return _isOpen; + try { + Mutex::Lock _l(_lock); + return _configuration.isOpen(); + } catch ( ... ) { + return false; + } } /** @@ -343,6 +371,27 @@ public: */ void requestConfiguration(); + /** + * Add or update a peer's membership certificate + * + * The certificate must already have been validated via signature checking. + * + * @param peer Peer that owns certificate + * @param cert Certificate itself + */ + inline void addMembershipCertificate(const Address &peer,const Certificate &cert) + { + Mutex::Lock _l(_lock); + _membershipCertificates[peer] = cert; + } + + bool isAllowed(const Address &peer) const; + + /** + * Perform periodic database cleaning such as removing expired membership certificates + */ + void clean(); + private: static void _CBhandleTapData(void *arg,const MAC &from,const MAC &to,unsigned int etherType,const Buffer<4096> &data); @@ -350,10 +399,11 @@ private: EthernetTap _tap; std::set _multicastGroups; - + std::map _membershipCertificates; + Config _configuration; + Certificate _myCertificate; + uint64_t _lastCertificateUpdate; uint64_t _id; - bool _isOpen; - Mutex _lock; AtomicCounter __refCount; diff --git a/node/Packet.cpp b/node/Packet.cpp index 0aae1e2d..94d9164b 100644 --- a/node/Packet.cpp +++ b/node/Packet.cpp @@ -60,8 +60,7 @@ const char *Packet::errorString(ErrorCode e) case ERROR_IDENTITY_COLLISION: return "IDENTITY_COLLISION"; case ERROR_IDENTITY_INVALID: return "IDENTITY_INVALID"; case ERROR_UNSUPPORTED_OPERATION: return "UNSUPPORTED_OPERATION"; - case ERROR_NO_MEMBER_CERTIFICATE_ON_FILE: return "NO_MEMBER_CERTIFICATE_ON_FILE"; - case ERROR_MEMBER_CERTIFICATE_UNQUALIFIED: return "MEMBER_CERTIFICATE_UNQUALIFIED"; + case ERROR_NO_MEMBER_CERTIFICATE: return "NO_MEMBER_CERTIFICATE"; } return "(unknown)"; } diff --git a/node/Packet.hpp b/node/Packet.hpp index 85ccb466..41acf512 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -537,10 +537,7 @@ public: ERROR_UNSUPPORTED_OPERATION = 6, /* Message to private network rejected -- no unexpired certificate on file */ - ERROR_NO_MEMBER_CERTIFICATE_ON_FILE = 7, - - /* Membership certificate no longer qualified for membership in network */ - ERROR_MEMBER_CERTIFICATE_UNQUALIFIED = 8 + ERROR_NO_MEMBER_CERTIFICATE = 7 }; /** diff --git a/node/Switch.cpp b/node/Switch.cpp index 1af5e41e..bb10b412 100644 --- a/node/Switch.cpp +++ b/node/Switch.cpp @@ -386,7 +386,7 @@ void Switch::announceMulticastGroups(const std::map< SharedPtr,std::set Packet outp((*p)->address(),_r->identity.address(),Packet::VERB_MULTICAST_LIKE); for(std::map< SharedPtr,std::set >::const_iterator nwmgs(allMemberships.begin());nwmgs!=allMemberships.end();++nwmgs) { - if ((nwmgs->first->open())||(_r->topology->isSupernode((*p)->address()))||(nwmgs->first->isMember((*p)->address()))) { + if ((_r->topology->isSupernode((*p)->address()))||(nwmgs->first->isAllowed((*p)->address()))) { for(std::set::iterator mg(nwmgs->second.begin());mg!=nwmgs->second.end();++mg) { if ((outp.size() + 18) > ZT_UDP_DEFAULT_PAYLOAD_MTU) { send(outp,true); diff --git a/node/Topology.cpp b/node/Topology.cpp index 7d770930..8487684e 100644 --- a/node/Topology.cpp +++ b/node/Topology.cpp @@ -133,7 +133,7 @@ SharedPtr Topology::getPeer(const Address &zta) } unsigned char ztatmp[ZT_ADDRESS_LENGTH]; - zta.copyTo(ztatmp); + zta.copyTo(ztatmp,ZT_ADDRESS_LENGTH); Buffer b(ZT_KISSDB_VALUE_SIZE); _dbm_m.lock(); @@ -309,7 +309,7 @@ void Topology::main() if (p->second->getAndResetDirty()) { try { uint64_t atmp[ZT_ADDRESS_LENGTH]; - p->second->identity().address().copyTo(atmp); + p->second->identity().address().copyTo(atmp,ZT_ADDRESS_LENGTH); Buffer b; p->second->serialize(b); b.zeroUnused(); @@ -340,7 +340,7 @@ void Topology::_reallyAddPeer(const SharedPtr &p) } try { uint64_t atmp[ZT_ADDRESS_LENGTH]; - p->address().copyTo(atmp); + p->address().copyTo(atmp,ZT_ADDRESS_LENGTH); Buffer b; p->serialize(b); b.zeroUnused(); -- cgit v1.2.3 From 3daea24d504af214bfac1bae41727e93ed8a6774 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 31 Jul 2013 09:27:55 -0400 Subject: Little bit of protocol changes before implementation of new verbs. --- node/Packet.hpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'node/Packet.hpp') diff --git a/node/Packet.hpp b/node/Packet.hpp index 41acf512..1d63929b 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -468,8 +468,8 @@ public: /* Network member certificate for sending peer: * <[8] 64-bit network ID> * <[2] 16-bit length of certificate> - * <[...] string-serialized certificate dictionary> * <[2] 16-bit length of signature> + * <[...] string-serialized certificate dictionary> * <[...] ECDSA signature of certificate> * * OK is generated on acceptance. ERROR is returned on failure. In both @@ -479,6 +479,8 @@ public: /* Network configuration request: * <[8] 64-bit network ID> + * <[2] 16-bit length of request meta-data dictionary> + * <[...] string-serialized request meta-data> * * This message requests network configuration from a node capable of * providing it. Such nodes run the netconf service, which must be @@ -486,11 +488,14 @@ public: * * OK response payload: * <[8] 64-bit network ID> + * <[2] 16-bit length of network configuration dictionary> * <[...] network configuration dictionary> * * OK returns a Dictionary (string serialized) containing the network's * configuration and IP address assignment information for the querying - * node. + * node. It also contains a membership certificate that the querying + * node can push to other peers to demonstrate its right to speak on + * a given network. * * ERROR may be NOT_FOUND if no such network is known, or * UNSUPPORTED_OPERATION if the netconf service isn't available. The @@ -505,7 +510,8 @@ public: * to request that nodes refresh their network configuration. It can * thus be used to "push" updates. * - * It is only a hint and does not presently elicit a response. + * It does not generate an OK or ERROR message, and is treated only as + * a hint to refresh now. */ VERB_NETWORK_CONFIG_REFRESH = 12 }; -- cgit v1.2.3 From 80d8b7d0ae56f1dce8b5b25ab7930df436755daf Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Fri, 2 Aug 2013 17:17:34 -0400 Subject: Netconf wired up, ready to test. --- netconf-service/netconf.cpp | 16 ++++++++++++++ node/Node.cpp | 46 +++++++++++++++++++++++++++++++++++++++ node/Packet.hpp | 13 ++++++++++++ node/PacketDecoder.cpp | 52 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 127 insertions(+) (limited to 'node/Packet.hpp') diff --git a/netconf-service/netconf.cpp b/netconf-service/netconf.cpp index 01f33120..af6ed4e6 100644 --- a/netconf-service/netconf.cpp +++ b/netconf-service/netconf.cpp @@ -219,6 +219,22 @@ int main(int argc,char **argv) StoreQueryResult rs = q.store(); if (rs.num_rows() > 0) isOpen = ((int)rs[0]["isOpen"] > 0); + else { + Dictionary response; + response["peer"] = peerIdentity.address().toString(); + response["nwid"] = request.get("nwid"); + response["type"] = "netconf-response"; + response["requestId"] = request.get("requestId"); + response["error"] = "NOT_FOUND"; + std::string respm = response.toString(); + uint32_t respml = (uint32_t)htonl((uint32_t)respm.length()); + + stdoutWriteLock.lock(); + write(STDOUT_FILENO,&respml,4); + write(STDOUT_FILENO,respm.data(),respm.length()); + stdoutWriteLock.unlock(); + continue; + } } Dictionary netconf; diff --git a/node/Node.cpp b/node/Node.cpp index e67dd526..8079e801 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -50,6 +50,7 @@ #include "Node.hpp" #include "Topology.hpp" #include "Demarc.hpp" +#include "Packet.hpp" #include "Switch.hpp" #include "Utils.hpp" #include "EthernetTap.hpp" @@ -192,9 +193,54 @@ struct _NodeImpl } }; +#ifndef __WINDOWS__ static void _netconfServiceMessageHandler(void *renv,Service &svc,const Dictionary &msg) { + if (!renv) + return; // sanity check + const RuntimeEnvironment *_r = (const RuntimeEnvironment *)renv; + + try { + const std::string &type = msg.get("type"); + if (type == "netconf-response") { + uint64_t inRePacketId = strtoull(msg.get("requestId").c_str(),(char **)0,16); + SharedPtr network = _r->nc->network(strtoull(msg.get("nwid").c_str(),(char **)0,16)); + Address peerAddress(msg.get("peer").c_str()); + + if ((network)&&(peerAddress)) { + if (msg.contains("error")) { + Packet::ErrorCode errCode = Packet::ERROR_INVALID_REQUEST; + const std::string &err = msg.get("error"); + if (err == "NOT_FOUND") + errCode = Packet::ERROR_NOT_FOUND; + + Packet outp(peerAddress,_r->identity.address(),Packet::VERB_ERROR); + outp.append((unsigned char)Packet::VERB_NETWORK_CONFIG_REQUEST); + outp.append(inRePacketId); + outp.append((unsigned char)errCode); + outp.append(network->id()); + _r->sw->send(outp,true); + } else if (msg.contains("netconf")) { + const std::string &netconf = msg.get("netconf"); + if (netconf.length() < 2048) { // sanity check + Packet outp(peerAddress,_r->identity.address(),Packet::VERB_OK); + outp.append((unsigned char)Packet::VERB_NETWORK_CONFIG_REQUEST); + outp.append(inRePacketId); + outp.append(network->id()); + outp.append((uint16_t)netconf.length()); + outp.append(netconf.data(),netconf.length()); + _r->sw->send(outp,true); + } + } + } + } + } catch (std::exception &exc) { + LOG("unexpected exception parsing response from netconf service: %s",exc.what()); + } catch ( ... ) { + LOG("unexpected exception parsing response from netconf service: unknown exception"); + } } +#endif // !__WINDOWS__ Node::Node(const char *hp) throw() : diff --git a/node/Packet.hpp b/node/Packet.hpp index 1d63929b..0e7ccea3 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -132,27 +132,34 @@ #define ZT_PROTO_VERB_MULTICAST_FRAME_BLOOM_FILTER_SIZE_BYTES 64 // Field incides for parsing verbs + #define ZT_PROTO_VERB_HELLO_IDX_PROTOCOL_VERSION (ZT_PACKET_IDX_PAYLOAD) #define ZT_PROTO_VERB_HELLO_IDX_MAJOR_VERSION (ZT_PROTO_VERB_HELLO_IDX_PROTOCOL_VERSION + 1) #define ZT_PROTO_VERB_HELLO_IDX_MINOR_VERSION (ZT_PROTO_VERB_HELLO_IDX_MAJOR_VERSION + 1) #define ZT_PROTO_VERB_HELLO_IDX_REVISION (ZT_PROTO_VERB_HELLO_IDX_MINOR_VERSION + 1) #define ZT_PROTO_VERB_HELLO_IDX_TIMESTAMP (ZT_PROTO_VERB_HELLO_IDX_REVISION + 2) #define ZT_PROTO_VERB_HELLO_IDX_IDENTITY (ZT_PROTO_VERB_HELLO_IDX_TIMESTAMP + 8) + #define ZT_PROTO_VERB_ERROR_IDX_IN_RE_VERB (ZT_PACKET_IDX_PAYLOAD) #define ZT_PROTO_VERB_ERROR_IDX_IN_RE_PACKET_ID (ZT_PROTO_VERB_ERROR_IDX_IN_RE_VERB + 1) #define ZT_PROTO_VERB_ERROR_IDX_ERROR_CODE (ZT_PROTO_VERB_ERROR_IDX_IN_RE_PACKET_ID + 8) #define ZT_PROTO_VERB_ERROR_IDX_PAYLOAD (ZT_PROTO_VERB_ERROR_IDX_ERROR_CODE + 1) + #define ZT_PROTO_VERB_OK_IDX_IN_RE_VERB (ZT_PACKET_IDX_PAYLOAD) #define ZT_PROTO_VERB_OK_IDX_IN_RE_PACKET_ID (ZT_PROTO_VERB_OK_IDX_IN_RE_VERB + 1) #define ZT_PROTO_VERB_OK_IDX_PAYLOAD (ZT_PROTO_VERB_OK_IDX_IN_RE_PACKET_ID + 8) + #define ZT_PROTO_VERB_WHOIS_IDX_ZTADDRESS (ZT_PACKET_IDX_PAYLOAD) + #define ZT_PROTO_VERB_RENDEZVOUS_IDX_ZTADDRESS (ZT_PACKET_IDX_PAYLOAD) #define ZT_PROTO_VERB_RENDEZVOUS_IDX_PORT (ZT_PROTO_VERB_RENDEZVOUS_IDX_ZTADDRESS + 5) #define ZT_PROTO_VERB_RENDEZVOUS_IDX_ADDRLEN (ZT_PROTO_VERB_RENDEZVOUS_IDX_PORT + 2) #define ZT_PROTO_VERB_RENDEZVOUS_IDX_ADDRESS (ZT_PROTO_VERB_RENDEZVOUS_IDX_ADDRLEN + 1) + #define ZT_PROTO_VERB_FRAME_IDX_NETWORK_ID (ZT_PACKET_IDX_PAYLOAD) #define ZT_PROTO_VERB_FRAME_IDX_ETHERTYPE (ZT_PROTO_VERB_FRAME_IDX_NETWORK_ID + 8) #define ZT_PROTO_VERB_FRAME_IDX_PAYLOAD (ZT_PROTO_VERB_FRAME_IDX_ETHERTYPE + 2) + #define ZT_PROTO_VERB_MULTICAST_FRAME_IDX_FLAGS (ZT_PACKET_IDX_PAYLOAD) #define ZT_PROTO_VERB_MULTICAST_FRAME_IDX_NETWORK_ID (ZT_PROTO_VERB_MULTICAST_FRAME_IDX_FLAGS + 1) #define ZT_PROTO_VERB_MULTICAST_FRAME_IDX_SUBMITTER_ADDRESS (ZT_PROTO_VERB_MULTICAST_FRAME_IDX_NETWORK_ID + 8) @@ -166,6 +173,12 @@ #define ZT_PROTO_VERB_MULTICAST_FRAME_IDX_SIGNATURE_LENGTH (ZT_PROTO_VERB_MULTICAST_FRAME_IDX_PAYLOAD_LENGTH + 2) #define ZT_PROTO_VERB_MULTICAST_FRAME_IDX_PAYLOAD (ZT_PROTO_VERB_MULTICAST_FRAME_IDX_SIGNATURE_LENGTH + 2) +#define ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_NETWORK_ID (ZT_PACKET_IDX_PAYLOAD) +#define ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT_LEN (ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_NETWORK_ID + 8) +#define ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT (ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT_LEN + 2) + +#define ZT_PROTO_VERB_NETWORK_CONFIG_REFRESH_IDX_NETWORK_ID (ZT_PACKET_IDX_PAYLOAD) + // Field indices for parsing OK and ERROR payloads of replies #define ZT_PROTO_VERB_HELLO__OK__IDX_TIMESTAMP (ZT_PROTO_VERB_OK_IDX_PAYLOAD) #define ZT_PROTO_VERB_WHOIS__OK__IDX_IDENTITY (ZT_PROTO_VERB_OK_IDX_PAYLOAD) diff --git a/node/PacketDecoder.cpp b/node/PacketDecoder.cpp index 0353625c..518ed9e7 100644 --- a/node/PacketDecoder.cpp +++ b/node/PacketDecoder.cpp @@ -25,6 +25,7 @@ * LLC. Start here: http://www.zerotier.com/ */ +#include "Constants.hpp" #include "RuntimeEnvironment.hpp" #include "Topology.hpp" #include "PacketDecoder.hpp" @@ -32,6 +33,7 @@ #include "Peer.hpp" #include "NodeConfig.hpp" #include "Filter.hpp" +#include "Service.hpp" namespace ZeroTier { @@ -546,14 +548,64 @@ bool PacketDecoder::_doMULTICAST_FRAME(const RuntimeEnvironment *_r,const Shared bool PacketDecoder::_doNETWORK_MEMBERSHIP_CERTIFICATE(const RuntimeEnvironment *_r,const SharedPtr &peer) { + // TODO: not implemented yet, will be needed for private networks. + + return true; } bool PacketDecoder::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *_r,const SharedPtr &peer) { + char tmp[128]; + try { + uint64_t nwid = at(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_NETWORK_ID); +#ifndef __WINDOWS__ + if (_r->netconfService) { + unsigned int dictLen = at(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT_LEN); + std::string dict((const char *)field(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT,dictLen),dictLen); + + Dictionary request; + request["type"] = "netconf-request"; + request["peerId"] = peer->identity().toString(false); + sprintf(tmp,"%llx",(unsigned long long)nwid); + request["nwid"] = tmp; + sprintf(tmp,"%llx",(unsigned long long)packetId()); + request["requestId"] = tmp; + _r->netconfService->send(request); + } else { +#endif // !__WINDOWS__ + Packet outp(source(),_r->identity.address(),Packet::VERB_ERROR); + outp.append((unsigned char)Packet::VERB_NETWORK_CONFIG_REQUEST); + outp.append(packetId()); + outp.append((unsigned char)Packet::ERROR_UNSUPPORTED_OPERATION); + outp.append(nwid); + outp.encrypt(peer->cryptKey()); + outp.hmacSet(peer->macKey()); + _r->demarc->send(_localPort,_remoteAddress,outp.data(),outp.size(),-1); + TRACE("sent ERROR(NETWORK_CONFIG_REQUEST,UNSUPPORTED_OPERATION) to %s(%s)",peer->address().toString().c_str(),_remoteAddress.toString().c_str()); +#ifndef __WINDOWS__ + } +#endif // !__WINDOWS__ + } catch (std::exception &exc) { + TRACE("dropped NETWORK_CONFIG_REQUEST from %s(%s): unexpected exception: %s",source().toString().c_str(),_remoteAddress.toString().c_str(),exc.what()); + } catch ( ... ) { + TRACE("dropped NETWORK_CONFIG_REQUEST from %s(%s): unexpected exception: (unknown)",source().toString().c_str(),_remoteAddress.toString().c_str()); + } + return true; } bool PacketDecoder::_doNETWORK_CONFIG_REFRESH(const RuntimeEnvironment *_r,const SharedPtr &peer) { + try { + uint64_t nwid = at(ZT_PROTO_VERB_NETWORK_CONFIG_REFRESH_IDX_NETWORK_ID); + SharedPtr nw(_r->nc->network(nwid)); + if ((nw)&&(source() == nw->controller())) // only respond to requests from controller + nw->requestConfiguration(); + } catch (std::exception &exc) { + TRACE("dropped NETWORK_CONFIG_REFRESH from %s(%s): unexpected exception: %s",source().toString().c_str(),_remoteAddress.toString().c_str(),exc.what()); + } catch ( ... ) { + TRACE("dropped NETWORK_CONFIG_REFRESH from %s(%s): unexpected exception: (unknown)",source().toString().c_str(),_remoteAddress.toString().c_str()); + } + return true; } } // namespace ZeroTier -- cgit v1.2.3