Browse Source

Integration of Filter into inbound and outbound packet path.

Adam Ierymenko 9 years ago
parent
commit
7404eb46c4
6 changed files with 150 additions and 61 deletions
  1. 40 14
      node/Filter.cpp
  2. 6 4
      node/Filter.hpp
  3. 65 16
      node/IncomingPacket.cpp
  4. 0 20
      node/NetworkConfig.hpp
  5. 38 6
      node/Switch.cpp
  6. 1 1
      version.h

+ 40 - 14
node/Filter.cpp

@@ -24,6 +24,9 @@
 #include "MAC.hpp"
 #include "InetAddress.hpp"
 #include "Filter.hpp"
+#include "Packet.hpp"
+#include "Switch.hpp"
+#include "Topology.hpp"
 
 // Returns true if packet appears valid; pos and proto will be set
 static bool _ipv6GetPayload(const uint8_t *frameData,unsigned int frameLen,unsigned int &pos,unsigned int &proto)
@@ -56,8 +59,9 @@ static bool _ipv6GetPayload(const uint8_t *frameData,unsigned int frameLen,unsig
 
 namespace ZeroTier {
 
-const ZT_VirtualNetworkRule *Filter::run(
+bool Filter::run(
 	const RuntimeEnvironment *RR,
+	const uint64_t nwid,
 	const Address &ztSource,
 	const Address &ztDest,
 	const MAC &macSource,
@@ -69,21 +73,49 @@ const ZT_VirtualNetworkRule *Filter::run(
 	const ZT_VirtualNetworkRule *rules,
 	const unsigned int ruleCount)
 {
+	// For each set of rules we start by assuming that they match (since no constraints
+	// yields a 'match all' rule).
 	uint8_t thisSetMatches = 1;
+
 	for(unsigned int rn=0;rn<ruleCount;++rn) {
 		const ZT_VirtualNetworkRuleType rt = (ZT_VirtualNetworkRuleType)(rules[rn].t & 0x7f);
 		uint8_t thisRuleMatches = 0;
 
 		switch(rt) {
+			// Actions end a set of ANDed rules
 			case ZT_NETWORK_RULE_ACTION_DROP:
 			case ZT_NETWORK_RULE_ACTION_ACCEPT:
 			case ZT_NETWORK_RULE_ACTION_TEE:
 			case ZT_NETWORK_RULE_ACTION_REDIRECT:
-				if (thisSetMatches)
-					return &(rules[rn]);
-				thisSetMatches = 1;
+				if (thisSetMatches) {
+					// This set did match, so perform action!
+					if (rt == ZT_NETWORK_RULE_ACTION_DROP) {
+						// DROP means do nothing at all.
+						return false;
+					} else {
+						if ((rt == ZT_NETWORK_RULE_ACTION_TEE)||(rt == ZT_NETWORK_RULE_ACTION_REDIRECT)) {
+							// Tee and redirect both want this frame copied to somewhere else.
+							Packet outp(Address(rules[rn].v.zt),RR->identity.address(),Packet::VERB_EXT_FRAME);
+							outp.append(nwid);
+							outp.append((unsigned char)0x00); // TODO: should maybe include COM if needed
+							macDest.appendTo(outp);
+							macSource.appendTo(outp);
+							outp.append((uint16_t)etherType);
+							outp.append(frameData,frameLen);
+							outp.compress();
+							RR->sw->send(outp,true,nwid);
+						}
+						// For REDIRECT we will want to DROP at this node. For TEE we ACCEPT at this node but
+						// also forward it along as we just did.
+						return (rt != ZT_NETWORK_RULE_ACTION_REDIRECT);
+					}
+				} else {
+					// Otherwise start a new set, assuming that it will match
+					thisSetMatches = 1;
+				}
 				break;
 
+			// A rule can consist of one or more MATCH criterion
 			case ZT_NETWORK_RULE_MATCH_SOURCE_ZEROTIER_ADDRESS:
 				thisRuleMatches = (uint8_t)(rules[rn].v.zt == ztSource.toInt());
 				break;
@@ -206,24 +238,18 @@ const ZT_VirtualNetworkRule *Filter::run(
 				}
 				break;
 			case ZT_NETWORK_RULE_MATCH_CHARACTERISTICS:
-				/*
-				if (etherType == ZT_ETHERTYPE_IPV4) {
-				} else if (etherType == ZT_ETHERTYPE_IPV6) {
-				} else {
-					thisRuleMatches = 0;
-				}
-				*/
+				// TODO: not supported yet
 				break;
 			case ZT_NETWORK_RULE_MATCH_FRAME_SIZE_RANGE:
 				thisRuleMatches = (uint8_t)((frameLen >= (unsigned int)rules[rn].v.frameSize[0])&&(frameLen <= (unsigned int)rules[rn].v.frameSize[1]));
 				break;
 		}
 
-		// thisSetMatches remains true if the current rule matches... or does NOT match if not bit (0x80) is 1
-		thisSetMatches &= (thisRuleMatches ^ ((rules[rn].t >> 8) & 1));
+		// thisSetMatches remains true if the current rule matched... or does NOT match if not bit (0x80) is 1
+		thisSetMatches &= (thisRuleMatches ^ ((rules[rn].t & 0x80) >> 7));
 	}
 
-	return (const ZT_VirtualNetworkRule *)0; // no matches
+	return false; // no matches, no rules, default action is therefore DROP
 }
 
 } // namespace ZeroTier

+ 6 - 4
node/Filter.hpp

@@ -39,9 +39,11 @@ public:
 	/**
 	 * Apply a list of rules to a packet
 	 *
-	 * This returns the matching TARGET rule entry if there is a match or NULL
-	 * if no match is found.
+	 * This returns whether or not the packet should be accepted and may also
+	 * take other actions for e.g. the TEE and REDIRECT targets.
 	 *
+	 * @param RR ZeroTier runtime environment (context)
+	 * @param nwid ZeroTier network ID
 	 * @param ztSource Source ZeroTier address
 	 * @param ztDest Destination ZeroTier address
 	 * @param macSource Ethernet layer source address
@@ -52,10 +54,10 @@ public:
 	 * @param vlanId 16-bit VLAN ID
 	 * @param rules Pointer to array of rules
 	 * @param ruleCount Number of rules
-	 * @return Pointer to rules[] to matching TARGET, or NULL if no match
 	 */
-	static const ZT_VirtualNetworkRule *run(
+	static bool run(
 		const RuntimeEnvironment *RR,
+		const uint64_t nwid,
 		const Address &ztSource,
 		const Address &ztDest,
 		const MAC &macSource,

+ 65 - 16
node/IncomingPacket.cpp

@@ -37,6 +37,7 @@
 #include "Cluster.hpp"
 #include "Node.hpp"
 #include "DeferredPackets.hpp"
+#include "Filter.hpp"
 
 namespace ZeroTier {
 
@@ -550,13 +551,27 @@ bool IncomingPacket::_doFRAME(const RuntimeEnvironment *RR,const SharedPtr<Peer>
 				}
 
 				const unsigned int etherType = at<uint16_t>(ZT_PROTO_VERB_FRAME_IDX_ETHERTYPE);
-				if (!network->config().permitsEtherType(etherType)) {
-					TRACE("dropped FRAME from %s(%s): ethertype %.4x not allowed on %.16llx",peer->address().toString().c_str(),_remoteAddress.toString().c_str(),(unsigned int)etherType,(unsigned long long)network->id());
-					return true;
+				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<const uint8_t *>(data()) + ZT_PROTO_VERB_FRAME_IDX_PAYLOAD;
+				if (Filter::run(
+					RR,
+					network->id(),
+					peer->address(),
+					RR->identity.address(),
+					sourceMac,
+					network->mac(),
+					frameData,
+					frameLen,
+					etherType,
+					0,
+					network->config().rules,
+					network->config().ruleCount))
+				{
+					RR->node->putFrame(network->id(),network->userPtr(),sourceMac,network->mac(),etherType,0,(const void *)frameData,frameLen);
+				} else {
+					TRACE("dropped FRAME from %s(%s): Filter::run() == false (will still log packet as received)",peer->address().toString().c_str(),_remoteAddress.toString().c_str(),(unsigned int)etherType,(unsigned long long)network->id());
 				}
-
-				const unsigned int payloadLen = size() - ZT_PROTO_VERB_FRAME_IDX_PAYLOAD;
-				RR->node->putFrame(network->id(),network->userPtr(),MAC(peer->address(),network->id()),network->mac(),etherType,0,field(ZT_PROTO_VERB_FRAME_IDX_PAYLOAD,payloadLen),payloadLen);
 			}
 
 			peer->received(_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_FRAME,0,Packet::VERB_NOP);
@@ -594,10 +609,6 @@ bool IncomingPacket::_doEXT_FRAME(const RuntimeEnvironment *RR,const SharedPtr<P
 				// of the certificate, if there was one...
 
 				const unsigned int etherType = at<uint16_t>(comLen + ZT_PROTO_VERB_EXT_FRAME_IDX_ETHERTYPE);
-				if (!network->config().permitsEtherType(etherType)) {
-					TRACE("dropped EXT_FRAME from %s(%s): ethertype %.4x not allowed on network %.16llx",peer->address().toString().c_str(),_remoteAddress.toString().c_str(),(unsigned int)etherType,(unsigned long long)network->id());
-					return true;
-				}
 
 				const MAC to(field(comLen + 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(comLen + ZT_PROTO_VERB_EXT_FRAME_IDX_FROM,ZT_PROTO_VERB_EXT_FRAME_LEN_FROM),ZT_PROTO_VERB_EXT_FRAME_LEN_FROM);
@@ -626,8 +637,26 @@ bool IncomingPacket::_doEXT_FRAME(const RuntimeEnvironment *RR,const SharedPtr<P
 					}
 				}
 
-				const unsigned int payloadLen = size() - (comLen + ZT_PROTO_VERB_EXT_FRAME_IDX_PAYLOAD);
-				RR->node->putFrame(network->id(),network->userPtr(),from,to,etherType,0,field(comLen + ZT_PROTO_VERB_EXT_FRAME_IDX_PAYLOAD,payloadLen),payloadLen);
+				const unsigned int frameLen = size() - (comLen + ZT_PROTO_VERB_EXT_FRAME_IDX_PAYLOAD);
+				const uint8_t *const frameData = (const uint8_t *)field(comLen + ZT_PROTO_VERB_EXT_FRAME_IDX_PAYLOAD,frameLen);
+				if (Filter::run(
+					RR,
+					network->id(),
+					peer->address(),
+					RR->identity.address(),
+					from,
+					to,
+					frameData,
+					frameLen,
+					etherType,
+					0,
+					network->config().rules,
+					network->config().ruleCount))
+				{
+					RR->node->putFrame(network->id(),network->userPtr(),from,to,etherType,0,(const void *)frameData,frameLen);
+				} else {
+					TRACE("dropped EXT_FRAME from %s(%s): Filter::run() == false (will still log packet as received)",peer->address().toString().c_str(),_remoteAddress.toString().c_str(),(unsigned int)etherType,(unsigned long long)network->id());
+				}
 			}
 
 			peer->received(_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP);
@@ -870,11 +899,11 @@ bool IncomingPacket::_doMULTICAST_FRAME(const RuntimeEnvironment *RR,const Share
 
 			const MulticastGroup to(MAC(field(offset + ZT_PROTO_VERB_MULTICAST_FRAME_IDX_DEST_MAC,6),6),at<uint32_t>(offset + ZT_PROTO_VERB_MULTICAST_FRAME_IDX_DEST_ADI));
 			const unsigned int etherType = at<uint16_t>(offset + ZT_PROTO_VERB_MULTICAST_FRAME_IDX_ETHERTYPE);
-			const unsigned int payloadLen = size() - (offset + ZT_PROTO_VERB_MULTICAST_FRAME_IDX_FRAME);
+			const unsigned int frameLen = size() - (offset + ZT_PROTO_VERB_MULTICAST_FRAME_IDX_FRAME);
 
-			//TRACE("<<MC FRAME %.16llx/%s from %s@%s flags %.2x length %u",nwid,to.toString().c_str(),from.toString().c_str(),peer->address().toString().c_str(),flags,payloadLen);
+			//TRACE("<<MC FRAME %.16llx/%s from %s@%s flags %.2x length %u",nwid,to.toString().c_str(),from.toString().c_str(),peer->address().toString().c_str(),flags,frameLen);
 
-			if ((payloadLen > 0)&&(payloadLen <= ZT_IF_MTU)) {
+			if ((frameLen > 0)&&(frameLen <= ZT_IF_MTU)) {
 				if (!to.mac().isMulticast()) {
 					TRACE("dropped MULTICAST_FRAME from %s@%s(%s) to %s: destination is unicast, must use FRAME or EXT_FRAME",from.toString().c_str(),peer->address().toString().c_str(),_remoteAddress.toString().c_str(),to.toString().c_str());
 					return true;
@@ -893,7 +922,27 @@ bool IncomingPacket::_doMULTICAST_FRAME(const RuntimeEnvironment *RR,const Share
 					}
 				}
 
-				RR->node->putFrame(network->id(),network->userPtr(),from,to.mac(),etherType,0,field(offset + ZT_PROTO_VERB_MULTICAST_FRAME_IDX_FRAME,payloadLen),payloadLen);
+				const uint8_t *const frameData = (const uint8_t *)field(offset + ZT_PROTO_VERB_MULTICAST_FRAME_IDX_FRAME,frameLen);
+				if (Filter::run(
+					RR,
+					network->id(),
+					peer->address(),
+					RR->identity.address(),
+					from,
+					to.mac(),
+					frameData,
+					frameLen,
+					etherType,
+					0,
+					network->config().rules,
+					network->config().ruleCount))
+				{
+					RR->node->putFrame(network->id(),network->userPtr(),from,to.mac(),etherType,0,(const void *)frameData,frameLen);
+				} else {
+					TRACE("dropped MULTICAST_FRAME from %s(%s): Filter::run() == false (will still do implicit gather)",peer->address().toString().c_str(),_remoteAddress.toString().c_str(),(unsigned int)etherType,(unsigned long long)network->id());
+					// Note: we continue here since we still do implicit gather in this case... we just do not putFrame() if it
+					// fails the filter check.
+				}
 			}
 
 			if (gatherLimit) {

+ 0 - 20
node/NetworkConfig.hpp

@@ -215,26 +215,6 @@ public:
 		return *this;
 	}
 
-	/**
-	 * @param etherType Ethernet frame type to check
-	 * @return True if allowed on this network
-	 */
-	inline bool permitsEtherType(unsigned int etherType) const
-	{
-		unsigned int et = 0;
-		for(unsigned int i=0;i<ruleCount;++i) {
-			ZT_VirtualNetworkRuleType rt = (ZT_VirtualNetworkRuleType)(rules[i].t & 0x7f);
-			if (rt == ZT_NETWORK_RULE_MATCH_ETHERTYPE) {
-				et = rules[i].v.etherType;
-			} else if (rt == ZT_NETWORK_RULE_ACTION_ACCEPT) {
-				if ((!et)||(et == etherType))
-					return true;
-				et = 0;
-			}
-		}
-		return false;
-	}
-
 	/**
 	 * Write this network config to a dictionary for transport
 	 *

+ 38 - 6
node/Switch.cpp

@@ -35,6 +35,7 @@
 #include "Peer.hpp"
 #include "SelfAwareness.hpp"
 #include "Packet.hpp"
+#include "Filter.hpp"
 #include "Cluster.hpp"
 
 namespace ZeroTier {
@@ -313,12 +314,6 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
 	if (to == network->mac())
 		return;
 
-	// Check to make sure this protocol is allowed on this network
-	if (!network->config().permitsEtherType(etherType)) {
-		TRACE("%.16llx: ignored tap: %s -> %s: ethertype %s not allowed on network %.16llx",network->id(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType),(unsigned long long)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()) {
@@ -443,6 +438,24 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &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 (!Filter::run(
+			RR,
+			network->id(),
+			RR->identity.address(),
+			Address(), // 0 destination ZT address for multicasts since this is unknown at time of send
+			from,
+			to,
+			(const uint8_t *)data,
+			len,
+			etherType,
+			vlanId,
+			network->config().rules,
+			network->config().ruleCount))
+		{
+			TRACE("%.16llx: %s -> %s %s packet not sent: Filter::run() == false (multicast)",network->id(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType));
+			return;
+		}
+
 		RR->mc->send(
 			((!network->config().isPublic())&&(network->config().com)) ? &(network->config().com) : (const CertificateOfMembership *)0,
 			network->config().multicastLimit,
@@ -463,6 +476,25 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
 
 		Address toZT(to.toAddress(network->id())); // since in-network MACs are derived from addresses and network IDs, we can reverse this
 		SharedPtr<Peer> toPeer(RR->topology->getPeer(toZT));
+
+		if (!Filter::run(
+			RR,
+			network->id(),
+			RR->identity.address(),
+			toZT,
+			from,
+			to,
+			(const uint8_t *)data,
+			len,
+			etherType,
+			vlanId,
+			network->config().rules,
+			network->config().ruleCount))
+		{
+			TRACE("%.16llx: %s -> %s %s packet not sent: Filter::run() == false",network->id(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType));
+			return;
+		}
+
 		const bool includeCom = ( (network->config().isPrivate()) && (network->config().com) && ((!toPeer)||(toPeer->needsOurNetworkMembershipCertificate(network->id(),RR->node->now(),true))) );
 		if ((fromBridged)||(includeCom)) {
 			Packet outp(toZT,RR->identity.address(),Packet::VERB_EXT_FRAME);

+ 1 - 1
version.h

@@ -32,6 +32,6 @@
 /**
  * Revision
  */
-#define ZEROTIER_ONE_VERSION_REVISION 14
+#define ZEROTIER_ONE_VERSION_REVISION 15
 
 #endif