Browse Source

More toward GitHub issue #56

Adam Ierymenko 11 years ago
parent
commit
aee742e767
12 changed files with 113 additions and 27 deletions
  1. 1 1
      node/AntiRecursion.hpp
  2. 10 0
      node/Constants.hpp
  3. 2 1
      node/Defaults.cpp
  4. 5 0
      node/Defaults.hpp
  5. 6 0
      node/InetAddress.hpp
  6. 14 0
      node/Node.cpp
  7. 16 0
      node/Packet.hpp
  8. 6 2
      node/Peer.cpp
  9. 14 0
      node/Peer.hpp
  10. 4 0
      node/SocketManager.cpp
  11. 31 14
      node/Switch.cpp
  12. 4 9
      node/Switch.hpp

+ 1 - 1
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;

+ 10 - 0
node/Constants.hpp

@@ -366,6 +366,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
  */

+ 2 - 1
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)
 {
 }
 

+ 5 - 0
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;

+ 6 - 0
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()
 	{

+ 14 - 0
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());

+ 16 - 0
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>
 {

+ 6 - 2
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;
 }
 

+ 14 - 0
node/Peer.hpp

@@ -175,6 +175,20 @@ public:
 		return _paths;
 	}
 
+	/**
+	 * @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
 	 */

+ 4 - 0
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

+ 31 - 14
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));

+ 4 - 9
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