Compare commits

...
15 Commits
Author SHA1 Message Date
Adam Ierymenko 1ecf6ed3d0 0.2.3 - OSX bug fix, multicast propagation fix, no incompatible protocol changes 2013-07-08 20:54:09 -04:00
Adam Ierymenko 775fef9ce9 Silly multicast propagation fix: exclude upstream sender to never send duplicate multicasts back to where they came from 2013-07-08 20:53:05 -04:00
Adam Ierymenko 6eb77da094 Fix for issue #6: OSX tap device forgets it has IPv6 2013-07-08 20:36:33 -04:00
Adam Ierymenko 366f556e5b Filter work 2013-07-08 20:05:29 -04:00
Adam Ierymenko e7f20ad5f9 More filter development. It builds but is not integrated with the rest of the code. 2013-07-08 19:52:40 -04:00
Adam Ierymenko f8cfdf973e More filter work, and doc updates 2013-07-08 00:21:43 -04:00
Adam Ierymenko e2a2d33f8f Filter code, work in progress, wont build yet 2013-07-07 19:36:57 -04:00
Adam Ierymenko c68ab6b70f add flattr button 2013-07-07 12:32:08 -04:00
Adam Ierymenko 3397273322 Increase verbosity of TRACE messages for dropped duplicate multicast frames to help debug multicast propagation 2013-07-06 22:18:19 -04:00
Adam Ierymenko 0a7a8b415d Merge pull request #5 from Cubox-/master
The -O6 flag is useless. -O3 will do the same.
2013-07-06 14:20:32 -07:00
Andy Pilate 9eec6adc8c -O6 Do not exist and is the same as -O3 2013-07-06 22:54:45 +02:00
Andy Pilate 5c543b3b4a -O6 Do not exist and is the same as -O3 2013-07-06 22:54:19 +02:00
Adam Ierymenko 2fffdfdaf5 Version 0.2.2 - still compatible, now measures latency for everyone, broadcast enabled 2013-07-06 16:37:15 -04:00
Adam Ierymenko ef08494237 Send HELLO instead of NOP for NAT-t in order to measure latency always. Also prevents a race that can cause the first NAT-t to fail where the NOP arrives before the WHOIS reply from the supernode. Now NAT-t initiators will push their own public keys anyway so that doesnt matter. 2013-07-06 16:20:35 -04:00
Adam Ierymenko 2eaac3891e Enable ff:ff:ff:ff:ff:ff w/no ADI a.k.a. broadcast. YOLO. 2013-07-06 15:56:12 -04:00
17 changed files with 765 additions and 82 deletions

No files matched your search

+1 -1
View File
@@ -6,7 +6,7 @@ ARCH=$(shell uname -m)
DEFS=-DZT_ARCH="$(ARCH)" -DZT_OSNAME="linux" -DZT_TRACE
# Uncomment for a release optimized build
CFLAGS=-Wall -O6 -fno-unroll-loops -pthread $(INCLUDES) -DNDEBUG $(DEFS)
CFLAGS=-Wall -O3 -fno-unroll-loops -pthread $(INCLUDES) -DNDEBUG $(DEFS)
STRIP=strip --strip-all
# Uncomment for a debug build
+1 -1
View File
@@ -5,7 +5,7 @@ INCLUDES=-Iext/bin/libcrypto/include -Iext/jsoncpp/include
DEFS=-DZT_ARCH="x86_combined" -DZT_OSNAME="mac" -DZT_TRACE
# Uncomment for a release optimized universal binary build
CFLAGS=-arch i386 -arch x86_64 -Wall -O6 -ftree-vectorize -pthread -mmacosx-version-min=10.6 -DNDEBUG $(INCLUDES) $(DEFS)
CFLAGS=-arch i386 -arch x86_64 -Wall -O3 -ftree-vectorize -pthread -mmacosx-version-min=10.6 -DNDEBUG $(INCLUDES) $(DEFS)
STRIP=strip
# Uncomment for a debug build
+5
View File
@@ -47,6 +47,9 @@ Check out the [blog](http://blog.zerotier.com/) for announcements, in-depth arti
**Q:** Can you see my traffic? What about other users? Can you sniff the LAN?
**A:** No. All unicast (direct computer to computer) traffic is encrypted end-to-end (even if it's being relayed), and the ZeroTier virtual LAN behaves like a LAN with a secure enterprise-grade switch that does not allow unicast conversations to be sniffed. Multicast and broadcast traffic will of course be seen by many recipients, but that's the idea.
**Q:** What about privacy? Does this hide my location on the network?
**A:** ZeroTier is not a connection anonymizer. Other than encryption, it doesn't do anything special to hide your identity or network location. If you want strong privacy protection there are already very advanced tools like [Tor](https://www.torproject.org) for that, and this isn't trying to duplicate their functionality. At the same time, ZT does not do anything special to harm your privacy either. It's not spyware or snoop-ware.
**Q:** You say "almost unlimited size." Isn't multicast and broadcast traffic eventually going to be too much? What happens then?
**A:** ZeroTier One uses an algorithm called *implicit social switching*. The overall maximum number of recipients for a multicast is limited, so if there are too many listeners to a given multicast address then obviously not everyone will receive every message. So who does? Social switching causes multicasts to propagate in the direction of peers to whom you have recently communicated. As a result, multicasts tend to propagate along lines of association. The people most likely to get your service announcements are those with whom you frequently connect.
@@ -89,4 +92,6 @@ Check out the [blog](http://blog.zerotier.com/) for announcements, in-depth arti
----
<a href="http://flattr.com/thing/1611614/ZeroTier-Networks" target="_blank"><img src="http://api.flattr.com/button/flattr-badge-large.png" alt="Flattr this" title="Flattr this" border="0" /></a>
(c)2012-2013 [ZeroTier Networks LLC](https://www.zerotier.com/)
+29 -12
View File
@@ -31,6 +31,10 @@
#include "Logger.hpp"
#include "RuntimeEnvironment.hpp"
#include "Mutex.hpp"
#include "MulticastGroup.hpp"
// ff:ff:ff:ff:ff:ff with no ADI
static const ZeroTier::MulticastGroup _blindWildcardMulticastGroup(ZeroTier::MAC(0xff),0);
/* ======================================================================== */
#if defined(__linux__) || defined(linux) || defined(__LINUX__) || defined(__linux)
@@ -160,6 +164,11 @@ EthernetTap::~EthernetTap()
delete [] _putBuf;
}
void EthernetTap::whack()
{
// Linux requires nothing here
}
static bool ___removeIp(const char *_dev,std::set<InetAddress> &_ips,const InetAddress &ip)
{
long cpid;
@@ -337,6 +346,8 @@ bool EthernetTap::updateMulticastGroups(std::set<MulticastGroup> &groups)
bool changed = false;
newGroups.insert(_blindWildcardMulticastGroup); // always join this
for(std::set<MulticastGroup>::iterator mg(newGroups.begin());mg!=newGroups.end();++mg) {
if (!groups.count(*mg)) {
groups.insert(*mg);
@@ -455,18 +466,7 @@ EthernetTap::EthernetTap(const RuntimeEnvironment *renv,const MAC &mac,unsigned
}
}
// OSX seems to require that IPv6 be turned on on tap devices
if ((cpid = (int)fork()) == 0) {
execl(ZT_MAC_IPCONFIG,ZT_MAC_IPCONFIG,"set",_dev,"AUTOMATIC-V6",(const char *)0);
exit(-1);
} else {
int exitcode = -1;
waitpid(cpid,&exitcode,0);
if (exitcode) {
::close(_fd);
throw std::runtime_error("ifconfig failure setting link-layer address and activating tap interface");
}
}
whack(); // turns on IPv6 on OSX
_putBuf = new unsigned char[((mtu + 14) * 2)];
_getBuf = _putBuf + (mtu + 14);
@@ -478,6 +478,21 @@ EthernetTap::~EthernetTap()
delete [] _putBuf;
}
void EthernetTap::whack()
{
int cpid = fork();
if (cpid == 0) {
execl(ZT_MAC_IPCONFIG,ZT_MAC_IPCONFIG,"set",_dev,"AUTOMATIC-V6",(const char *)0);
exit(-1);
} else {
int exitcode = -1;
waitpid(cpid,&exitcode,0);
if (exitcode) {
LOG("%s: ipconfig set AUTOMATIC-V6 failed",_dev);
}
}
}
// Helper function to actually remove IP from network device, execs ifconfig
static bool ___removeIp(const char *_dev,const InetAddress &ip)
{
@@ -658,6 +673,8 @@ bool EthernetTap::updateMulticastGroups(std::set<MulticastGroup> &groups)
bool changed = false;
newGroups.insert(_blindWildcardMulticastGroup); // always join this
for(std::set<MulticastGroup>::iterator mg(newGroups.begin());mg!=newGroups.end();++mg) {
if (!groups.count(*mg)) {
groups.insert(*mg);
+8
View File
@@ -68,6 +68,11 @@ public:
~EthernetTap();
/**
* Perform OS dependent actions on network configuration change detection
*/
void whack();
/**
* @return MAC address of this interface
*/
@@ -164,6 +169,9 @@ public:
* This populates a set or, if already populated, modifies it to contain
* only multicast groups in which this device is interested.
*
* This should always include the blind wildcard MulticastGroup (MAC of
* ff:ff:ff:ff:ff:ff and 0 ADI field).
*
* @param groups Set to modify in place
* @return True if set was changed since last call
*/
+241
View File
@@ -0,0 +1,241 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*
* --
*
* 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 <stdint.h>
#include "RuntimeEnvironment.hpp"
#include "Logger.hpp"
#include "Filter.hpp"
#include "Utils.hpp"
namespace ZeroTier {
bool Filter::Rule::operator()(unsigned int etype,const void *data,unsigned int len) const
{
if ((!_etherType)||(_etherType(etype))) { // ethertype is ANY, or matches
// Ethertype determines meaning of protocol and port
switch(etype) {
default:
if ((!_protocol)&&(!_port))
return true; // match other ethertypes if protocol and port are ANY, since we don't know what to do with them
break;
case ZT_ETHERTYPE_IPV4:
if (len > 20) {
if ((!_protocol)||(_protocol(((const uint8_t *)data)[9]))) { // IP protocol
if (!_port)
return true; // protocol matches or is ANY, port is ANY
// Don't match on fragments beyond fragment 0. If we've blocked
// fragment 0, further fragments will fall on deaf ears anyway.
if ((Utils::ntoh(((const uint16_t *)data)[3]) & 0x1fff))
return false;
// Internet header length determines where data begins, in multiples of 32 bits
unsigned int ihl = 4 * (((const uint8_t *)data)[0] & 0x0f);
switch(((const uint8_t *)data)[9]) { // port's meaning depends on IP protocol
case ZT_IPPROTO_ICMP:
return _port(((const uint8_t *)data)[ihl]); // port = ICMP type
case ZT_IPPROTO_TCP:
case ZT_IPPROTO_UDP:
case ZT_IPPROTO_SCTP:
case ZT_IPPROTO_UDPLITE:
return _port(((const uint16_t *)data)[(ihl / 2) + 1]); // destination port
}
return false; // no match on port
}
}
break;
case ZT_ETHERTYPE_IPV6:
if (len > 40) {
// see: http://stackoverflow.com/questions/17518951/is-the-ipv6-header-really-this-nutty
int nextHeader = ((const uint8_t *)data)[6];
unsigned int pos = 40;
while ((pos < len)&&(nextHeader >= 0)&&(nextHeader != 59)) { // 59 == no next header
fprintf(stderr,"[rule] V6: start header parse, header %.2x pos %d\n",nextHeader,pos);
switch(nextHeader) {
case 0: // hop-by-hop options
case 60: // destination options
case 43: // routing
case 135: // mobility (mobile IPv6 options)
if (_protocol((unsigned int)nextHeader))
return true; // match if our goal was to match any of these
nextHeader = ((const uint8_t *)data)[pos];
pos += 8 + (8 * ((const uint8_t *)data)[pos + 1]);
break;
case 44: // fragment
if (_protocol(44))
return true; // match if our goal was to match fragments
nextHeader = ((const uint8_t *)data)[pos];
pos += 8;
break;
case ZT_IPPROTO_AH: // AH
return _protocol(ZT_IPPROTO_AH); // true if AH is matched protocol, otherwise false since packet will be IPsec
case ZT_IPPROTO_ESP: // ESP
return _protocol(ZT_IPPROTO_ESP); // true if ESP is matched protocol, otherwise false since packet will be IPsec
case ZT_IPPROTO_ICMPV6:
if (_protocol(ZT_IPPROTO_ICMPV6)) { // only match ICMPv6 if specified
if ((!_port)||(_port(((const uint8_t *)data)[pos])))
return true; // protocol matches, port is ANY or matches ICMP type
}
break;
case ZT_IPPROTO_TCP:
case ZT_IPPROTO_UDP:
case ZT_IPPROTO_SCTP:
case ZT_IPPROTO_UDPLITE:
// If we encounter any of these, match if protocol matches or is wildcard as
// we'll consider these the "real payload" if present.
if ((!_protocol)||(_protocol(nextHeader))) {
if ((!_port)||(_port(((const uint16_t *)data)[(pos / 2) + 1])))
return true; // protocol matches or is ANY, port is ANY or matches
}
break;
}
fprintf(stderr,"[rule] V6: end header parse, next header %.2x, new pos %d\n",nextHeader,pos);
}
}
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<Entry>::iterator i(_chain.begin());i!=_chain.end();++i) {
if (i->rule == r) {
_chain.erase(i);
break;
}
}
_chain.push_back(Entry(r,a));
}
std::string Filter::toString(const char *sep) const
{
char buf[256];
if (!sep)
sep = ",";
std::string s;
Mutex::Lock _l(_chain_m);
for(std::vector<Entry>::const_iterator i(_chain.begin());i!=_chain.end();++i) {
bool first = (i == _chain.begin());
s.push_back('[');
if (i->rule.etherType()) {
if (i->rule.etherType().magnitude() > 1)
sprintf(buf,"%u-%u",i->rule.etherType().start,i->rule.etherType().end);
else sprintf(buf,"%u",i->rule.etherType().start);
s.append(buf);
} else s.push_back('*');
s.push_back(';');
if (i->rule.protocol()) {
if (i->rule.protocol().magnitude() > 1)
sprintf(buf,"%u-%u",i->rule.protocol().start,i->rule.protocol().end);
else sprintf(buf,"%u",i->rule.protocol().start);
s.append(buf);
} else s.push_back('*');
s.push_back(';');
if (i->rule.port()) {
if (i->rule.port().magnitude() > 1)
sprintf(buf,"%u-%u",i->rule.port().start,i->rule.port().end);
else sprintf(buf,"%u",i->rule.port().start);
s.append(buf);
} else s.push_back('*');
s.append("]:");
switch(i->action) {
case ACTION_DENY:
s.append("DENY");
break;
case ACTION_ALLOW:
s.append("ALLOW");
break;
case ACTION_LOG:
s.append("LOG");
break;
}
if (!first)
s.append(sep);
}
return s;
}
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
+300
View File
@@ -0,0 +1,300 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*
* --
*
* 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 <stdio.h>
#include <string.h>
#include <string>
#include <vector>
#include <utility>
#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_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
/* IPv4 ICMP types */
#define ZT_ICMP_ECHO_REPLY 0
#define ZT_ICMP_DESTINATION_UNREACHABLE 3
#define ZT_ICMP_SOURCE_QUENCH 4
#define ZT_ICMP_REDIRECT 5
#define ZT_ICMP_ALTERNATE_HOST_ADDRESS 6
#define ZT_ICMP_ECHO_REQUEST 8
#define ZT_ICMP_ROUTER_ADVERTISEMENT 9
#define ZT_ICMP_ROUTER_SOLICITATION 10
#define ZT_ICMP_TIME_EXCEEDED 11
#define ZT_ICMP_BAD_IP_HEADER 12
#define ZT_ICMP_TIMESTAMP 13
#define ZT_ICMP_TIMESTAMP_REPLY 14
#define ZT_ICMP_INFORMATION_REQUEST 15
#define ZT_ICMP_INFORMATION_REPLY 16
#define ZT_ICMP_ADDRESS_MASK_REQUEST 17
#define ZT_ICMP_ADDRESS_MASK_REPLY 18
#define ZT_ICMP_TRACEROUTE 30
#define ZT_ICMP_MOBILE_HOST_REDIRECT 32
#define ZT_ICMP_MOBILE_REGISTRATION_REQUEST 35
#define ZT_ICMP_MOBILE_REGISTRATION_REPLY 36
/* IPv6 ICMP types */
#define ZT_ICMP6_DESTINATION_UNREACHABLE 1
#define ZT_ICMP6_PACKET_TOO_BIG 2
#define ZT_ICMP6_TIME_EXCEEDED 3
#define ZT_ICMP6_PARAMETER_PROBLEM 4
#define ZT_ICMP6_ECHO_REQUEST 128
#define ZT_ICMP6_ECHO_REPLY 129
#define ZT_ICMP6_MULTICAST_LISTENER_QUERY 130
#define ZT_ICMP6_MULTICAST_LISTENER_REPORT 131
#define ZT_ICMP6_MULTICAST_LISTENER_DONE 132
#define ZT_ICMP6_ROUTER_SOLICITATION 133
#define ZT_ICMP6_ROUTER_ADVERTISEMENT 134
#define ZT_ICMP6_NEIGHBOR_SOLICITATION 135
#define ZT_ICMP6_NEIGHBOR_ADVERTISEMENT 136
#define ZT_ICMP6_REDIRECT_MESSAGE 137
#define ZT_ICMP6_ROUTER_RENUMBERING 138
#define ZT_ICMP6_NODE_INFORMATION_QUERY 139
#define ZT_ICMP6_NODE_INFORMATION_RESPONSE 140
#define ZT_ICMP6_INV_NEIGHBOR_SOLICITATION 141
#define ZT_ICMP6_INV_NEIGHBOR_ADVERTISEMENT 142
#define ZT_ICMP6_MLDV2 143
#define ZT_ICMP6_HOME_AGENT_ADDRESS_DISCOVERY_REQUEST 144
#define ZT_ICMP6_HOME_AGENT_ADDRESS_DISCOVERY_REPLY 145
#define ZT_ICMP6_MOBILE_PREFIX_SOLICITATION 146
#define ZT_ICMP6_MOBILE_PREFIX_ADVERTISEMENT 147
#define ZT_ICMP6_CERTIFICATION_PATH_SOLICITATION 148
#define ZT_ICMP6_CERTIFICATION_PATH_ADVERTISEMENT 149
#define ZT_ICMP6_MULTICAST_ROUTER_ADVERTISEMENT 151
#define ZT_ICMP6_MULTICAST_ROUTER_SOLICITATION 152
#define ZT_ICMP6_MULTICAST_ROUTER_TERMINATION 153
#define ZT_ICMP6_RPL_CONTROL_MESSAGE 155
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(),
_protocol(),
_port()
{
}
/**
* Construct a new rule
*
* @param etype Ethernet type or empty range for ANY
* @param prot Protocol or empty range for ANY (meaning depends on ethertype, e.g. IP protocol numbers)
* @param prt Port or empty range for ANY (only applies to some protocols)
*/
Rule(const Range<unsigned int> &etype,const Range<unsigned int> &prot,const Range<unsigned int> &prt)
throw() :
_etherType(etype),
_protocol(prot),
_port(prt)
{
}
inline const Range<unsigned int> &etherType() const throw() { return _etherType; }
inline const Range<unsigned int> &protocol() const throw() { return _protocol; }
inline const Range<unsigned 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:
Range<unsigned int> _etherType;
Range<unsigned int> _protocol;
Range<unsigned 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.size();
}
/**
* @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.size())
return _chain[i];
return Entry();
}
/**
* Get a string representation of this filter
*
* @param sep Separator between filter rules, or NULL for comma (default)
* @return Human-readable string
*/
std::string toString(const char *sep = (const char *)0) const;
/**
* @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<Entry> _chain;
Mutex _chain_m;
};
} // namespace ZeroTier
#endif
+2 -1
View File
@@ -273,11 +273,12 @@ Node::ReasonForTermination Node::run()
lastNetworkFingerprintCheck = now;
uint64_t fp = _r->sysEnv->getNetworkConfigurationFingerprint();
if (fp != networkConfigurationFingerprint) {
LOG("netconf fingerprint change: %.16llx != %.16llx, pinging all peers",networkConfigurationFingerprint,fp);
LOG("netconf fingerprint change: %.16llx != %.16llx, resyncing with network",networkConfigurationFingerprint,fp);
networkConfigurationFingerprint = fp;
pingAll = true;
lastAutoconfigureCheck = 0; // check autoconf after network config change
lastMulticastCheck = 0; // check multicast group membership after network config change
_r->nc->whackAllTaps(); // call whack() on all tap devices
}
}
+11
View File
@@ -78,6 +78,17 @@ public:
return nwlist;
}
/**
* Call whack() on all networks' tap devices
*/
inline void whackAllTaps()
{
std::vector< SharedPtr<Network> > nwlist;
Mutex::Lock _l(_networks_m);
for(std::map< uint64_t,SharedPtr<Network> >::const_iterator n(_networks.begin());n!=_networks.end();++n)
n->second->tap().whack();
}
/**
* @param nwid Network ID
* @return True if this network exists
+123
View File
@@ -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 <http://www.gnu.org/licenses/>.
*
* --
*
* 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<typename T>
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
+30 -23
View File
@@ -41,6 +41,7 @@
#include "Peer.hpp"
#include "NodeConfig.hpp"
#include "Demarc.hpp"
#include "Filter.hpp"
#include "../version.h"
@@ -233,7 +234,7 @@ Switch_onRemotePacket_complete_packet_handler:
void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,const MAC &to,unsigned int etherType,const Buffer<4096> &data)
{
if (from != network->tap().mac()) {
LOG("ignored tap: %s -> %s %s (bridging is not supported)",from.toString().c_str(),to.toString().c_str(),Utils::etherTypeName(etherType));
LOG("ignored tap: %s -> %s %s (bridging is not supported)",from.toString().c_str(),to.toString().c_str(),Filter::etherTypeName(etherType));
return;
}
@@ -245,16 +246,18 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
}
if ((etherType != ZT_ETHERTYPE_ARP)&&(etherType != ZT_ETHERTYPE_IPV4)&&(etherType != ZT_ETHERTYPE_IPV6)) {
LOG("ignored tap: %s -> %s %s (not a supported etherType)",from.toString().c_str(),to.toString().c_str(),Utils::etherTypeName(etherType));
LOG("ignored tap: %s -> %s %s (not a supported etherType)",from.toString().c_str(),to.toString().c_str(),Filter::etherTypeName(etherType));
return;
}
if (to.isMulticast()) {
MulticastGroup mg(to,0);
// Handle special cases: IPv4 ARP
if ((etherType == ZT_ETHERTYPE_ARP)&&(data.size() == 28)&&(data[2] == 0x08)&&(data[3] == 0x00)&&(data[4] == 6)&&(data[5] == 4)&&(data[7] == 0x01))
mg = MulticastGroup::deriveMulticastGroupForAddressResolution(InetAddress(data.field(24,4),4,0));
if (to.isBroadcast()) {
// Cram IPv4 IP into ADI field to make IPv4 ARP broadcast channel specific and scalable
if ((etherType == ZT_ETHERTYPE_ARP)&&(data.size() == 28)&&(data[2] == 0x08)&&(data[3] == 0x00)&&(data[4] == 6)&&(data[5] == 4)&&(data[7] == 0x01))
mg = MulticastGroup::deriveMulticastGroupForAddressResolution(InetAddress(data.field(24,4),4,0));
}
// Remember this message's CRC, but don't drop if we've already seen it
// since it's our own.
@@ -263,7 +266,7 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
// Start multicast propagation with empty bloom filter
unsigned char bloom[ZT_PROTO_VERB_MULTICAST_FRAME_BLOOM_FILTER_SIZE];
memset(bloom,0,sizeof(bloom));
_propagateMulticast(network,bloom,mg,0,0,from,etherType,data.data(),data.size());
_propagateMulticast(network,_r->identity.address(),bloom,mg,0,0,from,etherType,data.data(),data.size());
} else if (to.isZeroTier()) {
// Simple unicast frame from us to another node
Address toZT(to.data + 1);
@@ -275,10 +278,10 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
outp.compress();
send(outp,true);
} else {
TRACE("UNICAST: %s -> %s %s (dropped, destination not a member of closed network %llu)",from.toString().c_str(),to.toString().c_str(),Utils::etherTypeName(etherType),network->id());
TRACE("UNICAST: %s -> %s %s (dropped, destination not a member of closed network %llu)",from.toString().c_str(),to.toString().c_str(),Filter::etherTypeName(etherType),network->id());
}
} else {
TRACE("UNICAST: %s -> %s %s (dropped, destination MAC not ZeroTier)",from.toString().c_str(),to.toString().c_str(),Utils::etherTypeName(etherType));
TRACE("UNICAST: %s -> %s %s (dropped, destination MAC not ZeroTier)",from.toString().c_str(),to.toString().c_str(),Filter::etherTypeName(etherType));
}
}
@@ -312,6 +315,19 @@ void Switch::sendHELLO(const Address &dest)
send(outp,false);
}
bool Switch::sendHELLO(const SharedPtr<Peer> &dest,Demarc::Port localPort,const InetAddress &addr)
{
Packet outp(dest->address(),_r->identity.address(),Packet::VERB_HELLO);
outp.append((unsigned char)ZT_PROTO_VERSION);
outp.append((unsigned char)ZEROTIER_ONE_VERSION_MAJOR);
outp.append((unsigned char)ZEROTIER_ONE_VERSION_MINOR);
outp.append((uint16_t)ZEROTIER_ONE_VERSION_REVISION);
outp.append(Utils::now());
_r->identity.serialize(outp,false);
outp.hmacSet(dest->macKey());
return _r->demarc->send(localPort,addr,outp.data(),outp.size(),-1);
}
bool Switch::unite(const Address &p1,const Address &p2,bool force)
{
SharedPtr<Peer> p1p = _r->topology->getPeer(p1);
@@ -392,11 +408,8 @@ unsigned long Switch::doTimerTasks()
if (now >= i->second.fireAtTime) {
SharedPtr<Peer> withPeer = _r->topology->getPeer(i->first);
if (withPeer) {
TRACE("sending NAT-T NOP to %s(%s)",i->first.toString().c_str(),i->second.inaddr.toString().c_str());
Packet outp(i->first,_r->identity.address(),Packet::VERB_NOP);
outp.append("ZT",2); // arbitrary payload
outp.hmacSet(withPeer->macKey());
_r->demarc->send(i->second.localPort,i->second.inaddr,outp.data(),outp.size(),-1);
TRACE("sending NAT-T HELLO to %s(%s)",i->first.toString().c_str(),i->second.inaddr.toString().c_str());
sendHELLO(withPeer,i->second.localPort,i->second.inaddr);
}
_rendezvousQueue.erase(i++);
} else {
@@ -555,10 +568,10 @@ void Switch::_CBaddPeerFromWhois(void *arg,const SharedPtr<Peer> &p,Topology::Pe
}
}
void Switch::_propagateMulticast(const SharedPtr<Network> &network,unsigned char *bloom,const MulticastGroup &mg,unsigned int mcHops,unsigned int mcLoadFactor,const MAC &from,unsigned int etherType,const void *data,unsigned int len)
void Switch::_propagateMulticast(const SharedPtr<Network> &network,const Address &upstream,unsigned char *bloom,const MulticastGroup &mg,unsigned int mcHops,unsigned int mcLoadFactor,const MAC &from,unsigned int etherType,const void *data,unsigned int len)
{
SharedPtr<Peer> propPeers[ZT_MULTICAST_PROPAGATION_BREADTH];
unsigned int np = _r->topology->pickMulticastPropagationPeers(network->id(),Address(),bloom,ZT_PROTO_VERB_MULTICAST_FRAME_BLOOM_FILTER_SIZE * 8,ZT_MULTICAST_PROPAGATION_BREADTH,mg,propPeers);
unsigned int np = _r->topology->pickMulticastPropagationPeers(network->id(),upstream,bloom,ZT_PROTO_VERB_MULTICAST_FRAME_BLOOM_FILTER_SIZE * 8,ZT_MULTICAST_PROPAGATION_BREADTH,mg,propPeers);
for(unsigned int i=0;i<np;++i)
Utils::bloomAdd(bloom,ZT_PROTO_VERB_MULTICAST_FRAME_BLOOM_FILTER_SIZE,propPeers[i]->address().sum());
@@ -757,17 +770,11 @@ Switch::PacketServiceAttemptResult Switch::_tryHandleRemotePacket(Demarc::Port l
if ((fromMac.isZeroTier())&&(network->isAllowed(Address(fromMac)))) {
if (_checkAndUpdateMulticastHistory(fromMac,mg.mac(),packet.data() + ZT_PROTO_VERB_MULTICAST_FRAME_IDX_PAYLOAD,packet.size() - ZT_PROTO_VERB_MULTICAST_FRAME_IDX_PAYLOAD,network->id(),now)) {
TRACE("dropped MULTICAST_FRAME from %s: duplicate multicast",source.toString().c_str());
TRACE("dropped duplicate MULTICAST_FRAME: %s -> %s (adi: %.8lx), %u bytes, net: %llu",fromMac.toString().c_str(),mg.mac().toString().c_str(),(unsigned long)mg.adi(),packet.size() - ZT_PROTO_VERB_MULTICAST_FRAME_IDX_PAYLOAD,network->id());
} else {
//TRACE("MULTICAST_FRAME: %s -> %s (adi: %.8lx), %u bytes, net: %llu",fromMac.toString().c_str(),mg.mac().toString().c_str(),(unsigned long)mg.adi(),packet.size() - ZT_PROTO_VERB_MULTICAST_FRAME_IDX_PAYLOAD,network->id());
network->tap().put(fromMac,mg.mac(),etherType,packet.data() + ZT_PROTO_VERB_MULTICAST_FRAME_IDX_PAYLOAD,packet.size() - ZT_PROTO_VERB_MULTICAST_FRAME_IDX_PAYLOAD);
// TODO: implement load factor based propagation rate limitation
// How it will work: each node will adjust loadFactor based on
// its current load of multicast traffic. Then it will probabilistically
// fail to propagate, with the probability being based on load factor.
// This will need some in-the-field testing and tuning to get right.
_propagateMulticast(network,bloom,mg,hops+1,loadFactor,fromMac,etherType,packet.data() + ZT_PROTO_VERB_MULTICAST_FRAME_IDX_PAYLOAD,packet.size() - ZT_PROTO_VERB_MULTICAST_FRAME_IDX_PAYLOAD);
_propagateMulticast(network,source,bloom,mg,hops+1,loadFactor,fromMac,etherType,packet.data() + ZT_PROTO_VERB_MULTICAST_FRAME_IDX_PAYLOAD,packet.size() - ZT_PROTO_VERB_MULTICAST_FRAME_IDX_PAYLOAD);
}
} else {
TRACE("dropped MULTICAST_FRAME from %s: ultimate sender %s not a member of closed network %llu",source.toString().c_str(),fromMac.toString().c_str(),network->id());
+11 -1
View File
@@ -106,6 +106,16 @@ public:
*/
void sendHELLO(const Address &dest);
/**
* Send a HELLO announcement immediately to the indicated address
*
* @param localPort Originating local port or ANY_PORT to pick
* @param addr IP address to send to
* @param dest Destination peer
* @return True if send appears successful
*/
bool sendHELLO(const SharedPtr<Peer> &dest,Demarc::Port localPort,const InetAddress &addr);
/**
* Send RENDEZVOUS to two peers to permit them to directly connect
*
@@ -161,7 +171,7 @@ private:
static void _CBaddPeerFromHello(void *arg,const SharedPtr<Peer> &p,Topology::PeerVerifyResult result);
static void _CBaddPeerFromWhois(void *arg,const SharedPtr<Peer> &p,Topology::PeerVerifyResult result); // arg == this
void _propagateMulticast(const SharedPtr<Network> &network,unsigned char *bloom,const MulticastGroup &mg,unsigned int mcHops,unsigned int mcLoadFactor,const MAC &from,unsigned int etherType,const void *data,unsigned int len);
void _propagateMulticast(const SharedPtr<Network> &network,const Address &upstream,unsigned char *bloom,const MulticastGroup &mg,unsigned int mcHops,unsigned int mcLoadFactor,const MAC &from,unsigned int etherType,const void *data,unsigned int len);
PacketServiceAttemptResult _tryHandleRemotePacket(Demarc::Port localPort,const InetAddress &fromAddr,Packet &packet);
void _doHELLO(Demarc::Port localPort,const InetAddress &fromAddr,Packet &packet);
void _requestWhois(const Address &addr);
+1 -1
View File
@@ -242,7 +242,7 @@ unsigned int Topology::pickMulticastPropagationPeers(uint64_t nwid,const Address
if (g != mgm->second.end()) {
uint64_t now = Utils::now();
for(std::map< Address,uint64_t >::iterator m(g->second.begin());m!=g->second.end();) {
if ((now - m->second) < ZT_MULTICAST_LIKE_EXPIRE) {
if (((now - m->second) < ZT_MULTICAST_LIKE_EXPIRE)&&(m->first != exclude)) {
std::map< Address,SharedPtr<Peer> >::const_iterator p(_activePeers.find(m->first));
if (p != _activePeers.end()) {
possiblePeers[numPossiblePeers++] = p->second;
-25
View File
@@ -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;
-16
View File
@@ -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
+1
View File
@@ -11,6 +11,7 @@ OBJS=\
node/Demarc.o \
node/EllipticCurveKeyPair.o \
node/EthernetTap.o \
node/Filter.o \
node/HMAC.o \
node/Http.o \
node/Identity.o \
+1 -1
View File
@@ -41,6 +41,6 @@
/**
* Revision: 16-bit (0-65535)
*/
#define ZEROTIER_ONE_VERSION_REVISION 1
#define ZEROTIER_ONE_VERSION_REVISION 3
#endif