Browse Source

Add Peer method for getting preferred cipher, various cleanup in VL1

Adam Ierymenko 5 years ago
parent
commit
9ad8dd3700
7 changed files with 121 additions and 90 deletions
  1. 8 5
      node/Locator.cpp
  2. 1 1
      node/Locator.hpp
  3. 4 3
      node/Peer.cpp
  4. 11 1
      node/Peer.hpp
  5. 36 32
      node/Protocol.hpp
  6. 59 46
      node/VL1.cpp
  7. 2 2
      node/VL1.hpp

+ 8 - 5
node/Locator.cpp

@@ -42,8 +42,9 @@ int Locator::marshal(uint8_t data[ZT_LOCATOR_MARSHAL_SIZE_MAX],const bool exclud
 	if ((_endpointCount > ZT_LOCATOR_MAX_ENDPOINTS)||(_signatureLength > ZT_SIGNATURE_BUFFER_SIZE))
 		return -1;
 
-	Utils::storeBigEndian<int64_t>(data,_ts);
-	int p = 8;
+	data[0] = 0xff; // version byte, currently 0xff to never be the same as byte 0 of an identity for legacy compatibility reasons
+	Utils::storeBigEndian<int64_t>(data + 1,_ts);
+	int p = 9;
 
 	if (_ts > 0) {
 		Utils::storeBigEndian(data + p,(uint16_t)_endpointCount);
@@ -71,11 +72,13 @@ int Locator::marshal(uint8_t data[ZT_LOCATOR_MARSHAL_SIZE_MAX],const bool exclud
 
 int Locator::unmarshal(const uint8_t *restrict data,const int len) noexcept
 {
-	if (len <= (8 + 2 + 48))
+	if (len <= (1 + 8 + 2 + 48))
 		return -1;
 
-	_ts = Utils::loadBigEndian<int64_t>(data);
-	int p = 8;
+	if (data[0] != 0xff)
+		return -1;
+	_ts = Utils::loadBigEndian<int64_t>(data + 1);
+	int p = 9;
 
 	if (_ts > 0) {
 		const unsigned int ec = Utils::loadBigEndian<uint16_t>(data + p);

+ 1 - 1
node/Locator.hpp

@@ -24,7 +24,7 @@
 #include "TriviallyCopyable.hpp"
 
 #define ZT_LOCATOR_MAX_ENDPOINTS 8
-#define ZT_LOCATOR_MARSHAL_SIZE_MAX (8 + 2 + (ZT_ENDPOINT_MARSHAL_SIZE_MAX * ZT_LOCATOR_MAX_ENDPOINTS) + 2 + 2 + ZT_SIGNATURE_BUFFER_SIZE)
+#define ZT_LOCATOR_MARSHAL_SIZE_MAX (1 + 8 + 2 + (ZT_ENDPOINT_MARSHAL_SIZE_MAX * ZT_LOCATOR_MAX_ENDPOINTS) + 2 + 2 + ZT_SIGNATURE_BUFFER_SIZE)
 
 namespace ZeroTier {
 

+ 4 - 3
node/Peer.cpp

@@ -72,7 +72,8 @@ void Peer::received(
 	const unsigned int hops,
 	const uint64_t packetId,
 	const unsigned int payloadLength,
-	const Protocol::Verb verb)
+	const Protocol::Verb verb,
+	const Protocol::Verb inReVerb)
 {
 	const int64_t now = RR->node->now();
 	_lastReceive = now;
@@ -240,7 +241,7 @@ void Peer::sendNOP(void *tPtr,const int64_t localSocket,const InetAddress &atAdd
 	RR->identity.address().copyTo(ph.source);
 	ph.flags = 0;
 	ph.verb = Protocol::VERB_NOP;
-	Protocol::armor(outp,sizeof(Protocol::Header),_key,ZT_PROTO_CIPHER_SUITE__POLY1305_SALSA2012);
+	Protocol::armor(outp,sizeof(Protocol::Header),_key,this->cipher());
 	RR->node->putPacket(tPtr,localSocket,atAddress,outp.b,sizeof(Protocol::Header));
 }
 
@@ -354,7 +355,7 @@ void Peer::save(void *tPtr) const
 	free(buf);
 }
 
-void Peer::contact(void *tPtr,const Endpoint &ep,const int64_t now,bool behindSymmetric,bool bfg1024)
+void Peer::contact(void *tPtr,const Endpoint &ep,const int64_t now,const bool behindSymmetric,const bool bfg1024)
 {
 	static uint8_t junk = 0;
 

+ 11 - 1
node/Peer.hpp

@@ -101,6 +101,7 @@ public:
 	 * @param hops ZeroTier (not IP) hops
 	 * @param packetId Packet ID
 	 * @param verb Packet verb
+	 * @param inReVerb In-reply verb for OK or ERROR verbs
 	 */
 	void received(
 		void *tPtr,
@@ -108,7 +109,8 @@ public:
 		unsigned int hops,
 		uint64_t packetId,
 		unsigned int payloadLength,
-		Protocol::Verb verb);
+		Protocol::Verb verb,
+		Protocol::Verb inReVerb);
 
 	/**
 	 * Send a HELLO to this peer at a specified physical address
@@ -207,6 +209,14 @@ public:
 	 */
 	ZT_ALWAYS_INLINE const unsigned char *key() const noexcept { return _key; }
 
+	/**
+	 * @return Preferred cipher suite for normal encrypted P2P communication
+	 */
+	ZT_ALWAYS_INLINE uint8_t cipher() const noexcept
+	{
+		return ZT_PROTO_CIPHER_SUITE__POLY1305_SALSA2012;
+	}
+
 	/**
 	 * @return Incoming probe packet (in big-endian byte order)
 0	 */

+ 36 - 32
node/Protocol.hpp

@@ -305,43 +305,42 @@ enum Verb
 	 *   <[...] meta-data dictionary>
 	 *   <[2] 16-bit length of any additional fields>
 	 *   [... end encrypted region ...]
-	 *   <[48] HMAC-SHA384 of full plaintext payload>
+	 *   <[48] HMAC-SHA384 of packet (with hops field masked to 0)>
 	 *
-	 * HELLO is sent with authentication but without the usual encryption so
-	 * that peers can exchange identities.
+	 * HELLO is sent using the POLY1305_NONE cipher setting (MAC but
+	 * no encryption) and as of protocol version 11 contains an extra
+	 * HMAC-SHA384 MAC for additional authentication hardening.
 	 *
-	 * Destination address is the actual wire address to which the packet
-	 * was sent. See InetAddress::serialize() for format.
+	 * The physical desgination address is the raw InetAddress to which the
+	 * packet was sent, regardless of any relaying used.
 	 *
-	 * Starting at "begin encrypted section" the reset of the packet is
-	 * encrypted with Salsa20/12. This is not the normal packet encryption
-	 * and is technically not necessary as nothing in HELLO is secret. It
-	 * exists merely to shield meta-data info from passive listeners to
-	 * slightly improve privacy, and for backward compatibility with older
-	 * nodes that required it.
+	 * HELLO packets have an encrypted section that is encrypted with
+	 * Salsa20/12 using the two peers' long-term negotiated keys and with
+	 * the packet ID (with least significant 3 bits masked to 0 for legacy
+	 * reasons) as the Salsa20/12 IV. This encryption is technically not
+	 * necessary but serves to protect the privacy of locators and other
+	 * fields for a little added defense in depth. Note to auditors: for FIPS
+	 * or other auditing purposes this crypto can be ignored as its
+	 * compromise poses no risk to peer or network authentication or transport
+	 * data privacy. HMAC is computed after this encryption is performed and
+	 * is verified before decryption is performed.
 	 *
-	 * HELLO (and its OK response) ends with a large 384-bit HMAC to allow
-	 * identity exchanges to be authenticated with additional strength beyond
-	 * ordinary packet authentication.
+	 * A valid and successfully authenticated HELLO will generate the following
+	 * OK response which contains much of the same information about the
+	 * responding peer.
 	 *
 	 * OK payload:
-	 *   [... HMAC-384 starts here ...]
-	 *   <[8] HELLO timestamp field echo>
+	 *   <[8] timestamp echoed from original HELLO packet>
 	 *   <[1] protocol version>
 	 *   <[1] software major version>
 	 *   <[1] software minor version>
 	 *   <[2] software revision>
 	 *   <[...] physical destination address of packet>
-	 *   <[2] 16-bit reserved (legacy) field, always 0>
+	 *   <[2] 16-bit reserved (legacy) field, currently must be 0>
 	 *   <[2] 16-bit length of meta-data dictionary>
 	 *   <[...] meta-data dictionary>
 	 *   <[2] 16-bit length of any additional fields>
-	 *   <[48] HMAC-SHA384 of all fields to this point (as plaintext)>
-	 *
-	 * With the exception of the timestamp, the other fields pertain to the
-	 * respondent who is sending OK and are not echoes.
-	 *
-	 * ERROR has no payload.
+	 *   <[48] HMAC-SHA384 of plaintext packet (with hops masked to 0)>
 	 */
 	VERB_HELLO = 0x01,
 
@@ -350,10 +349,10 @@ enum Verb
 	 *   <[1] in-re verb>
 	 *   <[8] in-re packet ID>
 	 *   <[1] error code>
-	 *   <[...] error-dependent payload>
+	 *   <[...] error-dependent payload, may be empty>
 	 *
-	 * If this is not in response to a single packet then verb can be
-	 * NOP and packet ID can be zero.
+	 * An ERROR that does not pertain to a specific packet will have its verb
+	 * set to VERB_NOP and its packet ID set to zero.
 	 */
 	VERB_ERROR = 0x02,
 
@@ -380,8 +379,7 @@ enum Verb
 	 * be performed.
 	 *
 	 * It is possible for an identity but a null/empty locator to be returned
-	 * if no locator is known for a node. Older versions will also send no
-	 * locator field at all.
+	 * if no locator is known for a node. Older versions may omit the locator.
 	 */
 	VERB_WHOIS = 0x04,
 
@@ -390,13 +388,19 @@ enum Verb
 	 *   <[1] flags (unused, currently 0)>
 	 *   <[5] ZeroTier address of peer that might be found at this address>
 	 *   <[2] 16-bit protocol address port>
-	 *   <[1] protocol address length (4 for IPv4, 16 for IPv6)>
+	 *   <[1] protocol address length / type>
 	 *   <[...] protocol address (network byte order)>
 	 *
-	 * An upstream node can send this to inform both sides of a relay of
-	 * information they might use to establish a direct connection.
+	 * This is sent by a third party node to inform a node of where another
+	 * may be located. These are currently only allowed from roots.
+	 *
+	 * The protocol address format differs from the standard InetAddress
+	 * encoding for legacy reasons, but it's not hard to decode. The following
+	 * values are valid for the protocol address length (type) field:
 	 *
-	 * Upon receipt a peer sends HELLO to establish a direct link.
+	 *   4 - IPv4 IP address
+	 *   16 - IPv6 IP address
+	 *   255 - Endpoint object, unmarshaled in place (port ignored)
 	 *
 	 * No OK or ERROR is generated.
 	 */

+ 59 - 46
node/VL1.cpp

@@ -335,7 +335,8 @@ void VL1::onRemotePacket(void *const tPtr,const int64_t localSocket,const InetAd
 
 		const Protocol::Verb verb = (Protocol::Verb)(ph->verb & ZT_PROTO_VERB_MASK);
 
-		// Note that all verbs except HELLO require MAC.
+		// All verbs except HELLO require authentication before being handled. The HELLO
+		// handler does its own authentication.
 		if (((!authenticated)||(!peer))&&(verb != Protocol::VERB_HELLO)) {
 			RR->t->incomingPacketDropped(tPtr,0x5b001099,ph->packetId,0,identityFromPeerPtr(peer),path->address(),hops,verb,ZT_TRACE_PACKET_DROP_REASON_MAC_FAILED);
 			return;
@@ -376,12 +377,13 @@ void VL1::onRemotePacket(void *const tPtr,const int64_t localSocket,const InetAd
 		 * understand the handlers for VL2 data paths have been moved to a VL2 class.
 		 */
 
-		bool ok = true;
+		bool ok = true; // set to false if a packet turns out to be invalid
+		Protocol::Verb inReVerb = Protocol::VERB_NOP; // set via result parameter to _ERROR and _OK
 		switch(verb) {
 			case Protocol::VERB_NOP:                        break;
 			case Protocol::VERB_HELLO:                      ok = _HELLO(tPtr,path,peer,*pkt.b,(int)packetSize,authenticated); break;
-			case Protocol::VERB_ERROR:                      ok = _ERROR(tPtr,path,peer,*pkt.b,(int)packetSize); break;
-			case Protocol::VERB_OK:                         ok = _OK(tPtr,path,peer,*pkt.b,(int)packetSize); break;
+			case Protocol::VERB_ERROR:                      ok = _ERROR(tPtr,path,peer,*pkt.b,(int)packetSize,inReVerb); break;
+			case Protocol::VERB_OK:                         ok = _OK(tPtr,path,peer,*pkt.b,(int)packetSize,inReVerb); break;
 			case Protocol::VERB_WHOIS:                      ok = _WHOIS(tPtr,path,peer,*pkt.b,(int)packetSize); break;
 			case Protocol::VERB_RENDEZVOUS:                 ok = _RENDEZVOUS(tPtr,path,peer,*pkt.b,(int)packetSize); break;
 			case Protocol::VERB_FRAME:                      ok = RR->vl2->_FRAME(tPtr,path,peer,*pkt.b,(int)packetSize); break;
@@ -402,7 +404,7 @@ void VL1::onRemotePacket(void *const tPtr,const int64_t localSocket,const InetAd
 				break;
 		}
 		if (ok)
-			peer->received(tPtr,path,hops,ph->packetId,packetSize - ZT_PROTO_PACKET_PAYLOAD_START,verb);
+			peer->received(tPtr,path,hops,ph->packetId,packetSize - ZT_PROTO_PACKET_PAYLOAD_START,verb,inReVerb);
 	} catch ( ... ) {
 		RR->t->unexpectedError(tPtr,0xea1b6dea,"unexpected exception in onRemotePacket() parsing packet from %s",Trace::str(path->address()).s);
 	}
@@ -469,14 +471,14 @@ void VL1::_sendPendingWhois(void *const tPtr,const int64_t now)
 		}
 
 		if (outl > sizeof(Protocol::Header)) {
-			Protocol::armor(outp,outl,root->key(),ZT_PROTO_CIPHER_SUITE__POLY1305_SALSA2012);
+			Protocol::armor(outp,outl,root->key(),peer->cipher());
 			RR->expect->sending(ph.packetId,now);
 			rootPath->send(RR,tPtr,outp.b,outl,now);
 		}
 	}
 }
 
-bool VL1::_HELLO(void *tPtr,const SharedPtr<Path> &path,SharedPtr<Peer> &peer,Buf &pkt,int packetSize,bool authenticated)
+bool VL1::_HELLO(void *tPtr,const SharedPtr<Path> &path,SharedPtr<Peer> &peer,Buf &pkt,int packetSize,const bool authenticated)
 {
 	if (packetSize < sizeof(Protocol::HELLO)) {
 		RR->t->incomingPacketDropped(tPtr,0x2bdb0001,0,0,identityFromPeerPtr(peer),path->address(),0,Protocol::VERB_HELLO,ZT_TRACE_PACKET_DROP_REASON_MALFORMED_PACKET);
@@ -504,7 +506,6 @@ bool VL1::_HELLO(void *tPtr,const SharedPtr<Path> &path,SharedPtr<Peer> &peer,Bu
 
 	// Packet is basically valid and identity unmarshaled successfully --------------------------------------------------
 
-	// Get long-term static key for this node.
 	uint8_t key[ZT_PEER_SECRET_KEY_LENGTH];
 	if ((peer) && (id == peer->identity())) {
 		memcpy(key,peer->key(),ZT_PEER_SECRET_KEY_LENGTH);
@@ -516,8 +517,7 @@ bool VL1::_HELLO(void *tPtr,const SharedPtr<Path> &path,SharedPtr<Peer> &peer,Bu
 		}
 	}
 
-	// Verify packet using Poly1305 MAC
-	{
+	if ((!peer)||(!authenticated)) {
 		uint8_t perPacketKey[ZT_PEER_SECRET_KEY_LENGTH];
 		uint8_t macKey[ZT_POLY1305_KEY_LEN];
 		Protocol::salsa2012DeriveKey(peer->key(),perPacketKey,pkt,packetSize);
@@ -538,15 +538,16 @@ bool VL1::_HELLO(void *tPtr,const SharedPtr<Path> &path,SharedPtr<Peer> &peer,Bu
 			RR->t->incomingPacketDropped(tPtr,0x1000662a,p.h.packetId,0,id,path->address(),hops,Protocol::VERB_NOP,ZT_TRACE_PACKET_DROP_REASON_MAC_FAILED);
 			return false;
 		}
+		packetSize -= ZT_HMACSHA384_LEN;
 		KBKDFHMACSHA384(key,ZT_PROTO_KDF_KEY_LABEL_HELLO_HMAC,0,0,hmacKey); // iter == 0 for HELLO, 1 for OK(HELLO)
-		HMACSHA384(hmacKey,pkt.b,packetSize - ZT_HMACSHA384_LEN,hmac);
-		if (!Utils::secureEq(pkt.b + (packetSize - ZT_HMACSHA384_LEN),hmac,ZT_HMACSHA384_LEN)) {
+		HMACSHA384(hmacKey,pkt.b,packetSize,hmac);
+		if (!Utils::secureEq(pkt.b + packetSize,hmac,ZT_HMACSHA384_LEN)) {
 			RR->t->incomingPacketDropped(tPtr,0x1000662a,p.h.packetId,0,id,path->address(),hops,Protocol::VERB_NOP,ZT_TRACE_PACKET_DROP_REASON_MAC_FAILED);
 			return false;
 		}
 	}
 
-	// Packet has passed HMAC-SHA384 (if present and/or forced) ---------------------------------------------------------
+	// Packet has passed HMAC-SHA384 (if present) -----------------------------------------------------------------------
 
 	InetAddress externalSurfaceAddress;
 	Dictionary nodeMetaData;
@@ -559,43 +560,39 @@ bool VL1::_HELLO(void *tPtr,const SharedPtr<Path> &path,SharedPtr<Peer> &peer,Bu
 		}
 	}
 
-	if (((ptr + ZT_HMACSHA384_LEN) < packetSize)&&(peer->remoteVersionProtocol() >= 11)) {
+	if ((ptr < packetSize)&&(peer->remoteVersionProtocol() >= 11)) {
 		// Everything after this point is encrypted with Salsa20/12. This is only a privacy measure
 		// since there's nothing truly secret in a HELLO packet. It also means that an observer
 		// can't even get ephemeral public keys without first knowing the long term secret key,
 		// adding a little defense in depth.
 		uint8_t iv[8];
 		for (int i = 0; i < 8; ++i) iv[i] = pkt.b[i];
-		iv[7] &= 0xf8U;
+		iv[7] &= 0xf8U; // this exists for pure legacy reasons, meh...
 		Salsa20 s20(key,iv);
-		s20.crypt12(pkt.b + ptr,pkt.b + ptr,(packetSize - ZT_HMACSHA384_LEN) - ptr);
+		s20.crypt12(pkt.b + ptr,pkt.b + ptr,packetSize - ptr);
 
 		ptr += pkt.rI16(ptr); // skip length field which currently is always zero in v2.0+
+
 		if (ptr < packetSize) {
 			const unsigned int dictionarySize = pkt.rI16(ptr);
-			const void *const dictionaryBytes = pkt.b + ptr;
-			if ((ptr += (int)dictionarySize) > packetSize) {
-				RR->t->incomingPacketDropped(tPtr,0x0d0f0112,p.h.packetId,0,id,path->address(),hops,Protocol::VERB_HELLO,ZT_TRACE_PACKET_DROP_REASON_INVALID_OBJECT);
-				return false;
-			}
-
-			ptr += pkt.rI16(ptr); // skip any additional fields, currently always 0
-			if (ptr > packetSize) {
-				RR->t->incomingPacketDropped(tPtr,0x451f2341,0,p.h.packetId,id,path->address(),0,Protocol::VERB_HELLO,ZT_TRACE_PACKET_DROP_REASON_MALFORMED_PACKET);
+			const void *const dictionaryBytes = pkt.rBnc(ptr,dictionarySize);
+			if (Buf::readOverflow(ptr,packetSize)) {
+				RR->t->incomingPacketDropped(tPtr,0x0d0f0112,p.h.packetId,0,id,path->address(),hops,Protocol::VERB_HELLO,ZT_TRACE_PACKET_DROP_REASON_MALFORMED_PACKET);
 				return false;
 			}
-
 			if (dictionarySize) {
 				if (!nodeMetaData.decode(dictionaryBytes,dictionarySize)) {
 					RR->t->incomingPacketDropped(tPtr,0x67192344,p.h.packetId,0,id,path->address(),hops,Protocol::VERB_HELLO,ZT_TRACE_PACKET_DROP_REASON_INVALID_OBJECT);
 					return false;
 				}
 			}
+
+			ptr += pkt.rI16(ptr); // skip any additional fields, currently always 0
 		}
 	}
 
 	if (Buf::readOverflow(ptr,packetSize)) { // sanity check, should be impossible
-		RR->t->incomingPacketDropped(tPtr,0x457f2347,0,p.h.packetId,id,path->address(),0,Protocol::VERB_HELLO,ZT_TRACE_PACKET_DROP_REASON_MALFORMED_PACKET);
+		RR->t->incomingPacketDropped(tPtr,0x50003470,0,p.h.packetId,id,path->address(),0,Protocol::VERB_HELLO,ZT_TRACE_PACKET_DROP_REASON_MALFORMED_PACKET);
 		return false;
 	}
 
@@ -620,6 +617,10 @@ bool VL1::_HELLO(void *tPtr,const SharedPtr<Path> &path,SharedPtr<Peer> &peer,Bu
 	if ((hops == 0) && (externalSurfaceAddress))
 		RR->sa->iam(tPtr,id,path->localSocket(),path->address(),externalSurfaceAddress,RR->topology->isRoot(id),now);
 
+	peer->setRemoteVersion(p.versionProtocol,p.versionMajor,p.versionMinor,Utils::ntoh(p.versionRev));
+
+	// Compose and send OK(HELLO) ---------------------------------------------------------------------------------------
+
 	std::vector<uint8_t> myNodeMetaDataBin;
 	{
 		Dictionary myNodeMetaData;
@@ -651,8 +652,9 @@ bool VL1::_HELLO(void *tPtr,const SharedPtr<Path> &path,SharedPtr<Peer> &peer,Bu
 	int outl = sizeof(Protocol::OK::HELLO);
 	outp.wO(outl,path->address());
 
+	outp.wI(outl,(uint16_t)0); // legacy field, always 0
+
 	if (p.versionProtocol >= 11) {
-		outp.wI(outl,(uint16_t)0); // legacy field, always 0
 		outp.wI(outl,(uint16_t)myNodeMetaDataBin.size());
 		outp.wB(outl,myNodeMetaDataBin.data(),(unsigned int)myNodeMetaDataBin.size());
 		outp.wI(outl,(uint16_t)0); // length of additional fields, currently 0
@@ -665,21 +667,20 @@ bool VL1::_HELLO(void *tPtr,const SharedPtr<Path> &path,SharedPtr<Peer> &peer,Bu
 		outl += ZT_HMACSHA384_LEN;
 	}
 
-	Protocol::armor(outp,outl,peer->key(),ZT_PROTO_CIPHER_SUITE__POLY1305_SALSA2012);
+	Protocol::armor(outp,outl,peer->key(),peer->cipher());
 	path->send(RR,tPtr,outp.b,outl,now);
 
-	peer->setRemoteVersion(p.versionProtocol,p.versionMajor,p.versionMinor,Utils::ntoh(p.versionRev));
-
 	return true;
 }
 
-bool VL1::_ERROR(void *tPtr,const SharedPtr<Path> &path,const SharedPtr<Peer> &peer,Buf &pkt,int packetSize)
+bool VL1::_ERROR(void *tPtr,const SharedPtr<Path> &path,const SharedPtr<Peer> &peer,Buf &pkt,int packetSize,Protocol::Verb &inReVerb)
 {
 	if (packetSize < sizeof(Protocol::ERROR::Header)) {
 		RR->t->incomingPacketDropped(tPtr,0x3beb1947,0,0,identityFromPeerPtr(peer),path->address(),0,Protocol::VERB_ERROR,ZT_TRACE_PACKET_DROP_REASON_MALFORMED_PACKET);
 		return false;
 	}
 	Protocol::ERROR::Header &eh = pkt.as<Protocol::ERROR::Header>();
+	inReVerb = (Protocol::Verb)eh.inReVerb;
 
 	const int64_t now = RR->node->now();
 	if (!RR->expect->expecting(eh.inRePacketId,now)) {
@@ -717,13 +718,14 @@ bool VL1::_ERROR(void *tPtr,const SharedPtr<Path> &path,const SharedPtr<Peer> &p
 	return true;
 }
 
-bool VL1::_OK(void *tPtr,const SharedPtr<Path> &path,const SharedPtr<Peer> &peer,Buf &pkt,int packetSize)
+bool VL1::_OK(void *tPtr,const SharedPtr<Path> &path,const SharedPtr<Peer> &peer,Buf &pkt,int packetSize,Protocol::Verb &inReVerb)
 {
 	if (packetSize < sizeof(Protocol::OK::Header)) {
 		RR->t->incomingPacketDropped(tPtr,0x4c1f1ff7,0,0,identityFromPeerPtr(peer),path->address(),0,Protocol::VERB_OK,ZT_TRACE_PACKET_DROP_REASON_MALFORMED_PACKET);
 		return false;
 	}
 	Protocol::OK::Header &oh = pkt.as<Protocol::OK::Header>();
+	inReVerb = (Protocol::Verb)oh.inReVerb;
 
 	const int64_t now = RR->node->now();
 	if (!RR->expect->expecting(oh.inRePacketId,now)) {
@@ -793,7 +795,7 @@ bool VL1::_WHOIS(void *tPtr,const SharedPtr<Path> &path,const SharedPtr<Peer> &p
 		}
 
 		if (outl > sizeof(Protocol::OK::WHOIS)) {
-			Protocol::armor(outp,outl,peer->key(),ZT_PROTO_CIPHER_SUITE__POLY1305_SALSA2012);
+			Protocol::armor(outp,outl,peer->key(),peer->cipher());
 			path->send(RR,tPtr,outp.b,outl,RR->node->now());
 		}
 	}
@@ -803,7 +805,6 @@ bool VL1::_WHOIS(void *tPtr,const SharedPtr<Path> &path,const SharedPtr<Peer> &p
 
 bool VL1::_RENDEZVOUS(void *tPtr,const SharedPtr<Path> &path,const SharedPtr<Peer> &peer,Buf &pkt,int packetSize)
 {
-	static uint16_t junk = 0;
 	if (RR->topology->isRoot(peer->identity())) {
 		if (packetSize < sizeof(Protocol::RENDEZVOUS)) {
 			RR->t->incomingPacketDropped(tPtr,0x43e90ab3,Protocol::packetId(pkt,packetSize),0,peer->identity(),path->address(),Protocol::packetHops(pkt,packetSize),Protocol::VERB_RENDEZVOUS,ZT_TRACE_PACKET_DROP_REASON_MALFORMED_PACKET);
@@ -813,23 +814,33 @@ bool VL1::_RENDEZVOUS(void *tPtr,const SharedPtr<Path> &path,const SharedPtr<Pee
 
 		const SharedPtr<Peer> with(RR->topology->peer(tPtr,Address(rdv.peerAddress)));
 		if (with) {
+			const int64_t now = RR->node->now();
 			const unsigned int port = Utils::ntoh(rdv.port);
 			if (port != 0) {
 				switch(rdv.addressLength) {
 					case 4:
-						if ((sizeof(Protocol::RENDEZVOUS) + 4) <= packetSize) {
-							InetAddress atAddr(pkt.b + sizeof(Protocol::RENDEZVOUS),4,port);
-							++junk;
-							RR->node->putPacket(tPtr,path->localSocket(),atAddr,(const void *)&junk,2,2); // IPv4 "firewall opener" hack
-							with->sendHELLO(tPtr,path->localSocket(),atAddr,RR->node->now());
+					case 16:
+						if ((sizeof(Protocol::RENDEZVOUS) + rdv.addressLength) <= packetSize) {
+							const InetAddress atAddr(pkt.b + sizeof(Protocol::RENDEZVOUS),rdv.addressLength,port);
+							peer->contact(tPtr,Endpoint(atAddr),now,false,false);
 							RR->t->tryingNewPath(tPtr,0x55a19aaa,with->identity(),atAddr,path->address(),Protocol::packetId(pkt,packetSize),Protocol::VERB_RENDEZVOUS,peer->address(),peer->identity().hash(),ZT_TRACE_TRYING_NEW_PATH_REASON_RENDEZVOUS);
 						}
 						break;
-					case 16:
-						if ((sizeof(Protocol::RENDEZVOUS) + 16) <= packetSize) {
-							InetAddress atAddr(pkt.b + sizeof(Protocol::RENDEZVOUS),16,port);
-							with->sendHELLO(tPtr,path->localSocket(),atAddr,RR->node->now());
-							RR->t->tryingNewPath(tPtr,0x54bada09,with->identity(),atAddr,path->address(),Protocol::packetId(pkt,packetSize),Protocol::VERB_RENDEZVOUS,peer->address(),peer->identity().hash(),ZT_TRACE_TRYING_NEW_PATH_REASON_RENDEZVOUS);
+					case 255:
+						if ((sizeof(Protocol::RENDEZVOUS) + 1) <= packetSize) {
+							Endpoint ep;
+							int epl = ep.unmarshal(pkt.b + sizeof(Protocol::RENDEZVOUS),packetSize - (int)sizeof(Protocol::RENDEZVOUS));
+							if ((epl > 0) && (ep)) {
+								switch (ep.type()) {
+									case Endpoint::INETADDR_V4:
+									case Endpoint::INETADDR_V6:
+										peer->contact(tPtr,ep,now,false,false);
+										RR->t->tryingNewPath(tPtr,0x55a19aab,with->identity(),ep.inetAddr(),path->address(),Protocol::packetId(pkt,packetSize),Protocol::VERB_RENDEZVOUS,peer->address(),peer->identity().hash(),ZT_TRACE_TRYING_NEW_PATH_REASON_RENDEZVOUS);
+										break;
+									default:
+										break;
+								}
+							}
 						}
 						break;
 				}
@@ -847,6 +858,7 @@ bool VL1::_ECHO(void *tPtr,const SharedPtr<Path> &path,const SharedPtr<Peer> &pe
 		RR->t->incomingPacketDropped(tPtr,0x14d70bb0,packetId,0,peer->identity(),path->address(),Protocol::packetHops(pkt,packetSize),Protocol::VERB_ECHO,ZT_TRACE_PACKET_DROP_REASON_MALFORMED_PACKET);
 		return false;
 	}
+
 	if (peer->rateGateEchoRequest(now)) {
 		Buf outp;
 		Protocol::OK::ECHO &outh = outp.as<Protocol::OK::ECHO>();
@@ -865,11 +877,12 @@ bool VL1::_ECHO(void *tPtr,const SharedPtr<Path> &path,const SharedPtr<Peer> &pe
 			return false;
 		}
 
-		Protocol::armor(outp,outl,peer->key(),ZT_PROTO_CIPHER_SUITE__POLY1305_SALSA2012);
+		Protocol::armor(outp,outl,peer->key(),peer->cipher());
 		path->send(RR,tPtr,outp.b,outl,now);
 	} else {
 		RR->t->incomingPacketDropped(tPtr,0x27878bc1,packetId,0,peer->identity(),path->address(),Protocol::packetHops(pkt,packetSize),Protocol::VERB_ECHO,ZT_TRACE_PACKET_DROP_REASON_RATE_LIMIT_EXCEEDED);
 	}
+
 	return true;
 }
 

+ 2 - 2
node/VL1.hpp

@@ -70,8 +70,8 @@ private:
 
 	// Handlers for VL1 verbs -- for clarity's sake VL2 verbs are in the VL2 class.
 	bool _HELLO(void *tPtr,const SharedPtr<Path> &path,SharedPtr<Peer> &peer,Buf &pkt,int packetSize,bool authenticated);
-	bool _ERROR(void *tPtr,const SharedPtr<Path> &path,const SharedPtr<Peer> &peer,Buf &pkt,int packetSize);
-	bool _OK(void *tPtr,const SharedPtr<Path> &path,const SharedPtr<Peer> &peer,Buf &pkt,int packetSize);
+	bool _ERROR(void *tPtr,const SharedPtr<Path> &path,const SharedPtr<Peer> &peer,Buf &pkt,int packetSize,Protocol::Verb &inReVerb);
+	bool _OK(void *tPtr,const SharedPtr<Path> &path,const SharedPtr<Peer> &peer,Buf &pkt,int packetSize,Protocol::Verb &inReVerb);
 	bool _WHOIS(void *tPtr,const SharedPtr<Path> &path,const SharedPtr<Peer> &peer,Buf &pkt,int packetSize);
 	bool _RENDEZVOUS(void *tPtr,const SharedPtr<Path> &path,const SharedPtr<Peer> &peer,Buf &pkt,int packetSize);
 	bool _ECHO(void *tPtr,const SharedPtr<Path> &path,const SharedPtr<Peer> &peer,Buf &pkt,int packetSize);