From ab19e19f00dcefc530d061cbeb6b149eae329362 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 11 Jan 2016 09:09:24 -0800 Subject: Fix a bug that we visually found in Windows code -- it was not advertising uPnP addresses?!? --- include/ZeroTierOne.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h index 377af63f..18776ad8 100644 --- a/include/ZeroTierOne.h +++ b/include/ZeroTierOne.h @@ -1334,8 +1334,10 @@ void ZT_Node_freeQueryResult(ZT_Node *node,void *qr); /** * Add a local interface address * - * Take care that these are never ZeroTier interface addresses, otherwise - * strange things might happen or they simply won't work. + * It is the responsibility of the caller to take care that these are never + * ZeroTier interface addresses, whether these are assigned by ZeroTier or + * are otherwise assigned to an interface managed by this ZeroTier instance. + * This can cause recursion or other undesirable behavior. * * Addresses can also be added here if they are the result of a UPnP or * NAT-PMP port mapping or other discovery or mapping means. -- cgit v1.2.3 From ba2a89c760f9bfa4936f3cf89155aafd047af917 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 11 Jan 2016 09:13:41 -0800 Subject: docs --- include/ZeroTierOne.h | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h index 18776ad8..39959221 100644 --- a/include/ZeroTierOne.h +++ b/include/ZeroTierOne.h @@ -1334,20 +1334,21 @@ void ZT_Node_freeQueryResult(ZT_Node *node,void *qr); /** * Add a local interface address * + * This is used to make ZeroTier aware of those local interface addresses + * that you wish to use for ZeroTier communication. This is optional, and if + * it is not used ZeroTier will rely upon upstream peers (and roots) to + * perform empirical address discovery and NAT traversal. But the use of this + * method is recommended as it improves peer discovery when both peers are + * on the same LAN. + * * It is the responsibility of the caller to take care that these are never * ZeroTier interface addresses, whether these are assigned by ZeroTier or * are otherwise assigned to an interface managed by this ZeroTier instance. * This can cause recursion or other undesirable behavior. * - * Addresses can also be added here if they are the result of a UPnP or - * NAT-PMP port mapping or other discovery or mapping means. - * * This returns a boolean indicating whether or not the address was * accepted. ZeroTier will only communicate over certain address types - * and (for IP) address classes. Thus it's safe to just dump your OS's - * entire remote IP list (excluding ZeroTier interface IPs) into here - * and let ZeroTier determine which addresses it will use. It will - * reject bad, empty, and unusable addresses. + * and (for IP) address classes. * * @param addr Local interface address * @return Boolean: non-zero if address was accepted and added -- cgit v1.2.3 From b3e3d4cacca37a4850e4e1a91fb8c42a5b13cb26 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 11 Jan 2016 10:17:44 -0800 Subject: Instead of using binary packet comparison, add a callback to the API to explicitly check whether paths should be used. Check in with this callback (if present) when learning new paths or sending initial packets. --- include/ZeroTierOne.h | 45 ++++++++++++++++++++++++++++++++++++++------- node/IncomingPacket.cpp | 10 ++++++---- node/InetAddress.cpp | 24 ++++++++++++++++++++++++ node/InetAddress.hpp | 8 ++++++++ node/Node.cpp | 26 +++++++++++++++++++++++++- node/Node.hpp | 9 +++++++++ node/Peer.cpp | 2 +- service/OneService.cpp | 20 ++++++++++++++++++++ 8 files changed, 131 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h index 39959221..e087904f 100644 --- a/include/ZeroTierOne.h +++ b/include/ZeroTierOne.h @@ -1097,13 +1097,42 @@ typedef int (*ZT_DataStorePutFunction)( * delivery. It only means that the packet appears to have been sent. */ typedef int (*ZT_WirePacketSendFunction)( - ZT_Node *, /* Node */ - void *, /* User ptr */ - const struct sockaddr_storage *, /* Local address */ - const struct sockaddr_storage *, /* Remote address */ - const void *, /* Packet data */ - unsigned int, /* Packet length */ - unsigned int); /* TTL or 0 to use default */ + ZT_Node *, /* Node */ + void *, /* User ptr */ + const struct sockaddr_storage *, /* Local address */ + const struct sockaddr_storage *, /* Remote address */ + const void *, /* Packet data */ + unsigned int, /* Packet length */ + unsigned int); /* TTL or 0 to use default */ + +/** + * Function to check whether a path should be used for ZeroTier traffic + * + * Paramters: + * (1) Node + * (2) User pointer + * (3) Local interface address + * (4) Remote address + * + * This function must return nonzero (true) if the path should be used. + * + * If no path check function is specified, ZeroTier will still exclude paths + * that overlap with ZeroTier-assigned and managed IP address blocks. But the + * use of a path check function is recommended to ensure that recursion does + * not occur in cases where addresses are assigned by the OS or managed by + * an out of band mechanism like DHCP. The path check function should examine + * all configured ZeroTier interfaces and check to ensure that the supplied + * addresses will not result in ZeroTier traffic being sent over a ZeroTier + * interface (recursion). + * + * Obviously this is not required in configurations where this can't happen, + * such as network containers or embedded. + */ +typedef int (*ZT_PathCheckFunction)( + ZT_Node *, /* Node */ + void *, /* User ptr */ + const struct sockaddr_storage *, /* Local address */ + const struct sockaddr_storage *); /* Remote address */ /****************************************************************************/ /* C Node API */ @@ -1121,6 +1150,7 @@ typedef int (*ZT_WirePacketSendFunction)( * @param dataStoreGetFunction Function called to get objects from persistent storage * @param dataStorePutFunction Function called to put objects in persistent storage * @param virtualNetworkConfigFunction Function to be called when virtual LANs are created, deleted, or their config parameters change + * @param pathCheckFunction A function to check whether a path should be used for ZeroTier traffic, or NULL to allow any path * @param eventCallback Function to receive status updates and non-fatal error notices * @return OK (0) or error code if a fatal error condition has occurred */ @@ -1133,6 +1163,7 @@ enum ZT_ResultCode ZT_Node_new( ZT_WirePacketSendFunction wirePacketSendFunction, ZT_VirtualNetworkFrameFunction virtualNetworkFrameFunction, ZT_VirtualNetworkConfigFunction virtualNetworkConfigFunction, + ZT_PathCheckFunction pathCheckFunction, ZT_EventCallback eventCallback); /** diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index 5c9279dd..c63d70b7 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -507,10 +507,12 @@ bool IncomingPacket::_doRENDEZVOUS(const RuntimeEnvironment *RR,const SharedPtr< const unsigned int port = at(ZT_PROTO_VERB_RENDEZVOUS_IDX_PORT); const unsigned int addrlen = (*this)[ZT_PROTO_VERB_RENDEZVOUS_IDX_ADDRLEN]; if ((port > 0)&&((addrlen == 4)||(addrlen == 16))) { + peer->received(_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_RENDEZVOUS,0,Packet::VERB_NOP); + InetAddress atAddr(field(ZT_PROTO_VERB_RENDEZVOUS_IDX_ADDRESS,addrlen),addrlen,port); TRACE("RENDEZVOUS from %s says %s might be at %s, starting NAT-t",peer->address().toString().c_str(),with.toString().c_str(),atAddr.toString().c_str()); - peer->received(_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_RENDEZVOUS,0,Packet::VERB_NOP); - RR->sw->rendezvous(withPeer,_localAddress,atAddr); + if (RR->node->shouldUsePathForZeroTierTraffic(_localAddress,atAddr)) + RR->sw->rendezvous(withPeer,_localAddress,atAddr); } else { TRACE("dropped corrupt RENDEZVOUS from %s(%s) (bad address or port)",peer->address().toString().c_str(),_remoteAddress.toString().c_str()); } @@ -941,7 +943,7 @@ bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const Sha switch(addrType) { case 4: { InetAddress a(field(ptr,4),4,at(ptr + 4)); - if ( ((flags & 0x01) == 0) && (Path::isAddressValidForPath(a)) && (!peer->hasActivePathTo(now,a)) ) { + if ( ((flags & 0x01) == 0) && (Path::isAddressValidForPath(a)) && (!peer->hasActivePathTo(now,a)) && (RR->node->shouldUsePathForZeroTierTraffic(_localAddress,a)) ) { if (++countPerScope[(int)a.ipScope()][0] <= ZT_PUSH_DIRECT_PATHS_MAX_PER_SCOPE_AND_FAMILY) { TRACE("attempting to contact %s at pushed direct path %s",peer->address().toString().c_str(),a.toString().c_str()); peer->sendHELLO(_localAddress,a,now); @@ -952,7 +954,7 @@ bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const Sha } break; case 6: { InetAddress a(field(ptr,16),16,at(ptr + 16)); - if ( ((flags & 0x01) == 0) && (Path::isAddressValidForPath(a)) && (!peer->hasActivePathTo(now,a)) ) { + if ( ((flags & 0x01) == 0) && (Path::isAddressValidForPath(a)) && (!peer->hasActivePathTo(now,a)) && (RR->node->shouldUsePathForZeroTierTraffic(_localAddress,a)) ) { if (++countPerScope[(int)a.ipScope()][1] <= ZT_PUSH_DIRECT_PATHS_MAX_PER_SCOPE_AND_FAMILY) { TRACE("attempting to contact %s at pushed direct path %s",peer->address().toString().c_str(),a.toString().c_str()); peer->sendHELLO(_localAddress,a,now); diff --git a/node/InetAddress.cpp b/node/InetAddress.cpp index f35eb9c3..1b3a8064 100644 --- a/node/InetAddress.cpp +++ b/node/InetAddress.cpp @@ -280,6 +280,30 @@ InetAddress InetAddress::network() const return r; } +bool InetAddress::containsAddress(const InetAddress &addr) const +{ + if (addr.ss_family == ss_family) { + switch(ss_family) { + case AF_INET: { + const unsigned int bits = netmaskBits(); + return ( (Utils::ntoh((uint32_t)reinterpret_cast(&addr)->sin_addr.s_addr) >> (32 - bits)) == (Utils::ntoh((uint32_t)reinterpret_cast(this)->sin_addr.s_addr) >> (32 - bits)) ); + } + case AF_INET6: { + const InetAddress mask(netmask()); + const uint8_t *m = reinterpret_cast(reinterpret_cast(&mask)->sin6_addr.s6_addr); + const uint8_t *a = reinterpret_cast(reinterpret_cast(&addr)->sin6_addr.s6_addr); + const uint8_t *b = reinterpret_cast(reinterpret_cast(this)->sin6_addr.s6_addr); + for(unsigned int i=0;i<16;++i) { + if ((a[i] & m[i]) != b[i]) + return false; + } + return true; + } + } + } + return false; +} + bool InetAddress::isNetwork() const throw() { diff --git a/node/InetAddress.hpp b/node/InetAddress.hpp index 2573e694..201271f7 100644 --- a/node/InetAddress.hpp +++ b/node/InetAddress.hpp @@ -325,6 +325,14 @@ struct InetAddress : public sockaddr_storage */ InetAddress network() const; + /** + * Test whether this IP/netmask contains this address + * + * @param addr Address to check + * @return True if this IP/netmask (route) contains this address + */ + bool containsAddress(const InetAddress &addr) const; + /** * @return True if this is an IPv4 address */ diff --git a/node/Node.cpp b/node/Node.cpp index 19675bb5..f65aa843 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -64,6 +64,7 @@ Node::Node( ZT_WirePacketSendFunction wirePacketSendFunction, ZT_VirtualNetworkFrameFunction virtualNetworkFrameFunction, ZT_VirtualNetworkConfigFunction virtualNetworkConfigFunction, + ZT_PathCheckFunction pathCheckFunction, ZT_EventCallback eventCallback) : _RR(this), RR(&_RR), @@ -73,6 +74,7 @@ Node::Node( _wirePacketSendFunction(wirePacketSendFunction), _virtualNetworkFrameFunction(virtualNetworkFrameFunction), _virtualNetworkConfigFunction(virtualNetworkConfigFunction), + _pathCheckFunction(pathCheckFunction), _eventCallback(eventCallback), _networks(), _networks_m(), @@ -671,6 +673,27 @@ std::string Node::dataStoreGet(const char *name) return r; } +bool Node::shouldUsePathForZeroTierTraffic(const InetAddress &localAddress,const InetAddress &remoteAddress) +{ + { + Mutex::Lock _l(_networks_m); + for(std::vector< std::pair< uint64_t, SharedPtr > >::const_iterator i=_networks.begin();i!=_networks.end();++i) { + SharedPtr nc(i->second->config2()); + if (nc) { + for(std::vector::const_iterator a(nc->staticIps().begin());a!=nc->staticIps().end();++a) { + if (a->containsAddress(remoteAddress)) { + return false; + } + } + } + } + } + + if (_pathCheckFunction) + return (_pathCheckFunction(reinterpret_cast(this),_uPtr,reinterpret_cast(&localAddress),reinterpret_cast(&remoteAddress)) != 0); + else return true; +} + #ifdef ZT_TRACE void Node::postTrace(const char *module,unsigned int line,const char *fmt,...) { @@ -743,11 +766,12 @@ enum ZT_ResultCode ZT_Node_new( ZT_WirePacketSendFunction wirePacketSendFunction, ZT_VirtualNetworkFrameFunction virtualNetworkFrameFunction, ZT_VirtualNetworkConfigFunction virtualNetworkConfigFunction, + ZT_PathCheckFunction pathCheckFunction, ZT_EventCallback eventCallback) { *node = (ZT_Node *)0; try { - *node = reinterpret_cast(new ZeroTier::Node(now,uptr,dataStoreGetFunction,dataStorePutFunction,wirePacketSendFunction,virtualNetworkFrameFunction,virtualNetworkConfigFunction,eventCallback)); + *node = reinterpret_cast(new ZeroTier::Node(now,uptr,dataStoreGetFunction,dataStorePutFunction,wirePacketSendFunction,virtualNetworkFrameFunction,virtualNetworkConfigFunction,pathCheckFunction,eventCallback)); return ZT_RESULT_OK; } catch (std::bad_alloc &exc) { return ZT_RESULT_FATAL_ERROR_OUT_OF_MEMORY; diff --git a/node/Node.hpp b/node/Node.hpp index 15295139..b6b32363 100644 --- a/node/Node.hpp +++ b/node/Node.hpp @@ -71,6 +71,7 @@ public: ZT_WirePacketSendFunction wirePacketSendFunction, ZT_VirtualNetworkFrameFunction virtualNetworkFrameFunction, ZT_VirtualNetworkConfigFunction virtualNetworkConfigFunction, + ZT_PathCheckFunction pathCheckFunction, ZT_EventCallback eventCallback); ~Node(); @@ -189,6 +190,13 @@ public: len); } + /** + * @param localAddress Local address + * @param remoteAddress Remote address + * @return True if path should be used + */ + bool shouldUsePathForZeroTierTraffic(const InetAddress &localAddress,const InetAddress &remoteAddress); + inline SharedPtr network(uint64_t nwid) const { Mutex::Lock _l(_networks_m); @@ -288,6 +296,7 @@ private: ZT_WirePacketSendFunction _wirePacketSendFunction; ZT_VirtualNetworkFrameFunction _virtualNetworkFrameFunction; ZT_VirtualNetworkConfigFunction _virtualNetworkConfigFunction; + ZT_PathCheckFunction _pathCheckFunction; ZT_EventCallback _eventCallback; std::vector< std::pair< uint64_t, SharedPtr > > _networks; diff --git a/node/Peer.cpp b/node/Peer.cpp index 04e5bdf0..c75a3e46 100644 --- a/node/Peer.cpp +++ b/node/Peer.cpp @@ -156,7 +156,7 @@ void Peer::received( } } - if (!pathIsConfirmed) { + if ((!pathIsConfirmed)&&(RR->node->shouldUsePathForZeroTierTraffic(localAddr,remoteAddr))) { if (verb == Packet::VERB_OK) { Path *slot = (Path *)0; diff --git a/service/OneService.cpp b/service/OneService.cpp index 57e1718d..84ebdd87 100644 --- a/service/OneService.cpp +++ b/service/OneService.cpp @@ -397,6 +397,7 @@ static long SnodeDataStoreGetFunction(ZT_Node *node,void *uptr,const char *name, static int SnodeDataStorePutFunction(ZT_Node *node,void *uptr,const char *name,const void *data,unsigned long len,int secure); static int SnodeWirePacketSendFunction(ZT_Node *node,void *uptr,const struct sockaddr_storage *localAddr,const struct sockaddr_storage *addr,const void *data,unsigned int len,unsigned int ttl); static void SnodeVirtualNetworkFrameFunction(ZT_Node *node,void *uptr,uint64_t nwid,uint64_t sourceMac,uint64_t destMac,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len); +static int SnodePathCheckFunction(ZT_Node *node,void *uptr,const struct sockaddr_storage *localAddr,const struct sockaddr_storage *remoteAddr); #ifdef ZT_ENABLE_CLUSTER static void SclusterSendFunction(void *uptr,unsigned int toMemberId,const void *data,unsigned int len); @@ -592,6 +593,7 @@ public: SnodeWirePacketSendFunction, SnodeVirtualNetworkFrameFunction, SnodeVirtualNetworkConfigFunction, + SnodePathCheckFunction, SnodeEventCallback); #ifdef ZT_USE_MINIUPNPC @@ -1393,6 +1395,22 @@ public: t->second->put(MAC(sourceMac),MAC(destMac),etherType,data,len); } + inline int nodePathCheckFunction(const struct sockaddr_storage *localAddr,const struct sockaddr_storage *remoteAddr) + { + Mutex::Lock _l(_taps_m); + for(std::map< uint64_t,EthernetTap * >::const_iterator t(_taps.begin());t!=_taps.end();++t) { + if (t->second) { + std::vector ips(t->second->ips()); + for(std::vector::const_iterator i(ips.begin());i!=ips.end();++i) { + if (i->containsAddress(*(reinterpret_cast(remoteAddr)))) { + return 0; + } + } + } + } + return 1; + } + inline void tapFrameHandler(uint64_t nwid,const MAC &from,const MAC &to,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len) { _node->processVirtualNetworkFrame(OSUtils::now(),nwid,from.toInt(),to.toInt(),etherType,vlanId,data,len,&_nextBackgroundTaskDeadline); @@ -1527,6 +1545,8 @@ static int SnodeWirePacketSendFunction(ZT_Node *node,void *uptr,const struct soc { return reinterpret_cast(uptr)->nodeWirePacketSendFunction(localAddr,addr,data,len,ttl); } static void SnodeVirtualNetworkFrameFunction(ZT_Node *node,void *uptr,uint64_t nwid,uint64_t sourceMac,uint64_t destMac,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len) { reinterpret_cast(uptr)->nodeVirtualNetworkFrameFunction(nwid,sourceMac,destMac,etherType,vlanId,data,len); } +static int SnodePathCheckFunction(ZT_Node *node,void *uptr,const struct sockaddr_storage *localAddr,const struct sockaddr_storage *remoteAddr) +{ return reinterpret_cast(uptr)->nodePathCheckFunction(localAddr,remoteAddr); } #ifdef ZT_ENABLE_CLUSTER static void SclusterSendFunction(void *uptr,unsigned int toMemberId,const void *data,unsigned int len) -- cgit v1.2.3