summaryrefslogtreecommitdiff
path: root/node
diff options
context:
space:
mode:
Diffstat (limited to 'node')
-rw-r--r--node/Filter.cpp229
-rw-r--r--node/Filter.hpp73
-rw-r--r--node/NetworkConfig.cpp20
3 files changed, 302 insertions, 20 deletions
diff --git a/node/Filter.cpp b/node/Filter.cpp
new file mode 100644
index 00000000..d0fccb9d
--- /dev/null
+++ b/node/Filter.cpp
@@ -0,0 +1,229 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdint.h>
+
+#include "Constants.hpp"
+#include "RuntimeEnvironment.hpp"
+#include "Address.hpp"
+#include "MAC.hpp"
+#include "InetAddress.hpp"
+#include "Filter.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)
+{
+ if (frameLen < 40)
+ return false;
+ pos = 40;
+ proto = frameData[6];
+ while (pos <= frameLen) {
+ switch(proto) {
+ case 0: // hop-by-hop options
+ case 43: // routing
+ case 60: // destination options
+ case 135: // mobility options
+ if ((pos + 8) > frameLen)
+ return false; // invalid!
+ proto = frameData[pos];
+ pos += ((unsigned int)frameData[pos + 1] * 8) + 8;
+ break;
+
+ //case 44: // fragment -- we currently can't parse these and they are deprecated in IPv6 anyway
+ //case 50:
+ //case 51: // IPSec ESP and AH -- we have to stop here since this is encrypted stuff
+ default:
+ return true;
+ }
+ }
+ return false; // overflow == invalid
+}
+
+namespace ZeroTier {
+
+const ZT_VirtualNetworkRule *Filter::run(
+ const RuntimeEnvironment *RR,
+ const Address &ztSource,
+ const Address &ztDest,
+ const MAC &macSource,
+ const MAC &macDest,
+ const uint8_t *frameData,
+ const unsigned int frameLen,
+ const unsigned int etherType,
+ const unsigned int vlanId,
+ const ZT_VirtualNetworkRule *rules,
+ const unsigned int ruleCount)
+{
+ 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) {
+ 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;
+ break;
+
+ case ZT_NETWORK_RULE_MATCH_SOURCE_ZEROTIER_ADDRESS:
+ thisRuleMatches = (uint8_t)(rules[rn].v.zt == ztSource.toInt());
+ break;
+ case ZT_NETWORK_RULE_MATCH_DEST_ZEROTIER_ADDRESS:
+ thisRuleMatches = (uint8_t)(rules[rn].v.zt == ztDest.toInt());
+ break;
+ case ZT_NETWORK_RULE_MATCH_VLAN_ID:
+ thisRuleMatches = (uint8_t)(rules[rn].v.vlanId == (uint16_t)vlanId);
+ break;
+ case ZT_NETWORK_RULE_MATCH_VLAN_PCP:
+ // NOT SUPPORTED YET
+ thisRuleMatches = (uint8_t)(rules[rn].v.vlanPcp == 0);
+ break;
+ case ZT_NETWORK_RULE_MATCH_VLAN_DEI:
+ // NOT SUPPORTED YET
+ thisRuleMatches = (uint8_t)(rules[rn].v.vlanDei == 0);
+ break;
+ case ZT_NETWORK_RULE_MATCH_ETHERTYPE:
+ thisRuleMatches = (uint8_t)(rules[rn].v.etherType == (uint16_t)etherType);
+ break;
+ case ZT_NETWORK_RULE_MATCH_MAC_SOURCE:
+ thisRuleMatches = (uint8_t)(MAC(rules[rn].v.mac,6) == macSource);
+ break;
+ case ZT_NETWORK_RULE_MATCH_MAC_DEST:
+ thisRuleMatches = (uint8_t)(MAC(rules[rn].v.mac,6) == macDest);
+ break;
+ case ZT_NETWORK_RULE_MATCH_IPV4_SOURCE:
+ if ((etherType == ZT_ETHERTYPE_IPV4)&&(frameLen >= 20)) {
+ thisRuleMatches = (uint8_t)(InetAddress((const void *)&(rules[rn].v.ipv4.ip),4,rules[rn].v.ipv4.mask).containsAddress(InetAddress((const void *)(frameData + 12),4,0)));
+ } else {
+ thisRuleMatches = 0;
+ }
+ break;
+ case ZT_NETWORK_RULE_MATCH_IPV4_DEST:
+ if ((etherType == ZT_ETHERTYPE_IPV4)&&(frameLen >= 20)) {
+ thisRuleMatches = (uint8_t)(InetAddress((const void *)&(rules[rn].v.ipv4.ip),4,rules[rn].v.ipv4.mask).containsAddress(InetAddress((const void *)(frameData + 16),4,0)));
+ } else {
+ thisRuleMatches = 0;
+ }
+ break;
+ case ZT_NETWORK_RULE_MATCH_IPV6_SOURCE:
+ if ((etherType == ZT_ETHERTYPE_IPV6)&&(frameLen >= 40)) {
+ thisRuleMatches = (uint8_t)(InetAddress((const void *)rules[rn].v.ipv6.ip,16,rules[rn].v.ipv6.mask).containsAddress(InetAddress((const void *)(frameData + 8),16,0)));
+ } else {
+ thisRuleMatches = 0;
+ }
+ break;
+ case ZT_NETWORK_RULE_MATCH_IPV6_DEST:
+ if ((etherType == ZT_ETHERTYPE_IPV6)&&(frameLen >= 40)) {
+ thisRuleMatches = (uint8_t)(InetAddress((const void *)rules[rn].v.ipv6.ip,16,rules[rn].v.ipv6.mask).containsAddress(InetAddress((const void *)(frameData + 24),16,0)));
+ } else {
+ thisRuleMatches = 0;
+ }
+ break;
+ case ZT_NETWORK_RULE_MATCH_IP_TOS:
+ if ((etherType == ZT_ETHERTYPE_IPV4)&&(frameLen >= 20)) {
+ thisRuleMatches = (uint8_t)(rules[rn].v.ipTos == ((frameData[1] & 0xfc) >> 2));
+ } else if ((etherType == ZT_ETHERTYPE_IPV6)&&(frameLen >= 40)) {
+ const uint8_t trafficClass = ((frameData[0] << 4) & 0xf0) | ((frameData[1] >> 4) & 0x0f);
+ thisRuleMatches = (uint8_t)(rules[rn].v.ipTos == ((trafficClass & 0xfc) >> 2));
+ } else {
+ thisRuleMatches = 0;
+ }
+ break;
+ case ZT_NETWORK_RULE_MATCH_IP_PROTOCOL:
+ if ((etherType == ZT_ETHERTYPE_IPV4)&&(frameLen >= 20)) {
+ thisRuleMatches = (uint8_t)(rules[rn].v.ipProtocol == frameData[9]);
+ } else if (etherType == ZT_ETHERTYPE_IPV6) {
+ unsigned int pos = 0,proto = 0;
+ if (_ipv6GetPayload(frameData,frameLen,pos,proto)) {
+ thisRuleMatches = (uint8_t)(rules[rn].v.ipProtocol == (uint8_t)proto);
+ } else {
+ thisRuleMatches = 0;
+ }
+ } else {
+ thisRuleMatches = 0;
+ }
+ break;
+ case ZT_NETWORK_RULE_MATCH_IP_SOURCE_PORT_RANGE:
+ case ZT_NETWORK_RULE_MATCH_IP_DEST_PORT_RANGE:
+ if ((etherType == ZT_ETHERTYPE_IPV4)&&(frameLen >= 20)) {
+ const unsigned int headerLen = 4 * (frameData[0] & 0xf);
+ int p = -1;
+ switch(frameData[9]) { // IP protocol number
+ // All these start with 16-bit source and destination port in that order
+ case 0x06: // TCP
+ case 0x11: // UDP
+ case 0x84: // SCTP
+ case 0x88: // UDPLite
+ if (frameLen > (headerLen + 4)) {
+ unsigned int pos = headerLen + (((unsigned int)(rt == ZT_NETWORK_RULE_MATCH_IP_DEST_PORT_RANGE)) << 1); // headerLen or +2 for destination port
+ p = (int)frameData[pos++] << 8;
+ p |= (int)frameData[pos];
+ }
+ break;
+ }
+ thisRuleMatches = (p > 0) ? (uint8_t)((p >= (int)rules[rn].v.port[0])&&(p <= (int)rules[rn].v.port[1])) : (uint8_t)0;
+ } else if (etherType == ZT_ETHERTYPE_IPV6) {
+ unsigned int pos = 0,proto = 0;
+ if (_ipv6GetPayload(frameData,frameLen,pos,proto)) {
+ int p = -1;
+ switch(proto) { // IP protocol number
+ // All these start with 16-bit source and destination port in that order
+ case 0x06: // TCP
+ case 0x11: // UDP
+ case 0x84: // SCTP
+ case 0x88: // UDPLite
+ if (frameLen > (pos + 4)) {
+ p = (int)frameData[pos++] << 8;
+ p |= (int)frameData[pos];
+ }
+ break;
+ }
+ thisRuleMatches = (p > 0) ? (uint8_t)((p >= (int)rules[rn].v.port[0])&&(p <= (int)rules[rn].v.port[1])) : (uint8_t)0;
+ } else {
+ thisRuleMatches = 0;
+ }
+ } else {
+ thisRuleMatches = 0;
+ }
+ break;
+ case ZT_NETWORK_RULE_MATCH_CHARACTERISTICS:
+ /*
+ if (etherType == ZT_ETHERTYPE_IPV4) {
+ } else if (etherType == ZT_ETHERTYPE_IPV6) {
+ } else {
+ thisRuleMatches = 0;
+ }
+ */
+ 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));
+ }
+
+ return (const ZT_VirtualNetworkRule *)0; // no matches
+}
+
+} // namespace ZeroTier
diff --git a/node/Filter.hpp b/node/Filter.hpp
new file mode 100644
index 00000000..c391e958
--- /dev/null
+++ b/node/Filter.hpp
@@ -0,0 +1,73 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef ZT_FILTER_HPP
+#define ZT_FILTER_HPP
+
+#include <stdint.h>
+
+#include "Constants.hpp"
+#include "../include/ZeroTierOne.h"
+
+namespace ZeroTier {
+
+class Address;
+class RuntimeEnvironment;
+class MAC;
+
+/**
+ * Network packet filter for rules engine
+ */
+class Filter
+{
+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.
+ *
+ * @param ztSource Source ZeroTier address
+ * @param ztDest Destination ZeroTier address
+ * @param macSource Ethernet layer source address
+ * @param macDest Ethernet layer destination address
+ * @param frameData Ethernet frame data
+ * @param frameLen Ethernet frame payload length
+ * @param etherType 16-bit ethernet type ID
+ * @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(
+ const RuntimeEnvironment *RR,
+ const Address &ztSource,
+ const Address &ztDest,
+ const MAC &macSource,
+ const MAC &macDest,
+ const uint8_t *frameData,
+ const unsigned int frameLen,
+ const unsigned int etherType,
+ const unsigned int vlanId,
+ const ZT_VirtualNetworkRule *rules,
+ const unsigned int ruleCount);
+};
+
+} // namespace ZeroTier
+
+#endif
diff --git a/node/NetworkConfig.cpp b/node/NetworkConfig.cpp
index 9d5c5f17..3a307fe7 100644
--- a/node/NetworkConfig.cpp
+++ b/node/NetworkConfig.cpp
@@ -245,17 +245,6 @@ bool NetworkConfig::toDictionary(Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> &d,b
tmp.append((uint16_t)rules[i].v.frameSize[0]);
tmp.append((uint16_t)rules[i].v.frameSize[1]);
break;
- case ZT_NETWORK_RULE_MATCH_TCP_RELATIVE_SEQUENCE_NUMBER_RANGE:
- tmp.append((uint8_t)8);
- tmp.append((uint32_t)rules[i].v.tcpseq[0]);
- tmp.append((uint32_t)rules[i].v.tcpseq[1]);
- break;
- case ZT_NETWORK_RULE_MATCH_COM_FIELD_GE:
- case ZT_NETWORK_RULE_MATCH_COM_FIELD_LE:
- tmp.append((uint8_t)16);
- tmp.append((uint64_t)rules[i].v.comIV[0]);
- tmp.append((uint64_t)rules[i].v.comIV[1]);
- break;
}
}
if (tmp.size()) {
@@ -472,15 +461,6 @@ bool NetworkConfig::fromDictionary(const Dictionary<ZT_NETWORKCONFIG_DICT_CAPACI
rules[ruleCount].v.frameSize[0] = tmp.at<uint16_t>(p);
rules[ruleCount].v.frameSize[0] = tmp.at<uint16_t>(p + 2);
break;
- case ZT_NETWORK_RULE_MATCH_TCP_RELATIVE_SEQUENCE_NUMBER_RANGE:
- rules[ruleCount].v.tcpseq[0] = tmp.at<uint32_t>(p);
- rules[ruleCount].v.tcpseq[1] = tmp.at<uint32_t>(p + 4);
- break;
- case ZT_NETWORK_RULE_MATCH_COM_FIELD_GE:
- case ZT_NETWORK_RULE_MATCH_COM_FIELD_LE:
- rules[ruleCount].v.comIV[0] = tmp.at<uint64_t>(p);
- rules[ruleCount].v.comIV[1] = tmp.at<uint64_t>(p + 8);
- break;
}
p += fieldLen;
++ruleCount;