diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h index 906edef20..736280a78 100644 --- a/include/ZeroTierOne.h +++ b/include/ZeroTierOne.h @@ -169,6 +169,16 @@ extern "C" { */ #define ZT_RULE_PACKET_CHARACTERISTICS_INBOUND 0x8000000000000000ULL +/** + * Packet characteristics flag: multicast or broadcast destination MAC + */ +#define ZT_RULE_PACKET_CHARACTERISTICS_MULTICAST 0x4000000000000000ULL + +/** + * Packet characteristics flag: broadcast destination MAC + */ +#define ZT_RULE_PACKET_CHARACTERISTICS_BROADCAST 0x2000000000000000ULL + /** * Packet characteristics flag: TCP left-most reserved bit */ diff --git a/node/Membership.cpp b/node/Membership.cpp index 969032ff3..59058a3e1 100644 --- a/node/Membership.cpp +++ b/node/Membership.cpp @@ -81,8 +81,7 @@ bool Membership::sendCredentialsIfNeeded(const RuntimeEnvironment *RR,const uint int Membership::addCredential(const RuntimeEnvironment *RR,const CertificateOfMembership &com) { - if (com.issuedTo() != RR->identity.address()) - return -1; + TRACE("addCredential(COM) for %.16llx signed by %s issued to %s",com.networkId(),com.signedBy().toString().c_str(),com.issuedTo().toString().c_str()); if (_com == com) return 0; const int vr = com.verify(RR); @@ -93,8 +92,6 @@ int Membership::addCredential(const RuntimeEnvironment *RR,const CertificateOfMe int Membership::addCredential(const RuntimeEnvironment *RR,const Tag &tag) { - if (tag.issuedTo() != RR->identity.address()) - return -1; TState *t = _tags.get(tag.id()); if ((t)&&(t->lastReceived != 0)&&(t->tag == tag)) return 0; @@ -112,8 +109,6 @@ int Membership::addCredential(const RuntimeEnvironment *RR,const Tag &tag) int Membership::addCredential(const RuntimeEnvironment *RR,const Capability &cap) { - if (cap.issuedTo() != RR->identity.address()) - return -1; std::map::iterator c(_caps.find(cap.id())); if ((c != _caps.end())&&(c->second.lastReceived != 0)&&(c->second.cap == cap)) return 0; diff --git a/node/Network.cpp b/node/Network.cpp index 97341ee27..ecbacd936 100644 --- a/node/Network.cpp +++ b/node/Network.cpp @@ -102,6 +102,7 @@ static bool _ipv6GetPayload(const uint8_t *frameData,unsigned int frameLen,unsig // 0 == no match, -1 == match/drop, 1 == match/accept static int _doZtFilter( const RuntimeEnvironment *RR, + const bool noRedirect, const NetworkConfig &nconf, const bool inbound, const Address &ztSource, @@ -154,15 +155,17 @@ static int _doZtFilter( break; case ZT_NETWORK_RULE_ACTION_TEE: case ZT_NETWORK_RULE_ACTION_REDIRECT: { - Packet outp(Address(rules[rn].v.fwd.address),RR->identity.address(),Packet::VERB_EXT_FRAME); - outp.append(nconf.networkId); - outp.append((uint8_t)( ((rt == ZT_NETWORK_RULE_ACTION_REDIRECT) ? 0x04 : 0x02) | (inbound ? 0x08 : 0x00) )); - macDest.appendTo(outp); - macSource.appendTo(outp); - outp.append((uint16_t)etherType); - outp.append(frameData,(rules[rn].v.fwd.length != 0) ? ((frameLen < (unsigned int)rules[rn].v.fwd.length) ? frameLen : (unsigned int)rules[rn].v.fwd.length) : frameLen); - outp.compress(); - RR->sw->send(outp,true); + if (!noRedirect) { + Packet outp(Address(rules[rn].v.fwd.address),RR->identity.address(),Packet::VERB_EXT_FRAME); + outp.append(nconf.networkId); + outp.append((uint8_t)( ((rt == ZT_NETWORK_RULE_ACTION_REDIRECT) ? 0x04 : 0x02) | (inbound ? 0x08 : 0x00) )); + macDest.appendTo(outp); + macSource.appendTo(outp); + outp.append((uint16_t)etherType); + outp.append(frameData,(rules[rn].v.fwd.length != 0) ? ((frameLen < (unsigned int)rules[rn].v.fwd.length) ? frameLen : (unsigned int)rules[rn].v.fwd.length) : frameLen); + outp.compress(); + RR->sw->send(outp,true); + } if (rt == ZT_NETWORK_RULE_ACTION_REDIRECT) { return -1; // match, drop packet (we redirected it) @@ -318,6 +321,8 @@ static int _doZtFilter( break; case ZT_NETWORK_RULE_MATCH_CHARACTERISTICS: { uint64_t cf = (inbound) ? ZT_RULE_PACKET_CHARACTERISTICS_INBOUND : 0ULL; + if (macDest.isMulticast()) cf |= ZT_RULE_PACKET_CHARACTERISTICS_MULTICAST; + if (macDest.isBroadcast()) cf |= ZT_RULE_PACKET_CHARACTERISTICS_BROADCAST; if ((etherType == ZT_ETHERTYPE_IPV4)&&(frameLen >= 20)&&(frameData[9] == 0x06)) { const unsigned int headerLen = 4 * (frameData[0] & 0xf); cf |= (uint64_t)frameData[headerLen + 13]; @@ -456,6 +461,7 @@ Network::~Network() } bool Network::filterOutgoingPacket( + const bool noRedirect, const Address &ztSource, const Address &ztDest, const MAC &macSource, @@ -475,21 +481,23 @@ bool Network::filterOutgoingPacket( Membership &m = _memberships[ztDest]; const unsigned int remoteTagCount = m.getAllTags(_config,remoteTagIds,remoteTagValues,ZT_MAX_NETWORK_TAGS); - switch(_doZtFilter(RR,_config,false,ztSource,ztDest,macSource,macDest,frameData,frameLen,etherType,vlanId,_config.rules,_config.ruleCount,_config.tags,_config.tagCount,remoteTagIds,remoteTagValues,remoteTagCount,relevantLocalTags,relevantLocalTagCount)) { + switch(_doZtFilter(RR,noRedirect,_config,false,ztSource,ztDest,macSource,macDest,frameData,frameLen,etherType,vlanId,_config.rules,_config.ruleCount,_config.tags,_config.tagCount,remoteTagIds,remoteTagValues,remoteTagCount,relevantLocalTags,relevantLocalTagCount)) { case -1: return false; case 1: - m.sendCredentialsIfNeeded(RR,RR->node->now(),ztDest,_config.com,(const Capability *)0,relevantLocalTags,relevantLocalTagCount); + if (ztDest) + m.sendCredentialsIfNeeded(RR,RR->node->now(),ztDest,_config.com,(const Capability *)0,relevantLocalTags,relevantLocalTagCount); return true; } for(unsigned int c=0;c<_config.capabilityCount;++c) { relevantLocalTagCount = 0; - switch (_doZtFilter(RR,_config,false,ztSource,ztDest,macSource,macDest,frameData,frameLen,etherType,vlanId,_config.capabilities[c].rules(),_config.capabilities[c].ruleCount(),_config.tags,_config.tagCount,remoteTagIds,remoteTagValues,remoteTagCount,relevantLocalTags,relevantLocalTagCount)) { + switch (_doZtFilter(RR,noRedirect,_config,false,ztSource,ztDest,macSource,macDest,frameData,frameLen,etherType,vlanId,_config.capabilities[c].rules(),_config.capabilities[c].ruleCount(),_config.tags,_config.tagCount,remoteTagIds,remoteTagValues,remoteTagCount,relevantLocalTags,relevantLocalTagCount)) { case -1: return false; case 1: - m.sendCredentialsIfNeeded(RR,RR->node->now(),ztDest,_config.com,&(_config.capabilities[c]),relevantLocalTags,relevantLocalTagCount); + if (ztDest) + m.sendCredentialsIfNeeded(RR,RR->node->now(),ztDest,_config.com,&(_config.capabilities[c]),relevantLocalTags,relevantLocalTagCount); return true; } } @@ -517,7 +525,7 @@ bool Network::filterIncomingPacket( Membership &m = _memberships[ztDest]; const unsigned int remoteTagCount = m.getAllTags(_config,remoteTagIds,remoteTagValues,ZT_MAX_NETWORK_TAGS); - switch (_doZtFilter(RR,_config,true,sourcePeer->address(),ztDest,macSource,macDest,frameData,frameLen,etherType,vlanId,_config.rules,_config.ruleCount,_config.tags,_config.tagCount,remoteTagIds,remoteTagValues,remoteTagCount,relevantLocalTags,relevantLocalTagCount)) { + switch (_doZtFilter(RR,false,_config,true,sourcePeer->address(),ztDest,macSource,macDest,frameData,frameLen,etherType,vlanId,_config.rules,_config.ruleCount,_config.tags,_config.tagCount,remoteTagIds,remoteTagValues,remoteTagCount,relevantLocalTags,relevantLocalTagCount)) { case -1: return false; case 1: @@ -528,7 +536,7 @@ bool Network::filterIncomingPacket( const Capability *c; while ((c = mci.next(_config))) { relevantLocalTagCount = 0; - switch(_doZtFilter(RR,_config,false,sourcePeer->address(),ztDest,macSource,macDest,frameData,frameLen,etherType,vlanId,c->rules(),c->ruleCount(),_config.tags,_config.tagCount,remoteTagIds,remoteTagValues,remoteTagCount,relevantLocalTags,relevantLocalTagCount)) { + switch(_doZtFilter(RR,false,_config,false,sourcePeer->address(),ztDest,macSource,macDest,frameData,frameLen,etherType,vlanId,c->rules(),c->ruleCount(),_config.tags,_config.tagCount,remoteTagIds,remoteTagValues,remoteTagCount,relevantLocalTags,relevantLocalTagCount)) { case -1: return false; case 1: @@ -698,6 +706,8 @@ void Network::requestConfiguration() return; _lastRequestedConfiguration = RR->node->now(); + const Address ctrl(controller()); + Dictionary rmd; rmd.add(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_VERSION,(uint64_t)ZT_NETWORKCONFIG_VERSION); rmd.add(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_PROTOCOL_VERSION,(uint64_t)ZT_PROTO_VERSION); @@ -710,7 +720,7 @@ void Network::requestConfiguration() rmd.add(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_MAX_NETWORK_TAGS,(uint64_t)ZT_MAX_NETWORK_TAGS); rmd.add(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_FLAGS,(uint64_t)0); - if (controller() == RR->identity.address()) { + if (ctrl == RR->identity.address()) { if (RR->localNetworkController) { NetworkConfig nconf; switch(RR->localNetworkController->doNetworkConfigRequest(InetAddress(),RR->identity,RR->identity,_id,rmd,nconf)) { @@ -732,9 +742,9 @@ void Network::requestConfiguration() } } - TRACE("requesting netconf for network %.16llx from controller %s",(unsigned long long)_id,controller().toString().c_str()); + TRACE("requesting netconf for network %.16llx from controller %s",(unsigned long long)_id,ctrl.toString().c_str()); - Packet outp(controller(),RR->identity.address(),Packet::VERB_NETWORK_CONFIG_REQUEST); + Packet outp(ctrl,RR->identity.address(),Packet::VERB_NETWORK_CONFIG_REQUEST); outp.append((uint64_t)_id); const unsigned int rmdSize = rmd.sizeBytes(); outp.append((uint16_t)rmdSize); diff --git a/node/Network.hpp b/node/Network.hpp index 382b16aa5..c5e7d570a 100644 --- a/node/Network.hpp +++ b/node/Network.hpp @@ -86,6 +86,7 @@ public: * certain actions may be taken such as pushing credentials to ztDest and * sending a copy of the packet to a TEE or REDIRECT target. * + * @param noRedirect If true, do not TEE or REDIRECT -- this is set for secondary filtrations done in multicast and bridge send paths * @param ztSource Source ZeroTier address * @param ztDest Destination ZeroTier address * @param macSource Ethernet layer source address @@ -97,6 +98,7 @@ public: * @return True if packet should be sent to destination peer */ bool filterOutgoingPacket( + const bool noRedirect, const Address &ztSource, const Address &ztDest, const MAC &macSource, diff --git a/node/OutboundMulticast.cpp b/node/OutboundMulticast.cpp index c99529279..6b583e7cf 100644 --- a/node/OutboundMulticast.cpp +++ b/node/OutboundMulticast.cpp @@ -86,7 +86,7 @@ void OutboundMulticast::init( void OutboundMulticast::sendOnly(const RuntimeEnvironment *RR,const Address &toAddr) { const SharedPtr nw(RR->node->network(_nwid)); - if ((nw)&&(nw->filterOutgoingPacket(RR->identity.address(),toAddr,_macSrc,_macDest,_frameData,_frameLen,_etherType,0))) { + if ((nw)&&(nw->filterOutgoingPacket(true,RR->identity.address(),toAddr,_macSrc,_macDest,_frameData,_frameLen,_etherType,0))) { //TRACE(">>MC %.16llx -> %s",(unsigned long long)this,toAddr.toString().c_str()); _packet.newInitializationVector(); _packet.setDestination(toAddr); diff --git a/node/Switch.cpp b/node/Switch.cpp index 37daff272..f6e4d1ab4 100644 --- a/node/Switch.cpp +++ b/node/Switch.cpp @@ -437,7 +437,11 @@ void Switch::onLocalEthernet(const SharedPtr &network,const MAC &from,c //TRACE("%.16llx: MULTICAST %s -> %s %s %u",network->id(),from.toString().c_str(),mg.toString().c_str(),etherTypeName(etherType),len); - if (!network->filterOutgoingPacket(RR->identity.address(),Address(),from,to,(const uint8_t *)data,len,etherType,vlanId)) { + // We filter with a NULL destination ZeroTier address first. Filtrations + // for each ZT destination are also done in OutboundMulticast, but these + // set noRedirect to true. This prevents multiple TEEs and REDIRECTs for + // multicast packets. + if (!network->filterOutgoingPacket(false,RR->identity.address(),Address(),from,to,(const uint8_t *)data,len,etherType,vlanId)) { TRACE("%.16llx: %s -> %s %s packet not sent: filterOutgoingPacket() returned false",network->id(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType)); return; } @@ -452,17 +456,13 @@ void Switch::onLocalEthernet(const SharedPtr &network,const MAC &from,c etherType, data, len); - - return; - } - - if (to[0] == MAC::firstOctetForNetwork(network->id())) { + } else if (to[0] == MAC::firstOctetForNetwork(network->id())) { // Destination is another ZeroTier peer on the same network Address toZT(to.toAddress(network->id())); // since in-network MACs are derived from addresses and network IDs, we can reverse this SharedPtr toPeer(RR->topology->getPeer(toZT)); - if (!network->filterOutgoingPacket(RR->identity.address(),toZT,from,to,(const uint8_t *)data,len,etherType,vlanId)) { + if (!network->filterOutgoingPacket(false,RR->identity.address(),toZT,from,to,(const uint8_t *)data,len,etherType,vlanId)) { TRACE("%.16llx: %s -> %s %s packet not sent: filterOutgoingPacket() returned false",network->id(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType)); return; } @@ -487,13 +487,17 @@ void Switch::onLocalEthernet(const SharedPtr &network,const MAC &from,c } //TRACE("%.16llx: UNICAST: %s -> %s etherType==%s(%.4x) vlanId==%u len==%u fromBridged==%d includeCom==%d",network->id(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType),etherType,vlanId,len,(int)fromBridged,(int)includeCom); - - return; - } - - { + } else { // Destination is bridged behind a remote peer + // We filter with a NULL destination ZeroTier address first. Filtrations + // for each ZT destination are also done below. This is the same rationale + // and design as for multicast. + if (!network->filterOutgoingPacket(false,RR->identity.address(),Address(),from,to,(const uint8_t *)data,len,etherType,vlanId)) { + TRACE("%.16llx: %s -> %s %s packet not sent: filterOutgoingPacket() returned false",network->id(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType)); + return; + } + Address bridges[ZT_MAX_BRIDGE_SPAM]; unsigned int numBridges = 0; @@ -527,16 +531,19 @@ void Switch::onLocalEthernet(const SharedPtr &network,const MAC &from,c } for(unsigned int b=0;b bridgePeer(RR->topology->getPeer(bridges[b])); - Packet outp(bridges[b],RR->identity.address(),Packet::VERB_EXT_FRAME); - outp.append(network->id()); - outp.append((uint8_t)0x00); - to.appendTo(outp); - from.appendTo(outp); - outp.append((uint16_t)etherType); - outp.append(data,len); - outp.compress(); - send(outp,true); + if (network->filterOutgoingPacket(true,RR->identity.address(),bridges[b],from,to,(const uint8_t *)data,len,etherType,vlanId)) { + Packet outp(bridges[b],RR->identity.address(),Packet::VERB_EXT_FRAME); + outp.append(network->id()); + outp.append((uint8_t)0x00); + to.appendTo(outp); + from.appendTo(outp); + outp.append((uint16_t)etherType); + outp.append(data,len); + outp.compress(); + send(outp,true); + } else { + TRACE("%.16llx: %s -> %s %s packet not sent: filterOutgoingPacket() returned false",network->id(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType)); + } } } } diff --git a/one.cpp b/one.cpp index f34429338..3cb6b775e 100644 --- a/one.cpp +++ b/one.cpp @@ -403,7 +403,9 @@ static int cli(int argc,char **argv) } } if (aa.length() == 0) aa = "-"; - out << "200 listnetworks " << n["nwid"].get() << " " << n["name"].get() << " " << n["mac"].get() << " " << n["status"].get() << " " << n["type"].get() << " " << n["portDeviceName"].get() << " " << aa << ZT_EOL_S; + std::string name = n["name"]; + if (name.length() == 0) name = "-"; + out << "200 listnetworks " << n["nwid"].get() << " " << name << " " << n["mac"].get() << " " << n["status"].get() << " " << n["type"].get() << " " << n["portDeviceName"].get() << " " << aa << ZT_EOL_S; } } }