Integration of Filter into inbound and outbound packet path.

This commit is contained in:
Adam Ierymenko 2016-07-25 16:51:10 -07:00
parent eaf6d6c938
commit 7404eb46c4
6 changed files with 150 additions and 61 deletions

View File

@ -24,6 +24,9 @@
#include "MAC.hpp" #include "MAC.hpp"
#include "InetAddress.hpp" #include "InetAddress.hpp"
#include "Filter.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 // 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) 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 { namespace ZeroTier {
const ZT_VirtualNetworkRule *Filter::run( bool Filter::run(
const RuntimeEnvironment *RR, const RuntimeEnvironment *RR,
const uint64_t nwid,
const Address &ztSource, const Address &ztSource,
const Address &ztDest, const Address &ztDest,
const MAC &macSource, const MAC &macSource,
@ -69,21 +73,49 @@ const ZT_VirtualNetworkRule *Filter::run(
const ZT_VirtualNetworkRule *rules, const ZT_VirtualNetworkRule *rules,
const unsigned int ruleCount) 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; uint8_t thisSetMatches = 1;
for(unsigned int rn=0;rn<ruleCount;++rn) { for(unsigned int rn=0;rn<ruleCount;++rn) {
const ZT_VirtualNetworkRuleType rt = (ZT_VirtualNetworkRuleType)(rules[rn].t & 0x7f); const ZT_VirtualNetworkRuleType rt = (ZT_VirtualNetworkRuleType)(rules[rn].t & 0x7f);
uint8_t thisRuleMatches = 0; uint8_t thisRuleMatches = 0;
switch(rt) { switch(rt) {
// Actions end a set of ANDed rules
case ZT_NETWORK_RULE_ACTION_DROP: case ZT_NETWORK_RULE_ACTION_DROP:
case ZT_NETWORK_RULE_ACTION_ACCEPT: case ZT_NETWORK_RULE_ACTION_ACCEPT:
case ZT_NETWORK_RULE_ACTION_TEE: case ZT_NETWORK_RULE_ACTION_TEE:
case ZT_NETWORK_RULE_ACTION_REDIRECT: case ZT_NETWORK_RULE_ACTION_REDIRECT:
if (thisSetMatches) if (thisSetMatches) {
return &(rules[rn]); // 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; thisSetMatches = 1;
}
break; break;
// A rule can consist of one or more MATCH criterion
case ZT_NETWORK_RULE_MATCH_SOURCE_ZEROTIER_ADDRESS: case ZT_NETWORK_RULE_MATCH_SOURCE_ZEROTIER_ADDRESS:
thisRuleMatches = (uint8_t)(rules[rn].v.zt == ztSource.toInt()); thisRuleMatches = (uint8_t)(rules[rn].v.zt == ztSource.toInt());
break; break;
@ -206,24 +238,18 @@ const ZT_VirtualNetworkRule *Filter::run(
} }
break; break;
case ZT_NETWORK_RULE_MATCH_CHARACTERISTICS: case ZT_NETWORK_RULE_MATCH_CHARACTERISTICS:
/* // TODO: not supported yet
if (etherType == ZT_ETHERTYPE_IPV4) {
} else if (etherType == ZT_ETHERTYPE_IPV6) {
} else {
thisRuleMatches = 0;
}
*/
break; break;
case ZT_NETWORK_RULE_MATCH_FRAME_SIZE_RANGE: 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])); thisRuleMatches = (uint8_t)((frameLen >= (unsigned int)rules[rn].v.frameSize[0])&&(frameLen <= (unsigned int)rules[rn].v.frameSize[1]));
break; break;
} }
// thisSetMatches remains true if the current rule matches... or does NOT match if not bit (0x80) is 1 // thisSetMatches remains true if the current rule matched... or does NOT match if not bit (0x80) is 1
thisSetMatches &= (thisRuleMatches ^ ((rules[rn].t >> 8) & 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 } // namespace ZeroTier

View File

@ -39,9 +39,11 @@ public:
/** /**
* Apply a list of rules to a packet * Apply a list of rules to a packet
* *
* This returns the matching TARGET rule entry if there is a match or NULL * This returns whether or not the packet should be accepted and may also
* if no match is found. * 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 ztSource Source ZeroTier address
* @param ztDest Destination ZeroTier address * @param ztDest Destination ZeroTier address
* @param macSource Ethernet layer source address * @param macSource Ethernet layer source address
@ -52,10 +54,10 @@ public:
* @param vlanId 16-bit VLAN ID * @param vlanId 16-bit VLAN ID
* @param rules Pointer to array of rules * @param rules Pointer to array of rules
* @param ruleCount Number 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 RuntimeEnvironment *RR,
const uint64_t nwid,
const Address &ztSource, const Address &ztSource,
const Address &ztDest, const Address &ztDest,
const MAC &macSource, const MAC &macSource,

View File

@ -37,6 +37,7 @@
#include "Cluster.hpp" #include "Cluster.hpp"
#include "Node.hpp" #include "Node.hpp"
#include "DeferredPackets.hpp" #include "DeferredPackets.hpp"
#include "Filter.hpp"
namespace ZeroTier { 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); const unsigned int etherType = at<uint16_t>(ZT_PROTO_VERB_FRAME_IDX_ETHERTYPE);
if (!network->config().permitsEtherType(etherType)) { const MAC sourceMac(peer->address(),network->id());
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()); const unsigned int frameLen = size() - ZT_PROTO_VERB_FRAME_IDX_PAYLOAD;
return true; 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); 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... // of the certificate, if there was one...
const unsigned int etherType = at<uint16_t>(comLen + ZT_PROTO_VERB_EXT_FRAME_IDX_ETHERTYPE); 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 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); 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); const unsigned int frameLen = 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 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); 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 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 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()) { 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()); 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; 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) { if (gatherLimit) {

View File

@ -215,26 +215,6 @@ public:
return *this; 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 * Write this network config to a dictionary for transport
* *

View File

@ -35,6 +35,7 @@
#include "Peer.hpp" #include "Peer.hpp"
#include "SelfAwareness.hpp" #include "SelfAwareness.hpp"
#include "Packet.hpp" #include "Packet.hpp"
#include "Filter.hpp"
#include "Cluster.hpp" #include "Cluster.hpp"
namespace ZeroTier { namespace ZeroTier {
@ -313,12 +314,6 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
if (to == network->mac()) if (to == network->mac())
return; 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 // Check if this packet is from someone other than the tap -- i.e. bridged in
bool fromBridged = false; bool fromBridged = false;
if (from != network->mac()) { 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); //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( RR->mc->send(
((!network->config().isPublic())&&(network->config().com)) ? &(network->config().com) : (const CertificateOfMembership *)0, ((!network->config().isPublic())&&(network->config().com)) ? &(network->config().com) : (const CertificateOfMembership *)0,
network->config().multicastLimit, 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 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)); 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))) ); const bool includeCom = ( (network->config().isPrivate()) && (network->config().com) && ((!toPeer)||(toPeer->needsOurNetworkMembershipCertificate(network->id(),RR->node->now(),true))) );
if ((fromBridged)||(includeCom)) { if ((fromBridged)||(includeCom)) {
Packet outp(toZT,RR->identity.address(),Packet::VERB_EXT_FRAME); Packet outp(toZT,RR->identity.address(),Packet::VERB_EXT_FRAME);

View File

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