diff --git a/controller/EmbeddedNetworkController.cpp b/controller/EmbeddedNetworkController.cpp index 29dd8ad70..ff2f34ec3 100644 --- a/controller/EmbeddedNetworkController.cpp +++ b/controller/EmbeddedNetworkController.cpp @@ -144,7 +144,6 @@ static json _renderRule(ZT_VirtualNetworkRule &rule) r["type"] = "ACTION_REDIRECT"; r["address"] = Address(rule.v.fwd.address).toString(); r["flags"] = (unsigned int)rule.v.fwd.flags; - r["length"] = (unsigned int)rule.v.fwd.length; break; case ZT_NETWORK_RULE_ACTION_DEBUG_LOG: r["type"] = "ACTION_DEBUG_LOG"; @@ -308,7 +307,6 @@ static bool _parseRule(json &r,ZT_VirtualNetworkRule &rule) rule.t |= ZT_NETWORK_RULE_ACTION_REDIRECT; rule.v.fwd.address = Utils::hexStrToU64(_jS(r["zt"],"0").c_str()) & 0xffffffffffULL; rule.v.fwd.flags = (uint32_t)(_jI(r["flags"],0ULL) & 0xffffffffULL); - rule.v.fwd.length = (uint16_t)(_jI(r["length"],0ULL) & 0xffffULL); return true; } else if (t == "ACTION_DEBUG_LOG") { rule.t |= ZT_NETWORK_RULE_ACTION_DEBUG_LOG; diff --git a/node/Capability.hpp b/node/Capability.hpp index 8e749e809..e23d79435 100644 --- a/node/Capability.hpp +++ b/node/Capability.hpp @@ -178,7 +178,7 @@ public: b.append((uint8_t)14); b.append((uint64_t)rules[i].v.fwd.address); b.append((uint32_t)rules[i].v.fwd.flags); - b.append((uint16_t)rules[i].v.fwd.length); + b.append((uint16_t)rules[i].v.fwd.length); // unused for redirect break; case ZT_NETWORK_RULE_MATCH_SOURCE_ZEROTIER_ADDRESS: case ZT_NETWORK_RULE_MATCH_DEST_ZEROTIER_ADDRESS: diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index c75125d34..4b0130783 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -560,7 +560,7 @@ bool IncomingPacket::_doFRAME(const RuntimeEnvironment *RR,const SharedPtr const MAC sourceMac(peer->address(),network->id()); const unsigned int frameLen = size() - ZT_PROTO_VERB_FRAME_IDX_PAYLOAD; const uint8_t *const frameData = reinterpret_cast(data()) + ZT_PROTO_VERB_FRAME_IDX_PAYLOAD; - if (network->filterIncomingPacket(peer,RR->identity.address(),sourceMac,network->mac(),frameData,frameLen,etherType,0)) + if (network->filterIncomingPacket(peer,RR->identity.address(),sourceMac,network->mac(),frameData,frameLen,etherType,0) > 0) RR->node->putFrame(network->id(),network->userPtr(),sourceMac,network->mac(),etherType,0,(const void *)frameData,frameLen); peer->received(_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_FRAME,0,Packet::VERB_NOP,true); } @@ -625,7 +625,7 @@ bool IncomingPacket::_doEXT_FRAME(const RuntimeEnvironment *RR,const SharedPtr

node->putFrame(network->id(),network->userPtr(),from,to,etherType,0,(const void *)frameData,frameLen); break; @@ -981,7 +981,7 @@ bool IncomingPacket::_doMULTICAST_FRAME(const RuntimeEnvironment *RR,const Share } const uint8_t *const frameData = (const uint8_t *)field(offset + ZT_PROTO_VERB_MULTICAST_FRAME_IDX_FRAME,frameLen); - if (network->filterIncomingPacket(peer,RR->identity.address(),from,to.mac(),frameData,frameLen,etherType,0)) { + if (network->filterIncomingPacket(peer,RR->identity.address(),from,to.mac(),frameData,frameLen,etherType,0) > 0) { RR->node->putFrame(network->id(),network->userPtr(),from,to.mac(),etherType,0,(const void *)frameData,frameLen); } } diff --git a/node/Network.cpp b/node/Network.cpp index f8b7c1d52..5a9b07cf1 100644 --- a/node/Network.cpp +++ b/node/Network.cpp @@ -76,15 +76,14 @@ static const char *_rtn(const ZT_VirtualNetworkRuleType rt) default: return "BAD_RULE_TYPE"; } } -static const void _dumpFilterTrace(const char *ruleName,uint8_t thisSetMatches,bool noRedirect,bool inbound,const Address &ztSource,const Address &ztDest,const MAC &macSource,const MAC &macDest,const std::vector &dlog,unsigned int frameLen,unsigned int etherType,const char *msg) +static const void _dumpFilterTrace(const char *ruleName,uint8_t thisSetMatches,bool inbound,const Address &ztSource,const Address &ztDest,const MAC &macSource,const MAC &macDest,const std::vector &dlog,unsigned int frameLen,unsigned int etherType,const char *msg) { static volatile unsigned long cnt = 0; - printf("%.6lu %c %s inbound=%d noRedirect=%d frameLen=%u etherType=%u" ZT_EOL_S, + printf("%.6lu %c %s %s frameLen=%u etherType=%u" ZT_EOL_S, cnt++, ((thisSetMatches) ? 'Y' : '.'), ruleName, - (int)inbound, - (int)noRedirect, + ((inbound) ? "INBOUND" : "OUTBOUND"), frameLen, etherType ); @@ -143,14 +142,20 @@ static bool _ipv6GetPayload(const uint8_t *frameData,unsigned int frameLen,unsig return false; // overflow == invalid } -// 0 == no match, -1 == match/drop, 1 == match/accept, 2 == match/accept even if bridged -static int _doZtFilter( +enum _doZtFilterResult +{ + DOZTFILTER_NO_MATCH = 0, + DOZTFILTER_DROP = 1, + DOZTFILTER_REDIRECT = 2, + DOZTFILTER_ACCEPT = 3, + DOZTFILTER_SUPER_ACCEPT = 4 +}; +static _doZtFilterResult _doZtFilter( const RuntimeEnvironment *RR, - const bool noRedirect, const NetworkConfig &nconf, const bool inbound, const Address &ztSource, - const Address &ztDest, + Address &ztDest, // MUTABLE const MAC &macSource, const MAC &macDest, const uint8_t *const frameData, @@ -163,7 +168,9 @@ static int _doZtFilter( const unsigned int localTagCount, const uint32_t *const remoteTagIds, const uint32_t *const remoteTagValues, - const unsigned int remoteTagCount) + const unsigned int remoteTagCount, + Address &cc, // MUTABLE + unsigned int &ccLength) // MUTABLE { // For each set of rules we start by assuming that they match (since no constraints // yields a 'match all' rule). @@ -181,75 +188,83 @@ static int _doZtFilter( case ZT_NETWORK_RULE_ACTION_DROP: if (thisSetMatches) { #ifdef ZT_RULES_ENGINE_DEBUGGING - _dumpFilterTrace("ACTION_DROP",thisSetMatches,noRedirect,inbound,ztSource,ztDest,macSource,macDest,dlog,frameLen,etherType,(const char *)0); + _dumpFilterTrace("ACTION_DROP",thisSetMatches,inbound,ztSource,ztDest,macSource,macDest,dlog,frameLen,etherType,(const char *)0); #endif // ZT_RULES_ENGINE_DEBUGGING - return -1; // match, drop packet + return DOZTFILTER_DROP; } else { #ifdef ZT_RULES_ENGINE_DEBUGGING - _dumpFilterTrace("ACTION_DROP",thisSetMatches,noRedirect,inbound,ztSource,ztDest,macSource,macDest,dlog,frameLen,etherType,(const char *)0); + _dumpFilterTrace("ACTION_DROP",thisSetMatches,inbound,ztSource,ztDest,macSource,macDest,dlog,frameLen,etherType,(const char *)0); dlog.clear(); #endif // ZT_RULES_ENGINE_DEBUGGING - thisSetMatches = 1; // no match, evaluate next set + thisSetMatches = 1; } continue; + case ZT_NETWORK_RULE_ACTION_ACCEPT: if (thisSetMatches) { #ifdef ZT_RULES_ENGINE_DEBUGGING - _dumpFilterTrace("ACTION_ACCEPT",thisSetMatches,noRedirect,inbound,ztSource,ztDest,macSource,macDest,dlog,frameLen,etherType,(const char *)0); + _dumpFilterTrace("ACTION_ACCEPT",thisSetMatches,inbound,ztSource,ztDest,macSource,macDest,dlog,frameLen,etherType,(const char *)0); #endif // ZT_RULES_ENGINE_DEBUGGING - return 1; // match, accept packet + return DOZTFILTER_ACCEPT; // match, accept packet } else { #ifdef ZT_RULES_ENGINE_DEBUGGING - _dumpFilterTrace("ACTION_ACCEPT",thisSetMatches,noRedirect,inbound,ztSource,ztDest,macSource,macDest,dlog,frameLen,etherType,(const char *)0); + _dumpFilterTrace("ACTION_ACCEPT",thisSetMatches,inbound,ztSource,ztDest,macSource,macDest,dlog,frameLen,etherType,(const char *)0); dlog.clear(); #endif // ZT_RULES_ENGINE_DEBUGGING - thisSetMatches = 1; // no match, evaluate next set + thisSetMatches = 1; } continue; + case ZT_NETWORK_RULE_ACTION_TEE: case ZT_NETWORK_RULE_ACTION_REDIRECT: { const Address fwdAddr(rules[rn].v.fwd.address); - if (fwdAddr == RR->identity.address()) { - // If we are the TEE or REDIRECT destination, don't TEE or REDIRECT - // to self. We should also accept here instead of interpreting - // REDIRECT as DROP since we are the destination. + if (fwdAddr == ztSource) { #ifdef ZT_RULES_ENGINE_DEBUGGING - _dumpFilterTrace(_rtn(rt),thisSetMatches,noRedirect,inbound,ztSource,ztDest,macSource,macDest,dlog,frameLen,etherType,"TEE/REDIRECT resulted in 'super-accept' since we are destination"); + _dumpFilterTrace(_rtn(rt),thisSetMatches,inbound,ztSource,ztDest,macSource,macDest,dlog,frameLen,etherType,"TEE/REDIRECT ignored since source is target"); #endif // ZT_RULES_ENGINE_DEBUGGING - return 2; // we should "super-accept" this packet since we are the TEE or REDIRECT destination - } else { - if (!noRedirect) { - Packet outp(fwdAddr,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) { + thisSetMatches = 1; + } else if (fwdAddr == RR->identity.address()) { + if (inbound) { #ifdef ZT_RULES_ENGINE_DEBUGGING - _dumpFilterTrace("ACTION_REDIRECT",thisSetMatches,noRedirect,inbound,ztSource,ztDest,macSource,macDest,dlog,frameLen,etherType,(noRedirect) ? "second-pass match, not actually redirecting" : (const char *)0); + _dumpFilterTrace(_rtn(rt),thisSetMatches,inbound,ztSource,ztDest,macSource,macDest,dlog,frameLen,etherType,"TEE/REDIRECT interpreted as super-accept since we are target"); #endif // ZT_RULES_ENGINE_DEBUGGING - return -1; // match, drop packet (we redirected it) + return DOZTFILTER_SUPER_ACCEPT; } else { #ifdef ZT_RULES_ENGINE_DEBUGGING - _dumpFilterTrace("ACTION_TEE",thisSetMatches,noRedirect,inbound,ztSource,ztDest,macSource,macDest,dlog,frameLen,etherType,(noRedirect) ? "second-pass match, not actually teeing" : (const char *)0); + _dumpFilterTrace(_rtn(rt),thisSetMatches,inbound,ztSource,ztDest,macSource,macDest,dlog,frameLen,etherType,"TEE/REDIRECT ignored on outbound since we are target"); +#endif // ZT_RULES_ENGINE_DEBUGGING + thisSetMatches = 1; + } + } else if (fwdAddr == ztDest) { +#ifdef ZT_RULES_ENGINE_DEBUGGING + _dumpFilterTrace(_rtn(rt),thisSetMatches,inbound,ztSource,ztDest,macSource,macDest,dlog,frameLen,etherType,"TEE/REDIRECT ignored since destination is target"); +#endif // ZT_RULES_ENGINE_DEBUGGING + thisSetMatches = 1; + } else { + if (rt == ZT_NETWORK_RULE_ACTION_REDIRECT) { +#ifdef ZT_RULES_ENGINE_DEBUGGING + _dumpFilterTrace("ACTION_REDIRECT",thisSetMatches,inbound,ztSource,ztDest,macSource,macDest,dlog,frameLen,etherType,(const char *)0); +#endif // ZT_RULES_ENGINE_DEBUGGING + ztDest = fwdAddr; + return DOZTFILTER_REDIRECT; + } else { +#ifdef ZT_RULES_ENGINE_DEBUGGING + _dumpFilterTrace("ACTION_TEE",thisSetMatches,inbound,ztSource,ztDest,macSource,macDest,dlog,frameLen,etherType,(const char *)0); dlog.clear(); #endif // ZT_RULES_ENGINE_DEBUGGING - thisSetMatches = 1; // TEE does not terminate evaluation + cc = fwdAddr; + ccLength = (rules[rn].v.fwd.length != 0) ? ((frameLen < (unsigned int)rules[rn].v.fwd.length) ? frameLen : (unsigned int)rules[rn].v.fwd.length) : frameLen; + thisSetMatches = 1; } } } continue; + case ZT_NETWORK_RULE_ACTION_DEBUG_LOG: // a no-op target specifically for debugging purposes #ifdef ZT_RULES_ENGINE_DEBUGGING - _dumpFilterTrace("ACTION_DEBUG_LOG",thisSetMatches,noRedirect,inbound,ztSource,ztDest,macSource,macDest,dlog,frameLen,etherType,(const char *)0); + _dumpFilterTrace("ACTION_DEBUG_LOG",thisSetMatches,inbound,ztSource,ztDest,macSource,macDest,dlog,frameLen,etherType,(const char *)0); dlog.clear(); #endif // ZT_RULES_ENGINE_DEBUGGING - thisSetMatches = 1; // DEBUG_LOG does not terminate evaluation + thisSetMatches = 1; continue; default: break; @@ -547,7 +562,7 @@ static int _doZtFilter( thisSetMatches &= (thisRuleMatches ^ ((rules[rn].t >> 7) & 1)); } - return 0; + return DOZTFILTER_NO_MATCH; } const ZeroTier::MulticastGroup Network::BROADCAST(ZeroTier::MAC(0xffffffffffffULL),0); @@ -614,7 +629,7 @@ Network::~Network() } bool Network::filterOutgoingPacket( - const bool noRedirect, + const bool noTee, const Address &ztSource, const Address &ztDest, const MAC &macSource, @@ -626,39 +641,94 @@ bool Network::filterOutgoingPacket( { uint32_t remoteTagIds[ZT_MAX_NETWORK_TAGS]; uint32_t remoteTagValues[ZT_MAX_NETWORK_TAGS]; + Address ztDest2(ztDest); + Address cc; + unsigned int ccLength = 0; + bool mainRuleTableMatch = false; + bool accept = false; Mutex::Lock _l(_lock); Membership &m = _memberships[ztDest]; const unsigned int remoteTagCount = m.getAllTags(_config,remoteTagIds,remoteTagValues,ZT_MAX_NETWORK_TAGS); - 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)) { - case -1: - if (ztDest) - m.sendCredentialsIfNeeded(RR,RR->node->now(),ztDest,_config,(const Capability *)0); + switch(_doZtFilter(RR,_config,false,ztSource,ztDest2,macSource,macDest,frameData,frameLen,etherType,vlanId,_config.rules,_config.ruleCount,_config.tags,_config.tagCount,remoteTagIds,remoteTagValues,remoteTagCount,cc,ccLength)) { + case DOZTFILTER_NO_MATCH: + break; + case DOZTFILTER_DROP: return false; - case 1: - case 2: - if (ztDest) - m.sendCredentialsIfNeeded(RR,RR->node->now(),ztDest,_config,(const Capability *)0); - return true; + case DOZTFILTER_REDIRECT: // interpreted as ACCEPT but ztDest2 will have been changed in _doZtFilter() + case DOZTFILTER_ACCEPT: + case DOZTFILTER_SUPER_ACCEPT: // no difference in behavior on outbound side + mainRuleTableMatch = true; + accept = true; + break; } - for(unsigned int c=0;c<_config.capabilityCount;++c) { - 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)) { - case -1: - if (ztDest) - m.sendCredentialsIfNeeded(RR,RR->node->now(),ztDest,_config,(const Capability *)0); - return false; - case 1: - case 2: - if (ztDest) - m.sendCredentialsIfNeeded(RR,RR->node->now(),ztDest,_config,&(_config.capabilities[c])); - return true; + const Capability *relevantCap = (const Capability *)0; + if (!mainRuleTableMatch) { + for(unsigned int c=0;c<_config.capabilityCount;++c) { + ztDest2 = ztDest; // sanity check + Address cc2; + unsigned int ccLength2 = 0; + switch (_doZtFilter(RR,_config,false,ztSource,ztDest2,macSource,macDest,frameData,frameLen,etherType,vlanId,_config.capabilities[c].rules(),_config.capabilities[c].ruleCount(),_config.tags,_config.tagCount,remoteTagIds,remoteTagValues,remoteTagCount,cc2,ccLength2)) { + case DOZTFILTER_NO_MATCH: + case DOZTFILTER_DROP: // explicit DROP in a capability just terminates its evaluation and is an anti-pattern + break; + case DOZTFILTER_REDIRECT: // interpreted as ACCEPT but ztDest2 will have been changed in _doZtFilter() + case DOZTFILTER_ACCEPT: + case DOZTFILTER_SUPER_ACCEPT: // no difference in behavior on outbound side + if ((!noTee)&&(cc2)) { + Packet outp(cc2,RR->identity.address(),Packet::VERB_EXT_FRAME); + outp.append(_id); + outp.append((uint8_t)0x02); // TEE/REDIRECT from outbound side: 0x02 + macDest.appendTo(outp); + macSource.appendTo(outp); + outp.append((uint16_t)etherType); + outp.append(frameData,ccLength2); + outp.compress(); + RR->sw->send(outp,true); + } + relevantCap = &(_config.capabilities[c]); + accept = true; + break; + } + if (accept) + break; } } - return false; + if (accept) { + if (ztDest2) + m.sendCredentialsIfNeeded(RR,RR->node->now(),ztDest2,_config,relevantCap); + + if ((!noTee)&&(cc)) { + Packet outp(cc,RR->identity.address(),Packet::VERB_EXT_FRAME); + outp.append(_id); + outp.append((uint8_t)0x02); // TEE/REDIRECT from outbound side: 0x02 + macDest.appendTo(outp); + macSource.appendTo(outp); + outp.append((uint16_t)etherType); + outp.append(frameData,ccLength); + outp.compress(); + RR->sw->send(outp,true); + } + + if (ztDest != ztDest2) { + Packet outp(ztDest2,RR->identity.address(),Packet::VERB_EXT_FRAME); + outp.append(_id); + outp.append((uint8_t)0x02); // TEE/REDIRECT from outbound side: 0x02 + macDest.appendTo(outp); + macSource.appendTo(outp); + outp.append((uint16_t)etherType); + outp.append(frameData,frameLen); + outp.compress(); + RR->sw->send(outp,true); + return false; // DROP locally, since we redirected + } + } + + return accept; } int Network::filterIncomingPacket( @@ -673,29 +743,97 @@ int Network::filterIncomingPacket( { uint32_t remoteTagIds[ZT_MAX_NETWORK_TAGS]; uint32_t remoteTagValues[ZT_MAX_NETWORK_TAGS]; + Address ztDest2(ztDest); + Address cc; + unsigned int ccLength = 0; + bool mainRuleTableMatch = false; + int accept = 0; Mutex::Lock _l(_lock); Membership &m = _memberships[ztDest]; const unsigned int remoteTagCount = m.getAllTags(_config,remoteTagIds,remoteTagValues,ZT_MAX_NETWORK_TAGS); - 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)) { - case -1: return 0; // DROP - case 1: return 1; // ACCEPT - case 2: return 2; // super-ACCEPT + switch (_doZtFilter(RR,_config,true,sourcePeer->address(),ztDest2,macSource,macDest,frameData,frameLen,etherType,vlanId,_config.rules,_config.ruleCount,_config.tags,_config.tagCount,remoteTagIds,remoteTagValues,remoteTagCount,cc,ccLength)) { + case DOZTFILTER_NO_MATCH: + break; + case DOZTFILTER_DROP: + return 0; // DROP + case DOZTFILTER_REDIRECT: // interpreted as ACCEPT but ztDest2 will have been changed in _doZtFilter() + case DOZTFILTER_ACCEPT: + mainRuleTableMatch = true; + accept = 1; // ACCEPT + break; + case DOZTFILTER_SUPER_ACCEPT: + mainRuleTableMatch = true; + accept = 2; // super-ACCEPT + break; } - Membership::CapabilityIterator mci(m); - const Capability *c; - while ((c = mci.next(_config))) { - switch(_doZtFilter(RR,false,_config,true,sourcePeer->address(),ztDest,macSource,macDest,frameData,frameLen,etherType,vlanId,c->rules(),c->ruleCount(),_config.tags,_config.tagCount,remoteTagIds,remoteTagValues,remoteTagCount)) { - case -1: return 0; // DROP - case 1: return 1; // ACCEPT - case 2: return 2; // super-ACCEPT + if (!mainRuleTableMatch) { + Membership::CapabilityIterator mci(m); + const Capability *c; + while ((c = mci.next(_config))) { + ztDest2 = ztDest; // sanity check + Address cc2; + unsigned int ccLength2 = 0; + switch(_doZtFilter(RR,_config,true,sourcePeer->address(),ztDest2,macSource,macDest,frameData,frameLen,etherType,vlanId,c->rules(),c->ruleCount(),_config.tags,_config.tagCount,remoteTagIds,remoteTagValues,remoteTagCount,cc2,ccLength2)) { + case DOZTFILTER_NO_MATCH: + case DOZTFILTER_DROP: // explicit DROP in a capability just terminates its evaluation and is an anti-pattern + break; + case DOZTFILTER_REDIRECT: // interpreted as ACCEPT but ztDest will have been changed in _doZtFilter() + case DOZTFILTER_ACCEPT: + accept = 1; // ACCEPT + break; + case DOZTFILTER_SUPER_ACCEPT: + accept = 2; // super-ACCEPT + break; + } + if (accept) { + if (cc2) { + Packet outp(cc2,RR->identity.address(),Packet::VERB_EXT_FRAME); + outp.append(_id); + outp.append((uint8_t)0x06); // TEE/REDIRECT from inbound side: 0x06 + macDest.appendTo(outp); + macSource.appendTo(outp); + outp.append((uint16_t)etherType); + outp.append(frameData,ccLength2); + outp.compress(); + RR->sw->send(outp,true); + } + break; + } } } - return 0; // DROP + if (accept) { + if (cc) { + Packet outp(cc,RR->identity.address(),Packet::VERB_EXT_FRAME); + outp.append(_id); + outp.append((uint8_t)0x06); // TEE/REDIRECT from inbound side: 0x06 + macDest.appendTo(outp); + macSource.appendTo(outp); + outp.append((uint16_t)etherType); + outp.append(frameData,ccLength); + outp.compress(); + RR->sw->send(outp,true); + } + + if (ztDest != ztDest2) { + Packet outp(ztDest2,RR->identity.address(),Packet::VERB_EXT_FRAME); + outp.append(_id); + outp.append((uint8_t)0x06); // TEE/REDIRECT from inbound side: 0x06 + macDest.appendTo(outp); + macSource.appendTo(outp); + outp.append((uint16_t)etherType); + outp.append(frameData,frameLen); + outp.compress(); + RR->sw->send(outp,true); + return 0; // DROP locally, since we redirected + } + } + + return accept; } bool Network::subscribedToMulticastGroup(const MulticastGroup &mg,bool includeBridgedGroups) const diff --git a/node/Network.hpp b/node/Network.hpp index aa4b67f8f..45a51bf27 100644 --- a/node/Network.hpp +++ b/node/Network.hpp @@ -82,11 +82,10 @@ public: * Apply filters to an outgoing packet * * This applies filters from our network config and, if that doesn't match, - * our capabilities in ascending order of capability ID. If there is a match - * certain actions may be taken such as pushing credentials to ztDest and - * sending a copy of the packet to a TEE or REDIRECT target. + * our capabilities in ascending order of capability ID. Additional actions + * such as TEE may be taken, and credentials may be pushed. * - * @param noRedirect If true, do not TEE or REDIRECT -- this is set for secondary filtrations done in multicast and bridge send paths + * @param noTee If true, do not TEE anything anywhere * @param ztSource Source ZeroTier address * @param ztDest Destination ZeroTier address * @param macSource Ethernet layer source address @@ -95,10 +94,10 @@ public: * @param frameLen Ethernet frame payload length * @param etherType 16-bit ethernet type ID * @param vlanId 16-bit VLAN ID - * @return True if packet should be sent to destination peer + * @return True if packet should be sent, false if dropped or redirected */ bool filterOutgoingPacket( - const bool noRedirect, + const bool noTee, const Address &ztSource, const Address &ztDest, const MAC &macSource, diff --git a/node/OutboundMulticast.cpp b/node/OutboundMulticast.cpp index 6b583e7cf..33c28f65f 100644 --- a/node/OutboundMulticast.cpp +++ b/node/OutboundMulticast.cpp @@ -86,10 +86,11 @@ void OutboundMulticast::init( void OutboundMulticast::sendOnly(const RuntimeEnvironment *RR,const Address &toAddr) { const SharedPtr nw(RR->node->network(_nwid)); - if ((nw)&&(nw->filterOutgoingPacket(true,RR->identity.address(),toAddr,_macSrc,_macDest,_frameData,_frameLen,_etherType,0))) { + Address toAddr2(toAddr); + if ((nw)&&(nw->filterOutgoingPacket(true,RR->identity.address(),toAddr2,_macSrc,_macDest,_frameData,_frameLen,_etherType,0))) { //TRACE(">>MC %.16llx -> %s",(unsigned long long)this,toAddr.toString().c_str()); _packet.newInitializationVector(); - _packet.setDestination(toAddr); + _packet.setDestination(toAddr2); RR->sw->send(_packet,true); } } diff --git a/node/Packet.hpp b/node/Packet.hpp index 570bace9a..27e289fdc 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -655,9 +655,8 @@ public: * * Flags: * 0x01 - Certificate of network membership attached (DEPRECATED) - * 0x02 - Packet is a TEE'd packet - * 0x04 - Packet is a REDIRECT'ed packet - * 0x08 - TEE/REDIRECT'ed packet is on inbound side of connection + * 0x02 - This is a TEE'd or REDIRECT'ed packet + * 0x04 - TEE/REDIRECT'ed packet is from inbound side * * An extended frame carries full MAC addressing, making them a * superset of VERB_FRAME. They're used for bridging or when we diff --git a/node/Switch.cpp b/node/Switch.cpp index 546c91574..8e41c89f7 100644 --- a/node/Switch.cpp +++ b/node/Switch.cpp @@ -437,10 +437,7 @@ 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); - // 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. + // First pass sets noTee to false, but noTee is set to true in OutboundMulticast to prevent duplicates. 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;