summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--node/AntiRecursion.hpp2
-rw-r--r--node/Constants.hpp10
-rw-r--r--node/Defaults.cpp3
-rw-r--r--node/Defaults.hpp5
-rw-r--r--node/InetAddress.hpp6
-rw-r--r--node/Node.cpp14
-rw-r--r--node/Packet.hpp16
-rw-r--r--node/Peer.cpp8
-rw-r--r--node/Peer.hpp14
-rw-r--r--node/SocketManager.cpp4
-rw-r--r--node/Switch.cpp45
-rw-r--r--node/Switch.hpp13
12 files changed, 113 insertions, 27 deletions
diff --git a/node/AntiRecursion.hpp b/node/AntiRecursion.hpp
index 82cf9e58..23b0f04c 100644
--- a/node/AntiRecursion.hpp
+++ b/node/AntiRecursion.hpp
@@ -89,7 +89,7 @@ public:
{
for(unsigned int h=0;h<ZT_ANTIRECURSION_HISTORY_SIZE;++h) {
ArItem *i = &(_history[h]);
- if ((len >= i->len)&&(!memcmp(((const unsigned char *)data) + (len - i->len),i->tail,i->len)))
+ if ((i->len > 0)&&(len >= i->len)&&(!memcmp(((const unsigned char *)data) + (len - i->len),i->tail,i->len)))
return false;
}
return true;
diff --git a/node/Constants.hpp b/node/Constants.hpp
index 589f8641..782ea463 100644
--- a/node/Constants.hpp
+++ b/node/Constants.hpp
@@ -367,6 +367,16 @@ error_no_byte_order_defined;
#define ZT_ANTIRECURSION_HISTORY_SIZE 16
/**
+ * How often to broadcast beacons over physical local LANs
+ */
+#define ZT_BEACON_INTERVAL ZT_PEER_DIRECT_PING_DELAY
+
+/**
+ * Do not respond to any beacon more often than this
+ */
+#define ZT_MIN_BEACON_RESPONSE_INTERVAL (ZT_BEACON_INTERVAL / 64)
+
+/**
* Minimum interval between attempts to do a software update
*/
#define ZT_UPDATE_MIN_INTERVAL 120000
diff --git a/node/Defaults.cpp b/node/Defaults.cpp
index 2bddeec2..9729bf59 100644
--- a/node/Defaults.cpp
+++ b/node/Defaults.cpp
@@ -174,7 +174,8 @@ Defaults::Defaults() :
defaultHomePath(_mkDefaultHomePath()),
supernodes(_mkSupernodeMap()),
updateAuthorities(_mkUpdateAuth()),
- updateLatestNfoURL(_mkUpdateUrl())
+ updateLatestNfoURL(_mkUpdateUrl()),
+ v4Broadcast(((uint32_t)0xffffffff),ZT_DEFAULT_UDP_PORT)
{
}
diff --git a/node/Defaults.hpp b/node/Defaults.hpp
index 50c2dce5..98110ac7 100644
--- a/node/Defaults.hpp
+++ b/node/Defaults.hpp
@@ -83,6 +83,11 @@ public:
* URL to latest .nfo for software updates
*/
const std::string updateLatestNfoURL;
+
+ /**
+ * Address for IPv4 LAN auto-location broadcasts: 255.255.255.255:9993
+ */
+ const InetAddress v4Broadcast;
};
extern const Defaults ZT_DEFAULTS;
diff --git a/node/InetAddress.hpp b/node/InetAddress.hpp
index e073172b..897b5242 100644
--- a/node/InetAddress.hpp
+++ b/node/InetAddress.hpp
@@ -101,6 +101,12 @@ public:
this->set(ipBytes,ipLen,port);
}
+ InetAddress(const uint32_t ipv4,unsigned int port)
+ throw()
+ {
+ this->set(&ipv4,4,port);
+ }
+
InetAddress(const std::string &ip,unsigned int port)
throw()
{
diff --git a/node/Node.cpp b/node/Node.cpp
index 655e3188..9be0b768 100644
--- a/node/Node.cpp
+++ b/node/Node.cpp
@@ -62,6 +62,7 @@
#include "Identity.hpp"
#include "Topology.hpp"
#include "SocketManager.hpp"
+#include "Packet.hpp"
#include "Switch.hpp"
#include "EthernetTap.hpp"
#include "CMWC4096.hpp"
@@ -534,6 +535,7 @@ Node::ReasonForTermination Node::run()
uint64_t lastNetworkFingerprintCheck = 0;
uint64_t lastMulticastCheck = 0;
uint64_t lastSupernodePingCheck = 0;
+ uint64_t lastBeacon = 0;
long lastDelayDelta = 0;
uint64_t networkConfigurationFingerprint = 0;
@@ -676,6 +678,18 @@ Node::ReasonForTermination Node::run()
_r->updater->checkIfMaxIntervalExceeded(now);
}
+ // Send beacons to physical local LANs
+ if ((resynchronize)||((now - lastBeacon) >= ZT_BEACON_INTERVAL)) {
+ lastBeacon = now;
+ char bcn[ZT_PROTO_BEACON_LENGTH];
+ *((uint32_t *)(bcn)) = _r->prng->next32();
+ *((uint32_t *)(bcn + 4)) = _r->prng->next32();
+ _r->identity.address().copyTo(bcn + ZT_PROTO_BEACON_IDX_ADDRESS,ZT_ADDRESS_LENGTH);
+ TRACE("sending LAN beacon to %s",ZT_DEFAULTS.v4Broadcast.toString().c_str());
+ _r->antiRec->logOutgoingZT(bcn,ZT_PROTO_BEACON_LENGTH);
+ _r->sm->send(ZT_DEFAULTS.v4Broadcast,false,false,bcn,ZT_PROTO_BEACON_LENGTH);
+ }
+
// Sleep for loop interval or until something interesting happens.
try {
unsigned long delay = std::min((unsigned long)ZT_MAX_SERVICE_LOOP_INTERVAL,_r->sw->doTimerTasks());
diff --git a/node/Packet.hpp b/node/Packet.hpp
index e2af0f76..ade44bf5 100644
--- a/node/Packet.hpp
+++ b/node/Packet.hpp
@@ -136,6 +136,12 @@
*/
#define ZT_PROTO_MIN_FRAGMENT_LENGTH ZT_PACKET_FRAGMENT_IDX_PAYLOAD
+/**
+ * Length of LAN beacon packets
+ */
+#define ZT_PROTO_BEACON_LENGTH 13
+#define ZT_PROTO_BEACON_IDX_ADDRESS 8
+
// Size of bloom filter used in multicast propagation graph exploration
#define ZT_PROTO_VERB_MULTICAST_FRAME_BLOOM_FILTER_SIZE_BITS 512
#define ZT_PROTO_VERB_MULTICAST_FRAME_BLOOM_FILTER_SIZE_BYTES 64
@@ -250,6 +256,16 @@ namespace ZeroTier {
*
* For unencrypted packets, MAC is computed on plaintext. Only HELLO is ever
* sent in the clear, as it's the "here is my public key" message.
+ *
+ * Beacon format and beacon packets:
+ * <[8] 8 random bytes>
+ * <[5] sender ZT address>
+ *
+ * A beacon is a 13-byte packet containing only the address of the sender.
+ * Receiving peers may or may not respond to beacons with a HELLO or other
+ * message to initiate direct communication.
+ *
+ * Beacons may be used for direct LAN announcement or NAT traversal.
*/
class Packet : public Buffer<ZT_PROTO_MAX_PACKET_LENGTH>
{
diff --git a/node/Peer.cpp b/node/Peer.cpp
index 3aeb821e..c0cdaf17 100644
--- a/node/Peer.cpp
+++ b/node/Peer.cpp
@@ -162,12 +162,16 @@ Path::Type Peer::send(const RuntimeEnvironment *_r,const void *data,unsigned int
} else { // we only have a normal path (or none at all, that case is caught below)
bestPath = bestNormalPath;
}
+ if (!bestPath)
+ return Path::PATH_TYPE_NULL;
- if ((bestPath)&&(_r->sm->send(bestPath->address(),bestPath->tcp(),bestPath->type() == Path::PATH_TYPE_TCP_OUT,data,len))) {
+ _r->antiRec->logOutgoingZT(data,len);
+
+ if (_r->sm->send(bestPath->address(),bestPath->tcp(),bestPath->type() == Path::PATH_TYPE_TCP_OUT,data,len)) {
bestPath->sent(now);
- _r->antiRec->logOutgoingZT(data,len);
return bestPath->type();
}
+
return Path::PATH_TYPE_NULL;
}
diff --git a/node/Peer.hpp b/node/Peer.hpp
index 39074ebc..af406895 100644
--- a/node/Peer.hpp
+++ b/node/Peer.hpp
@@ -176,6 +176,20 @@ public:
}
/**
+ * @param addr IP:port
+ * @return True if we have a UDP path to this address
+ */
+ inline bool haveUdpPath(const InetAddress &addr) const
+ {
+ Mutex::Lock _l(_lock);
+ for(std::vector<Path>::const_iterator p(_paths.begin());p!=_paths.end();++p) {
+ if ((p->type() == Path::PATH_TYPE_UDP)&&(p->address() == addr))
+ return true;
+ }
+ return false;
+ }
+
+ /**
* @return Last successfully sent firewall opener for any path
*/
inline uint64_t lastFirewallOpener() const
diff --git a/node/SocketManager.cpp b/node/SocketManager.cpp
index 85bf9dc3..f9459cea 100644
--- a/node/SocketManager.cpp
+++ b/node/SocketManager.cpp
@@ -247,10 +247,12 @@ SocketManager::SocketManager(
f = TRUE; setsockopt(s,IPPROTO_IPV6,IPV6_V6ONLY,(const char *)&f,sizeof(f));
f = FALSE; setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(const char *)&f,sizeof(f));
f = FALSE; setsockopt(s,IPPROTO_IPV6,IPV6_DONTFRAG,(const char *)&f,sizeof(f));
+ f = TRUE; setsockopt(s,SOL_SOCKET,SO_BROADCAST,(const char *)&f,sizeof(f));
#else
int f;
f = 1; setsockopt(s,IPPROTO_IPV6,IPV6_V6ONLY,(void *)&f,sizeof(f));
f = 0; setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(void *)&f,sizeof(f));
+ f = 1; setsockopt(s,SOL_SOCKET,SO_BROADCAST,(void *)&f,sizeof(f));
#ifdef IP_DONTFRAG
f = 0; setsockopt(s,IPPROTO_IP,IP_DONTFRAG,&f,sizeof(f));
#endif
@@ -304,9 +306,11 @@ SocketManager::SocketManager(
BOOL f;
f = FALSE; setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(const char *)&f,sizeof(f));
f = FALSE; setsockopt(s,IPPROTO_IP,IP_DONTFRAGMENT,(const char *)&f,sizeof(f));
+ f = TRUE; setsockopt(s,SOL_SOCKET,SO_BROADCAST,(const char *)&f,sizeof(f));
#else
int f;
f = 0; setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(void *)&f,sizeof(f));
+ f = 1; setsockopt(s,SOL_SOCKET,SO_BROADCAST,(void *)&f,sizeof(f));
#ifdef IP_DONTFRAG
f = 0; setsockopt(s,IPPROTO_IP,IP_DONTFRAG,&f,sizeof(f));
#endif
diff --git a/node/Switch.cpp b/node/Switch.cpp
index 2bdc1bef..b4ba174b 100644
--- a/node/Switch.cpp
+++ b/node/Switch.cpp
@@ -56,6 +56,7 @@ namespace ZeroTier {
Switch::Switch(const RuntimeEnvironment *renv) :
_r(renv),
+ _lastBeacon(0),
_multicastIdCounter((unsigned int)renv->prng->next32()) // start a random spot to minimize possible collisions on startup
{
}
@@ -67,7 +68,9 @@ Switch::~Switch()
void Switch::onRemotePacket(const SharedPtr<Socket> &fromSock,const InetAddress &fromAddr,Buffer<ZT_SOCKET_MAX_MESSAGE_LEN> &data)
{
try {
- if (data.size() > ZT_PROTO_MIN_FRAGMENT_LENGTH) {
+ if (data.size() == ZT_PROTO_BEACON_LENGTH) {
+ _handleBeacon(fromSock,fromAddr,data);
+ } else if (data.size() > ZT_PROTO_MIN_FRAGMENT_LENGTH) {
if (data[ZT_PACKET_FRAGMENT_IDX_FRAGMENT_INDICATOR] == ZT_PACKET_FRAGMENT_INDICATOR)
_handleRemotePacketFragment(fromSock,fromAddr,data);
else if (data.size() >= ZT_PROTO_MIN_PACKET_LENGTH)
@@ -87,7 +90,7 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
return;
if (!_r->antiRec->checkEthernetFrame(data.data(),data.size())) {
- TRACE("%s: rejected recursively addressed ZeroTier packet by tail match",network->tapDeviceName().c_str());
+ TRACE("%s: rejected recursively addressed ZeroTier packet by tail match (type %s, length: %u)",network->tapDeviceName().c_str(),etherTypeName(etherType),data.size());
return;
}
@@ -96,12 +99,12 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
return;
}
if (from != network->mac()) {
- LOG("ignored tap: %s -> %s %s (bridging not supported)",from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType));
+ LOG("%s: ignored tap: %s -> %s %s (bridging not supported)",network->tapDeviceName().c_str(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType));
return;
}
if (!nconf->permitsEtherType(etherType)) {
- LOG("ignored tap: %s -> %s: ethertype %s not allowed on network %.16llx",from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType),(unsigned long long)network->id());
+ LOG("%s: ignored tap: %s -> %s: ethertype %s not allowed on network %.16llx",network->tapDeviceName().c_str(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType),(unsigned long long)network->id());
return;
}
@@ -231,11 +234,8 @@ bool Switch::sendHELLO(const SharedPtr<Peer> &dest,const Path &path)
outp.append(now);
_r->identity.serialize(outp,false);
outp.armor(dest->key(),false);
- if (_r->sm->send(path.address(),path.tcp(),path.type() == Path::PATH_TYPE_TCP_OUT,outp.data(),outp.size())) {
- _r->antiRec->logOutgoingZT(outp.data(),outp.size());
- return true;
- }
- return false;
+ _r->antiRec->logOutgoingZT(outp.data(),outp.size());
+ return _r->sm->send(path.address(),path.tcp(),path.type() == Path::PATH_TYPE_TCP_OUT,outp.data(),outp.size());
}
bool Switch::sendHELLO(const SharedPtr<Peer> &dest,const InetAddress &destUdp)
@@ -249,11 +249,8 @@ bool Switch::sendHELLO(const SharedPtr<Peer> &dest,const InetAddress &destUdp)
outp.append(now);
_r->identity.serialize(outp,false);
outp.armor(dest->key(),false);
- if (_r->sm->send(destUdp,false,false,outp.data(),outp.size())) {
- _r->antiRec->logOutgoingZT(outp.data(),outp.size());
- return true;
- }
- return false;
+ _r->antiRec->logOutgoingZT(outp.data(),outp.size());
+ return _r->sm->send(destUdp,false,false,outp.data(),outp.size());
}
bool Switch::unite(const Address &p1,const Address &p2,bool force)
@@ -711,6 +708,26 @@ void Switch::_handleRemotePacketHead(const SharedPtr<Socket> &fromSock,const Ine
}
}
+void Switch::_handleBeacon(const SharedPtr<Socket> &fromSock,const InetAddress &fromAddr,const Buffer<4096> &data)
+{
+ Address beaconAddr(data.field(ZT_PROTO_BEACON_IDX_ADDRESS,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH);
+ if (beaconAddr == _r->identity.address())
+ return;
+ SharedPtr<Peer> peer(_r->topology->getPeer(beaconAddr));
+ if (peer) {
+ uint64_t now = Utils::now();
+ if (peer->haveUdpPath(fromAddr)) {
+ if ((now - peer->lastDirectReceive()) >= ZT_PEER_DIRECT_PING_DELAY)
+ peer->sendPing(_r,now);
+ } else {
+ if ((now - _lastBeacon) < ZT_MIN_BEACON_RESPONSE_INTERVAL)
+ return;
+ _lastBeacon = now;
+ sendHELLO(peer,fromAddr);
+ }
+ }
+}
+
Address Switch::_sendWhoisRequest(const Address &addr,const Address *peersAlreadyConsulted,unsigned int numPeersAlreadyConsulted)
{
SharedPtr<Peer> supernode(_r->topology->getBestSupernode(peersAlreadyConsulted,numPeersAlreadyConsulted,false));
diff --git a/node/Switch.hpp b/node/Switch.hpp
index ee78d9fd..64457109 100644
--- a/node/Switch.hpp
+++ b/node/Switch.hpp
@@ -221,15 +221,9 @@ public:
throw();
private:
- void _handleRemotePacketFragment(
- const SharedPtr<Socket> &fromSock,
- const InetAddress &fromAddr,
- const Buffer<4096> &data);
-
- void _handleRemotePacketHead(
- const SharedPtr<Socket> &fromSock,
- const InetAddress &fromAddr,
- const Buffer<4096> &data);
+ void _handleRemotePacketFragment(const SharedPtr<Socket> &fromSock,const InetAddress &fromAddr,const Buffer<4096> &data);
+ void _handleRemotePacketHead(const SharedPtr<Socket> &fromSock,const InetAddress &fromAddr,const Buffer<4096> &data);
+ void _handleBeacon(const SharedPtr<Socket> &fromSock,const InetAddress &fromAddr,const Buffer<4096> &data);
Address _sendWhoisRequest(
const Address &addr,
@@ -241,6 +235,7 @@ private:
bool encrypt);
const RuntimeEnvironment *const _r;
+ volatile uint64_t _lastBeacon;
volatile unsigned int _multicastIdCounter;
struct WhoisRequest