summaryrefslogtreecommitdiff
path: root/node/Switch.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'node/Switch.cpp')
-rw-r--r--node/Switch.cpp237
1 files changed, 151 insertions, 86 deletions
diff --git a/node/Switch.cpp b/node/Switch.cpp
index 59be6dc8..7e1d1f84 100644
--- a/node/Switch.cpp
+++ b/node/Switch.cpp
@@ -89,117 +89,182 @@ 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());
+ // This should not happen
+ if (to == network->mac()) {
+ LOG("%s: frame received from self, ignoring (bridge loop? OS bug?)",network->tapDeviceName().c_str());
return;
}
- if (to == network->mac()) {
- LOG("%s: frame received from self, ignoring (bridge loop? OS bug?)",network->tapDeviceName().c_str());
+ // 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()) {
- 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
- 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
- TRACE("%s: dropped broadcast since ff:ff:ff:ff:ff:ff is not enabled on network %.16llx",network->tapDeviceName().c_str(),network->id());
- return;
- }
- }
+ // 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 (!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());
+ 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
+ TRACE("%s: dropped broadcast since ff:ff:ff:ff:ff:ff is not enabled on network %.16llx",network->tapDeviceName().c_str(),network->id());
return;
}
+ }
- 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 *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());
+ // Learn multicast groups for bridged-in hosts
+ if (fromBridged)
+ network->learnBridgedMulticastGroup(mg);
- for(unsigned int prefix=0,np=((unsigned int)2 << (nconf->multicastPrefixBits() - 1));prefix<np;++prefix) {
- memset(bloom,0,sizeof(bloom));
+ // 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;
+ }
+
+ 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]; // 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;
+ }
+ }
- unsigned char *fifoPtr = fifo;
+ // 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
- if (!firstHop) {
- if (supernode)
- firstHop = supernode->address();
- else continue;
- }
-
- Packet outp(firstHop,_r->identity.address(),Packet::VERB_MULTICAST_FRAME);
- outp.append((uint16_t)0);
- outp.append(fifo + ZT_ADDRESS_LENGTH,ZT_PROTO_VERB_MULTICAST_FRAME_LEN_PROPAGATION_FIFO); // remainder of fifo is loaded into packet
- outp.append(bloom,ZT_PROTO_VERB_MULTICAST_FRAME_LEN_PROPAGATION_BLOOM);
- outp.append((nconf->com()) ? (unsigned char)ZT_PROTO_VERB_MULTICAST_FRAME_FLAGS_HAS_MEMBERSHIP_CERTIFICATE : (unsigned char)0);
- outp.append(network->id());
- outp.append(bloomNonce);
- outp.append((unsigned char)nconf->multicastPrefixBits());
- outp.append((unsigned char)prefix);
- _r->identity.address().appendTo(outp);
- outp.append((unsigned char)((mcid >> 16) & 0xff));
- outp.append((unsigned char)((mcid >> 8) & 0xff));
- outp.append((unsigned char)(mcid & 0xff));
- from.appendTo(outp);
- mg.mac().appendTo(outp);
- outp.append(mg.adi());
- outp.append((uint16_t)etherType);
- outp.append((uint16_t)data.size());
- outp.append(data);
-
- C25519::Signature sig(_r->identity.sign(outp.field(ZT_PROTO_VERB_MULTICAST_FRAME_IDX__START_OF_SIGNED_PORTION,signedPartLen),signedPartLen));
- outp.append((uint16_t)sig.size());
- outp.append(sig.data,(unsigned int)sig.size());
-
- // FIXME: now we send the netconf cert with every single multicast,
- // which pretty much ensures everyone has it ahead of time but adds
- // some redundant payload. Maybe think abouut this in the future.
- if (nconf->com())
- nconf->com().serialize(outp);
-
- outp.compress();
- send(outp,true);
}
- } else if (to[0] == MAC::firstOctetForNetwork(network->id())) {
- // Simple unicast frame from us to another node on the same virtual network
- Address toZT(to.toAddress(network->id()));
- if (network->isAllowed(toZT)) {
- network->pushMembershipCertificate(toZT,false,Utils::now());
-
- Packet outp(toZT,_r->identity.address(),Packet::VERB_FRAME);
- outp.append(network->id());
- outp.append((uint16_t)etherType);
- outp.append(data);
- outp.compress();
- send(outp,true);
- } 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());
+
+ // 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; // no first hop = nowhere to go, try next bit prefix
}
+
+ Packet outp(firstHop,_r->identity.address(),Packet::VERB_MULTICAST_FRAME);
+ outp.append((uint16_t)0);
+ outp.append(fifo + ZT_ADDRESS_LENGTH,ZT_PROTO_VERB_MULTICAST_FRAME_LEN_PROPAGATION_FIFO); // remainder of fifo is loaded into packet
+ outp.append(bloom,ZT_PROTO_VERB_MULTICAST_FRAME_LEN_PROPAGATION_BLOOM);
+ outp.append((nconf->com()) ? (unsigned char)ZT_PROTO_VERB_MULTICAST_FRAME_FLAGS_HAS_MEMBERSHIP_CERTIFICATE : (unsigned char)0);
+ outp.append(network->id());
+ outp.append(bloomNonce);
+ outp.append((unsigned char)nconf->multicastPrefixBits());
+ outp.append((unsigned char)prefix);
+ _r->identity.address().appendTo(outp);
+ outp.append((unsigned char)((mcid >> 16) & 0xff));
+ outp.append((unsigned char)((mcid >> 8) & 0xff));
+ outp.append((unsigned char)(mcid & 0xff));
+ from.appendTo(outp);
+ to.appendTo(outp);
+ outp.append(mg.adi());
+ outp.append((uint16_t)etherType);
+ outp.append((uint16_t)data.size());
+ outp.append(data);
+
+ C25519::Signature sig(_r->identity.sign(outp.field(ZT_PROTO_VERB_MULTICAST_FRAME_IDX__START_OF_SIGNED_PORTION,signedPartLen),signedPartLen));
+ outp.append((uint16_t)sig.size());
+ outp.append(sig.data,(unsigned int)sig.size());
+
+ // FIXME: now we send the netconf cert with every single multicast,
+ // which pretty much ensures everyone has it ahead of time but adds
+ // some redundant payload. Maybe think abouut this in the future.
+ if (nconf->com())
+ nconf->com().serialize(outp);
+
+ outp.compress();
+ send(outp,true);
+ }
+ } else if (to[0] == MAC::firstOctetForNetwork(network->id())) {
+ // Simple unicast frame from us to another node on the same virtual network
+ Address toZT(to.toAddress(network->id()));
+ if (network->isAllowed(toZT)) {
+ network->pushMembershipCertificate(toZT,false,Utils::now());
+
+ Packet outp(toZT,_r->identity.address(),Packet::VERB_FRAME);
+ outp.append(network->id());
+ outp.append((uint16_t)etherType);
+ outp.append(data);
+ outp.compress();
+ send(outp,true);
} 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());
+ 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 source 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 {
+ // 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);
+ }
}
}