Browse Source

Take the 0.6.0 opportunity to add flags to a few protocol verbs and do a bit more cleanup. Also fix it so certificates wont be accepted unless they are newer than existing ones.

Adam Ierymenko 11 years ago
parent
commit
ce14ba9004
7 changed files with 84 additions and 68 deletions
  1. 3 3
      node/CertificateOfMembership.hpp
  2. 34 22
      node/Network.cpp
  3. 1 1
      node/Packet.cpp
  4. 12 11
      node/Packet.hpp
  5. 31 30
      node/PacketDecoder.cpp
  6. 1 1
      node/PacketDecoder.hpp
  7. 2 0
      node/Switch.cpp

+ 3 - 3
node/CertificateOfMembership.hpp

@@ -198,14 +198,14 @@ public:
 	/**
 	 * @return Timestamp for this cert in ms since epoch (according to netconf's clock)
 	 */
-	inline Address timestamp() const
+	inline uint64_t timestamp() const
 		throw()
 	{
 		for(std::vector<_Qualifier>::const_iterator q(_qualifiers.begin());q!=_qualifiers.end();++q) {
 			if (q->id == COM_RESERVED_ID_TIMESTAMP)
-				return Address(q->value);
+				return q->value;
 		}
-		return Address();
+		return 0ULL;
 	}
 
 	/**

+ 34 - 22
node/Network.cpp

@@ -178,10 +178,14 @@ void Network::requestConfiguration()
 void Network::addMembershipCertificate(const CertificateOfMembership &cert)
 {
 	Mutex::Lock _l(_lock);
+
 	// We go ahead and accept certs provisionally even if _isOpen is true, since
 	// that might be changed in short order if the user is fiddling in the UI.
 	// These will be purged on clean() for open networks eventually.
-	_membershipCertificates[cert.issuedTo()] = cert;
+
+	CertificateOfMembership &old = _membershipCertificates[cert.issuedTo()];
+	if (cert.timestamp() >= old.timestamp())
+		old = cert;
 }
 
 bool Network::isAllowed(const Address &peer) const
@@ -299,30 +303,38 @@ void Network::_restoreState()
 
 		_membershipCertificates.clear();
 
-		try {
-			FILE *mcdb = fopen(mcdbPath.c_str(),"rb");
-			if (mcdb) {
-				for(;;) {
-					long rlen = (long)fread(buf.data() + buf.size(),1,ZT_NETWORK_CERT_WRITE_BUF_SIZE - buf.size(),mcdb);
-					if (rlen <= 0)
-						break;
-					buf.setSize(buf.size() + (unsigned int)rlen);
-					unsigned int ptr = 0;
-					while ((ptr < (ZT_NETWORK_CERT_WRITE_BUF_SIZE / 2))&&(ptr < buf.size())) {
-						ptr += com.deserialize(buf,ptr);
-						if (com.issuedTo())
-							_membershipCertificates[com.issuedTo()] = com;
-					}
-					if (ptr) {
-						memmove(buf.data(),buf.data() + ptr,buf.size() - ptr);
-						buf.setSize(buf.size() - ptr);
+		FILE *mcdb = fopen(mcdbPath.c_str(),"rb");
+		if (mcdb) {
+			try {
+				char magic[6];
+				if ((fread(magic,6,1,mcdb) == 1)&&(!memcmp("ZTMCD0",magic,6))) {
+					for(;;) {
+						long rlen = (long)fread(buf.data() + buf.size(),1,ZT_NETWORK_CERT_WRITE_BUF_SIZE - buf.size(),mcdb);
+						if (rlen <= 0)
+							break;
+						buf.setSize(buf.size() + (unsigned int)rlen);
+						unsigned int ptr = 0;
+						while ((ptr < (ZT_NETWORK_CERT_WRITE_BUF_SIZE / 2))&&(ptr < buf.size())) {
+							ptr += com.deserialize(buf,ptr);
+							if (com.issuedTo())
+								_membershipCertificates[com.issuedTo()] = com;
+						}
+						if (ptr) {
+							memmove(buf.data(),buf.data() + ptr,buf.size() - ptr);
+							buf.setSize(buf.size() - ptr);
+						}
 					}
+					fclose(mcdb);
+				} else {
+					fclose(mcdb);
+					Utils::rm(mcdbPath);
 				}
+			} catch ( ... ) {
+				// Membership cert dump file invalid. We'll re-learn them off the net.
+				_membershipCertificates.clear();
+				fclose(mcdb);
+				Utils::rm(mcdbPath);
 			}
-		} catch ( ... ) {
-			// Membership cert dump file invalid. We'll re-learn them off the net.
-			_membershipCertificates.clear();
-			Utils::rm(mcdbPath);
 		}
 	}
 }

+ 1 - 1
node/Packet.cpp

@@ -42,7 +42,7 @@ const char *Packet::verbString(Verb v)
 		case VERB_WHOIS: return "WHOIS";
 		case VERB_RENDEZVOUS: return "RENDEZVOUS";
 		case VERB_FRAME: return "FRAME";
-		case VERB_PROXY_FRAME: return "PROXY_FRAME";
+		case VERB_BRIDGED_FRAME: return "BRIDGED_FRAME";
 		case VERB_MULTICAST_FRAME: return "MULTICAST_FRAME";
 		case VERB_MULTICAST_LIKE: return "MULTICAST_LIKE";
 		case VERB_NETWORK_MEMBERSHIP_CERTIFICATE: return "NETWORK_MEMBERSHIP_CERTIFICATE";

+ 12 - 11
node/Packet.hpp

@@ -155,7 +155,8 @@
 
 #define ZT_PROTO_VERB_WHOIS_IDX_ZTADDRESS (ZT_PACKET_IDX_PAYLOAD)
 
-#define ZT_PROTO_VERB_RENDEZVOUS_IDX_ZTADDRESS (ZT_PACKET_IDX_PAYLOAD)
+#define ZT_PROTO_VERB_RENDEZVOUS_IDX_FLAGS (ZT_PACKET_IDX_PAYLOAD)
+#define ZT_PROTO_VERB_RENDEZVOUS_IDX_ZTADDRESS (ZT_PROTO_VERB_RENDEZVOUS_IDX_FLAGS + 1)
 #define ZT_PROTO_VERB_RENDEZVOUS_IDX_PORT (ZT_PROTO_VERB_RENDEZVOUS_IDX_ZTADDRESS + 5)
 #define ZT_PROTO_VERB_RENDEZVOUS_IDX_ADDRLEN (ZT_PROTO_VERB_RENDEZVOUS_IDX_PORT + 2)
 #define ZT_PROTO_VERB_RENDEZVOUS_IDX_ADDRESS (ZT_PROTO_VERB_RENDEZVOUS_IDX_ADDRLEN + 1)
@@ -199,8 +200,6 @@
 #define ZT_PROTO_VERB_MULTICAST_FRAME_LEN_FRAME_LEN 2
 #define ZT_PROTO_VERB_MULTICAST_FRAME_IDX_FRAME (ZT_PROTO_VERB_MULTICAST_FRAME_IDX_FRAME_LEN + ZT_PROTO_VERB_MULTICAST_FRAME_LEN_FRAME_LEN)
 
-#define ZT_PROTO_VERB_NETWORK_MEMBERSHIP_CERTIFICATE_IDX_CERTIFICATE (ZT_PACKET_IDX_PAYLOAD)
-
 #define ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_NETWORK_ID (ZT_PACKET_IDX_PAYLOAD)
 #define ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT_LEN (ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_NETWORK_ID + 8)
 #define ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT (ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT_LEN + 2)
@@ -434,11 +433,13 @@ public:
 		 * OK response payload:
 		 *   <[...] binary serialized identity>
 		 *
-		 * Error payload will be address queried.
+		 * ERROR response payload:
+		 *   <[5] address>
 		 */
 		VERB_WHOIS = 4,
 
 		/* Meet another node at a given protocol address:
+		 *   <[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)>
@@ -470,8 +471,7 @@ public:
 		 *   <[...] ethernet payload>
 		 *
 		 * MAC addresses are derived from the packet's source and destination
-		 * ZeroTier addresses. ZeroTier does not support VLANs or other extensions
-		 * beyond core Ethernet.
+		 * ZeroTier addresses.
 		 *
 		 * ERROR may be generated if a membership certificate is needed for a
 		 * closed network. Payload will be network ID.
@@ -479,7 +479,7 @@ public:
 		VERB_FRAME = 6,
 
 		/* TODO: not implemented yet */
-		VERB_PROXY_FRAME = 7,
+		VERB_BRIDGED_FRAME = 7,
 
 		/* A multicast frame:
 		 *   <[2] 16-bit propagation depth or 0xffff for "do not forward">
@@ -556,6 +556,7 @@ public:
 
 		/* Network member certificate:
 		 *   <[...] serialized certificate of membership>
+		 *   [ ... additional certificates may follow ...]
 		 *
 		 * Certificate contains network ID, peer it was issued for, etc.
 		 *
@@ -583,9 +584,8 @@ public:
 		 * node can push to other peers to demonstrate its right to speak on
 		 * a given network.
 		 *
-		 * ERROR may be NOT_FOUND if no such network is known, or
-		 * UNSUPPORTED_OPERATION if the netconf service isn't available. The
-		 * payload will be the network ID.
+		 * ERROR response payload:
+		 *   <[8] 64-bit network ID>
 		 */
 		VERB_NETWORK_CONFIG_REQUEST = 11,
 
@@ -594,7 +594,8 @@ public:
 		 *
 		 * This message can be sent by the network configuration master node
 		 * to request that nodes refresh their network configuration. It can
-		 * thus be used to "push" updates.
+		 * thus be used to "push" updates so that network config changes will
+		 * take effect quickly.
 		 *
 		 * It does not generate an OK or ERROR message, and is treated only as
 		 * a hint to refresh now.

+ 31 - 30
node/PacketDecoder.cpp

@@ -102,8 +102,8 @@ bool PacketDecoder::tryDecode(const RuntimeEnvironment *_r)
 				return _doRENDEZVOUS(_r,peer);
 			case Packet::VERB_FRAME:
 				return _doFRAME(_r,peer);
-			case Packet::VERB_PROXY_FRAME:
-				return _doPROXY_FRAME(_r,peer);
+			case Packet::VERB_BRIDGED_FRAME:
+				return _doBRIDGED_FRAME(_r,peer);
 			case Packet::VERB_MULTICAST_FRAME:
 				return _doMULTICAST_FRAME(_r,peer);
 			case Packet::VERB_MULTICAST_LIKE:
@@ -151,9 +151,6 @@ bool PacketDecoder::_doERROR(const RuntimeEnvironment *_r,const SharedPtr<Peer>
 				// if (_r->topology->isSupernode(source())) {}
 				break;
 			case Packet::ERROR_NEED_MEMBERSHIP_CERTIFICATE: {
-				// TODO: this allows anyone to request a membership cert, which is
-				// harmless until these contain possibly privacy-sensitive info.
-				// Then we'll need to be more careful.
 				SharedPtr<Network> network(_r->nc->network(at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD)));
 				if (network)
 					network->pushMembershipCertificate(source(),true,Utils::now());
@@ -399,7 +396,7 @@ bool PacketDecoder::_doFRAME(const RuntimeEnvironment *_r,const SharedPtr<Peer>
 	return true;
 }
 
-bool PacketDecoder::_doPROXY_FRAME(const RuntimeEnvironment *_r,const SharedPtr<Peer> &peer)
+bool PacketDecoder::_doBRIDGED_FRAME(const RuntimeEnvironment *_r,const SharedPtr<Peer> &peer)
 {
 	// TODO: bridging is not implemented yet
 	return true;
@@ -654,40 +651,44 @@ bool PacketDecoder::_doMULTICAST_LIKE(const RuntimeEnvironment *_r,const SharedP
 bool PacketDecoder::_doNETWORK_MEMBERSHIP_CERTIFICATE(const RuntimeEnvironment *_r,const SharedPtr<Peer> &peer)
 {
 	try {
-		CertificateOfMembership com(*this,ZT_PROTO_VERB_NETWORK_MEMBERSHIP_CERTIFICATE_IDX_CERTIFICATE);
-		if (!com.hasRequiredFields()) {
-			TRACE("dropped NETWORK_MEMBERSHIP_CERTIFICATE from %s(%s): invalid cert: at least one required field is missing",source().toString().c_str(),_remoteAddress.toString().c_str());
-			return true;
-		} else if (com.signedBy()) {
-			SharedPtr<Peer> signer(_r->topology->getPeer(com.signedBy()));
-			if (signer) {
-				if (com.verify(signer->identity())) {
-					uint64_t nwid = com.networkId();
-					SharedPtr<Network> network(_r->nc->network(nwid));
-					if (network) {
-						if (network->controller() == signer) {
-							network->addMembershipCertificate(com);
-							return true;
+		CertificateOfMembership com;
+		unsigned int ptr = ZT_PACKET_IDX_PAYLOAD;
+		while (ptr < size()) {
+			ptr += com.deserialize(*this,ptr);
+			if (!com.hasRequiredFields()) {
+				TRACE("dropped NETWORK_MEMBERSHIP_CERTIFICATE from %s(%s): invalid cert: at least one required field is missing",source().toString().c_str(),_remoteAddress.toString().c_str());
+				return true;
+			} else if (com.signedBy()) {
+				SharedPtr<Peer> signer(_r->topology->getPeer(com.signedBy()));
+				if (signer) {
+					if (com.verify(signer->identity())) {
+						uint64_t nwid = com.networkId();
+						SharedPtr<Network> network(_r->nc->network(nwid));
+						if (network) {
+							if (network->controller() == signer) {
+								network->addMembershipCertificate(com);
+								return true;
+							} else {
+								TRACE("dropped NETWORK_MEMBERSHIP_CERTIFICATE from %s(%s): signer %s is not the controller for network %.16llx",source().toString().c_str(),_remoteAddress.toString().c_str(),signer->address().toString().c_str(),(unsigned long long)nwid);
+								return true;
+							}
 						} else {
-							TRACE("dropped NETWORK_MEMBERSHIP_CERTIFICATE from %s(%s): signer %s is not the controller for network %.16llx",source().toString().c_str(),_remoteAddress.toString().c_str(),signer->address().toString().c_str(),(unsigned long long)nwid);
+							TRACE("dropped NETWORK_MEMBERSHIP_CERTIFICATE from %s(%s): not a member of network %.16llx",source().toString().c_str(),_remoteAddress.toString().c_str(),(unsigned long long)nwid);
 							return true;
 						}
 					} else {
-						TRACE("dropped NETWORK_MEMBERSHIP_CERTIFICATE from %s(%s): not a member of network %.16llx",source().toString().c_str(),_remoteAddress.toString().c_str(),(unsigned long long)nwid);
+						TRACE("dropped NETWORK_MEMBERSHIP_CERTIFICATE from %s(%s): failed signature verification for signer %s",source().toString().c_str(),_remoteAddress.toString().c_str(),signer->address().toString().c_str());
 						return true;
 					}
 				} else {
-					TRACE("dropped NETWORK_MEMBERSHIP_CERTIFICATE from %s(%s): failed signature verification for signer %s",source().toString().c_str(),_remoteAddress.toString().c_str(),signer->address().toString().c_str());
-					return true;
+					_r->sw->requestWhois(com.signedBy());
+					_step = DECODE_WAITING_FOR_NETWORK_MEMBERSHIP_CERTIFICATE_SIGNER_LOOKUP;
+					return false;
 				}
 			} else {
-				_r->sw->requestWhois(com.signedBy());
-				_step = DECODE_WAITING_FOR_NETWORK_MEMBERSHIP_CERTIFICATE_SIGNER_LOOKUP;
-				return false;
+				TRACE("dropped NETWORK_MEMBERSHIP_CERTIFICATE from %s(%s): invalid cert: no signature",source().toString().c_str(),_remoteAddress.toString().c_str());
+				return true;
 			}
-		} else {
-			TRACE("dropped NETWORK_MEMBERSHIP_CERTIFICATE from %s(%s): invalid cert: no signature",source().toString().c_str(),_remoteAddress.toString().c_str());
-			return true;
 		}
 	} catch (std::exception &ex) {
 		TRACE("dropped NETWORK_MEMBERSHIP_CERTIFICATE from %s(%s): unexpected exception: %s",source().toString().c_str(),_remoteAddress.toString().c_str(),ex.what());

+ 1 - 1
node/PacketDecoder.hpp

@@ -117,7 +117,7 @@ private:
 	bool _doWHOIS(const RuntimeEnvironment *_r,const SharedPtr<Peer> &peer);
 	bool _doRENDEZVOUS(const RuntimeEnvironment *_r,const SharedPtr<Peer> &peer);
 	bool _doFRAME(const RuntimeEnvironment *_r,const SharedPtr<Peer> &peer);
-	bool _doPROXY_FRAME(const RuntimeEnvironment *_r,const SharedPtr<Peer> &peer);
+	bool _doBRIDGED_FRAME(const RuntimeEnvironment *_r,const SharedPtr<Peer> &peer);
 	bool _doMULTICAST_FRAME(const RuntimeEnvironment *_r,const SharedPtr<Peer> &peer);
 	bool _doMULTICAST_LIKE(const RuntimeEnvironment *_r,const SharedPtr<Peer> &peer);
 	bool _doNETWORK_MEMBERSHIP_CERTIFICATE(const RuntimeEnvironment *_r,const SharedPtr<Peer> &peer);

+ 2 - 0
node/Switch.cpp

@@ -265,6 +265,7 @@ bool Switch::unite(const Address &p1,const Address &p2,bool force)
 
 	{	// tell p1 where to find p2
 		Packet outp(p1,_r->identity.address(),Packet::VERB_RENDEZVOUS);
+		outp.append((unsigned char)0);
 		p2.appendTo(outp);
 		outp.append((uint16_t)cg.first.port());
 		if (cg.first.isV6()) {
@@ -279,6 +280,7 @@ bool Switch::unite(const Address &p1,const Address &p2,bool force)
 	}
 	{	// tell p2 where to find p1
 		Packet outp(p2,_r->identity.address(),Packet::VERB_RENDEZVOUS);
+		outp.append((unsigned char)0);
 		p1.appendTo(outp);
 		outp.append((uint16_t)cg.second.port());
 		if (cg.second.isV6()) {