From 0c7f8e247cc1469e7f8206693e4fe4909f8e0a38 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 23 Jul 2013 10:23:55 -0700 Subject: Add amSupernode to make code clearer in the check-if-self-is-supernode case. --- node/Topology.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'node/Topology.cpp') diff --git a/node/Topology.cpp b/node/Topology.cpp index e627e767..43c5303e 100644 --- a/node/Topology.cpp +++ b/node/Topology.cpp @@ -39,7 +39,8 @@ namespace ZeroTier { Topology::Topology(const RuntimeEnvironment *renv,const char *dbpath) throw(std::runtime_error) : Thread(), - _r(renv) + _r(renv), + _amSupernode(false) { if (KISSDB_open(&_dbm,dbpath,KISSDB_OPEN_MODE_RWCREAT,ZT_KISSDB_HASH_TABLE_SIZE,ZT_KISSDB_KEY_SIZE,ZT_KISSDB_VALUE_SIZE)) { if (KISSDB_open(&_dbm,dbpath,KISSDB_OPEN_MODE_RWREPLACE,ZT_KISSDB_HASH_TABLE_SIZE,ZT_KISSDB_KEY_SIZE,ZT_KISSDB_VALUE_SIZE)) @@ -77,9 +78,11 @@ Topology::~Topology() void Topology::setSupernodes(const std::map< Identity,std::vector > &sn) { Mutex::Lock _l(_supernodes_m); + _supernodes = sn; _supernodeAddresses.clear(); _supernodePeers.clear(); + for(std::map< Identity,std::vector >::const_iterator i(sn.begin());i!=sn.end();++i) { if (i->first != _r->identity) { SharedPtr p(getPeer(i->first.address())); @@ -93,6 +96,8 @@ void Topology::setSupernodes(const std::map< Identity,std::vector > } _supernodeAddresses.insert(i->first.address()); } + + _amSupernode = (_supernodes.find(_r->identity) != _supernodes.end()); } void Topology::addPeer(const SharedPtr &candidate,void (*callback)(void *,const SharedPtr &,Topology::PeerVerifyResult),void *arg) -- cgit v1.2.3 From 9cf734b74a750b8af0c628809c16be57eef1ff21 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Thu, 25 Jul 2013 13:24:39 -0400 Subject: Sane-ify Address, get rid of goofy union thingy. --- node/Address.hpp | 164 ++++++++++++++++++++++++++++++++++++------------------ node/Identity.cpp | 8 ++- node/Identity.hpp | 2 +- node/Pack.cpp | 8 +-- node/Switch.cpp | 8 +-- node/Topology.cpp | 13 ++++- 6 files changed, 134 insertions(+), 69 deletions(-) (limited to 'node/Topology.cpp') diff --git a/node/Address.hpp b/node/Address.hpp index 4c912540..0e3f7b8e 100644 --- a/node/Address.hpp +++ b/node/Address.hpp @@ -28,70 +28,131 @@ #ifndef _ZT_ADDRESS_HPP #define _ZT_ADDRESS_HPP +#include +#include #include +#include #include #include "Utils.hpp" #include "MAC.hpp" #include "Constants.hpp" +#include "Buffer.hpp" namespace ZeroTier { /** - * ZeroTier address, which doubles as the last 5 octets of the MAC on taps - * - * Natural sort order will differ on big vs. little endian machines, but that - * won't matter when it's used as a local map/set key. + * A ZeroTier address */ class Address { -private: - union { - unsigned char o[ZT_ADDRESS_LENGTH]; - uint64_t v; - } _a; - public: Address() - throw() + throw() : + _a(0) { - _a.v = 0; } Address(const Address &a) + throw() : + _a(a._a) + { + } + + Address(uint64_t a) + throw() : + _a(a & 0xffffffffffULL) + { + } + + /** + * @param bits Raw address -- 5 bytes, big-endian byte order + */ + Address(const void *bits) + throw() + { + setTo(bits); + } + + inline Address &operator=(const Address &a) throw() { - _a.v = a._a.v; + _a = a._a; + return *this; + } + + inline Address &operator=(const uint64_t a) + throw() + { + _a = (a & 0xffffffffffULL); + return *this; } /** - * Create from a ZeroTier MAC - * - * @param m MAC (assumed to be a ZeroTier MAC) + * @param bits Raw address -- 5 bytes, big-endian byte order */ - Address(const MAC &m) + inline void setTo(const void *bits) throw() { - _a.v = 0; - for(int i=0;i> 32) & 0xff); + *(b++) = (unsigned char)((_a >> 24) & 0xff); + *(b++) = (unsigned char)((_a >> 16) & 0xff); + *(b++) = (unsigned char)((_a >> 8) & 0xff); + *b = (unsigned char)(_a & 0xff); } - inline Address &operator=(const Address &a) + /** + * Append to a buffer in big-endian byte order + * + * @param b Buffer to append to + */ + template + inline void appendTo(Buffer &b) const + throw(std::out_of_range) + { + b.append((unsigned char)((_a >> 32) & 0xff)); + b.append((unsigned char)((_a >> 24) & 0xff)); + b.append((unsigned char)((_a >> 16) & 0xff)); + b.append((unsigned char)((_a >> 8) & 0xff)); + b.append((unsigned char)(_a & 0xff)); + } + + /** + * @return String containing address as 5 binary bytes + */ + inline std::string toBinaryString() const + { + std::string b; + b.push_back((char)((_a >> 32) & 0xff)); + b.push_back((char)((_a >> 24) & 0xff)); + b.push_back((char)((_a >> 16) & 0xff)); + b.push_back((char)((_a >> 8) & 0xff)); + b.push_back((char)(_a & 0xff)); + return b; + } + + /** + * @return Integer containing address (0 to 2^40) + */ + inline uint64_t toInt() const throw() { - _a.v = a._a.v; - return *this; + return _a; } /** @@ -103,9 +164,7 @@ public: throw() { MAC m; - m.data[0] = ZT_MAC_FIRST_OCTET; - for(int i=1;i<6;++i) - m.data[i] = _a.o[i - 1]; + copyTo(m.data); return m; } @@ -114,18 +173,15 @@ public: */ inline std::string toString() const { - return Utils::hex(_a.o,ZT_ADDRESS_LENGTH); + char buf[16]; + sprintf(buf,"%.10llx",_a); + return std::string(buf); }; - /** - * Set address to zero - */ - inline void zero() throw() { _a.v = 0; } - /** * @return True if this address is not zero */ - inline operator bool() const throw() { return (_a.v); } + inline operator bool() const throw() { return (_a); } /** * @return Sum of all bytes in address @@ -133,10 +189,7 @@ public: inline unsigned int sum() const throw() { - unsigned int s = 0; - for(unsigned int i=0;i> 32) & 0xff) + ((_a >> 24) & 0xff) + ((_a >> 16) & 0xff) + ((_a >> 8) & 0xff) + (_a & 0xff)); } /** @@ -151,23 +204,24 @@ public: inline bool isReserved() const throw() { - return ((!_a.v)||(_a.o[0] == ZT_ADDRESS_RESERVED_PREFIX)); + return ((!_a)||((_a >> 32) == ZT_ADDRESS_RESERVED_PREFIX)); } - inline unsigned char *data() throw() { return _a.o; } - inline const unsigned char *data() const throw() { return _a.o; } - - inline unsigned int size() const throw() { return ZT_ADDRESS_LENGTH; } + /** + * @param i Value from 0 to 4 (inclusive) + * @return Byte at said position (address interpreted in big-endian order) + */ + inline unsigned char operator[](unsigned int i) const throw() { return (unsigned char)((_a >> (32 - (i * 8))) & 0xff); } - inline unsigned char &operator[](unsigned int i) throw() { return _a.o[i]; } - inline unsigned char operator[](unsigned int i) const throw() { return _a.o[i]; } + inline bool operator==(const Address &a) const throw() { return (_a == a._a); } + inline bool operator!=(const Address &a) const throw() { return (_a != a._a); } + inline bool operator>(const Address &a) const throw() { return (_a > a._a); } + inline bool operator<(const Address &a) const throw() { return (_a < a._a); } + inline bool operator>=(const Address &a) const throw() { return (_a >= a._a); } + inline bool operator<=(const Address &a) const throw() { return (_a <= a._a); } - inline bool operator==(const Address &a) const throw() { return (_a.v == a._a.v); } - inline bool operator!=(const Address &a) const throw() { return (_a.v != a._a.v); } - inline bool operator<(const Address &a) const throw() { return (_a.v < a._a.v); } - inline bool operator>(const Address &a) const throw() { return (_a.v > a._a.v); } - inline bool operator<=(const Address &a) const throw() { return (_a.v <= a._a.v); } - inline bool operator>=(const Address &a) const throw() { return (_a.v >= a._a.v); } +private: + uint64_t _a; }; } // namespace ZeroTier diff --git a/node/Identity.cpp b/node/Identity.cpp index f16947a0..a7ef403d 100644 --- a/node/Identity.cpp +++ b/node/Identity.cpp @@ -57,11 +57,13 @@ void Identity::generate() // the address of an identity will be detected as its signature will be // 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); SHA256_CTX sha; unsigned char dig[32]; unsigned char idtype = IDENTITY_TYPE_NIST_P_521,zero = 0; SHA256_Init(&sha); - SHA256_Update(&sha,_address.data(),ZT_ADDRESS_LENGTH); + SHA256_Update(&sha,atmp,ZT_ADDRESS_LENGTH); SHA256_Update(&sha,&zero,1); SHA256_Update(&sha,&idtype,1); SHA256_Update(&sha,&zero,1); @@ -73,11 +75,13 @@ void Identity::generate() bool Identity::locallyValidate(bool doAddressDerivationCheck) const { + unsigned char atmp[ZT_ADDRESS_LENGTH]; + _address.copyTo(atmp); SHA256_CTX sha; unsigned char dig[32]; unsigned char idtype = IDENTITY_TYPE_NIST_P_521,zero = 0; SHA256_Init(&sha); - SHA256_Update(&sha,_address.data(),ZT_ADDRESS_LENGTH); + SHA256_Update(&sha,atmp,ZT_ADDRESS_LENGTH); SHA256_Update(&sha,&zero,1); SHA256_Update(&sha,&idtype,1); SHA256_Update(&sha,&zero,1); diff --git a/node/Identity.hpp b/node/Identity.hpp index 5cdfe9f8..e5a11d6a 100644 --- a/node/Identity.hpp +++ b/node/Identity.hpp @@ -307,7 +307,7 @@ public: inline void serialize(Buffer &b,bool includePrivate = false) const throw(std::out_of_range) { - b.append(_address.data(),ZT_ADDRESS_LENGTH); + _address.appendTo(b); b.append((unsigned char)IDENTITY_TYPE_NIST_P_521); b.append((unsigned char)(_publicKey.size() & 0xff)); b.append(_publicKey.data(),_publicKey.size()); diff --git a/node/Pack.cpp b/node/Pack.cpp index 8bac1300..98686559 100644 --- a/node/Pack.cpp +++ b/node/Pack.cpp @@ -62,7 +62,7 @@ const Pack::Entry *Pack::put(const std::string &name,const std::string &content) SHA256_Update(&sha,content.data(),content.length()); SHA256_Final(e.sha256,&sha); - e.signedBy.zero(); + e.signedBy = 0; e.signature.assign((const char *)0,0); return &e; @@ -81,7 +81,7 @@ std::string Pack::serialize() const 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(std::string((const char *)e->second.signedBy.data(),e->second.signedBy.size())); + entry.push_back(e->second.signedBy.toBinaryString()); entry.push_back(e->second.signature); archive.push_back(entry.serialize()); } @@ -123,8 +123,8 @@ bool Pack::deserialize(const void *sd,unsigned int sdlen) memcpy(e.sha256,dig,32); if (entry[3].length() == ZT_ADDRESS_LENGTH) - e.signedBy = entry[3].data(); - else e.signedBy.zero(); + e.signedBy.setTo(entry[3].data()); + else e.signedBy = 0; e.signature = entry[4]; } return true; diff --git a/node/Switch.cpp b/node/Switch.cpp index 5275f5ad..1af5e41e 100644 --- a/node/Switch.cpp +++ b/node/Switch.cpp @@ -124,7 +124,7 @@ void Switch::onLocalEthernet(const SharedPtr &network,const MAC &from,c Packet outpTmpl(propPeers[0]->address(),_r->identity.address(),Packet::VERB_MULTICAST_FRAME); outpTmpl.append((uint8_t)0); outpTmpl.append((uint64_t)network->id()); - outpTmpl.append(_r->identity.address().data(),ZT_ADDRESS_LENGTH); + _r->identity.address().appendTo(outpTmpl); outpTmpl.append(from.data,6); outpTmpl.append(mg.mac().data,6); outpTmpl.append((uint32_t)mg.adi()); @@ -246,7 +246,7 @@ bool Switch::unite(const Address &p1,const Address &p2,bool force) { // tell p1 where to find p2 Packet outp(p1,_r->identity.address(),Packet::VERB_RENDEZVOUS); - outp.append(p2.data(),ZT_ADDRESS_LENGTH); + p2.appendTo(outp); outp.append((uint16_t)cg.first.port()); if (cg.first.isV6()) { outp.append((unsigned char)16); @@ -262,7 +262,7 @@ bool Switch::unite(const Address &p1,const Address &p2,bool force) } { // tell p2 where to find p1 Packet outp(p2,_r->identity.address(),Packet::VERB_RENDEZVOUS); - outp.append(p1.data(),ZT_ADDRESS_LENGTH); + p1.appendTo(outp); outp.append((uint16_t)cg.second.port()); if (cg.second.isV6()) { outp.append((unsigned char)16); @@ -592,7 +592,7 @@ Address Switch::_sendWhoisRequest(const Address &addr,const Address *peersAlread SharedPtr supernode(_r->topology->getBestSupernode(peersAlreadyConsulted,numPeersAlreadyConsulted,false)); if (supernode) { Packet outp(supernode->address(),_r->identity.address(),Packet::VERB_WHOIS); - outp.append(addr.data(),ZT_ADDRESS_LENGTH); + addr.appendTo(outp); outp.encrypt(supernode->cryptKey()); outp.hmacSet(supernode->macKey()); diff --git a/node/Topology.cpp b/node/Topology.cpp index 43c5303e..7d770930 100644 --- a/node/Topology.cpp +++ b/node/Topology.cpp @@ -132,9 +132,12 @@ SharedPtr Topology::getPeer(const Address &zta) return ap->second; } + unsigned char ztatmp[ZT_ADDRESS_LENGTH]; + zta.copyTo(ztatmp); + Buffer b(ZT_KISSDB_VALUE_SIZE); _dbm_m.lock(); - if (!KISSDB_get(&_dbm,zta.data(),b.data())) { + if (!KISSDB_get(&_dbm,ztatmp,b.data())) { _dbm_m.unlock(); SharedPtr p(new Peer()); @@ -305,11 +308,13 @@ void Topology::main() for(std::map< Address,SharedPtr >::iterator p(_activePeers.begin());p!=_activePeers.end();++p) { if (p->second->getAndResetDirty()) { try { + uint64_t atmp[ZT_ADDRESS_LENGTH]; + p->second->identity().address().copyTo(atmp); Buffer b; p->second->serialize(b); b.zeroUnused(); _dbm_m.lock(); - if (KISSDB_put(&_dbm,p->second->identity().address().data(),b.data())) { + if (KISSDB_put(&_dbm,atmp,b.data())) { TRACE("error writing %s to peer.db",p->second->identity().address().toString().c_str()); } _dbm_m.unlock(); @@ -334,11 +339,13 @@ void Topology::_reallyAddPeer(const SharedPtr &p) _activePeers[p->identity().address()] = p; } try { + uint64_t atmp[ZT_ADDRESS_LENGTH]; + p->address().copyTo(atmp); Buffer b; p->serialize(b); b.zeroUnused(); _dbm_m.lock(); - if (KISSDB_put(&_dbm,p->identity().address().data(),b.data())) { + if (KISSDB_put(&_dbm,atmp,b.data())) { TRACE("error writing %s to peerdb",p->address().toString().c_str()); } else p->getAndResetDirty(); _dbm_m.unlock(); -- 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/Topology.cpp') 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