From f3128a18fee6745317cdf1918fe3c3901958b1de Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 25 Sep 2013 10:55:27 -0400 Subject: Work in progress... --- node/Multicaster.cpp | 116 +++++++++++++++++++++++++++++++++++++++++++++++ node/Multicaster.hpp | 121 +++++++++++++++++-------------------------------- node/Network.cpp | 2 - node/Network.hpp | 13 ------ node/NodeConfig.cpp | 4 -- node/NodeConfig.hpp | 1 + node/Packet.cpp | 2 + node/Packet.hpp | 13 ++++-- node/PacketDecoder.cpp | 59 +++++++++++------------- node/PacketDecoder.hpp | 22 ++++++++- node/Poly1305.cpp | 2 +- objects.mk | 1 + 12 files changed, 218 insertions(+), 138 deletions(-) create mode 100644 node/Multicaster.cpp diff --git a/node/Multicaster.cpp b/node/Multicaster.cpp new file mode 100644 index 00000000..be5daf43 --- /dev/null +++ b/node/Multicaster.cpp @@ -0,0 +1,116 @@ +/* + * 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/ + */ + +#include "Constants.hpp" +#include "Multicaster.hpp" +#include "Utils.hpp" + +namespace ZeroTier { + +Multicaster::Multicaster() +{ +} + +Multicaster::~Multicaster() +{ +} + +void Multicaster::likesGroup(uint64_t nwid,const Address &a,const MulticastGroup &mg,uint64_t now) +{ + Mutex::Lock _l(_lock); + _NetInfo &n = _nets[nwid]; + _SubInfo &si = n.subscriptions[_Subscription(a,mg)]; + if (!si.lastLike) { // on first LIKE, we must add to _proximity[mg] + std::list< Address > &p = n.proximity[mg]; + p.push_front(a); + si.proximitySlot = p.begin(); // list's iterators remain valid until erase() + } + si.lastLike = now; +} + +void Multicaster::bringCloser(uint64_t nwid,const Address &a) +{ + Mutex::Lock _l(_lock); + + std::map< uint64_t,_NetInfo >::iterator n(_nets.find(nwid)); + if (n == _nets.end()) + return; + + /* _subscriptions contains pairs of , so we can + * easily iterate through all subscriptions for a given address by + * starting with the default all-zero MulticastGroup() as lower bound + * and stopping when we're not looking at the right address anymore. + * Then we can look up _proximity and rapidly splice() the list using + * the saved iterator in _SubInfo. */ + + std::map< _Subscription,_SubInfo >::iterator s(n->second.subscriptions.lower_bound(_Subscription(a,MulticastGroup()))); + while ((s != n->second.subscriptions.end())&&(s->first.first == a)) { + std::map< MulticastGroup,std::list< Address > >::iterator p(n->second.proximity.find(s->first.second)); + if (s->second.proximitySlot != p->second.begin()) + p->second.splice(p->second.begin(),p->second,s->second.proximitySlot); + ++s; + } +} + +void Multicaster::got(uint64_t nwid,const Address &peer,uint64_t mcGuid,uint64_t now) +{ + Mutex::Lock _l(_lock); + _NetInfo &n = _nets[nwid]; + std::pair< uint64_t,std::set
> &g = n.got[mcGuid]; + g.first = now; + g.second.insert(peer); +} + +void Multicaster::clean(uint64_t now) +{ + Mutex::Lock _l(_lock); + + for(std::map< uint64_t,_NetInfo >::iterator n(_nets.begin());n!=_nets.end();) { + for(std::map< uint64_t,std::pair< uint64_t,std::set
> >::iterator g(n->second.got.begin());g!=n->second.got.end();) { + if ((now - g->second.first) > ZT_MULTICAST_MAGNET_STATE_EXPIRE) + n->second.got.erase(g++); + else ++g; + } + + for(std::map< _Subscription,_SubInfo >::iterator s(n->second.subscriptions.begin());s!=n->second.subscriptions.end();) { + if ((now - s->second.lastLike) > ZT_MULTICAST_LIKE_EXPIRE) { + std::map< MulticastGroup,std::list< Address > >::iterator p(n->second.proximity.find(s->first.second)); + p->second.erase(s->second.proximitySlot); + if (p->second.empty()) + n->second.proximity.erase(p); + n->second.subscriptions.erase(s++); + } else ++s; + } + + if (n->second.got.empty()&&n->second.proximity.empty()&&n->second.subscriptions.empty()) + _nets.erase(n++); + else ++n; + } +} + +} // namespace ZeroTier + diff --git a/node/Multicaster.hpp b/node/Multicaster.hpp index cd8b447e..b83e1f31 100644 --- a/node/Multicaster.hpp +++ b/node/Multicaster.hpp @@ -33,109 +33,61 @@ #include #include +#include #include #include #include "Constants.hpp" #include "Mutex.hpp" #include "MulticastGroup.hpp" -#include "Utils.hpp" #include "Address.hpp" namespace ZeroTier { /** - * Multicast propagation algorithm + * Multicast propagation algorithm core and database */ class Multicaster { public: - Multicaster() {} + Multicaster(); + ~Multicaster(); /** * Add or renew a peer's subscription to a multicast group * + * @param nwid Network ID * @param a Address that LIKEd * @param mg Multicast group * @param now Current time */ - inline void likesGroup(const Address &a,const MulticastGroup &mg,uint64_t now) - { - Mutex::Lock _l(_lock); - _SubInfo &si = _subscriptions[_Subscription(a,mg)]; - if (!si.lastLike) { // on first LIKE, we must add to _proximity[mg] - std::list< Address > &p = _proximity[mg]; - p.push_front(a); - si.proximitySlot = p.begin(); // list's iterators remain valid until erase() - } - si.lastLike = now; - } + void likesGroup(uint64_t nwid,const Address &a,const MulticastGroup &mg,uint64_t now); /** * Bring a peer closer in terms of propagation priority * + * @param nwid Network ID * @param a Address to bring closer (e.g. due to unicast message) * @param now Current time */ - inline void bringCloser(const Address &a) - { - Mutex::Lock _l(_lock); - - // _subscriptions contains pairs of , so we can - // easily iterate through all subscriptions for a given address by - // starting with the default all-zero MulticastGroup() as lower bound - // and stopping when we're not looking at the right address anymore. - // Then we can look up _proximity and rapidly splice() the list using - // the saved iterator in _SubInfo. - std::map< _Subscription,_SubInfo >::iterator s(_subscriptions.lower_bound(_Subscription(a,MulticastGroup()))); - while ((s != _subscriptions.end())&&(s->first.first == a)) { - std::map< MulticastGroup,std::list< Address > >::iterator p(_proximity.find(s->first.second)); - if (s->second.proximitySlot != p->second.begin()) - p->second.splice(p->second.begin(),p->second,s->second.proximitySlot); - ++s; - } - } + void bringCloser(uint64_t nwid,const Address &a); /** * Indicate that a peer reported that it GOT a multicast * * This only happens on magnet nodes for a propagation. * + * @param nwid Network ID * @param mcGuid Multicast GUID * @param peer Peer that GOT multicast * @param now Current time */ - inlien void got(const Address &peer,uint64_t mcGuid,uint64_t now) - { - Mutex::Lock _l(_lock); - std::pair< uint64_t,std::set
> &g = _got[mcGuid]; - g.first = now; - g.second.insert(peer); - } + void got(uint64_t nwid,const Address &peer,uint64_t mcGuid,uint64_t now); /** * Erase entries for expired LIKEs and GOT records */ - inline void clean(uint64_t now) - { - Mutex::Lock _l(_lock); - - for(std::map< uint64_t,std::pair< uint64_t,std::set
> >::iterator g(_got.begin());g!=_got.end();) { - if ((now - g->second.first) > ZT_MULTICAST_MAGNET_STATE_EXPIRE) - _got.erase(g++); - else ++g; - } - - for(std::map< _Subscription,_SubInfo >::iterator s(_subscriptions.begin());s!=_subscriptions.end();) { - if ((now - s->second.lastLike) > ZT_MULTICAST_LIKE_EXPIRE) { - std::map< MulticastGroup,std::list< Address > > p(_proximity.find(s->first.second)); - p->second.erase(s->second.proximitySlot); - if (p->second.empty()) - _proximity.erase(p); - _subscriptions.erase(s++); - } else ++s; - } - } + void clean(uint64_t now); /** * Pick next hops for a multicast by proximity @@ -143,36 +95,33 @@ public: * The function or function object must return true if more hops are desired * or false to stop finding new hops and return. * + * @param nwid Network ID * @param mg Multicast group * @param mcGuid Multicast message GUID (signer and signer unique ID) * @param nextHopFunc Function to call for each address, search stops if it returns false */ template - inline void getNextHops(const MulticastGroup &mg,uint64_t mcGuid,F nextHopFunc) + inline void getNextHops(uint64_t nwid,const MulticastGroup &mg,uint64_t mcGuid,F nextHopFunc) { Mutex::Lock _l(_lock); - std::map< uint64_t,std::pair< uint64_t,std::set< Address > > > g(_got.find(mcGuid)); - std::map< MulticastGroup,std::list< Address > > p(_proximity.find(mg)); - if (p != _proximity.end()) { - for(std::list< Address >::iterator a(p->second.begin());a!=p->second.end();++a) { - if ((g == _got.end())||(!g->second.second.count(*a))) { - if (!nextHopFunc(*a)) - break; - } + + std::map< uint64_t,_NetInfo >::iterator n(_nets.find(nwid)); + if (n == _nets.end()) + return; + std::map< MulticastGroup,std::list< Address > >::iterator p(n->second.proximity.find(mg)); + if (p == n->second.proximity.end()) + return; + std::map< uint64_t,std::pair< uint64_t,std::set< Address > > >::iterator g(n->second.got.find(mcGuid)); + + for(std::list< Address >::iterator a(p->second.begin());a!=p->second.end();++a) { + if ((g == n->second.got.end())||(!g->second.second.count(*a))) { + if (!nextHopFunc(*a)) + break; } } } private: - // GOTs by multicast GUID: time of last GOT, addresses that GOT - std::map< uint64_t,std::pair< uint64_t,std::set< Address > > > _got; - - // Peer proximity ordering for peers subscribed to each group - std::map< MulticastGroup,std::list< Address > > _proximity; - - // An address and multicast group tuple - typedef std::pair _Subscription; - // Information about a subscription struct _SubInfo { @@ -187,9 +136,23 @@ private: std::list< Address >::iterator proximitySlot; }; - // Peer subscriptions to multicast groups - std::map< _Subscription,_SubInfo > _subscriptions; + // An address and multicast group tuple + typedef std::pair _Subscription; + + // Multicast info for a given network + struct _NetInfo + { + // GOTs by multicast GUID: time of last GOT, addresses that GOT + std::map< uint64_t,std::pair< uint64_t,std::set< Address > > > got; + + // Peer proximity ordering for peers subscribed to each group + std::map< MulticastGroup,std::list< Address > > proximity; + + // Peer subscriptions to multicast groups + std::map< _Subscription,_SubInfo > subscriptions; + }; + std::map< uint64_t,_NetInfo > _nets; Mutex _lock; }; diff --git a/node/Network.cpp b/node/Network.cpp index 2d43e383..65e61738 100644 --- a/node/Network.cpp +++ b/node/Network.cpp @@ -262,8 +262,6 @@ void Network::clean() { std::string mcdbPath(_r->homePath + ZT_PATH_SEPARATOR_S + "networks.d" + ZT_PATH_SEPARATOR_S + idString() + ".mcerts"); - _multicaster.clean(Utils::now()); - Mutex::Lock _l(_lock); if ((!_id)||(_isOpen)) { diff --git a/node/Network.hpp b/node/Network.hpp index 9663b982..d5c75cef 100644 --- a/node/Network.hpp +++ b/node/Network.hpp @@ -51,7 +51,6 @@ #include "Identity.hpp" #include "InetAddress.hpp" #include "BandwidthAccount.hpp" -#include "Multicaster.hpp" namespace ZeroTier { @@ -584,15 +583,6 @@ public: //return tmp; } - /** - * @return Multicaster for this network - */ - inline Multicaster &multicaster() - throw() - { - return _multicaster; - } - private: static void _CBhandleTapData(void *arg,const MAC &from,const MAC &to,unsigned int etherType,const Buffer<4096> &data); void _restoreState(); @@ -619,9 +609,6 @@ private: // Ethertype whitelist bit field, set from config, for really fast lookup unsigned char _etWhitelist[65536 / 8]; - // Multicast propagation database - Multicaster _multicaster; - uint64_t _id; volatile uint64_t _lastConfigUpdate; volatile bool _destroyOnDelete; diff --git a/node/NodeConfig.cpp b/node/NodeConfig.cpp index 12509e9f..4c9f284f 100644 --- a/node/NodeConfig.cpp +++ b/node/NodeConfig.cpp @@ -56,10 +56,6 @@ #include "SHA512.hpp" #include "Node.hpp" -#ifdef __WINDOWS__ -#define strtoull _strtoui64 -#endif - namespace ZeroTier { NodeConfig::NodeConfig(const RuntimeEnvironment *renv,const char *authToken,unsigned int controlPort) diff --git a/node/NodeConfig.hpp b/node/NodeConfig.hpp index c32d14d9..12bc32b3 100644 --- a/node/NodeConfig.hpp +++ b/node/NodeConfig.hpp @@ -167,6 +167,7 @@ private: unsigned char _controlSocketKey[32]; UdpSocket _controlSocket; + std::map< uint64_t,SharedPtr > _networks; Mutex _networks_m; }; diff --git a/node/Packet.cpp b/node/Packet.cpp index e6ae921d..f9731752 100644 --- a/node/Packet.cpp +++ b/node/Packet.cpp @@ -29,6 +29,8 @@ namespace ZeroTier { +const unsigned char Packet::ZERO_KEY[32] { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; + const char *Packet::verbString(Verb v) throw() { diff --git a/node/Packet.hpp b/node/Packet.hpp index cde381dd..932cccfc 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -164,6 +164,9 @@ #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_GOT_IDX_NETWORK_ID (ZT_PACKET_IDX_PAYLOAD) +#define ZT_PROTO_VERB_MULTICAST_GOT_IDX_MULTICAST_GUID (ZT_PROTO_VERB_MULTICAST_GOT_IDX_NETWORK_ID + 8) + #define ZT_PROTO_VERB_MULTICAST_FRAME_IDX_COUNTER (ZT_PACKET_IDX_PAYLOAD) #define ZT_PROTO_VERB_MULTICAST_FRAME_IDX_QUEUE (ZT_PROTO_VERB_MULTICAST_FRAME_IDX_COUNTER + 2) #define ZT_PROTO_VERB_MULTICAST_FRAME_IDX_MAGNET (ZT_PROTO_VERB_MULTICAST_FRAME_IDX_QUEUE + 320) @@ -836,9 +839,8 @@ public: Salsa20 s20(mangledKey,256,field(ZT_PACKET_IDX_IV,8)); // MAC key is always the first 32 bytes of the Salsa20 key stream - // This is the same technique DJB's NaCl library uses to use poly1305 - memset(macKey,0,sizeof(macKey)); - s20.encrypt(macKey,macKey,sizeof(macKey)); + // This is the same construction DJB's NaCl library uses + s20.encrypt(ZERO_KEY,macKey,sizeof(macKey)); if (encryptPayload) s20.encrypt(payload,payload,payloadLen); @@ -864,8 +866,7 @@ public: _mangleKey((const unsigned char *)key,mangledKey); Salsa20 s20(mangledKey,256,field(ZT_PACKET_IDX_IV,8)); - memset(macKey,0,sizeof(macKey)); - s20.encrypt(macKey,macKey,sizeof(macKey)); + s20.encrypt(ZERO_KEY,macKey,sizeof(macKey)); Poly1305::compute(mac,payload,payloadLen,macKey); if (!Utils::secureEq(mac,field(ZT_PACKET_IDX_MAC,8),8)) return false; @@ -931,6 +932,8 @@ public: } private: + static const unsigned char ZERO_KEY[32]; + /** * Deterministically mangle a 256-bit crypto key based on packet * diff --git a/node/PacketDecoder.cpp b/node/PacketDecoder.cpp index ca7af0bf..334eb861 100644 --- a/node/PacketDecoder.cpp +++ b/node/PacketDecoder.cpp @@ -37,20 +37,6 @@ #include "Filter.hpp" #include "Service.hpp" -/* - * The big picture: - * - * tryDecode() gets called for a given fully-assembled packet until it returns - * true or the packet's time to live has been exceeded. The state machine must - * therefore be re-entrant if it ever returns false. Take care here! - * - * Stylistic note: - * - * There's a lot of unnecessary if nesting. It's mostly to allow TRACE to - * print informative messages on every possible reason something gets - * rejected or fails. - */ - namespace ZeroTier { bool PacketDecoder::tryDecode(const RuntimeEnvironment *_r) @@ -58,15 +44,15 @@ bool PacketDecoder::tryDecode(const RuntimeEnvironment *_r) { if ((!encrypted())&&(verb() == Packet::VERB_HELLO)) { // Unencrypted HELLOs are handled here since they are used to - // populate our identity cache in the first place. Thus we might get - // a HELLO for someone for whom we don't have a Peer record. + // populate our identity cache in the first place. _doHELLO() is special + // in that it contains its own authentication logic. TRACE("HELLO from %s(%s)",source().toString().c_str(),_remoteAddress.toString().c_str()); return _doHELLO(_r); } SharedPtr peer = _r->topology->getPeer(source()); if (peer) { - // Resume saved state? + // Resume saved intermediate decode state? if (_step == DECODE_WAITING_FOR_MULTICAST_FRAME_ORIGINAL_SENDER_LOOKUP) { // In this state we have already authenticated and decrypted the // packet and are waiting for the lookup of the original sender @@ -74,22 +60,10 @@ bool PacketDecoder::tryDecode(const RuntimeEnvironment *_r) return _doMULTICAST_FRAME(_r,peer); } - // No saved state? Verify MAC before we proceed. - if (!macVerify(peer->macKey())) { - TRACE("dropped packet from %s(%s), authentication failed (size: %u)",source().toString().c_str(),_remoteAddress.toString().c_str(),size()); + if (!dearmor(peer->key())) { + TRACE("dropped packet from %s(%s), MAC authentication failed (size: %u)",source().toString().c_str(),_remoteAddress.toString().c_str(),size()); return true; } - - // If MAC authentication passed, decrypt and uncompress - if (encrypted()) { - decrypt(peer->cryptKey()); - } else { - // Unencrypted is tolerated in case we want to run this on - // devices where squeezing out cycles matters. MAC is - // what's really important. But log it in debug to catch any - // packets being mistakenly sent in the clear. - TRACE("ODD: %s from %s(%s) wasn't encrypted",Packet::verbString(verb()),source().toString().c_str(),_remoteAddress.toString().c_str()); - } if (!uncompress()) { TRACE("dropped packet from %s(%s), compressed data invalid",source().toString().c_str(),_remoteAddress.toString().c_str()); return true; @@ -107,7 +81,7 @@ bool PacketDecoder::tryDecode(const RuntimeEnvironment *_r) TRACE("NOP from %s(%s)",source().toString().c_str(),_remoteAddress.toString().c_str()); return true; case Packet::VERB_HELLO: - return _doHELLO(_r); + return _doHELLO(_r); // legal, but why? :) case Packet::VERB_ERROR: return _doERROR(_r,peer); case Packet::VERB_OK: @@ -120,6 +94,8 @@ bool PacketDecoder::tryDecode(const RuntimeEnvironment *_r) return _doFRAME(_r,peer); case Packet::VERB_MULTICAST_LIKE: return _doMULTICAST_LIKE(_r,peer); + case Packet::VERB_MULTICAST_GOT: + return _doMULTICAST_GOT(_r,peer); case Packet::VERB_MULTICAST_FRAME: return _doMULTICAST_FRAME(_r,peer); case Packet::VERB_NETWORK_MEMBERSHIP_CERTIFICATE: @@ -474,6 +450,25 @@ bool PacketDecoder::_doMULTICAST_LIKE(const RuntimeEnvironment *_r,const SharedP } catch ( ... ) { TRACE("dropped MULTICAST_LIKE from %s(%s): unexpected exception: (unknown)",source().toString().c_str(),_remoteAddress.toString().c_str()); } + + return true; +} + +bool PacketDecoder::_doMULTICAST_GOT(const RuntimeEnvironment *_r,const SharedPtr &peer) +{ + // Right now only supernodes act as propagation hubs + if (!_r->topology->amSupernode()) { + TRACE("dropped MULTICAST_GOT from %s: I am not a supernode",source().toString().c_str()); + return true; + } + + try { + } catch (std::exception &ex) { + TRACE("dropped MULTICAST_GOT from %s(%s): unexpected exception: %s",source().toString().c_str(),_remoteAddress.toString().c_str(),ex.what()); + } catch ( ... ) { + TRACE("dropped MULTICAST_GOT from %s(%s): unexpected exception: (unknown)",source().toString().c_str(),_remoteAddress.toString().c_str()); + } + return true; } diff --git a/node/PacketDecoder.hpp b/node/PacketDecoder.hpp index 1a713ade..8e10578e 100644 --- a/node/PacketDecoder.hpp +++ b/node/PacketDecoder.hpp @@ -38,6 +38,22 @@ #include "AtomicCounter.hpp" #include "Peer.hpp" +/* + * The big picture: + * + * tryDecode gets called for a given fully-assembled packet until it returns + * true or the packet's time to live has been exceeded, in which case it is + * discarded as failed decode. Any exception thrown by tryDecode also causes + * the packet to be discarded. + * + * Thus a return of false from tryDecode() indicates that it should be called + * again. Logic is very simple as to when, and it's in doAnythingWaitingForPeer + * in Switch. This might be expanded to be more fine grained in the future. + * + * A return value of true indicates that the packet is done. tryDecode must + * never be called again after that. + */ + namespace ZeroTier { class RuntimeEnvironment; @@ -76,7 +92,8 @@ public: * Note that this returns 'true' if processing is complete. This says nothing * about whether the packet was valid. A rejection is 'complete.' * - * Once true is returned, this should not be called again. + * Once true is returned, this must not be called again. The packet's state + * may no longer be valid. * * @param _r Runtime environment * @return True if decoding and processing is complete, false if caller should try again @@ -87,7 +104,7 @@ public: throw(std::out_of_range,std::runtime_error); /** - * @return Time of packet receipt + * @return Time of packet receipt / start of decode */ inline uint64_t receiveTime() const throw() { return _receiveTime; } @@ -121,6 +138,7 @@ private: bool _doRENDEZVOUS(const RuntimeEnvironment *_r,const SharedPtr &peer); bool _doFRAME(const RuntimeEnvironment *_r,const SharedPtr &peer); bool _doMULTICAST_LIKE(const RuntimeEnvironment *_r,const SharedPtr &peer); + bool _doMULTICAST_GOT(const RuntimeEnvironment *_r,const SharedPtr &peer); bool _doMULTICAST_FRAME(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); diff --git a/node/Poly1305.cpp b/node/Poly1305.cpp index 01f8de30..01889b51 100644 --- a/node/Poly1305.cpp +++ b/node/Poly1305.cpp @@ -90,7 +90,7 @@ static void mulmod(unsigned int h[17],const unsigned int r[17]) squeeze(h); } -static int crypto_onetimeauth(unsigned char *out,const unsigned char *in,unsigned long long inlen,const unsigned char *k) +static inline int crypto_onetimeauth(unsigned char *out,const unsigned char *in,unsigned long long inlen,const unsigned char *k) { unsigned int j; unsigned int r[17]; diff --git a/objects.mk b/objects.mk index ff5498a5..e1280b5c 100644 --- a/objects.mk +++ b/objects.mk @@ -10,6 +10,7 @@ OBJS=\ node/Identity.o \ node/InetAddress.o \ node/Logger.o \ + node/Multicaster.o \ node/Network.o \ node/Node.o \ node/NodeConfig.o \ -- cgit v1.2.3