diff options
author | Adam Ierymenko <adam.ierymenko@gmail.com> | 2016-07-25 16:51:10 -0700 |
---|---|---|
committer | Adam Ierymenko <adam.ierymenko@gmail.com> | 2016-07-25 16:51:10 -0700 |
commit | 7404eb46c4279b1e2ecce29aece14e15fbedbffd (patch) | |
tree | ac2cc184f70e433c2f05c04bf25ef5fca8aff10e /node | |
parent | eaf6d6c9384ce31d025ea5b82013de7064b0c047 (diff) | |
download | infinitytier-7404eb46c4279b1e2ecce29aece14e15fbedbffd.tar.gz infinitytier-7404eb46c4279b1e2ecce29aece14e15fbedbffd.zip |
Integration of Filter into inbound and outbound packet path.
Diffstat (limited to 'node')
-rw-r--r-- | node/Filter.cpp | 54 | ||||
-rw-r--r-- | node/Filter.hpp | 10 | ||||
-rw-r--r-- | node/IncomingPacket.cpp | 81 | ||||
-rw-r--r-- | node/NetworkConfig.hpp | 20 | ||||
-rw-r--r-- | node/Switch.cpp | 44 |
5 files changed, 149 insertions, 60 deletions
diff --git a/node/Filter.cpp b/node/Filter.cpp index d0fccb9d..1510f820 100644 --- a/node/Filter.cpp +++ b/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 diff --git a/node/Filter.hpp b/node/Filter.hpp index c391e958..f8b66134 100644 --- a/node/Filter.hpp +++ b/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, diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index 37af8425..b666e42c 100644 --- a/node/IncomingPacket.cpp +++ b/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) { diff --git a/node/NetworkConfig.hpp b/node/NetworkConfig.hpp index 5271c5ad..f2dab6d3 100644 --- a/node/NetworkConfig.hpp +++ b/node/NetworkConfig.hpp @@ -216,26 +216,6 @@ public: } /** - * @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 * * @param d Dictionary diff --git a/node/Switch.cpp b/node/Switch.cpp index bf3afe33..f644774f 100644 --- a/node/Switch.cpp +++ b/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); |