|
|
|
|
@@ -89,28 +89,41 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
|
|
|
|
|
if (!nconf)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (!_r->antiRec->checkEthernetFrame(data.data(),data.size())) {
|
|
|
|
|
TRACE("%s: rejected recursively addressed ZeroTier packet by tail match (type %s, length: %u)",network->tapDeviceName().c_str(),etherTypeName(etherType),data.size());
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This should not happen
|
|
|
|
|
if (to == network->mac()) {
|
|
|
|
|
LOG("%s: frame received from self, ignoring (bridge loop? OS bug?)",network->tapDeviceName().c_str());
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check anti-recursion module to ensure that this is not ZeroTier talking over its own links
|
|
|
|
|
if (!_r->antiRec->checkEthernetFrame(data.data(),data.size())) {
|
|
|
|
|
TRACE("%s: rejected recursively addressed ZeroTier packet by tail match (type %s, length: %u)",network->tapDeviceName().c_str(),etherTypeName(etherType),data.size());
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check to make sure this protocol is allowed on this network
|
|
|
|
|
if (!nconf->permitsEtherType(etherType)) {
|
|
|
|
|
LOG("%s: ignored tap: %s -> %s: ethertype %s not allowed on network %.16llx",network->tapDeviceName().c_str(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType),(unsigned long long)network->id());
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (from == network->mac()) {
|
|
|
|
|
// Check if this packet is from someone other than the tap -- i.e. bridged in
|
|
|
|
|
bool fromBridged = false;
|
|
|
|
|
if (from != network->mac()) {
|
|
|
|
|
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());
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
fromBridged = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (to.isMulticast()) {
|
|
|
|
|
MulticastGroup mg(to,0);
|
|
|
|
|
|
|
|
|
|
if (to.isBroadcast()) {
|
|
|
|
|
if ((etherType == ZT_ETHERTYPE_ARP)&&(data.size() == 28)&&(data[2] == 0x08)&&(data[3] == 0x00)&&(data[4] == 6)&&(data[5] == 4)&&(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
|
|
|
|
|
mg = MulticastGroup::deriveMulticastGroupForAddressResolution(InetAddress(data.field(24,4),4,0));
|
|
|
|
|
} else if (!nconf->enableBroadcast()) {
|
|
|
|
|
// Don't transmit broadcasts if this network doesn't want them
|
|
|
|
|
@@ -119,6 +132,11 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Learn multicast groups for bridged-in hosts
|
|
|
|
|
if (fromBridged)
|
|
|
|
|
network->learnBridgedMulticastGroup(mg);
|
|
|
|
|
|
|
|
|
|
// Check multicast/broadcast bandwidth quotas
|
|
|
|
|
if (!network->updateAndCheckMulticastBalance(_r->identity.address(),mg,data.size())) {
|
|
|
|
|
TRACE("%s: didn't multicast %d bytes, quota exceeded for multicast group %s",network->tapDeviceName().c_str(),(int)data.size(),mg.toString().c_str());
|
|
|
|
|
return;
|
|
|
|
|
@@ -127,24 +145,38 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
|
|
|
|
|
const unsigned int mcid = ++_multicastIdCounter & 0xffffff;
|
|
|
|
|
const uint16_t bloomNonce = (uint16_t)(_r->prng->next32() & 0xffff); // doesn't need to be cryptographically strong
|
|
|
|
|
unsigned char bloom[ZT_PROTO_VERB_MULTICAST_FRAME_LEN_PROPAGATION_BLOOM];
|
|
|
|
|
unsigned char fifo[ZT_PROTO_VERB_MULTICAST_FRAME_LEN_PROPAGATION_FIFO + ZT_ADDRESS_LENGTH];
|
|
|
|
|
unsigned char fifo[ZT_PROTO_VERB_MULTICAST_FRAME_LEN_PROPAGATION_FIFO + ZT_ADDRESS_LENGTH]; // extra ZT_ADDRESS_LENGTH is for first hop, not put in packet but serves as destination for packet
|
|
|
|
|
unsigned char *const fifoEnd = fifo + sizeof(fifo);
|
|
|
|
|
const unsigned int signedPartLen = (ZT_PROTO_VERB_MULTICAST_FRAME_IDX_FRAME - ZT_PROTO_VERB_MULTICAST_FRAME_IDX__START_OF_SIGNED_PORTION) + data.size();
|
|
|
|
|
const SharedPtr<Peer> supernode(_r->topology->getBestSupernode());
|
|
|
|
|
|
|
|
|
|
// For each bit prefix send a packet to a list of destinations within it
|
|
|
|
|
for(unsigned int prefix=0,np=((unsigned int)2 << (nconf->multicastPrefixBits() - 1));prefix<np;++prefix) {
|
|
|
|
|
memset(bloom,0,sizeof(bloom));
|
|
|
|
|
|
|
|
|
|
unsigned char *fifoPtr = fifo;
|
|
|
|
|
|
|
|
|
|
// All multicasts visit all active bridges first -- this is one of the two active/passive bridge differences
|
|
|
|
|
for(std::set<Address>::const_iterator ab(nconf->activeBridges().begin());ab!=nconf->activeBridges().end();++ab) {
|
|
|
|
|
if ((*ab != _r->identity.address())&&(ab->withinMulticastPropagationPrefix(prefix,nconf->multicastPrefixBits()))) {
|
|
|
|
|
ab->copyTo(fifoPtr,ZT_ADDRESS_LENGTH);
|
|
|
|
|
if ((fifoPtr += ZT_ADDRESS_LENGTH) == fifoEnd)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Then visit next hops according to multicaster (if there's room... almost certainly will be)
|
|
|
|
|
if (fifoPtr != fifoEnd) {
|
|
|
|
|
_r->mc->getNextHops(network->id(),mg,Multicaster::AddToPropagationQueue(&fifoPtr,fifoEnd,bloom,bloomNonce,_r->identity.address(),nconf->multicastPrefixBits(),prefix));
|
|
|
|
|
while (fifoPtr != fifoEnd)
|
|
|
|
|
*(fifoPtr++) = (unsigned char)0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Address firstHop(fifo,ZT_ADDRESS_LENGTH); // fifo is +1 in size, with first element being used here
|
|
|
|
|
// First element in FIFO is first hop, rest of FIFO is sent in packet *to* first hop
|
|
|
|
|
Address firstHop(fifo,ZT_ADDRESS_LENGTH);
|
|
|
|
|
if (!firstHop) {
|
|
|
|
|
if (supernode)
|
|
|
|
|
firstHop = supernode->address();
|
|
|
|
|
else continue;
|
|
|
|
|
else continue; // no first hop = nowhere to go, try next bit prefix
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Packet outp(firstHop,_r->identity.address(),Packet::VERB_MULTICAST_FRAME);
|
|
|
|
|
@@ -161,7 +193,7 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
|
|
|
|
|
outp.append((unsigned char)((mcid >> 8) & 0xff));
|
|
|
|
|
outp.append((unsigned char)(mcid & 0xff));
|
|
|
|
|
from.appendTo(outp);
|
|
|
|
|
mg.mac().appendTo(outp);
|
|
|
|
|
to.appendTo(outp);
|
|
|
|
|
outp.append(mg.adi());
|
|
|
|
|
outp.append((uint16_t)etherType);
|
|
|
|
|
outp.append((uint16_t)data.size());
|
|
|
|
|
@@ -196,10 +228,43 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
|
|
|
|
|
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 {
|
|
|
|
|
LOG("%s: UNICAST %s -> %s %s dropped, bridging disabled, unicast destination not on network %.16llx",network->tapDeviceName().c_str(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType),network->id());
|
|
|
|
|
}
|
|
|
|
|
// 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));
|
|
|
|
|
if ((bridges[0])&&(bridges[0] != _r->identity.address())&&(network->isAllowed(bridges[0]))&&(network->permitsBridging(bridges[0]))) {
|
|
|
|
|
++numBridges;
|
|
|
|
|
} 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 {
|
|
|
|
|
LOG("%s: UNICAST %s -> %s %s dropped, bridging disabled, unicast source not on network %.16llx",network->tapDeviceName().c_str(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType),network->id());
|
|
|
|
|
// 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) {
|
|
|
|
|
Packet outp(bridges[b],_r->identity.address(),Packet::VERB_EXT_FRAME);
|
|
|
|
|
outp.append(network->id());
|
|
|
|
|
outp.append((unsigned char)0);
|
|
|
|
|
to.appendTo(outp);
|
|
|
|
|
from.appendTo(outp);
|
|
|
|
|
outp.append((uint16_t)etherType);
|
|
|
|
|
outp.append(data);
|
|
|
|
|
outp.compress();
|
|
|
|
|
send(outp,true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|