diff --git a/node/EthernetTap.cpp b/node/EthernetTap.cpp index 688844640..b90c8708d 100644 --- a/node/EthernetTap.cpp +++ b/node/EthernetTap.cpp @@ -470,7 +470,7 @@ EthernetTap::EthernetTap(const RuntimeEnvironment *renv,const MAC &mac,unsigned waitpid(cpid,&exitcode,0); if (exitcode) { ::close(_fd); - throw std::runtime_error("ifconfig failure setting link-layer address and activating tap interface"); + throw std::runtime_error("ipconfig failure enabling IPv6 link-local addressing"); } } diff --git a/node/Filter.cpp b/node/Filter.cpp new file mode 100644 index 000000000..df72ccc5d --- /dev/null +++ b/node/Filter.cpp @@ -0,0 +1,106 @@ +/* + * ZeroTier One - Global Peer to Peer Ethernet + * Copyright (C) 2012-2013 ZeroTier Networks LLC + * + * 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 . + * + * -- + * + * ZeroTier may be used and distributed under the terms of the GPLv3, which + * are available at: http://www.gnu.org/licenses/gpl-3.0.html + * + * If you would like to embed ZeroTier into a commercial application or + * redistribute it in a modified binary form, please contact ZeroTier Networks + * LLC. Start here: http://www.zerotier.com/ + */ + +#include + +#include "RuntimeEnvironment.hpp" +#include "Logger.hpp" +#include "Filter.hpp" + +namespace ZeroTier { + +bool Filter::Rule::operator()(unsigned int etype,const void *data,unsigned int len) const +{ + if ((_etherType >= 0)&&(etype != (unsigned int)_etherType)) + return false; // ethertype mismatch + + switch(etype) { + case ZT_ETHERTYPE_IPV4: + if (len < 20) + return false; // invalid packets don't match + if ((_protocol >= 0)&&(((const uint8_t *)data)[9] != (uint8_t)(_protocol & 0xff))) + return false; // IP protocol # mismatch + + switch(((const uint8_t *)data)[9]) { + } + + break; + case ZT_ETHERTYPE_IPV6 + break; + } + + return false; +} + +Filter::Filter(const RuntimeEnvironment *renv) : + _r(renv) +{ +} + +Filter::~Filter() +{ +} + +void Filter::add(const Rule &r,const Action &a) +{ + Mutex::Lock _l(_chain_m); + for(std::vector::iterator i(_chain.begin());i!=_chain.end();++i) { + if (i->rule == r) { + _chain.erase(i); + break; + } + } + _chain.push_back(Entry(r,a)); +} + +const char *Filter::etherTypeName(const unsigned int etherType) + throw() +{ + static char tmp[6]; + switch(etherType) { + case ZT_ETHERTYPE_IPV4: + return "IPV4"; + case ZT_ETHERTYPE_ARP: + return "ARP"; + case ZT_ETHERTYPE_RARP: + return "RARP"; + case ZT_ETHERTYPE_ATALK: + return "ATALK"; + case ZT_ETHERTYPE_AARP: + return "AARP"; + case ZT_ETHERTYPE_IPX_A: + return "IPX_A"; + case ZT_ETHERTYPE_IPX_B: + return "IPX_B"; + case ZT_ETHERTYPE_IPV6: + return "IPV6"; + } + sprintf(tmp,"%.4x",etherType); + return tmp; // technically not thread safe, but we're only going to see this in debugging if ever +} + +} // namespace ZeroTier diff --git a/node/Filter.hpp b/node/Filter.hpp new file mode 100644 index 000000000..8f2825080 --- /dev/null +++ b/node/Filter.hpp @@ -0,0 +1,239 @@ +/* + * ZeroTier One - Global Peer to Peer Ethernet + * Copyright (C) 2012-2013 ZeroTier Networks LLC + * + * 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 . + * + * -- + * + * ZeroTier may be used and distributed under the terms of the GPLv3, which + * are available at: http://www.gnu.org/licenses/gpl-3.0.html + * + * If you would like to embed ZeroTier into a commercial application or + * redistribute it in a modified binary form, please contact ZeroTier Networks + * LLC. Start here: http://www.zerotier.com/ + */ + +#ifndef _ZT_FILTER_HPP +#define _ZT_FILTER_HPP + +#include +#include +#include +#include +#include + +#include "Mutex.hpp" +#include "Range.hpp" + +/* Ethernet frame types that might be relevant to us */ +#define ZT_ETHERTYPE_IPV4 0x0800 +#define ZT_ETHERTYPE_ARP 0x0806 +#define ZT_ETHERTYPE_RARP 0x8035 +#define ZT_ETHERTYPE_ATALK 0x809b +#define ZT_ETHERTYPE_AARP 0x80f3 +#define ZT_ETHERTYPE_IPX_A 0x8137 +#define ZT_ETHERTYPE_IPX_B 0x8138 +#define ZT_ETHERTYPE_IPV6 0x86dd + +/* IP protocols we might care about */ +#define ZT_IPPROTO_ICMP 0x01 +#define ZT_IPPROTO_IGMP 0x02 +#define ZT_IPPROTO_TCP 0x06 +#define ZT_IPPROTO_UDP 0x11 +#define ZT_IPPROTO_RDP 0x1b +#define ZT_IPPROTO_GRE 0x2f +#define ZT_IPPROTO_ESP 0x32 +#define ZT_IPPROTO_AH 0x33 +#define ZT_IPPROTO_ICMPV6 0x3a +#define ZT_IPPROTO_OSPF 0x59 +#define ZT_IPPROTO_IPIP 0x5e +#define ZT_IPPROTO_IPCOMP 0x6c +#define ZT_IPPROTO_L2TP 0x73 +#define ZT_IPPROTO_SCTP 0x84 +#define ZT_IPPROTO_FC 0x85 +#define ZT_IPPROTO_UDPLITE 0x88 +#define ZT_IPPROTO_HIP 0x8b + +namespace ZeroTier { + +class RuntimeEnvironment; + +/** + * A simple Ethernet frame level filter supporting basic IP port DENY + */ +class Filter +{ +public: + /** + * A filter rule + * + * This behaves as an immutable value object. + */ + class Rule + { + public: + Rule() + throw() : + _etherType(-1), + _protocol(-1), + _port(-1) + { + } + + /** + * Construct a new rule + * + * @param etype Ethernet type or negative for ANY + * @param prot Protocol or negative for ANY (meaning depends on ethertype, e.g. IP protocol numbers) + * @param prt Port or negative for ANY (only applies to some protocols) + */ + Rule(int etype,int prot,int prt) + throw() : + _etherType((etype >= 0) ? etype : -1), + _protocol((prot >= 0) ? prot : -1), + _port((prt >= 0) ? prt : -1) + { + } + + inline int etherType() const throw() { return _etherType; } + inline int protocol() const throw() { return _protocol; } + inline int port() const throw() { return _port; } + + /** + * Test this rule against a frame + * + * @param etype Type of ethernet frame + * @param data Ethernet frame data + * @param len Length of ethernet frame + * @return True if rule matches + */ + bool operator()(unsigned int etype,const void *data,unsigned int len) const; + + inline bool operator==(const Rule &r) const throw() { return ((_etherType == r._etherType)&&(_protocol == r._protocol)&&(_port == r._port)); } + inline bool operator!=(const Rule &r) const throw() { return !(*this == r); } + inline bool operator<(const Rule &r) const + throw() + { + if (_etherType < r._etherType) + return true; + else if (_etherType == r._etherType) { + if (_protocol < r._protocol) + return true; + else if (_protocol == r._protocol) { + if (_port < r._port) + return true; + } + } + return false; + } + inline bool operator>(const Rule &r) const throw() { return (r < *this); } + inline bool operator<=(const Rule &r) const throw() { return !(r < *this); } + inline bool operator>=(const Rule &r) const throw() { return !(*this < r); } + + private: + int _etherType; + int _protocol; + int _port; + }; + + /** + * Action if a rule matches + */ + enum Action + { + ACTION_DENY = 0, + ACTION_ALLOW = 1, + ACTION_LOG = 2 + }; + + /** + * Entry in filter chain + */ + struct Entry + { + Entry() {} + Entry(const Rule &r,const Action &a) : + rule(r), + action(a) + { + } + + Rule rule; + Action action; + }; + + Filter(const RuntimeEnvironment *renv); + ~Filter(); + + /** + * Remove all filter entries + */ + inline void clear() + { + Mutex::Lock _l(_chain_m); + _chain.clear(); + } + + /** + * Append a rule/action pair to this chain + * + * If an identical rule already exists it is removed and a new entry is + * added to the end with the new action. (Two identical rules with the + * same action wouldn't make sense.) + * + * @param r Rule to add + * @param a Action if rule matches + */ + void add(const Rule &r,const Action &a); + + /** + * @return Number of rules in filter chain + */ + inline unsigned int length() const + throw() + { + Mutex::Lock _l(_chain_m); + return _chain.length(); + } + + /** + * @return Entry in filter chain or null entry if out of bounds + */ + inline Entry operator[](const unsigned int i) const + throw() + { + Mutex::Lock _l(_chain_m); + if (i < _chain.length()) + return _chain[i]; + return Entry(); + } + + /** + * @param etherType Ethernet type ID + * @return Name of Ethernet protocol (e.g. ARP, IPV4) + */ + static const char *etherTypeName(const unsigned int etherType) + throw(); + +private: + const RuntimeEnvironment *_r; + + std::vector _chain; + Mutex _chain_m; +}; + +} // namespace ZeroTier + +#endif diff --git a/node/Range.hpp b/node/Range.hpp new file mode 100644 index 000000000..7e3785b59 --- /dev/null +++ b/node/Range.hpp @@ -0,0 +1,123 @@ +/* + * ZeroTier One - Global Peer to Peer Ethernet + * Copyright (C) 2012-2013 ZeroTier Networks LLC + * + * 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 . + * + * -- + * + * ZeroTier may be used and distributed under the terms of the GPLv3, which + * are available at: http://www.gnu.org/licenses/gpl-3.0.html + * + * If you would like to embed ZeroTier into a commercial application or + * redistribute it in a modified binary form, please contact ZeroTier Networks + * LLC. Start here: http://www.zerotier.com/ + */ + +#ifndef _ZT_RANGE_HPP +#define _ZT_RANGE_HPP + +namespace ZeroTier { + +/** + * A range of numeric values + * + * @tparam T Type, can be any numeric value (int, float, double, etc.) + */ +template +class Range +{ +public: + /** + * Construct an empty range + */ + Range() + throw() : + start(0), + end(0) + { + } + + /** + * @param s Starting value (inclusive) + * @param e Ending value (exclusive) + */ + Range(T s,T e) + throw() : + start(s), + end(e) + { + } + + /** + * Construct a range containing from n to n+1 (thus only n for integers) + * + * @param n Number to contain + */ + Range(T n) + throw() : + start(n), + end(n+1) + { + } + + /** + * @return end - start + */ + inline T magnitude() const + throw() + { + return (end - start); + } + + /** + * @return True if range contains something (magnitude is nonzero) + */ + inline operator bool() const + throw() + { + return (end > start); + } + + /** + * @param v Value to test + * @return True if value is between start (inclusive) and end (exclusive) + */ + inline bool operator()(const T &v) const + throw() + { + return ((v >= start)&&(v < end)); + } + + inline bool operator==(const Range &r) const throw() { return ((start == r.start)&&(end == r.end)); } + inline bool operator!=(const Range &r) const throw() { return (!(*this == r)); } + inline bool operator<(const Range &r) const throw() { return ((start < r.start) ? true : ((start == r.start) ? (end < r.end) : false)); } + inline bool operator>(const Range &r) const throw() { return (r < *this); } + inline bool operator<=(const Range &r) const throw() { return !(r < *this); } + inline bool operator>=(const Range &r) const throw() { return !(*this < r); } + + /** + * Start of range (may be modified directly) + */ + T start; + + /** + * End of range (may be modified directly) + */ + T end; +}; + +} // namespace ZeroTier + +#endif diff --git a/node/Utils.cpp b/node/Utils.cpp index 7e55d26ba..b9db07b9a 100644 --- a/node/Utils.cpp +++ b/node/Utils.cpp @@ -283,31 +283,6 @@ std::string Utils::base64Decode(const char *data,unsigned int len) return out.substr(0,outLen); } -const char *Utils::etherTypeName(const unsigned int etherType) -{ - static char tmp[6]; - switch(etherType) { - case ZT_ETHERTYPE_IPV4: - return "IPV4"; - case ZT_ETHERTYPE_ARP: - return "ARP"; - case ZT_ETHERTYPE_RARP: - return "RARP"; - case ZT_ETHERTYPE_ATALK: - return "ATALK"; - case ZT_ETHERTYPE_AARP: - return "AARP"; - case ZT_ETHERTYPE_IPX_A: - return "IPX_A"; - case ZT_ETHERTYPE_IPX_B: - return "IPX_B"; - case ZT_ETHERTYPE_IPV6: - return "IPV6"; - } - sprintf(tmp,"%.4x",etherType); - return tmp; // technically not thread safe, but we're only going to see this in debugging if ever -} - std::string Utils::hex(const void *data,unsigned int len) { std::string r; diff --git a/node/Utils.hpp b/node/Utils.hpp index b8aced638..5cdf10def 100644 --- a/node/Utils.hpp +++ b/node/Utils.hpp @@ -44,16 +44,6 @@ #include "Constants.hpp" -/* Ethernet frame types that might be relevant to us */ -#define ZT_ETHERTYPE_IPV4 0x0800 -#define ZT_ETHERTYPE_ARP 0x0806 -#define ZT_ETHERTYPE_RARP 0x8035 -#define ZT_ETHERTYPE_ATALK 0x809b -#define ZT_ETHERTYPE_AARP 0x80f3 -#define ZT_ETHERTYPE_IPX_A 0x8137 -#define ZT_ETHERTYPE_IPX_B 0x8138 -#define ZT_ETHERTYPE_IPV6 0x86dd - /** * Maximum compression/decompression block size (do not change) */ @@ -67,12 +57,6 @@ namespace ZeroTier { class Utils { public: - /** - * @param etherType Ethernet type ID - * @return Name of Ethernet protocol (e.g. ARP, IPV4) - */ - static const char *etherTypeName(const unsigned int etherType); - /** * @param data Data to convert to hex * @param len Length of data