Bridging pretty much ready to test! Got Switch all wired up. Also fix a latent probably-never-triggered bug in MULTICAST_FRAME handling. GitHub issue #68

This commit is contained in:
Adam Ierymenko 2014-06-13 21:06:34 -07:00
parent 5682f0b772
commit 6802da457e
4 changed files with 88 additions and 75 deletions

View file

@ -177,7 +177,8 @@ public:
* @param prefixBits Number of bits in prefix bit pattern * @param prefixBits Number of bits in prefix bit pattern
* @return True if address is within prefix * @return True if address is within prefix
*/ */
inline bool withinMulticastPropagationPrefix(uint64_t prefix,unsigned int prefixBits) inline bool withinMulticastPropagationPrefix(uint64_t prefix,unsigned int prefixBits) const
throw()
{ {
return ((_a & (0xffffffffffffffffULL >> (64 - prefixBits))) == prefix); return ((_a & (0xffffffffffffffffULL >> (64 - prefixBits))) == prefix);
} }

View file

@ -410,6 +410,6 @@ error_no_byte_order_defined;
/** /**
* If there is no known route, spam to up to this many active bridges * If there is no known route, spam to up to this many active bridges
*/ */
#define ZT_MAX_BRIDGE_SPAM 4 #define ZT_MAX_BRIDGE_SPAM 16
#endif #endif

View file

@ -457,9 +457,15 @@ bool PacketDecoder::_doEXT_FRAME(const RuntimeEnvironment *_r,const SharedPtr<Pe
try { try {
SharedPtr<Network> network(_r->nc->network(at<uint64_t>(ZT_PROTO_VERB_EXT_FRAME_IDX_NETWORK_ID))); SharedPtr<Network> network(_r->nc->network(at<uint64_t>(ZT_PROTO_VERB_EXT_FRAME_IDX_NETWORK_ID)));
if (network) { if (network) {
if ((*this)[ZT_PROTO_VERB_EXT_FRAME_IDX_FLAGS] != 0) {
TRACE("dropped EXT_FRAME due to unknown flags");
return true;
}
const MAC to(field(ZT_PROTO_VERB_EXT_FRAME_IDX_TO,ZT_PROTO_VERB_EXT_FRAME_LEN_TO),ZT_PROTO_VERB_EXT_FRAME_LEN_TO); const MAC to(field(ZT_PROTO_VERB_EXT_FRAME_IDX_TO,ZT_PROTO_VERB_EXT_FRAME_LEN_TO),ZT_PROTO_VERB_EXT_FRAME_LEN_TO);
const MAC from(field(ZT_PROTO_VERB_EXT_FRAME_IDX_FROM,ZT_PROTO_VERB_EXT_FRAME_LEN_FROM),ZT_PROTO_VERB_EXT_FRAME_LEN_FROM); const MAC from(field(ZT_PROTO_VERB_EXT_FRAME_IDX_FROM,ZT_PROTO_VERB_EXT_FRAME_LEN_FROM),ZT_PROTO_VERB_EXT_FRAME_LEN_FROM);
unsigned int etherType = at<uint16_t>(ZT_PROTO_VERB_EXT_FRAME_IDX_ETHERTYPE); unsigned int etherType = at<uint16_t>(ZT_PROTO_VERB_EXT_FRAME_IDX_ETHERTYPE);
if (size() > ZT_PROTO_VERB_EXT_FRAME_IDX_PAYLOAD) { if (size() > ZT_PROTO_VERB_EXT_FRAME_IDX_PAYLOAD) {
if ((!from)||(from.isMulticast())||(!to)) { if ((!from)||(from.isMulticast())||(!to)) {
TRACE("dropped EXT_FRAME from %s@%s(%s) to %s: invalid source or destination MAC",from.toString().c_str(),peer->address().toString().c_str(),_remoteAddress.toString().c_str(),to.toString().c_str()); TRACE("dropped EXT_FRAME from %s@%s(%s) to %s: invalid source or destination MAC",from.toString().c_str(),peer->address().toString().c_str(),_remoteAddress.toString().c_str(),to.toString().c_str());
@ -657,46 +663,44 @@ bool PacketDecoder::_doMULTICAST_FRAME(const RuntimeEnvironment *_r,const Shared
if (network) { if (network) {
SharedPtr<NetworkConfig> nconf(network->config2()); SharedPtr<NetworkConfig> nconf(network->config2());
if (nconf) { if (nconf) {
// Learn real maxDepth from netconf
maxDepth = std::min((unsigned int)ZT_MULTICAST_GLOBAL_MAX_DEPTH,nconf->multicastDepth()); maxDepth = std::min((unsigned int)ZT_MULTICAST_GLOBAL_MAX_DEPTH,nconf->multicastDepth());
if (!maxDepth) if (!maxDepth)
maxDepth = ZT_MULTICAST_GLOBAL_MAX_DEPTH; maxDepth = ZT_MULTICAST_GLOBAL_MAX_DEPTH;
if (!network->isAllowed(origin)) { if (!network->isAllowed(origin)) {
TRACE("didn't inject MULTICAST_FRAME from %s(%s) into %.16llx: sender %s not allowed or we don't have a certificate",source().toString().c_str(),_remoteAddress.toString().c_str(),nwid,origin.toString().c_str()); // Papers, please...
// Tell them we need a certificate
Packet outp(source(),_r->identity.address(),Packet::VERB_ERROR); Packet outp(source(),_r->identity.address(),Packet::VERB_ERROR);
outp.append((unsigned char)Packet::VERB_FRAME); outp.append((unsigned char)Packet::VERB_MULTICAST_FRAME);
outp.append(packetId()); outp.append(packetId());
outp.append((unsigned char)Packet::ERROR_NEED_MEMBERSHIP_CERTIFICATE); outp.append((unsigned char)Packet::ERROR_NEED_MEMBERSHIP_CERTIFICATE);
outp.append(nwid); outp.append(nwid);
outp.armor(peer->key(),true); outp.armor(peer->key(),true);
_fromSock->send(_remoteAddress,outp.data(),outp.size()); _fromSock->send(_remoteAddress,outp.data(),outp.size());
TRACE("dropped MULTICAST_FRAME from %s(%s) into %.16llx: sender %s not allowed or we don't have a certificate",source().toString().c_str(),_remoteAddress.toString().c_str(),nwid,origin.toString().c_str());
// We do not terminate here, since if the member just has an out of
// date cert or hasn't sent us a cert yet we still want to propagate
// the message so multicast keeps working downstream.
} else if ((!nconf->permitsBridging(origin))&&(MAC(origin,network->id()) != sourceMac)) {
// This *does* terminate propagation, since it's technically a
// security violation of the network's bridging policy. But if we
// were to keep propagating it wouldn't hurt anything, just waste
// bandwidth as everyone else would reject it too.
#ifdef ZT_TRACE_MULTICAST
Utils::snprintf(mct,sizeof(mct),
"%.16llx %.2u %.3u%s %c %s dropped: bridging not allowed",
guid,
prefix,
depth,
mctdepth,
(_r->topology->amSupernode() ? 'S' : '-'),
_r->identity.address().toString().c_str());
_r->sm->sendUdp(ZT_DEFAULTS.multicastTraceWatcher,mct,strlen(mct));
#endif
TRACE("dropped MULTICAST_FRAME from %s(%s) into %.16llx: source mac %s doesn't belong to %s, and bridging is not supported on network",source().toString().c_str(),_remoteAddress.toString().c_str(),nwid,sourceMac.toString().c_str(),origin.toString().c_str());
return true; return true;
} else if (!nconf->permitsEtherType(etherType)) { }
// Ditto for this-- halt propagation if this is for an ethertype
// this network doesn't allow. Same principle as bridging test. if (MAC(origin,network->id()) != sourceMac) {
if (!nconf->permitsBridging(origin)) {
#ifdef ZT_TRACE_MULTICAST
Utils::snprintf(mct,sizeof(mct),
"%.16llx %.2u %.3u%s %c %s dropped: bridging not allowed",
guid,
prefix,
depth,
mctdepth,
(_r->topology->amSupernode() ? 'S' : '-'),
_r->identity.address().toString().c_str());
_r->sm->sendUdp(ZT_DEFAULTS.multicastTraceWatcher,mct,strlen(mct));
#endif
TRACE("dropped MULTICAST_FRAME from %s(%s) into %.16llx: source mac %s doesn't belong to %s, and bridging is not supported on network",source().toString().c_str(),_remoteAddress.toString().c_str(),nwid,sourceMac.toString().c_str(),origin.toString().c_str());
return true;
}
network->learnBridgeRoute(sourceMac,origin);
}
if (!nconf->permitsEtherType(etherType)) {
#ifdef ZT_TRACE_MULTICAST #ifdef ZT_TRACE_MULTICAST
Utils::snprintf(mct,sizeof(mct), Utils::snprintf(mct,sizeof(mct),
"%.16llx %.2u %.3u%s %c %s dropped: ethertype not allowed", "%.16llx %.2u %.3u%s %c %s dropped: ethertype not allowed",
@ -710,7 +714,9 @@ bool PacketDecoder::_doMULTICAST_FRAME(const RuntimeEnvironment *_r,const Shared
#endif #endif
TRACE("dropped MULTICAST_FRAME from %s(%s) into %.16llx: ethertype %u is not allowed",source().toString().c_str(),nwid,_remoteAddress.toString().c_str(),etherType); TRACE("dropped MULTICAST_FRAME from %s(%s) into %.16llx: ethertype %u is not allowed",source().toString().c_str(),nwid,_remoteAddress.toString().c_str(),etherType);
return true; return true;
} else if (!network->updateAndCheckMulticastBalance(origin,dest,frameLen)) { }
if (!network->updateAndCheckMulticastBalance(origin,dest,frameLen)) {
// Rate limits can only be checked by members of this network, but // Rate limits can only be checked by members of this network, but
// there should be enough of them that over-limit multicasts get // there should be enough of them that over-limit multicasts get
// their propagation aborted. // their propagation aborted.
@ -727,9 +733,9 @@ bool PacketDecoder::_doMULTICAST_FRAME(const RuntimeEnvironment *_r,const Shared
#endif #endif
TRACE("dropped MULTICAST_FRAME from %s(%s): rate limits exceeded for sender %s",source().toString().c_str(),_remoteAddress.toString().c_str(),origin.toString().c_str()); TRACE("dropped MULTICAST_FRAME from %s(%s): rate limits exceeded for sender %s",source().toString().c_str(),_remoteAddress.toString().c_str(),origin.toString().c_str());
return true; return true;
} else {
network->tapPut(sourceMac,dest.mac(),etherType,frame,frameLen);
} }
network->tapPut(sourceMac,dest.mac(),etherType,frame,frameLen);
} }
} }
} }

View file

@ -89,11 +89,9 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
if (!nconf) if (!nconf)
return; return;
// This should not happen // Sanity check -- bridge loop? OS problem?
if (to == network->mac()) { if (to == network->mac())
LOG("%s: frame received from self, ignoring (bridge loop? OS bug?)",network->tapDeviceName().c_str());
return; return;
}
// Check anti-recursion module to ensure that this is not ZeroTier talking over its own links // Check anti-recursion module to ensure that this is not ZeroTier talking over its own links
if (!_r->antiRec->checkEthernetFrame(data.data(),data.size())) { if (!_r->antiRec->checkEthernetFrame(data.data(),data.size())) {
@ -111,12 +109,13 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
bool fromBridged = false; bool fromBridged = false;
if (from != network->mac()) { if (from != network->mac()) {
if (!network->permitsBridging(_r->identity.address())) { if (!network->permitsBridging(_r->identity.address())) {
LOG("%s: UNICAST %s -> %s %s dropped, bridging disabled on network %.16llx",network->tapDeviceName().c_str(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType),network->id()); LOG("%s: %s -> %s %s not forwarded, bridging disabled on %.16llx or this peer not a bridge",network->tapDeviceName().c_str(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType),network->id());
return; return;
} }
fromBridged = true; fromBridged = true;
} }
// Multicast, either bridged or local source
if (to.isMulticast()) { if (to.isMulticast()) {
MulticastGroup mg(to,0); MulticastGroup mg(to,0);
@ -167,6 +166,8 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
// Then visit next hops according to multicaster (if there's room... almost certainly will be) // Then visit next hops according to multicaster (if there's room... almost certainly will be)
if (fifoPtr != fifoEnd) { if (fifoPtr != fifoEnd) {
_r->mc->getNextHops(network->id(),mg,Multicaster::AddToPropagationQueue(&fifoPtr,fifoEnd,bloom,bloomNonce,_r->identity.address(),nconf->multicastPrefixBits(),prefix)); _r->mc->getNextHops(network->id(),mg,Multicaster::AddToPropagationQueue(&fifoPtr,fifoEnd,bloom,bloomNonce,_r->identity.address(),nconf->multicastPrefixBits(),prefix));
// Pad remainder of FIFO with zeroes
while (fifoPtr != fifoEnd) while (fifoPtr != fifoEnd)
*(fifoPtr++) = (unsigned char)0; *(fifoPtr++) = (unsigned char)0;
} }
@ -176,7 +177,7 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
if (!firstHop) { if (!firstHop) {
if (supernode) if (supernode)
firstHop = supernode->address(); firstHop = supernode->address();
else continue; // no first hop = nowhere to go, try next bit prefix else continue; // nowhere to go
} }
Packet outp(firstHop,_r->identity.address(),Packet::VERB_MULTICAST_FRAME); Packet outp(firstHop,_r->identity.address(),Packet::VERB_MULTICAST_FRAME);
@ -193,7 +194,7 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
outp.append((unsigned char)((mcid >> 8) & 0xff)); outp.append((unsigned char)((mcid >> 8) & 0xff));
outp.append((unsigned char)(mcid & 0xff)); outp.append((unsigned char)(mcid & 0xff));
from.appendTo(outp); from.appendTo(outp);
to.appendTo(outp); mg.mac().appendTo(outp);
outp.append(mg.adi()); outp.append(mg.adi());
outp.append((uint16_t)etherType); outp.append((uint16_t)etherType);
outp.append((uint16_t)data.size()); outp.append((uint16_t)data.size());
@ -212,8 +213,12 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
outp.compress(); outp.compress();
send(outp,true); send(outp,true);
} }
} else if (to[0] == MAC::firstOctetForNetwork(network->id())) {
// Simple unicast frame from us to another node on the same virtual network return;
}
// Unicast from local peer to another non-bridged ZeroTier node
if ((!fromBridged)&&(to[0] == MAC::firstOctetForNetwork(network->id()))) {
Address toZT(to.toAddress(network->id())); Address toZT(to.toAddress(network->id()));
if (network->isAllowed(toZT)) { if (network->isAllowed(toZT)) {
network->pushMembershipCertificate(toZT,false,Utils::now()); network->pushMembershipCertificate(toZT,false,Utils::now());
@ -227,44 +232,45 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
} else { } else {
TRACE("%s: UNICAST: %s -> %s %s dropped, destination not a member of closed network %.16llx",network->tapDeviceName().c_str(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType),network->id()); TRACE("%s: UNICAST: %s -> %s %s dropped, destination not a member of closed network %.16llx",network->tapDeviceName().c_str(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType),network->id());
} }
} else { }
// Simple unicast from us to another node behind another bridge
Address bridges[ZT_MAX_BRIDGE_SPAM];
unsigned int numBridges = 0;
bridges[0] = network->findBridgeTo(to)); // Unicast to another node behind another bridge, whether from us or not
if ((bridges[0])&&(bridges[0] != _r->identity.address())&&(network->isAllowed(bridges[0]))&&(network->permitsBridging(bridges[0]))) {
++numBridges; Address bridges[ZT_MAX_BRIDGE_SPAM];
} else if (!nconf->activeBridges().empty()) { unsigned int numBridges = 0;
// If there is no known route, spam to up to ZT_MAX_BRIDGE_SPAM active bridges
std::set<Address>::const_iterator ab(nconf->activeBridges().begin()); bridges[0] = network->findBridgeTo(to);
if (nconf->activeBridges().size() <= ZT_MAX_BRIDGE_SPAM) { if ((bridges[0])&&(bridges[0] != _r->identity.address())&&(network->isAllowed(bridges[0]))&&(network->permitsBridging(bridges[0]))) {
// If there are <= ZT_MAX_BRIDGE_SPAM active bridges, just take them all ++numBridges;
while (numBridges < nconf->activeBridges().size()) } else if (!nconf->activeBridges().empty()) {
// If there is no known route, spam to up to ZT_MAX_BRIDGE_SPAM active bridges
std::set<Address>::const_iterator ab(nconf->activeBridges().begin());
if (nconf->activeBridges().size() <= ZT_MAX_BRIDGE_SPAM) {
// If there are <= ZT_MAX_BRIDGE_SPAM active bridges, just take them all
while (numBridges < nconf->activeBridges().size())
bridges[numBridges++] = *(ab++);
} else {
// Otherwise do this less efficient multipass thing to pick randomly from an ordered set until we have enough
while (numBridges < ZT_MAX_BRIDGE_SPAM) {
if (ab == nconf->activeBridges().end())
ab = nconf->activeBridges().begin();
if (((unsigned long)_r->prng->next32() % (unsigned long)nconf->activeBridges().size()) == 0)
bridges[numBridges++] = *(ab++); bridges[numBridges++] = *(ab++);
} else { else ++ab;
// Otherwise do this less efficient multipass thing to pick randomly from an ordered set until we have enough
while (numBridges < ZT_MAX_BRIDGE_SPAM) {
if (ab == nconf->activeBridges().end())
ab = nconf->activeBridges().begin();
if (((unsigned long)_r->prng->next32() % (unsigned long)nconf->activeBridges().size()) == 0)
bridges[numBridges++] = *(ab++);
else ++ab;
}
} }
} }
}
for(unsigned int b=0;b<numBridges;++b) { for(unsigned int b=0;b<numBridges;++b) {
Packet outp(bridges[b],_r->identity.address(),Packet::VERB_EXT_FRAME); Packet outp(bridges[b],_r->identity.address(),Packet::VERB_EXT_FRAME);
outp.append(network->id()); outp.append(network->id());
outp.append((unsigned char)0); outp.append((unsigned char)0);
to.appendTo(outp); to.appendTo(outp);
from.appendTo(outp); from.appendTo(outp);
outp.append((uint16_t)etherType); outp.append((uint16_t)etherType);
outp.append(data); outp.append(data);
outp.compress(); outp.compress();
send(outp,true); send(outp,true);
}
} }
} }