From ab0228f626573381db93173cd5849cb934481ca5 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 7 Oct 2015 10:30:47 -0700 Subject: More cleanup and simple refactoring, consolidate InetAddres serialize/deserialize into the class. --- node/InetAddress.hpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'node/InetAddress.hpp') diff --git a/node/InetAddress.hpp b/node/InetAddress.hpp index c376a032..6970e92d 100644 --- a/node/InetAddress.hpp +++ b/node/InetAddress.hpp @@ -366,7 +366,8 @@ struct InetAddress : public sockaddr_storage template inline void serialize(Buffer &b) const { - // Format is the same as in VERB_HELLO in Packet.hpp + // This is used in the protocol and must be the same as describe in places + // like VERB_HELLO in Packet.hpp. switch(ss_family) { case AF_INET: b.append((uint8_t)0x04); @@ -387,8 +388,8 @@ struct InetAddress : public sockaddr_storage template inline unsigned int deserialize(const Buffer &b,unsigned int startAt = 0) { - unsigned int p = startAt; memset(this,0,sizeof(InetAddress)); + unsigned int p = startAt; switch(b[p++]) { case 0: return 1; -- cgit v1.2.3 From a95fa379cca0ddbce98d476b143c3606f3ae7bce Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Fri, 9 Oct 2015 14:51:38 -0700 Subject: Circuit tests basically work but need some tweaks, and fix some issues found with valgrind. --- controller/SqliteNetworkController.cpp | 2 - controller/SqliteNetworkController.hpp | 2 - examples/api/circuit-test-pingpong.json | 13 ++++++ examples/docker/main.sh | 2 +- node/InetAddress.hpp | 72 ++++++++++++++++++++------------- node/Node.cpp | 2 +- 6 files changed, 59 insertions(+), 34 deletions(-) create mode 100644 examples/api/circuit-test-pingpong.json (limited to 'node/InetAddress.hpp') diff --git a/controller/SqliteNetworkController.cpp b/controller/SqliteNetworkController.cpp index 40fafd79..d87e5624 100644 --- a/controller/SqliteNetworkController.cpp +++ b/controller/SqliteNetworkController.cpp @@ -258,8 +258,6 @@ SqliteNetworkController::~SqliteNetworkController() sqlite3_finalize(_sCreateMember); sqlite3_finalize(_sGetNodeIdentity); sqlite3_finalize(_sCreateOrReplaceNode); - sqlite3_finalize(_sUpdateNode); - sqlite3_finalize(_sUpdateNode2); sqlite3_finalize(_sGetEtherTypesFromRuleTable); sqlite3_finalize(_sGetActiveBridges); sqlite3_finalize(_sGetIpAssignmentsForNode); diff --git a/controller/SqliteNetworkController.hpp b/controller/SqliteNetworkController.hpp index 7a01487c..a3d5dfc7 100644 --- a/controller/SqliteNetworkController.hpp +++ b/controller/SqliteNetworkController.hpp @@ -155,8 +155,6 @@ private: sqlite3_stmt *_sCreateMember; sqlite3_stmt *_sGetNodeIdentity; sqlite3_stmt *_sCreateOrReplaceNode; - sqlite3_stmt *_sUpdateNode; - sqlite3_stmt *_sUpdateNode2; sqlite3_stmt *_sGetEtherTypesFromRuleTable; sqlite3_stmt *_sGetActiveBridges; sqlite3_stmt *_sGetIpAssignmentsForNode; diff --git a/examples/api/circuit-test-pingpong.json b/examples/api/circuit-test-pingpong.json new file mode 100644 index 00000000..8fcc5d94 --- /dev/null +++ b/examples/api/circuit-test-pingpong.json @@ -0,0 +1,13 @@ +{ + "hops": [ + [ "4cbc810d4c" ], + [ "868cd1664f" ], + [ "4cbc810d4c" ], + [ "868cd1664f" ], + [ "4cbc810d4c" ], + [ "868cd1664f" ], + [ "4cbc810d4c" ], + [ "868cd1664f" ] + ], + "reportAtEveryHop": true +} diff --git a/examples/docker/main.sh b/examples/docker/main.sh index e9febb13..53fb6540 100644 --- a/examples/docker/main.sh +++ b/examples/docker/main.sh @@ -20,6 +20,6 @@ else fi rm -f /var/lib/zerotier-one/identity.* -echo "$ZEROTIER_IDENTITY_SECRET" >identity.secret +echo "$ZEROTIER_IDENTITY_SECRET" >/var/lib/zerotier-one/identity.secret /var/lib/zerotier-one/zerotier-one diff --git a/node/InetAddress.hpp b/node/InetAddress.hpp index 6970e92d..50db272a 100644 --- a/node/InetAddress.hpp +++ b/node/InetAddress.hpp @@ -100,74 +100,88 @@ struct InetAddress : public sockaddr_storage inline InetAddress &operator=(const InetAddress &a) throw() { - memcpy(this,&a,sizeof(InetAddress)); + if (&a != this) + memcpy(this,&a,sizeof(InetAddress)); return *this; } inline InetAddress &operator=(const InetAddress *a) throw() { - memcpy(this,a,sizeof(InetAddress)); + if (a != this) + memcpy(this,a,sizeof(InetAddress)); return *this; } inline InetAddress &operator=(const struct sockaddr_storage &ss) throw() { - memcpy(this,&ss,sizeof(InetAddress)); + if (reinterpret_cast(&ss) != this) + memcpy(this,&ss,sizeof(InetAddress)); return *this; } inline InetAddress &operator=(const struct sockaddr_storage *ss) throw() { - memcpy(this,ss,sizeof(InetAddress)); + if (reinterpret_cast(ss) != this) + memcpy(this,ss,sizeof(InetAddress)); return *this; } inline InetAddress &operator=(const struct sockaddr_in &sa) throw() { - memset(this,0,sizeof(InetAddress)); - memcpy(this,&sa,sizeof(struct sockaddr_in)); + if (reinterpret_cast(&sa) != this) { + memset(this,0,sizeof(InetAddress)); + memcpy(this,&sa,sizeof(struct sockaddr_in)); + } return *this; } inline InetAddress &operator=(const struct sockaddr_in *sa) throw() { - memset(this,0,sizeof(InetAddress)); - memcpy(this,sa,sizeof(struct sockaddr_in)); + if (reinterpret_cast(sa) != this) { + memset(this,0,sizeof(InetAddress)); + memcpy(this,sa,sizeof(struct sockaddr_in)); + } return *this; } inline InetAddress &operator=(const struct sockaddr_in6 &sa) throw() { - memset(this,0,sizeof(InetAddress)); - memcpy(this,&sa,sizeof(struct sockaddr_in6)); + if (reinterpret_cast(&sa) != this) { + memset(this,0,sizeof(InetAddress)); + memcpy(this,&sa,sizeof(struct sockaddr_in6)); + } return *this; } inline InetAddress &operator=(const struct sockaddr_in6 *sa) throw() { - memset(this,0,sizeof(InetAddress)); - memcpy(this,sa,sizeof(struct sockaddr_in6)); + if (reinterpret_cast(sa) != this) { + memset(this,0,sizeof(InetAddress)); + memcpy(this,sa,sizeof(struct sockaddr_in6)); + } return *this; } inline InetAddress &operator=(const struct sockaddr &sa) throw() { - memset(this,0,sizeof(InetAddress)); - switch(sa.sa_family) { - case AF_INET: - memcpy(this,&sa,sizeof(struct sockaddr_in)); - break; - case AF_INET6: - memcpy(this,&sa,sizeof(struct sockaddr_in6)); - break; + if (reinterpret_cast(&sa) != this) { + memset(this,0,sizeof(InetAddress)); + switch(sa.sa_family) { + case AF_INET: + memcpy(this,&sa,sizeof(struct sockaddr_in)); + break; + case AF_INET6: + memcpy(this,&sa,sizeof(struct sockaddr_in6)); + break; + } } return *this; } @@ -175,14 +189,16 @@ struct InetAddress : public sockaddr_storage inline InetAddress &operator=(const struct sockaddr *sa) throw() { - memset(this,0,sizeof(InetAddress)); - switch(sa->sa_family) { - case AF_INET: - memcpy(this,sa,sizeof(struct sockaddr_in)); - break; - case AF_INET6: - memcpy(this,sa,sizeof(struct sockaddr_in6)); - break; + if (reinterpret_cast(sa) != this) { + memset(this,0,sizeof(InetAddress)); + switch(sa->sa_family) { + case AF_INET: + memcpy(this,sa,sizeof(struct sockaddr_in)); + break; + case AF_INET6: + memcpy(this,sa,sizeof(struct sockaddr_in6)); + break; + } } return *this; } diff --git a/node/Node.cpp b/node/Node.cpp index 84452146..1eb21914 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -491,7 +491,7 @@ ZT_ResultCode Node::circuitTestBegin(ZT_CircuitTest *test,void (*reportCallback) for(unsigned int a=0;ahops[0].breadth;++a) { outp.newInitializationVector(); outp.setDestination(Address(test->hops[0].addresses[a])); - RR->sw->send(outp,true,test->credentialNetworkId); + RR->sw->send(outp,true,0); } } catch ( ... ) { return ZT_RESULT_FATAL_ERROR_INTERNAL; // probably indicates FIFO too big for packet -- cgit v1.2.3 From 3ce5ad9e2c1a31e1a3cc34f0ac8d950e4f0cf7b4 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 26 Oct 2015 10:42:30 -0700 Subject: For forward compatibility, add minimal parse for some future physical address types. --- node/InetAddress.hpp | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'node/InetAddress.hpp') diff --git a/node/InetAddress.hpp b/node/InetAddress.hpp index 50db272a..ecafcf51 100644 --- a/node/InetAddress.hpp +++ b/node/InetAddress.hpp @@ -409,6 +409,16 @@ struct InetAddress : public sockaddr_storage switch(b[p++]) { case 0: return 1; + case 0x01: + // TODO: Ethernet address (but accept for forward compatibility) + return 7; + case 0x02: + // TODO: Bluetooth address (but accept for forward compatibility) + return 7; + case 0x03: + // TODO: Other address types (but accept for forward compatibility) + // These could be extended/optional things like AF_UNIX, LTE Direct, shared memory, etc. + return (unsigned int)(b.template at(p) + 3); // other addresses begin with 16-bit non-inclusive length case 0x04: ss_family = AF_INET; memcpy(&(reinterpret_cast(this)->sin_addr.s_addr),b.field(p,4),4); p += 4; -- cgit v1.2.3 From 88b100e5d0db5df16622fa48899cf652e09b3e91 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 27 Oct 2015 17:59:17 -0700 Subject: More cleanup. --- node/IncomingPacket.cpp | 49 ++++++++++++++++++++++++++++++++----------------- node/InetAddress.hpp | 11 ++++++++--- 2 files changed, 40 insertions(+), 20 deletions(-) (limited to 'node/InetAddress.hpp') diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index 7015535a..b1a9af3b 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -45,6 +45,7 @@ #include "World.hpp" #include "Cluster.hpp" #include "Node.hpp" +#include "AntiRecursion.hpp" namespace ZeroTier { @@ -122,7 +123,7 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,const SharedPtr case Packet::ERROR_OBJ_NOT_FOUND: if (inReVerb == Packet::VERB_WHOIS) { - if (RR->topology->isRoot(peer->identity())) + if (RR->topology->isUpstream(peer->identity())) RR->sw->cancelWhoisRequest(Address(field(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH)); } else if (inReVerb == Packet::VERB_NETWORK_CONFIG_REQUEST) { SharedPtr network(RR->node->network(at(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD))); @@ -155,6 +156,7 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,const SharedPtr Packet outp(peer->address(),RR->identity.address(),Packet::VERB_NETWORK_MEMBERSHIP_CERTIFICATE); nconf->com().serialize(outp); outp.armor(peer->key(),true); + RR->antiRec->logOutgoingZT(outp.data(),outp.size()); RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size()); } } @@ -202,13 +204,13 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR) const uint64_t timestamp = at(ZT_PROTO_VERB_HELLO_IDX_TIMESTAMP); Identity id; - InetAddress destAddr; + InetAddress externalSurfaceAddress; uint64_t worldId = ZT_WORLD_ID_NULL; uint64_t worldTimestamp = 0; { unsigned int ptr = ZT_PROTO_VERB_HELLO_IDX_IDENTITY + id.deserialize(*this,ZT_PROTO_VERB_HELLO_IDX_IDENTITY); if (ptr < size()) // ZeroTier One < 1.0.3 did not include physical destination address info - ptr += destAddr.deserialize(*this,ptr); + ptr += externalSurfaceAddress.deserialize(*this,ptr); if ((ptr + 16) <= size()) { // older versions also did not include World IDs or timestamps worldId = at(ptr); ptr += 8; worldTimestamp = at(ptr); @@ -281,11 +283,8 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR) // VALID -- if we made it here, packet passed identity and authenticity checks! - peer->setRemoteVersion(protoVersion,vMajor,vMinor,vRevision); - peer->received(RR,_localAddress,_remoteAddress,hops(),pid,Packet::VERB_HELLO,0,Packet::VERB_NOP); - - if (destAddr) - RR->sa->iam(id.address(),_remoteAddress,destAddr,RR->topology->isRoot(id),RR->node->now()); + if (externalSurfaceAddress) + RR->sa->iam(id.address(),_remoteAddress,externalSurfaceAddress,RR->topology->isRoot(id),RR->node->now()); Packet outp(id.address(),RR->identity.address(),Packet::VERB_OK); outp.append((unsigned char)Packet::VERB_HELLO); @@ -308,7 +307,11 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR) } outp.armor(peer->key(),true); + RR->antiRec->logOutgoingZT(outp.data(),outp.size()); RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size()); + + peer->setRemoteVersion(protoVersion,vMajor,vMinor,vRevision); + peer->received(RR,_localAddress,_remoteAddress,hops(),pid,Packet::VERB_HELLO,0,Packet::VERB_NOP); } catch ( ... ) { TRACE("dropped HELLO from %s(%s): unexpected exception",source().toString().c_str(),_remoteAddress.toString().c_str()); } @@ -332,12 +335,17 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,const SharedPtr &p const unsigned int vMinor = (*this)[ZT_PROTO_VERB_HELLO__OK__IDX_MINOR_VERSION]; const unsigned int vRevision = at(ZT_PROTO_VERB_HELLO__OK__IDX_REVISION); + if (vProto < ZT_PROTO_VERSION_MIN) { + TRACE("%s(%s): OK(HELLO) dropped, protocol version too old",source().toString().c_str(),_remoteAddress.toString().c_str()); + return true; + } + const bool trusted = RR->topology->isRoot(peer->identity()); - InetAddress destAddr; + InetAddress externalSurfaceAddress; unsigned int ptr = ZT_PROTO_VERB_HELLO__OK__IDX_REVISION + 2; if (ptr < size()) // ZeroTier One < 1.0.3 did not include this field - ptr += destAddr.deserialize(*this,ptr); + ptr += externalSurfaceAddress.deserialize(*this,ptr); if ((trusted)&&((ptr + 2) <= size())) { // older versions also did not include this field, and right now we only use if from a root World worldUpdate; const unsigned int worldLen = at(ptr); ptr += 2; @@ -348,18 +356,13 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,const SharedPtr &p } } - if (vProto < ZT_PROTO_VERSION_MIN) { - TRACE("%s(%s): OK(HELLO) dropped, protocol version too old",source().toString().c_str(),_remoteAddress.toString().c_str()); - return true; - } - TRACE("%s(%s): OK(HELLO), version %u.%u.%u, latency %u, reported external address %s",source().toString().c_str(),_remoteAddress.toString().c_str(),vMajor,vMinor,vRevision,latency,((destAddr) ? destAddr.toString().c_str() : "(none)")); peer->addDirectLatencyMeasurment(latency); peer->setRemoteVersion(vProto,vMajor,vMinor,vRevision); - if (destAddr) - RR->sa->iam(peer->address(),_remoteAddress,destAddr,trusted,RR->node->now()); + if (externalSurfaceAddress) + RR->sa->iam(peer->address(),_remoteAddress,externalSurfaceAddress,trusted,RR->node->now()); } break; case Packet::VERB_WHOIS: { @@ -443,6 +446,7 @@ bool IncomingPacket::_doWHOIS(const RuntimeEnvironment *RR,const SharedPtr outp.append(packetId()); queried.serialize(outp,false); outp.armor(peer->key(),true); + RR->antiRec->logOutgoingZT(outp.data(),outp.size()); RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size()); } else { Packet outp(peer->address(),RR->identity.address(),Packet::VERB_ERROR); @@ -451,6 +455,7 @@ bool IncomingPacket::_doWHOIS(const RuntimeEnvironment *RR,const SharedPtr outp.append((unsigned char)Packet::ERROR_OBJ_NOT_FOUND); outp.append(payload(),ZT_ADDRESS_LENGTH); outp.armor(peer->key(),true); + RR->antiRec->logOutgoingZT(outp.data(),outp.size()); RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size()); } } else { @@ -608,6 +613,7 @@ bool IncomingPacket::_doECHO(const RuntimeEnvironment *RR,const SharedPtr outp.append((unsigned char)Packet::VERB_ECHO); outp.append((uint64_t)pid); outp.append(field(ZT_PACKET_IDX_PAYLOAD,size() - ZT_PACKET_IDX_PAYLOAD),size() - ZT_PACKET_IDX_PAYLOAD); + RR->antiRec->logOutgoingZT(outp.data(),outp.size()); RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size()); peer->received(RR,_localAddress,_remoteAddress,hops(),pid,Packet::VERB_ECHO,0,Packet::VERB_NOP); } catch ( ... ) { @@ -693,6 +699,7 @@ bool IncomingPacket::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,cons if (outp.size() > ZT_PROTO_MAX_PACKET_LENGTH) { // sanity check TRACE("NETWORK_CONFIG_REQUEST failed: internal error: netconf size %u is too large",(unsigned int)netconfStr.length()); } else { + RR->antiRec->logOutgoingZT(outp.data(),outp.size()); RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size()); } } @@ -705,6 +712,7 @@ bool IncomingPacket::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,cons outp.append((unsigned char)Packet::ERROR_OBJ_NOT_FOUND); outp.append(nwid); outp.armor(peer->key(),true); + RR->antiRec->logOutgoingZT(outp.data(),outp.size()); RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size()); } break; @@ -715,6 +723,7 @@ bool IncomingPacket::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,cons outp.append((unsigned char)Packet::ERROR_NETWORK_ACCESS_DENIED_); outp.append(nwid); outp.armor(peer->key(),true); + RR->antiRec->logOutgoingZT(outp.data(),outp.size()); RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size()); } break; @@ -737,6 +746,7 @@ bool IncomingPacket::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,cons outp.append((unsigned char)Packet::ERROR_UNSUPPORTED_OPERATION); outp.append(nwid); outp.armor(peer->key(),true); + RR->antiRec->logOutgoingZT(outp.data(),outp.size()); RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size()); } } catch ( ... ) { @@ -781,6 +791,7 @@ bool IncomingPacket::_doMULTICAST_GATHER(const RuntimeEnvironment *RR,const Shar outp.append((uint32_t)mg.adi()); if (RR->mc->gather(peer->address(),nwid,mg,outp,gatherLimit)) { outp.armor(peer->key(),true); + RR->antiRec->logOutgoingZT(outp.data(),outp.size()); RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size()); } } @@ -873,6 +884,7 @@ bool IncomingPacket::_doMULTICAST_FRAME(const RuntimeEnvironment *RR,const Share outp.append((unsigned char)0x02); // flag 0x02 = contains gather results if (RR->mc->gather(peer->address(),nwid,to,outp,gatherLimit)) { outp.armor(peer->key(),true); + RR->antiRec->logOutgoingZT(outp.data(),outp.size()); RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size()); } } @@ -1173,6 +1185,7 @@ bool IncomingPacket::_doREQUEST_PROOF_OF_WORK(const RuntimeEnvironment *RR,const outp.append((uint16_t)sizeof(result)); outp.append(result,sizeof(result)); outp.armor(peer->key(),true); + RR->antiRec->logOutgoingZT(outp.data(),outp.size()); RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size()); } else { Packet outp(peer->address(),RR->identity.address(),Packet::VERB_ERROR); @@ -1180,6 +1193,7 @@ bool IncomingPacket::_doREQUEST_PROOF_OF_WORK(const RuntimeEnvironment *RR,const outp.append(pid); outp.append((unsigned char)Packet::ERROR_INVALID_REQUEST); outp.armor(peer->key(),true); + RR->antiRec->logOutgoingZT(outp.data(),outp.size()); RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size()); } } break; @@ -1285,6 +1299,7 @@ void IncomingPacket::_sendErrorNeedCertificate(const RuntimeEnvironment *RR,cons outp.append((unsigned char)Packet::ERROR_NEED_MEMBERSHIP_CERTIFICATE); outp.append(nwid); outp.armor(peer->key(),true); + RR->antiRec->logOutgoingZT(outp.data(),outp.size()); RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size()); } diff --git a/node/InetAddress.hpp b/node/InetAddress.hpp index ecafcf51..fcbed4b1 100644 --- a/node/InetAddress.hpp +++ b/node/InetAddress.hpp @@ -346,14 +346,19 @@ struct InetAddress : public sockaddr_storage } /** + * Performs an IP-only comparison or, if that is impossible, a memcmp() + * * @param a InetAddress to compare again * @return True if only IP portions are equal (false for non-IP or null addresses) */ inline bool ipsEqual(const InetAddress &a) const { - switch(ss_family) { - case AF_INET: return (reinterpret_cast(this)->sin_addr.s_addr == reinterpret_cast(&a)->sin_addr.s_addr); - case AF_INET6: return (memcmp(reinterpret_cast(this)->sin6_addr.s6_addr,reinterpret_cast(&a)->sin6_addr.s6_addr,16) == 0); + if (ss_family == a.ss_family) { + if (ss_family == AF_INET) + return (reinterpret_cast(this)->sin_addr.s_addr == reinterpret_cast(&a)->sin_addr.s_addr); + if (ss_family == AF_INET6) + return (memcmp(reinterpret_cast(this)->sin6_addr.s6_addr,reinterpret_cast(&a)->sin6_addr.s6_addr,16) == 0); + return (memcmp(this,&a,sizeof(InetAddress)) == 0); } return false; } -- cgit v1.2.3 From da9371284625d2481ed496921505c8afd2dae1f0 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 28 Oct 2015 09:11:30 -0700 Subject: Clean up PUSH_DIRECT_PATH limits a bit more and make them a bit smarter. --- node/Constants.hpp | 10 +++++----- node/IncomingPacket.cpp | 25 +++++++++++++++---------- node/InetAddress.hpp | 8 +++++++- 3 files changed, 27 insertions(+), 16 deletions(-) (limited to 'node/InetAddress.hpp') diff --git a/node/Constants.hpp b/node/Constants.hpp index 53cc64c7..5a4d4a4d 100644 --- a/node/Constants.hpp +++ b/node/Constants.hpp @@ -347,11 +347,6 @@ */ #define ZT_DIRECT_PATH_PUSH_INTERVAL 120000 -/** - * Maximum number of endpoints to contact per address type (to limit pushes like GitHub issue #235) - */ -#define ZT_PUSH_DIRECT_PATHS_MAX_ENDPOINTS_PER_TYPE 5 - /** * Time horizon for push direct paths cutoff */ @@ -366,6 +361,11 @@ */ #define ZT_PUSH_DIRECT_PATHS_CUTOFF_LIMIT 5 +/** + * Maximum number of paths per IP scope (e.g. global, link-local) and family (e.g. v4/v6) + */ +#define ZT_PUSH_DIRECT_PATHS_MAX_PER_SCOPE_AND_FAMILY 1 + /** * A test pseudo-network-ID that can be joined * diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index e985b34c..f06eb30c 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -901,16 +901,19 @@ bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const Sha { try { const uint64_t now = RR->node->now(); + + // First, subject this to a rate limit if (!peer->shouldRespondToDirectPathPush(now)) { TRACE("dropped PUSH_DIRECT_PATHS from %s(%s): circuit breaker tripped",source().toString().c_str(),_remoteAddress.toString().c_str()); return true; } - const Path *currentBest = peer->getBestPath(now); + // Second, limit addresses by scope and type + uint8_t countPerScope[ZT_INETADDRESS_MAX_SCOPE+1][2]; // [][0] is v4, [][1] is v6 + memset(countPerScope,0,sizeof(countPerScope)); unsigned int count = at(ZT_PACKET_IDX_PAYLOAD); unsigned int ptr = ZT_PACKET_IDX_PAYLOAD + 2; - unsigned int v4Count = 0,v6Count = 0; while (count--) { // if ptr overflows Buffer will throw // TODO: some flags are not yet implemented @@ -925,20 +928,22 @@ bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const Sha case 4: { InetAddress a(field(ptr,4),4,at(ptr + 4)); if ( ((flags & 0x01) == 0) && (Path::isAddressValidForPath(a)) ) { - TRACE("attempting to contact %s at pushed direct path %s",peer->address().toString().c_str(),a.toString().c_str()); - if (v4Count++ < ZT_PUSH_DIRECT_PATHS_MAX_ENDPOINTS_PER_TYPE) { - if ((!currentBest)||(currentBest->address() != a)) - peer->attemptToContactAt(RR,_localAddress,a,now); + 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->attemptToContactAt(RR,_localAddress,a,now); + } else { + TRACE("ignoring contact for %s at %s -- too many per scope",peer->address().toString().c_str(),a.toString().c_str()); } } } break; case 6: { InetAddress a(field(ptr,16),16,at(ptr + 16)); if ( ((flags & 0x01) == 0) && (Path::isAddressValidForPath(a)) ) { - TRACE("attempting to contact %s at pushed direct path %s",peer->address().toString().c_str(),a.toString().c_str()); - if (v6Count++ < ZT_PUSH_DIRECT_PATHS_MAX_ENDPOINTS_PER_TYPE) { - if ((!currentBest)||(currentBest->address() != a)) - peer->attemptToContactAt(RR,_localAddress,a,now); + 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->attemptToContactAt(RR,_localAddress,a,now); + } else { + TRACE("ignoring contact for %s at %s -- too many per scope",peer->address().toString().c_str(),a.toString().c_str()); } } } break; diff --git a/node/InetAddress.hpp b/node/InetAddress.hpp index fcbed4b1..5e5eb06e 100644 --- a/node/InetAddress.hpp +++ b/node/InetAddress.hpp @@ -42,6 +42,11 @@ namespace ZeroTier { +/** + * Maximum integer value of enum IpScope + */ +#define ZT_INETADDRESS_MAX_SCOPE 7 + /** * Extends sockaddr_storage with friendly C++ methods * @@ -66,7 +71,8 @@ struct InetAddress : public sockaddr_storage * IP address scope * * Note that these values are in ascending order of path preference and - * MUST remain that way or Path must be changed to reflect. + * MUST remain that way or Path must be changed to reflect. Also be sure + * to change ZT_INETADDRESS_MAX_SCOPE if the max changes. */ enum IpScope { -- cgit v1.2.3 From fdc3e103ccc3207c4a00b8476d8635772c6c6dc4 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 28 Oct 2015 09:38:33 -0700 Subject: Cleanup and docs. --- node/InetAddress.hpp | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) (limited to 'node/InetAddress.hpp') diff --git a/node/InetAddress.hpp b/node/InetAddress.hpp index 5e5eb06e..c4d5cfda 100644 --- a/node/InetAddress.hpp +++ b/node/InetAddress.hpp @@ -52,8 +52,8 @@ namespace ZeroTier { * * This is basically a "mixin" for sockaddr_storage. It adds methods and * operators, but does not modify the structure. This can be cast to/from - * sockaddr_storage and used interchangeably. Don't change this as it's - * used in a few places. + * sockaddr_storage and used interchangeably. DO NOT change this by e.g. + * adding non-static fields, since much code depends on this identity. */ struct InetAddress : public sockaddr_storage { @@ -326,7 +326,7 @@ struct InetAddress : public sockaddr_storage inline bool isV6() const throw() { return (ss_family == AF_INET6); } /** - * @return pointer to raw IP address bytes + * @return pointer to raw address bytes or NULL if not available */ inline const void *rawIpData() const throw() @@ -338,19 +338,6 @@ struct InetAddress : public sockaddr_storage } } - /** - * @return pointer to raw IP address bytes - */ - inline void *rawIpData() - throw() - { - switch(ss_family) { - case AF_INET: return (void *)&(reinterpret_cast(this)->sin_addr.s_addr); - case AF_INET6: return (void *)(reinterpret_cast(this)->sin6_addr.s6_addr); - default: return 0; - } - } - /** * Performs an IP-only comparison or, if that is impossible, a memcmp() * -- cgit v1.2.3 From c9e6e60c091db9274bcd550f53d4387793593528 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 3 Nov 2015 10:46:41 -0800 Subject: Handle ZeroTier-RFC4193 IPv6 address NDP queries inline by spoofing responses if the addressing scheme matches -- this allows multicast-free instant lookup of local IPv6 for better performance and reliability. --- node/InetAddress.hpp | 2 +- node/Switch.cpp | 85 ++++++++++++++++++++++++++++++++++++++++++++-------- selftest.cpp | 1 + 3 files changed, 74 insertions(+), 14 deletions(-) (limited to 'node/InetAddress.hpp') diff --git a/node/InetAddress.hpp b/node/InetAddress.hpp index c4d5cfda..74efc943 100644 --- a/node/InetAddress.hpp +++ b/node/InetAddress.hpp @@ -411,7 +411,7 @@ struct InetAddress : public sockaddr_storage // TODO: Ethernet address (but accept for forward compatibility) return 7; case 0x02: - // TODO: Bluetooth address (but accept for forward compatibility) + // TODO: Bluetooth address (but accept for forward compatibility) return 7; case 0x03: // TODO: Other address types (but accept for forward compatibility) diff --git a/node/Switch.cpp b/node/Switch.cpp index b7a9c522..97befbc6 100644 --- a/node/Switch.cpp +++ b/node/Switch.cpp @@ -154,25 +154,84 @@ void Switch::onLocalEthernet(const SharedPtr &network,const MAC &from,c MulticastGroup mg(to,0); if (to.isBroadcast()) { - if ( - (etherType == ZT_ETHERTYPE_ARP)&& - (len >= 28)&& - ( - (((const unsigned char *)data)[2] == 0x08)&& - (((const unsigned char *)data)[3] == 0x00)&& - (((const unsigned char *)data)[4] == 6)&& - (((const unsigned char *)data)[5] == 4)&& - (((const unsigned char *)data)[7] == 0x01) - ) - ) { - // Cram IPv4 IP into ADI field to make IPv4 ARP broadcast channel specific and scalable - // Also: enableBroadcast() does not apply to ARP since it's required for IPv4 + if ( (etherType == ZT_ETHERTYPE_ARP) && (len >= 28) && ((((const uint8_t *)data)[2] == 0x08)&&(((const uint8_t *)data)[3] == 0x00)&&(((const uint8_t *)data)[4] == 6)&&(((const uint8_t *)data)[5] == 4)&&(((const uint8_t *)data)[7] == 0x01)) ) { + /* IPv4 ARP is one of the few special cases that we impose upon what is + * otherwise a straightforward Ethernet switch emulation. Vanilla ARP + * is dumb old broadcast and simply doesn't scale. ZeroTier multicast + * groups have an additional field called ADI (additional distinguishing + * information) which was added specifically for ARP though it could + * be used for other things too. We then take ARP broadcasts and turn + * them into multicasts by stuffing the IP address being queried into + * the 32-bit ADI field. In practice this uses our multicast pub/sub + * system to implement a kind of extended/distributed ARP table. */ mg = MulticastGroup::deriveMulticastGroupForAddressResolution(InetAddress(((const unsigned char *)data) + 24,4,0)); } else if (!nconf->enableBroadcast()) { // Don't transmit broadcasts if this network doesn't want them TRACE("%.16llx: dropped broadcast since ff:ff:ff:ff:ff:ff is not enabled",network->id()); return; } + } else if ((etherType == ZT_ETHERTYPE_IPV6)&&(len >= (40 + 8 + 16))) { + /* IPv6 NDP emulation on ZeroTier-RFC4193 addressed networks! This allows + * for multicast-free operation in IPv6 networks, which both improves + * performance and is friendlier to mobile and (especially) IoT devices. + * In the future there may be a no-multicast build option for embedded + * and IoT use and this will be the preferred addressing mode. Note that + * it plays nice with our L2 emulation philosophy and even with bridging. + * While "real" devices behind the bridge can't have ZT-RFC4193 addresses + * themselves, they can look these addresses up with NDP and it will + * work just fine. */ + if ((reinterpret_cast(data)[6] == 0x3a)&&(reinterpret_cast(data)[40] == 0x87)) { // ICMPv6 neighbor solicitation + for(std::vector::const_iterator sip(nconf->staticIps().begin()),sipend(nconf->staticIps().end());sip!=sipend;++sip) { + if ((sip->ss_family == AF_INET6)&&(Utils::ntoh((uint16_t)reinterpret_cast(&(*sip))->sin6_port) == 88)) { + const uint8_t *my6 = reinterpret_cast(reinterpret_cast(&(*sip))->sin6_addr.s6_addr); + if ((my6[0] == 0xfd)&&(my6[9] == 0x99)&&(my6[10] == 0x93)) { // ZT-RFC4193 == fd__:____:____:____:__99:93__:____:____ / 88 + const uint8_t *pkt6 = reinterpret_cast(data) + 40 + 8; + unsigned int ptr = 0; + while (ptr != 11) { + if (pkt6[ptr] != my6[ptr]) + break; + ++ptr; + } + if (ptr == 11) { // /88 matches an assigned address on this network + const Address atPeer(pkt6 + ptr,5); + if (atPeer != RR->identity.address()) { + const MAC atPeerMac(atPeer,network->id()); + TRACE("ZT-RFC4193 NDP emulation: %.16llx: forging response for %s/%s",network->id(),atPeer.toString().c_str(),atPeerMac.toString().c_str()); + + uint8_t adv[72]; + adv[0] = 0x60; adv[1] = 0x00; adv[2] = 0x00; adv[3] = 0x00; + adv[4] = 0x00; adv[5] = 0x20; + adv[6] = 0x3a; adv[7] = 0xff; + for(int i=0;i<16;++i) adv[8 + i] = pkt6[i]; + for(int i=0;i<16;++i) adv[24 + i] = my6[i]; + adv[40] = 0x88; adv[41] = 0x00; + adv[42] = 0x00; adv[43] = 0x00; // future home of checksum + adv[44] = 0x60; adv[45] = 0x00; adv[46] = 0x00; adv[47] = 0x00; + for(int i=0;i<16;++i) adv[48 + i] = pkt6[i]; + adv[64] = 0x02; adv[65] = 0x01; + adv[66] = atPeerMac[0]; adv[67] = atPeerMac[1]; adv[68] = atPeerMac[2]; adv[69] = atPeerMac[3]; adv[70] = atPeerMac[4]; adv[71] = atPeerMac[5]; + + uint16_t pseudo_[36]; + uint8_t *const pseudo = reinterpret_cast(pseudo_); + for(int i=0;i<32;++i) pseudo[i] = adv[8 + i]; + pseudo[32] = 0x00; pseudo[33] = 0x00; pseudo[34] = 0x00; pseudo[35] = 0x20; + pseudo[36] = 0x00; pseudo[37] = 0x00; pseudo[38] = 0x00; pseudo[39] = 0x3a; + for(int i=0;i<32;++i) pseudo[40 + i] = adv[40 + i]; + uint32_t checksum = 0; + for(int i=0;i<36;++i) checksum += Utils::hton(pseudo_[i]); + while ((checksum >> 16)) checksum = (checksum & 0xffff) + (checksum >> 16); + checksum = ~checksum; + adv[42] = (checksum >> 8) & 0xff; + adv[43] = checksum & 0xff; + + RR->node->putFrame(network->id(),atPeerMac,from,ZT_ETHERTYPE_IPV6,0,adv,72); + return; // stop processing: we have handled this frame with a spoofed local reply so no need to send it anywhere + } + } + } + } + } + } } /* Learn multicast groups for bridged-in hosts. diff --git a/selftest.cpp b/selftest.cpp index 0787925f..fa8df48b 100644 --- a/selftest.cpp +++ b/selftest.cpp @@ -41,6 +41,7 @@ #include "node/InetAddress.hpp" #include "node/Utils.hpp" #include "node/Identity.hpp" +#include "node/Buffer.hpp" #include "node/Packet.hpp" #include "node/Salsa20.hpp" #include "node/MAC.hpp" -- cgit v1.2.3 From 8ac411a9e41ce62a6a6340f693ba70a59550df75 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Thu, 12 Nov 2015 10:47:50 -0800 Subject: Stashing utun work for OSX -- abandoning for now since utun excludes mDNS among other issues. --- attic/OSXEthernetTap.cpp.utun-work-in-progress | 832 +++++++++++++++++++++++++ attic/OSXEthernetTap.hpp.utun-work-in-progress | 101 +++ node/InetAddress.cpp | 41 +- node/InetAddress.hpp | 18 +- 4 files changed, 969 insertions(+), 23 deletions(-) create mode 100644 attic/OSXEthernetTap.cpp.utun-work-in-progress create mode 100644 attic/OSXEthernetTap.hpp.utun-work-in-progress (limited to 'node/InetAddress.hpp') diff --git a/attic/OSXEthernetTap.cpp.utun-work-in-progress b/attic/OSXEthernetTap.cpp.utun-work-in-progress new file mode 100644 index 00000000..b7a05334 --- /dev/null +++ b/attic/OSXEthernetTap.cpp.utun-work-in-progress @@ -0,0 +1,832 @@ +/* + * ZeroTier One - Network Virtualization Everywhere + * Copyright (C) 2011-2015 ZeroTier, Inc. + * + * 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 +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// OSX compile fix... in6_var defines this in a struct which namespaces it for C++ ... why?!? +struct prf_ra { + u_char onlink : 1; + u_char autonomous : 1; + u_char reserved : 6; +} prf_ra; + +#include +#include + +// These are KERNEL_PRIVATE... why? +#ifndef SIOCAUTOCONF_START +#define SIOCAUTOCONF_START _IOWR('i', 132, struct in6_ifreq) /* accept rtadvd on this interface */ +#endif +#ifndef SIOCAUTOCONF_STOP +#define SIOCAUTOCONF_STOP _IOWR('i', 133, struct in6_ifreq) /* stop accepting rtadv for this interface */ +#endif + +// -------------------------------------------------------------------------- +// -------------------------------------------------------------------------- +// This source is from: +// http://www.opensource.apple.com/source/Libinfo/Libinfo-406.17/gen.subproj/getifmaddrs.c?txt +// It's here because OSX 10.6 does not have this convenience function. + +#define SALIGN (sizeof(uint32_t) - 1) +#define SA_RLEN(sa) ((sa)->sa_len ? (((sa)->sa_len + SALIGN) & ~SALIGN) : \ +(SALIGN + 1)) +#define MAX_SYSCTL_TRY 5 +#define RTA_MASKS (RTA_GATEWAY | RTA_IFP | RTA_IFA) + +/* FreeBSD uses NET_RT_IFMALIST and RTM_NEWMADDR from */ +/* We can use NET_RT_IFLIST2 and RTM_NEWMADDR2 on Darwin */ +//#define DARWIN_COMPAT + +//#ifdef DARWIN_COMPAT +#define GIM_SYSCTL_MIB NET_RT_IFLIST2 +#define GIM_RTM_ADDR RTM_NEWMADDR2 +//#else +//#define GIM_SYSCTL_MIB NET_RT_IFMALIST +//#define GIM_RTM_ADDR RTM_NEWMADDR +//#endif + +// Not in 10.6 includes so use our own +struct _intl_ifmaddrs { + struct _intl_ifmaddrs *ifma_next; + struct sockaddr *ifma_name; + struct sockaddr *ifma_addr; + struct sockaddr *ifma_lladdr; +}; + +static inline int _intl_getifmaddrs(struct _intl_ifmaddrs **pif) +{ + int icnt = 1; + int dcnt = 0; + int ntry = 0; + size_t len; + size_t needed; + int mib[6]; + int i; + char *buf; + char *data; + char *next; + char *p; + struct ifma_msghdr2 *ifmam; + struct _intl_ifmaddrs *ifa, *ift; + struct rt_msghdr *rtm; + struct sockaddr *sa; + + mib[0] = CTL_NET; + mib[1] = PF_ROUTE; + mib[2] = 0; /* protocol */ + mib[3] = 0; /* wildcard address family */ + mib[4] = GIM_SYSCTL_MIB; + mib[5] = 0; /* no flags */ + do { + if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) + return (-1); + if ((buf = (char *)malloc(needed)) == NULL) + return (-1); + if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) { + if (errno != ENOMEM || ++ntry >= MAX_SYSCTL_TRY) { + free(buf); + return (-1); + } + free(buf); + buf = NULL; + } + } while (buf == NULL); + + for (next = buf; next < buf + needed; next += rtm->rtm_msglen) { + rtm = (struct rt_msghdr *)(void *)next; + if (rtm->rtm_version != RTM_VERSION) + continue; + switch (rtm->rtm_type) { + case GIM_RTM_ADDR: + ifmam = (struct ifma_msghdr2 *)(void *)rtm; + if ((ifmam->ifmam_addrs & RTA_IFA) == 0) + break; + icnt++; + p = (char *)(ifmam + 1); + for (i = 0; i < RTAX_MAX; i++) { + if ((RTA_MASKS & ifmam->ifmam_addrs & + (1 << i)) == 0) + continue; + sa = (struct sockaddr *)(void *)p; + len = SA_RLEN(sa); + dcnt += len; + p += len; + } + break; + } + } + + data = (char *)malloc(sizeof(struct _intl_ifmaddrs) * icnt + dcnt); + if (data == NULL) { + free(buf); + return (-1); + } + + ifa = (struct _intl_ifmaddrs *)(void *)data; + data += sizeof(struct _intl_ifmaddrs) * icnt; + + memset(ifa, 0, sizeof(struct _intl_ifmaddrs) * icnt); + ift = ifa; + + for (next = buf; next < buf + needed; next += rtm->rtm_msglen) { + rtm = (struct rt_msghdr *)(void *)next; + if (rtm->rtm_version != RTM_VERSION) + continue; + + switch (rtm->rtm_type) { + case GIM_RTM_ADDR: + ifmam = (struct ifma_msghdr2 *)(void *)rtm; + if ((ifmam->ifmam_addrs & RTA_IFA) == 0) + break; + + p = (char *)(ifmam + 1); + for (i = 0; i < RTAX_MAX; i++) { + if ((RTA_MASKS & ifmam->ifmam_addrs & + (1 << i)) == 0) + continue; + sa = (struct sockaddr *)(void *)p; + len = SA_RLEN(sa); + switch (i) { + case RTAX_GATEWAY: + ift->ifma_lladdr = + (struct sockaddr *)(void *)data; + memcpy(data, p, len); + data += len; + break; + + case RTAX_IFP: + ift->ifma_name = + (struct sockaddr *)(void *)data; + memcpy(data, p, len); + data += len; + break; + + case RTAX_IFA: + ift->ifma_addr = + (struct sockaddr *)(void *)data; + memcpy(data, p, len); + data += len; + break; + + default: + data += len; + break; + } + p += len; + } + ift->ifma_next = ift + 1; + ift = ift->ifma_next; + break; + } + } + + free(buf); + + if (ift > ifa) { + ift--; + ift->ifma_next = NULL; + *pif = ifa; + } else { + *pif = NULL; + free(ifa); + } + return (0); +} + +static inline void _intl_freeifmaddrs(struct _intl_ifmaddrs *ifmp) +{ + free(ifmp); +} + +// -------------------------------------------------------------------------- +// -------------------------------------------------------------------------- + +#include +#include +#include +#include + +#include "../node/Constants.hpp" +#include "../node/Utils.hpp" +#include "../node/Mutex.hpp" +#include "../node/Dictionary.hpp" +#include "Arp.hpp" +#include "OSUtils.hpp" +#include "OSXEthernetTap.hpp" + +// ff:ff:ff:ff:ff:ff with no ADI +static const ZeroTier::MulticastGroup _blindWildcardMulticastGroup(ZeroTier::MAC(0xff),0); + +static inline bool _setIpv6Stuff(const char *ifname,bool performNUD,bool acceptRouterAdverts) +{ + struct in6_ndireq nd; + struct in6_ifreq ifr; + + int s = socket(AF_INET6,SOCK_DGRAM,0); + if (s <= 0) + return false; + + memset(&nd,0,sizeof(nd)); + strncpy(nd.ifname,ifname,sizeof(nd.ifname)); + + if (ioctl(s,SIOCGIFINFO_IN6,&nd)) { + close(s); + return false; + } + + unsigned long oldFlags = (unsigned long)nd.ndi.flags; + + if (performNUD) + nd.ndi.flags |= ND6_IFF_PERFORMNUD; + else nd.ndi.flags &= ~ND6_IFF_PERFORMNUD; + + if (oldFlags != (unsigned long)nd.ndi.flags) { + if (ioctl(s,SIOCSIFINFO_FLAGS,&nd)) { + close(s); + return false; + } + } + + memset(&ifr,0,sizeof(ifr)); + strncpy(ifr.ifr_name,ifname,sizeof(ifr.ifr_name)); + if (ioctl(s,acceptRouterAdverts ? SIOCAUTOCONF_START : SIOCAUTOCONF_STOP,&ifr)) { + close(s); + return false; + } + + close(s); + return true; +} + +// Create an OSX-native utun device (utun# where # is desiredNumber) +// Adapted from public domain utun example code by Jonathan Levin +static int _make_utun(int desiredNumber) +{ + struct sockaddr_ctl sc; + struct ctl_info ctlInfo; + struct ifreq ifr; + + memset(&ctlInfo, 0, sizeof(ctlInfo)); + if (strlcpy(ctlInfo.ctl_name, UTUN_CONTROL_NAME, sizeof(ctlInfo.ctl_name)) >= sizeof(ctlInfo.ctl_name)) { + return -1; + } + + int fd = socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL); + if (fd == -1) + return -1; + if (ioctl(fd, CTLIOCGINFO, &ctlInfo) == -1) { + close(fd); + return -1; + } + + sc.sc_id = ctlInfo.ctl_id; + sc.sc_len = sizeof(sc); + sc.sc_family = AF_SYSTEM; + sc.ss_sysaddr = AF_SYS_CONTROL; + sc.sc_unit = desiredNumber + 1; + + if (connect(fd, (struct sockaddr *)&sc, sizeof(sc)) == -1) { + close(fd); + return -1; + } + + memset(&ifr,0,sizeof(ifr)); + sprintf(ifr.ifr_name,"utun%d",desiredNumber); + if (ioctl(fd,SIOCGIFFLAGS,(void *)&ifr) < 0) { + printf("SIOCGIFFLAGS failed\n"); + } + ifr.ifr_flags &= ~IFF_POINTOPOINT; + if (ioctl(fd,SIOCSIFFLAGS,(void *)&ifr) < 0) { + printf("clear IFF_POINTOPOINT failed\n"); + } + + return fd; +} + +namespace ZeroTier { + +static long globalTapsRunning = 0; +static Mutex globalTapCreateLock; + +OSXEthernetTap::OSXEthernetTap( + const char *homePath, + const MAC &mac, + unsigned int mtu, + unsigned int metric, + uint64_t nwid, + const char *friendlyName, + void (*handler)(void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *data,unsigned int len), + void *arg) : + _handler(handler), + _arg(arg), + _arp((Arp *)0), + _nwid(nwid), + _homePath(homePath), + _mtu(mtu), + _metric(metric), + _fd(0), + _utun(false), + _enabled(true) +{ + char devpath[64],ethaddr[64],mtustr[32],metstr[32],nwids[32]; + struct stat stattmp; + + Utils::snprintf(nwids,sizeof(nwids),"%.16llx",nwid); + + if (mtu > 2800) + throw std::runtime_error("max tap MTU is 2800"); + + Mutex::Lock _gl(globalTapCreateLock); + + // Read remembered previous device name, if any -- we'll try to reuse + Dictionary devmap; + std::string desiredDevice; + { + std::string devmapbuf; + if (OSUtils::readFile((_homePath + ZT_PATH_SEPARATOR_S + "devicemap").c_str(),devmapbuf)) { + devmap.fromString(devmapbuf); + desiredDevice = devmap.get(nwids,""); + } + } + + if (::stat((_homePath + ZT_PATH_SEPARATOR_S + "tap.kext").c_str(),&stattmp) == 0) { + // Try to init kext if it's there, otherwise revert to utun mode + + if (::stat("/dev/zt0",&stattmp)) { + long kextpid = (long)vfork(); + if (kextpid == 0) { + ::chdir(homePath); + OSUtils::redirectUnixOutputs("/dev/null",(const char *)0); + ::execl("/sbin/kextload","/sbin/kextload","-q","-repository",homePath,"tap.kext",(const char *)0); + ::_exit(-1); + } else if (kextpid > 0) { + int exitcode = -1; + ::waitpid(kextpid,&exitcode,0); + } + ::usleep(500); // give tap device driver time to start up and try again + if (::stat("/dev/zt0",&stattmp)) + _utun = true; + } + + if (!_utun) { + // See if we can re-use the last device we had. + bool recalledDevice = false; + if (desiredDevice.length() > 2) { + Utils::snprintf(devpath,sizeof(devpath),"/dev/%s",desiredDevice.c_str()); + if (stat(devpath,&stattmp) == 0) { + _fd = ::open(devpath,O_RDWR); + if (_fd > 0) { + _dev = desiredDevice; + recalledDevice = true; + } + } + } + + // Open the first unused tap device if we didn't recall a previous one. + if (!recalledDevice) { + for(int i=0;i<64;++i) { + Utils::snprintf(devpath,sizeof(devpath),"/dev/zt%d",i); + if (stat(devpath,&stattmp)) { + _utun = true; + break; + } + _fd = ::open(devpath,O_RDWR); + if (_fd > 0) { + char foo[16]; + Utils::snprintf(foo,sizeof(foo),"zt%d",i); + _dev = foo; + break; + } + } + } + if (_fd <= 0) + _utun = true; + } + } else { + _utun = true; + } + + if (_utun) { + // Use OSX built-in utun device if kext is not available or doesn't work + + int utunNo = 0; + + if ((desiredDevice.length() > 4)&&(desiredDevice.substr(0,4) == "utun")) { + utunNo = Utils::strToInt(desiredDevice.substr(4).c_str()); + if (utunNo >= 0) + _fd = _make_utun(utunNo); + } + + if (_fd <= 0) { + // Start at utun8 to leave lower utuns unused since other stuff might + // want them -- OpenVPN, cjdns, etc. I'm not sure if those are smart + // enough to scan upward like this. + for(utunNo=8;utunNo<=256;++utunNo) { + if ((_fd = _make_utun(utunNo)) > 0) + break; + } + } + + if (_fd <= 0) + throw std::runtime_error("unable to find/load ZeroTier tap driver OR use built-in utun driver in OSX; permission or system problem or too many open devices?"); + + Utils::snprintf(devpath,sizeof(devpath),"utun%d",utunNo); + _dev = devpath; + + // Configure address and bring it up + Utils::snprintf(mtustr,sizeof(mtustr),"%u",_mtu); + Utils::snprintf(metstr,sizeof(metstr),"%u",_metric); + long cpid = (long)vfork(); + if (cpid == 0) { + ::execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),"mtu",mtustr,"metric",metstr,"up",(const char *)0); + ::_exit(-1); + } else if (cpid > 0) { + int exitcode = -1; + ::waitpid(cpid,&exitcode,0); + if (exitcode) { + ::close(_fd); + throw std::runtime_error("ifconfig failure activating utun interface"); + } + } + + } else { + // Use our ZeroTier OSX tun/tap driver for zt# Ethernet tap device + + if (fcntl(_fd,F_SETFL,fcntl(_fd,F_GETFL) & ~O_NONBLOCK) == -1) { + ::close(_fd); + throw std::runtime_error("unable to set flags on file descriptor for TAP device"); + } + + // Configure MAC address and MTU, bring interface up + Utils::snprintf(ethaddr,sizeof(ethaddr),"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",(int)mac[0],(int)mac[1],(int)mac[2],(int)mac[3],(int)mac[4],(int)mac[5]); + Utils::snprintf(mtustr,sizeof(mtustr),"%u",_mtu); + Utils::snprintf(metstr,sizeof(metstr),"%u",_metric); + long cpid = (long)vfork(); + if (cpid == 0) { + ::execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),"lladdr",ethaddr,"mtu",mtustr,"metric",metstr,"up",(const char *)0); + ::_exit(-1); + } else if (cpid > 0) { + int exitcode = -1; + ::waitpid(cpid,&exitcode,0); + if (exitcode) { + ::close(_fd); + throw std::runtime_error("ifconfig failure setting link-layer address and activating tap interface"); + } + } + + _setIpv6Stuff(_dev.c_str(),true,false); + } + + // Set close-on-exec so that devices cannot persist if we fork/exec for update + fcntl(_fd,F_SETFD,fcntl(_fd,F_GETFD) | FD_CLOEXEC); + + ::pipe(_shutdownSignalPipe); + + ++globalTapsRunning; + + devmap[nwids] = _dev; + OSUtils::writeFile((_homePath + ZT_PATH_SEPARATOR_S + "devicemap").c_str(),devmap.toString()); + + _thread = Thread::start(this); +} + +OSXEthernetTap::~OSXEthernetTap() +{ + Mutex::Lock _gl(globalTapCreateLock); + + ::write(_shutdownSignalPipe[1],(const void *)this,1); // writing a byte causes thread to exit + Thread::join(_thread); + + ::close(_fd); + ::close(_shutdownSignalPipe[0]); + ::close(_shutdownSignalPipe[1]); + + if (_utun) { + delete _arp; + } else { + if (--globalTapsRunning <= 0) { + globalTapsRunning = 0; // sanity check -- should not be possible + + char tmp[16384]; + sprintf(tmp,"%s/%s",_homePath.c_str(),"tap.kext"); + long kextpid = (long)vfork(); + if (kextpid == 0) { + OSUtils::redirectUnixOutputs("/dev/null",(const char *)0); + ::execl("/sbin/kextunload","/sbin/kextunload",tmp,(const char *)0); + ::_exit(-1); + } else if (kextpid > 0) { + int exitcode = -1; + ::waitpid(kextpid,&exitcode,0); + } + } + } +} + +void OSXEthernetTap::setEnabled(bool en) +{ + _enabled = en; + // TODO: interface status change +} + +bool OSXEthernetTap::enabled() const +{ + return _enabled; +} + +static bool ___removeIp(const std::string &_dev,const InetAddress &ip) +{ + long cpid = (long)vfork(); + if (cpid == 0) { + execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),"inet",ip.toIpString().c_str(),"-alias",(const char *)0); + _exit(-1); + } else if (cpid > 0) { + int exitcode = -1; + waitpid(cpid,&exitcode,0); + return (exitcode == 0); + } + return false; // never reached, make compiler shut up about return value +} + +bool OSXEthernetTap::addIp(const InetAddress &ip) +{ + if (!ip) + return false; + + std::vector allIps(ips()); + if (std::binary_search(allIps.begin(),allIps.end(),ip)) + return true; + + // Remove and reconfigure if address is the same but netmask is different + for(std::vector::iterator i(allIps.begin());i!=allIps.end();++i) { + if ((i->ipsEqual(ip))&&(i->netmaskBits() != ip.netmaskBits())) { + if (___removeIp(_dev,*i)) + break; + } + } + + if (_utun) { + long cpid = (long)vfork(); + if (cpid == 0) { + if (ip.ss_family == AF_INET6) { + ::execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),"inet6",ip.toString().c_str(),"alias",(const char *)0); + } else { + ::execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),ip.toString().c_str(),ip.toIpString().c_str(),"alias",(const char *)0); + } + ::_exit(-1); + } else if (cpid > 0) { + int exitcode = -1; + ::waitpid(cpid,&exitcode,0); + + if (exitcode == 0) { + if (ip.ss_family == AF_INET) { + // Add route to network over tun for IPv4 -- otherwise it behaves + // as a simple point to point tunnel instead of a true route. + cpid = (long)vfork(); + if (cpid == 0) { + ::close(STDERR_FILENO); + ::close(STDOUT_FILENO); + ::execl("/sbin/route","/sbin/route","add",ip.network().toString().c_str(),ip.toIpString().c_str(),(const char *)0); + ::exit(-1); + } else if (cpid > 0) { + int exitcode = -1; + ::waitpid(cpid,&exitcode,0); + return (exitcode == 0); + } + } else return true; + } + } + } else { + long cpid = (long)vfork(); + if (cpid == 0) { + ::execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),ip.isV4() ? "inet" : "inet6",ip.toString().c_str(),"alias",(const char *)0); + ::_exit(-1); + } else if (cpid > 0) { + int exitcode = -1; + ::waitpid(cpid,&exitcode,0); + return (exitcode == 0); + } + } + + return false; +} + +bool OSXEthernetTap::removeIp(const InetAddress &ip) +{ + if (!ip) + return true; + std::vector allIps(ips()); + if (!std::binary_search(allIps.begin(),allIps.end(),ip)) { + if (___removeIp(_dev,ip)) + return true; + } + return false; +} + +std::vector OSXEthernetTap::ips() const +{ + struct ifaddrs *ifa = (struct ifaddrs *)0; + if (getifaddrs(&ifa)) + return std::vector(); + + std::vector r; + + struct ifaddrs *p = ifa; + while (p) { + if ((!strcmp(p->ifa_name,_dev.c_str()))&&(p->ifa_addr)&&(p->ifa_netmask)&&(p->ifa_addr->sa_family == p->ifa_netmask->sa_family)) { + switch(p->ifa_addr->sa_family) { + case AF_INET: { + struct sockaddr_in *sin = (struct sockaddr_in *)p->ifa_addr; + struct sockaddr_in *nm = (struct sockaddr_in *)p->ifa_netmask; + r.push_back(InetAddress(&(sin->sin_addr.s_addr),4,Utils::countBits((uint32_t)nm->sin_addr.s_addr))); + } break; + case AF_INET6: { + struct sockaddr_in6 *sin = (struct sockaddr_in6 *)p->ifa_addr; + struct sockaddr_in6 *nm = (struct sockaddr_in6 *)p->ifa_netmask; + uint32_t b[4]; + memcpy(b,nm->sin6_addr.s6_addr,sizeof(b)); + r.push_back(InetAddress(sin->sin6_addr.s6_addr,16,Utils::countBits(b[0]) + Utils::countBits(b[1]) + Utils::countBits(b[2]) + Utils::countBits(b[3]))); + } break; + } + } + p = p->ifa_next; + } + + if (ifa) + freeifaddrs(ifa); + + std::sort(r.begin(),r.end()); + std::unique(r.begin(),r.end()); + + return r; +} + +void OSXEthernetTap::put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len) +{ + char putBuf[4096]; + if ((_fd > 0)&&(len <= _mtu)&&(_enabled)) { + to.copyTo(putBuf,6); + from.copyTo(putBuf + 6,6); + *((uint16_t *)(putBuf + 12)) = htons((uint16_t)etherType); + memcpy(putBuf + 14,data,len); + len += 14; + ::write(_fd,putBuf,len); + } +} + +std::string OSXEthernetTap::deviceName() const +{ + return _dev; +} + +void OSXEthernetTap::setFriendlyName(const char *friendlyName) +{ +} + +void OSXEthernetTap::scanMulticastGroups(std::vector &added,std::vector &removed) +{ + std::vector newGroups; + + struct _intl_ifmaddrs *ifmap = (struct _intl_ifmaddrs *)0; + if (!_intl_getifmaddrs(&ifmap)) { + struct _intl_ifmaddrs *p = ifmap; + while (p) { + if (p->ifma_addr->sa_family == AF_LINK) { + struct sockaddr_dl *in = (struct sockaddr_dl *)p->ifma_name; + struct sockaddr_dl *la = (struct sockaddr_dl *)p->ifma_addr; + if ((la->sdl_alen == 6)&&(in->sdl_nlen <= _dev.length())&&(!memcmp(_dev.data(),in->sdl_data,in->sdl_nlen))) + newGroups.push_back(MulticastGroup(MAC(la->sdl_data + la->sdl_nlen,6),0)); + } + p = p->ifma_next; + } + _intl_freeifmaddrs(ifmap); + } + + std::vector allIps(ips()); + for(std::vector::iterator ip(allIps.begin());ip!=allIps.end();++ip) + newGroups.push_back(MulticastGroup::deriveMulticastGroupForAddressResolution(*ip)); + + std::sort(newGroups.begin(),newGroups.end()); + std::unique(newGroups.begin(),newGroups.end()); + + for(std::vector::iterator m(newGroups.begin());m!=newGroups.end();++m) { + if (!std::binary_search(_multicastGroups.begin(),_multicastGroups.end(),*m)) + added.push_back(*m); + } + for(std::vector::iterator m(_multicastGroups.begin());m!=_multicastGroups.end();++m) { + if (!std::binary_search(newGroups.begin(),newGroups.end(),*m)) + removed.push_back(*m); + } + + _multicastGroups.swap(newGroups); +} + +void OSXEthernetTap::threadMain() + throw() +{ + fd_set readfds,nullfds; + MAC to,from; + int n,nfds,r; + char getBuf[8194]; + + Thread::sleep(500); + + FD_ZERO(&readfds); + FD_ZERO(&nullfds); + nfds = (int)std::max(_shutdownSignalPipe[0],_fd) + 1; + + r = 0; + for(;;) { + FD_SET(_shutdownSignalPipe[0],&readfds); + FD_SET(_fd,&readfds); + select(nfds,&readfds,&nullfds,&nullfds,(struct timeval *)0); + + if (FD_ISSET(_shutdownSignalPipe[0],&readfds)) // writes to shutdown pipe terminate thread + break; + + if (FD_ISSET(_fd,&readfds)) { + n = (int)::read(_fd,getBuf + r,sizeof(getBuf) - r); + if (n < 0) { + if ((errno != EINTR)&&(errno != ETIMEDOUT)) + break; + } else { + // Some tap drivers like to send the ethernet frame and the + // payload in two chunks, so handle that by accumulating + // data until we have at least a frame. + r += n; + if (r > 14) { + if (r > ((int)_mtu + 14)) // sanity check for weird TAP behavior on some platforms + r = _mtu + 14; + + if (_enabled) { + to.setTo(getBuf,6); + from.setTo(getBuf + 6,6); + unsigned int etherType = ntohs(((const uint16_t *)getBuf)[6]); + // TODO: VLAN support + _handler(_arg,_nwid,from,to,etherType,0,(const void *)(getBuf + 14),r - 14); + } + + r = 0; + } + } + } + } +} + +} // namespace ZeroTier diff --git a/attic/OSXEthernetTap.hpp.utun-work-in-progress b/attic/OSXEthernetTap.hpp.utun-work-in-progress new file mode 100644 index 00000000..8ece87b3 --- /dev/null +++ b/attic/OSXEthernetTap.hpp.utun-work-in-progress @@ -0,0 +1,101 @@ +/* + * ZeroTier One - Network Virtualization Everywhere + * Copyright (C) 2011-2015 ZeroTier, Inc. + * + * 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_OSXETHERNETTAP_HPP +#define ZT_OSXETHERNETTAP_HPP + +#include +#include + +#include +#include +#include + +#include "../node/Constants.hpp" +#include "../node/MAC.hpp" +#include "../node/InetAddress.hpp" +#include "../node/MulticastGroup.hpp" + +#include "Thread.hpp" + +namespace ZeroTier { + +class Arp; + +/** + * OSX Ethernet tap supporting either ZeroTier tun/tap kext or OSX-native utun + */ +class OSXEthernetTap +{ +public: + OSXEthernetTap( + const char *homePath, + const MAC &mac, + unsigned int mtu, + unsigned int metric, + uint64_t nwid, + const char *friendlyName, + void (*handler)(void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int), + void *arg); + + ~OSXEthernetTap(); + + void setEnabled(bool en); + bool enabled() const; + bool addIp(const InetAddress &ip); + bool removeIp(const InetAddress &ip); + std::vector ips() const; + void put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len); + std::string deviceName() const; + void setFriendlyName(const char *friendlyName); + void scanMulticastGroups(std::vector &added,std::vector &removed); + + inline bool isNativeUtun() const { return _utun; } + + void threadMain() + throw(); + +private: + void (*_handler)(void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int); + void *_arg; + Arp *_arp; // created and used if utun is enabled + uint64_t _nwid; + Thread _thread; + std::string _homePath; + std::string _dev; + std::vector _multicastGroups; + unsigned int _mtu; + unsigned int _metric; + int _fd; + int _shutdownSignalPipe[2]; + bool _utun; + volatile bool _enabled; +}; + +} // namespace ZeroTier + +#endif diff --git a/node/InetAddress.cpp b/node/InetAddress.cpp index abb46240..f35eb9c3 100644 --- a/node/InetAddress.cpp +++ b/node/InetAddress.cpp @@ -234,7 +234,6 @@ void InetAddress::fromString(const std::string &ipSlashPort) } InetAddress InetAddress::netmask() const - throw() { InetAddress r(*this); switch(r.ss_family) { @@ -242,36 +241,40 @@ InetAddress InetAddress::netmask() const reinterpret_cast(&r)->sin_addr.s_addr = Utils::hton((uint32_t)(0xffffffff << (32 - netmaskBits()))); break; case AF_INET6: { - unsigned char *bf = reinterpret_cast(reinterpret_cast(&r)->sin6_addr.s6_addr); - signed int bitsLeft = (signed int)netmaskBits(); - for(unsigned int i=0;i<16;++i) { - if (bitsLeft > 0) { - bf[i] |= (unsigned char)((bitsLeft >= 8) ? 0x00 : (0xff >> bitsLeft)); - bitsLeft -= 8; - } - } + uint64_t nm[2]; + const unsigned int bits = netmaskBits(); + nm[0] = Utils::hton((uint64_t)((bits >= 64) ? 0xffffffffffffffffULL : (0xffffffffffffffffULL << (64 - bits)))); + nm[1] = Utils::hton((uint64_t)((bits <= 64) ? 0ULL : (0xffffffffffffffffULL << (128 - bits)))); + memcpy(reinterpret_cast(&r)->sin6_addr.s6_addr,nm,16); } break; } return r; } InetAddress InetAddress::broadcast() const - throw() +{ + if (ss_family == AF_INET) { + InetAddress r(*this); + reinterpret_cast(&r)->sin_addr.s_addr |= Utils::hton((uint32_t)(0xffffffff >> netmaskBits())); + return r; + } + return InetAddress(); +} + +InetAddress InetAddress::network() const { InetAddress r(*this); switch(r.ss_family) { case AF_INET: - reinterpret_cast(&r)->sin_addr.s_addr |= Utils::hton((uint32_t)(0xffffffff >> netmaskBits())); + reinterpret_cast(&r)->sin_addr.s_addr &= Utils::hton((uint32_t)(0xffffffff << (32 - netmaskBits()))); break; case AF_INET6: { - unsigned char *bf = reinterpret_cast(reinterpret_cast(&r)->sin6_addr.s6_addr); - signed int bitsLeft = (signed int)netmaskBits(); - for(unsigned int i=0;i<16;++i) { - if (bitsLeft > 0) { - bf[i] |= (unsigned char)((bitsLeft >= 8) ? 0x00 : (0xff >> bitsLeft)); - bitsLeft -= 8; - } - } + uint64_t nm[2]; + const unsigned int bits = netmaskBits(); + memcpy(nm,reinterpret_cast(&r)->sin6_addr.s6_addr,16); + nm[0] &= Utils::hton((uint64_t)((bits >= 64) ? 0xffffffffffffffffULL : (0xffffffffffffffffULL << (64 - bits)))); + nm[1] &= Utils::hton((uint64_t)((bits <= 64) ? 0ULL : (0xffffffffffffffffULL << (128 - bits)))); + memcpy(reinterpret_cast(&r)->sin6_addr.s6_addr,nm,16); } break; } return r; diff --git a/node/InetAddress.hpp b/node/InetAddress.hpp index 74efc943..2573e694 100644 --- a/node/InetAddress.hpp +++ b/node/InetAddress.hpp @@ -303,17 +303,27 @@ struct InetAddress : public sockaddr_storage /** * Construct a full netmask as an InetAddress + * + * @return Netmask such as 255.255.255.0 if this address is /24 (port field will be unchanged) */ - InetAddress netmask() const - throw(); + InetAddress netmask() const; /** * Constructs a broadcast address from a network/netmask address * + * This is only valid for IPv4 and will return a NULL InetAddress for other + * address families. + * * @return Broadcast address (only IP portion is meaningful) */ - InetAddress broadcast() const - throw(); + InetAddress broadcast() const; + + /** + * Return the network -- a.k.a. the IP ANDed with the netmask + * + * @return Network e.g. 10.0.1.0/24 from 10.0.1.200/24 + */ + InetAddress network() const; /** * @return True if this is an IPv4 address -- cgit v1.2.3