From a369c690910d014ca79b55d07603af45343b37f5 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 6 Jan 2015 13:45:10 -0800 Subject: [PATCH] C++ netconf master Redis plumbing. --- node/NetworkConfigMaster.cpp | 233 +++++++++++++++++++++++++++++++++++ node/NetworkConfigMaster.hpp | 121 ++++++++++++++++++ objects.mk | 1 + redis-schema.md | 2 - 4 files changed, 355 insertions(+), 2 deletions(-) create mode 100644 node/NetworkConfigMaster.cpp create mode 100644 node/NetworkConfigMaster.hpp diff --git a/node/NetworkConfigMaster.cpp b/node/NetworkConfigMaster.cpp new file mode 100644 index 000000000..3357b150f --- /dev/null +++ b/node/NetworkConfigMaster.cpp @@ -0,0 +1,233 @@ +/* + * ZeroTier One - Global Peer to Peer Ethernet + * Copyright (C) 2011-2015 ZeroTier Networks + * + * 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/ + */ + +#include "Constants.hpp" + +#ifdef ZT_ENABLE_NETCONF_MASTER + +#include +#include +#include +#include +#include +#include +#include + +#include "NetworkConfigMaster.hpp" +#include "RuntimeEnvironment.hpp" +#include "Switch.hpp" +#include "Packet.hpp" +#include "NetworkConfig.hpp" +#include "Utils.hpp" +#include "Node.hpp" +#include "Logger.hpp" + +// Redis timeout in seconds +#define ZT_NETCONF_REDIS_TIMEOUT 10 + +namespace ZeroTier { + +NetworkConfigMaster::NetworkConfigMaster( + const RuntimeEnvironment *renv, + const char *redisHost, + unsigned int redisPort, + const char *redisPassword, + unsigned int redisDatabaseNumber) : + _lock(), + _redisHost(redisHost), + _redisPassword((redisPassword) ? redisPassword : ""), + _redisPort(redisPort), + _redisDatabaseNumber(redisDatabaseNumber), + RR(renv), + _rc((redisContext *)0) +{ +} + +NetworkConfigMaster::~NetworkConfigMaster() +{ + Mutex::Lock _l(_lock); + if (_rc) + redisFree(_rc); +} + +void NetworkConfigMaster::doNetworkConfigRequest( + uint64_t packetId, + const Address &from, + uint64_t nwid, + const Dictionary &metaData, + uint64_t haveTimestamp) +{ +} + +bool NetworkConfigMaster::_reconnect() +{ + struct timeval tv; + + if (_rc) + redisFree(_rc); + + tv.tv_sec = ZT_NETCONF_REDIS_TIMEOUT; + tv.tv_usec = 0; + _rc = redisConnectWithTimeout(_redisHost.c_str(),_redisPort,&tv); + if (!_rc) + return false; + if (_rc->err) { + redisFree(_rc); + _rc = (redisContext *)0; + return false; + } + redisSetTimeout(_rc,&tv); // necessary??? + + // TODO: support AUTH and SELECT !!! + + return true; +} + +bool NetworkConfigMaster::_hgetall(const char *key,std::map &hdata) +{ + if (!_rc) { + if (!_reconnect()) + return false; + } + + redisReply *reply = (redisReply *)redisCommand(_rc,"HGETALL %s",key); + if (!reply) { + if (_reconnect()) + return _hgetall(key,hdata); + return false; + } + + hdata.clear(); + if (reply->type == REDIS_REPLY_ARRAY) { + for(long i=0;ielements;) { + try { + const char *k = reply->elements[i]->str; + if (++i >= reply->elements) + break; + if ((k)&&(reply->elements[i]->str)) + hdata[k] = reply->elements[i]->str; + ++i; + } catch ( ... ) { + break; // memory safety + } + } + } + + freeReplyObject(reply); + + return true; +} + +bool NetworkConfigMaster::_hmset(const char *key,const std::map &hdata) +{ + const const char *hargv[1024]; + + if (!hdata.size()) + return true; + + if (!_rc) { + if (!_reconnect()) + return false; + } + + hargv[0] = "HMSET"; + hargv[1] = key; + int hargc = 2; + for(std::map::const_iterator i(hdata.begin());i!=hdata.end();++i) { + if (hargc >= 1024) + break; + hargv[hargc++] = i->first.c_str(); + hargv[hargc++] = i->second.c_str(); + } + + redisReply *reply = (redisReply *)redisCommandArgv(_rc,hargc,hargv,(const size_t *)0); + if (!reply) { + if (_reconnect()) + return _hmset(key,hdata); + return false; + } + + if (reply->type == REDIS_REPLY_ERROR) { + freeReplyObject(reply); + return false; + } + + freeReplyObject(reply); + + return true; +} + +bool NetworkConfigMaster::_hget(const char *key,const char *hashKey,std::string &value) +{ + if (!_rc) { + if (!_reconnect()) + return false; + } + + redisReply *reply = (redisReply *)redisCommand(_rc,"HGET %s %s",key,hashKey); + if (!reply) { + if (_reconnect()) + return _hget(key,hashKey,value); + return false; + } + + if (reply->type == REDIS_REPLY_STRING) + value = reply->str; + else value = ""; + + freeReplyObject(reply); + + return true; +} + +bool NetworkConfigMaster::_hset(const char *key,const char *hashKey,const char *value) +{ + if (!_rc) { + if (!_reconnect()) + return false; + } + + redisReply *reply = (redisReply *)redisCommand(_rc,"HSET %s %s %s",key,hashKey,value); + if (!reply) { + if (_reconnect()) + return _hset(key,hashKey,value); + return false; + } + + if (reply->type == REDIS_REPLY_ERROR) { + freeReplyObject(reply); + return false; + } + + freeReplyObject(reply); + + return true; +} + +} // namespace ZeroTier + +#endif // ZT_ENABLE_NETCONF_MASTER diff --git a/node/NetworkConfigMaster.hpp b/node/NetworkConfigMaster.hpp new file mode 100644 index 000000000..636b197ef --- /dev/null +++ b/node/NetworkConfigMaster.hpp @@ -0,0 +1,121 @@ +/* + * ZeroTier One - Global Peer to Peer Ethernet + * Copyright (C) 2011-2015 ZeroTier Networks + * + * 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_NETWORKCONFIGMASTER_HPP +#define ZT_NETWORKCONFIGMASTER_HPP + +#include "Constants.hpp" + +#define ZT_ENABLE_NETCONF_MASTER + +#ifdef ZT_ENABLE_NETCONF_MASTER + +#include +#include +#include + +#include "Address.hpp" +#include "Dictionary.hpp" +#include "Mutex.hpp" + +#include + +namespace ZeroTier { + +class RuntimeEnvironment; + +/** + * Network configuration master -- responds to NETCONF requests + * + * This requires the 'hiredis' C library to build. + */ +class NetworkConfigMaster +{ +public: + /** + * Create netconf master + * + * This doesn't connect to Redis until the first request is received. + * + * @param renv Runtime environment + * @param redisHost Hostname or IP of Redis server + * @param redisPort Redis IP port number + * @param redisPassword Redis AUTH password or NULL if none + * @param redisDatabaseNumber Redis database number (usually 0) + */ + NetworkConfigMaster( + const RuntimeEnvironment *renv, + const char *redisHost, + unsigned int redisPort, + const char *redisPassword, + unsigned int redisDatabaseNumber); + + ~NetworkConfigMaster(); + + /** + * Handle a network config request, sending replies if necessary + * + * This is a blocking call, so rate is limited by Redis. It will fail + * and log its failure if the Redis server is not available or times out. + * + * @param packetId 64-bit packet ID + * @param from Originating peer ZeroTier address + * @param nwid 64-bit network ID + * @param metaData Meta-data bundled with request (empty if none) + * @param haveTimestamp Timestamp requesting peer has or 0 if none or not included + */ + void doNetworkConfigRequest( + uint64_t packetId, + const Address &from, + uint64_t nwid, + const Dictionary &metaData, + uint64_t haveTimestamp); + +private: + // These assume _lock is locked + bool _reconnect(); + bool _hgetall(const char *key,std::map &hdata); + bool _hmset(const char *key,const std::map &hdata); + bool _hget(const char *key,const char *hashKey,std::string &value); + bool _hset(const char *key,const char *hashKey,const char *value); + + Mutex _lock; + + std::string _redisHost; + std::string _redisPassword; + unsigned int _redisPort; + unsigned int _redisDatabaseNumber; + + const RuntimeEnvironment *RR; + redisContext *_rc; +}; + +} // namespace ZeroTier + +#endif // ZT_ENABLE_NETCONF_MASTER + +#endif diff --git a/objects.mk b/objects.mk index 0b6c9c4f9..7f9895960 100644 --- a/objects.mk +++ b/objects.mk @@ -17,6 +17,7 @@ OBJS=\ node/Multicaster.o \ node/Network.o \ node/NetworkConfig.o \ + node/NetworkConfigMaster.o \ node/Node.o \ node/NodeConfig.o \ node/OutboundMulticast.o \ diff --git a/redis-schema.md b/redis-schema.md index f4870520d..84457cc72 100644 --- a/redis-schema.md +++ b/redis-schema.md @@ -11,8 +11,6 @@ - Hexadecimal: all hex values must be lower case - 16-digit network IDs and 10-digit addresses are left zero-padded to 16 and 10 digits respectively, as they are everywhere else in the ZT1 universe. -Note: right now KEYS globbing is used in a few places in the code to search for stuff. In the future we'll want to add SET/ZSET index fields for these to optimize, but that won't become an issue until there are at least millions of records. (Millions of users will give us lots of "good problems to have." :) - ### Field attribute flags used in this documentation (not in database) - ! required