C++ netconf master Redis plumbing.

This commit is contained in:
Adam Ierymenko 2015-01-06 13:45:10 -08:00
parent f043321281
commit a369c69091
4 changed files with 355 additions and 2 deletions

View File

@ -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 <http://www.gnu.org/licenses/>.
*
* --
*
* 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 <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
#include <sys/types.h>
#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<std::string,std::string> &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;i<reply->elements;) {
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<std::string,std::string> &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<std::string,std::string>::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

View File

@ -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 <http://www.gnu.org/licenses/>.
*
* --
*
* 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 <stdint.h>
#include <string>
#include <map>
#include "Address.hpp"
#include "Dictionary.hpp"
#include "Mutex.hpp"
#include <hiredis/hiredis.h>
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<std::string,std::string> &hdata);
bool _hmset(const char *key,const std::map<std::string,std::string> &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

View File

@ -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 \

View File

@ -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