diff options
Diffstat (limited to 'controller')
| -rw-r--r-- | controller/SqliteNetworkController.cpp | 972 | ||||
| -rw-r--r-- | controller/SqliteNetworkController.hpp | 46 | ||||
| -rw-r--r-- | controller/schema.sql | 13 | ||||
| -rw-r--r-- | controller/schema.sql.c | 13 | 
4 files changed, 566 insertions, 478 deletions
| diff --git a/controller/SqliteNetworkController.cpp b/controller/SqliteNetworkController.cpp index 2a004bda..d16c5996 100644 --- a/controller/SqliteNetworkController.cpp +++ b/controller/SqliteNetworkController.cpp @@ -166,7 +166,7 @@ SqliteNetworkController::SqliteNetworkController(const char *dbPath) :  			/* Node */  			||(sqlite3_prepare_v2(_db,"SELECT identity FROM Node WHERE id = ?",-1,&_sGetNodeIdentity,(const char **)0) != SQLITE_OK) -			||(sqlite3_prepare_v2(_db,"INSERT INTO Node (id,identity) VALUES (?,?)",-1,&_sCreateNode,(const char **)0) != SQLITE_OK) +			||(sqlite3_prepare_v2(_db,"INSERT OR REPLACE INTO Node (id,identity) VALUES (?,?)",-1,&_sCreateOrReplaceNode,(const char **)0) != SQLITE_OK)  			/* Rule */  			||(sqlite3_prepare_v2(_db,"SELECT etherType FROM Rule WHERE networkId = ? AND \"action\" = 'accept'",-1,&_sGetEtherTypesFromRuleTable,(const char **)0) != SQLITE_OK) @@ -203,17 +203,13 @@ SqliteNetworkController::SqliteNetworkController(const char *dbPath) :  			||(sqlite3_prepare_v2(_db,"UPDATE Member SET authorized = ?,memberRevision = (SELECT memberRevisionCounter FROM Network WHERE id = ?) WHERE rowid = ?",-1,&_sUpdateMemberAuthorized,(const char **)0) != SQLITE_OK)  			||(sqlite3_prepare_v2(_db,"UPDATE Member SET activeBridge = ?,memberRevision = (SELECT memberRevisionCounter FROM Network WHERE id = ?) WHERE rowid = ?",-1,&_sUpdateMemberActiveBridge,(const char **)0) != SQLITE_OK)  			||(sqlite3_prepare_v2(_db,"DELETE FROM Member WHERE networkId = ? AND nodeId = ?",-1,&_sDeleteMember,(const char **)0) != SQLITE_OK) +			||(sqlite3_prepare_v2(_db,"DELETE FROM Member WHERE networkId = ?",-1,&_sDeleteAllNetworkMembers,(const char **)0) != SQLITE_OK)  			/* Gateway */  			||(sqlite3_prepare_v2(_db,"SELECT \"ip\",ipVersion,metric FROM Gateway WHERE networkId = ? ORDER BY metric ASC",-1,&_sGetGateways,(const char **)0) != SQLITE_OK)  			||(sqlite3_prepare_v2(_db,"DELETE FROM Gateway WHERE networkId = ?",-1,&_sDeleteGateways,(const char **)0) != SQLITE_OK)  			||(sqlite3_prepare_v2(_db,"INSERT INTO Gateway (networkId,\"ip\",ipVersion,metric) VALUES (?,?,?,?)",-1,&_sCreateGateway,(const char **)0) != SQLITE_OK) -			/* Log */ -			||(sqlite3_prepare_v2(_db,"INSERT INTO \"Log\" (networkId,nodeId,\"ts\",\"authorized\",\"version\",fromAddr) VALUES (?,?,?,?,?,?)",-1,&_sPutLog,(const char **)0) != SQLITE_OK) -			||(sqlite3_prepare_v2(_db,"SELECT \"ts\",\"authorized\",\"version\",fromAddr FROM \"Log\" WHERE networkId = ? AND nodeId = ? AND \"ts\" >= ? ORDER BY \"ts\" ASC",-1,&_sGetMemberLog,(const char **)0) != SQLITE_OK) -			||(sqlite3_prepare_v2(_db,"SELECT \"ts\",\"authorized\",\"version\",fromAddr FROM \"Log\" WHERE networkId = ? AND nodeId = ? ORDER BY \"ts\" DESC LIMIT 10",-1,&_sGetRecentMemberLog,(const char **)0) != SQLITE_OK) -  			/* Config */  			||(sqlite3_prepare_v2(_db,"SELECT \"v\" FROM \"Config\" WHERE \"k\" = ?",-1,&_sGetConfig,(const char **)0) != SQLITE_OK)  			||(sqlite3_prepare_v2(_db,"INSERT OR REPLACE INTO \"Config\" (\"k\",\"v\") VALUES (?,?)",-1,&_sSetConfig,(const char **)0) != SQLITE_OK) @@ -256,7 +252,7 @@ SqliteNetworkController::~SqliteNetworkController()  		sqlite3_finalize(_sGetMember);  		sqlite3_finalize(_sCreateMember);  		sqlite3_finalize(_sGetNodeIdentity); -		sqlite3_finalize(_sCreateNode); +		sqlite3_finalize(_sCreateOrReplaceNode);  		sqlite3_finalize(_sUpdateNode);  		sqlite3_finalize(_sUpdateNode2);  		sqlite3_finalize(_sGetEtherTypesFromRuleTable); @@ -287,6 +283,7 @@ SqliteNetworkController::~SqliteNetworkController()  		sqlite3_finalize(_sUpdateMemberAuthorized);  		sqlite3_finalize(_sUpdateMemberActiveBridge);  		sqlite3_finalize(_sDeleteMember); +		sqlite3_finalize(_sDeleteAllNetworkMembers);  		sqlite3_finalize(_sDeleteNetwork);  		sqlite3_finalize(_sGetGateways);  		sqlite3_finalize(_sDeleteGateways); @@ -294,425 +291,14 @@ SqliteNetworkController::~SqliteNetworkController()  		sqlite3_finalize(_sIncrementMemberRevisionCounter);  		sqlite3_finalize(_sGetConfig);  		sqlite3_finalize(_sSetConfig); -		sqlite3_finalize(_sPutLog); -		sqlite3_finalize(_sGetMemberLog); -		sqlite3_finalize(_sGetRecentMemberLog);  		sqlite3_close(_db);  	}  } -NetworkController::ResultCode SqliteNetworkController::doNetworkConfigRequest(const InetAddress &fromAddr,const Identity &signingId,const Identity &identity,uint64_t nwid,const Dictionary &metaData,uint64_t haveRevision,Dictionary &netconf) +NetworkController::ResultCode SqliteNetworkController::doNetworkConfigRequest(const InetAddress &fromAddr,const Identity &signingId,const Identity &identity,uint64_t nwid,const Dictionary &metaData,Dictionary &netconf)  { -	// Decode some stuff from metaData -	const unsigned int clientMajorVersion = (unsigned int)metaData.getHexUInt(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_MAJOR_VERSION,0); -	const unsigned int clientMinorVersion = (unsigned int)metaData.getHexUInt(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_MINOR_VERSION,0); -	const unsigned int clientRevision = (unsigned int)metaData.getHexUInt(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_REVISION,0); -	const bool clientIs104 = (Utils::compareVersion(clientMajorVersion,clientMinorVersion,clientRevision,1,0,4) >= 0); -  	Mutex::Lock _l(_lock); - -	// Note: we can't reuse prepared statements that return const char * pointers without -	// making our own copy in e.g. a std::string first. - -	if ((!signingId)||(!signingId.hasPrivate())) { -		netconf["error"] = "signing identity invalid or lacks private key"; -		return NetworkController::NETCONF_QUERY_INTERNAL_SERVER_ERROR; -	} -	if (signingId.address().toInt() != (nwid >> 24)) { -		netconf["error"] = "signing identity address does not match most significant 40 bits of network ID"; -		return NetworkController::NETCONF_QUERY_INTERNAL_SERVER_ERROR; -	} - -	// Check rate limit - -	{ -		uint64_t &lrt = _lastRequestTime[std::pair<Address,uint64_t>(identity.address(),nwid)]; -		uint64_t lrt2 = lrt; -		if (((lrt = OSUtils::now()) - lrt2) <= ZT_NETCONF_MIN_REQUEST_PERIOD) -			return NetworkController::NETCONF_QUERY_IGNORE; -	} - -	NetworkRecord network; -	memset(&network,0,sizeof(network)); -	Utils::snprintf(network.id,sizeof(network.id),"%.16llx",(unsigned long long)nwid); - -	MemberRecord member; -	memset(&member,0,sizeof(member)); -	Utils::snprintf(member.nodeId,sizeof(member.nodeId),"%.10llx",(unsigned long long)identity.address().toInt()); - -	// Create Node record or do full identity check if we already have one - -	sqlite3_reset(_sGetNodeIdentity); -	sqlite3_bind_text(_sGetNodeIdentity,1,member.nodeId,10,SQLITE_STATIC); -	if (sqlite3_step(_sGetNodeIdentity) == SQLITE_ROW) { -		try { -			Identity alreadyKnownIdentity((const char *)sqlite3_column_text(_sGetNodeIdentity,0)); -			if (alreadyKnownIdentity != identity) -				return NetworkController::NETCONF_QUERY_ACCESS_DENIED; -		} catch ( ... ) { // identity stored in database is not valid or is NULL -			return NetworkController::NETCONF_QUERY_ACCESS_DENIED; -		} -	} else { -		std::string idstr(identity.toString(false)); -		sqlite3_reset(_sCreateNode); -		sqlite3_bind_text(_sCreateNode,1,member.nodeId,10,SQLITE_STATIC); -		sqlite3_bind_text(_sCreateNode,2,idstr.c_str(),-1,SQLITE_STATIC); -		if (sqlite3_step(_sCreateNode) != SQLITE_DONE) { -			netconf["error"] = "unable to create new Node record"; -			return NetworkController::NETCONF_QUERY_INTERNAL_SERVER_ERROR; -		} -	} - -	// Fetch Network record - -	sqlite3_reset(_sGetNetworkById); -	sqlite3_bind_text(_sGetNetworkById,1,network.id,16,SQLITE_STATIC); -	if (sqlite3_step(_sGetNetworkById) == SQLITE_ROW) { -		network.name = (const char *)sqlite3_column_text(_sGetNetworkById,0); -		network.isPrivate = (sqlite3_column_int(_sGetNetworkById,1) > 0); -		network.enableBroadcast = (sqlite3_column_int(_sGetNetworkById,2) > 0); -		network.allowPassiveBridging = (sqlite3_column_int(_sGetNetworkById,3) > 0); -		network.v4AssignMode = (const char *)sqlite3_column_text(_sGetNetworkById,4); -		network.v6AssignMode = (const char *)sqlite3_column_text(_sGetNetworkById,5); -		network.multicastLimit = sqlite3_column_int(_sGetNetworkById,6); -		network.creationTime = (uint64_t)sqlite3_column_int64(_sGetNetworkById,7); -		network.revision = (uint64_t)sqlite3_column_int64(_sGetNetworkById,8); -		network.memberRevisionCounter = (uint64_t)sqlite3_column_int64(_sGetNetworkById,9); -	} else { -		return NetworkController::NETCONF_QUERY_OBJECT_NOT_FOUND; -	} - -	// Fetch Member record - -	bool foundMember = false; -	sqlite3_reset(_sGetMember); -	sqlite3_bind_text(_sGetMember,1,network.id,16,SQLITE_STATIC); -	sqlite3_bind_text(_sGetMember,2,member.nodeId,10,SQLITE_STATIC); -	if (sqlite3_step(_sGetMember) == SQLITE_ROW) { -		foundMember = true; -		member.rowid = (int64_t)sqlite3_column_int64(_sGetMember,0); -		member.authorized = (sqlite3_column_int(_sGetMember,1) > 0); -		member.activeBridge = (sqlite3_column_int(_sGetMember,2) > 0); -	} - -	// Create Member record for unknown nodes, auto-authorizing if network is public - -	if (!foundMember) { -		member.authorized = (network.isPrivate ? false : true); -		member.activeBridge = false; -		sqlite3_reset(_sCreateMember); -		sqlite3_bind_text(_sCreateMember,1,network.id,16,SQLITE_STATIC); -		sqlite3_bind_text(_sCreateMember,2,member.nodeId,10,SQLITE_STATIC); -		sqlite3_bind_int(_sCreateMember,3,(member.authorized ? 1 : 0)); -		sqlite3_bind_text(_sCreateMember,4,network.id,16,SQLITE_STATIC); -		if (sqlite3_step(_sCreateMember) != SQLITE_DONE) { -			netconf["error"] = "unable to create new member record"; -			return NetworkController::NETCONF_QUERY_INTERNAL_SERVER_ERROR; -		} -		member.rowid = (int64_t)sqlite3_last_insert_rowid(_db); - -		sqlite3_reset(_sIncrementMemberRevisionCounter); -		sqlite3_bind_text(_sIncrementMemberRevisionCounter,1,network.id,16,SQLITE_STATIC); -		sqlite3_step(_sIncrementMemberRevisionCounter); -	} - -	// Add log entry -	{ -		char ver[16]; -		std::string fa; -		if (fromAddr) { -			fa = fromAddr.toString(); -			if (fa.length() > 64) -				fa = fa.substr(0,64); -		} -		sqlite3_reset(_sPutLog); -		sqlite3_bind_text(_sPutLog,1,network.id,16,SQLITE_STATIC); -		sqlite3_bind_text(_sPutLog,2,member.nodeId,10,SQLITE_STATIC); -		sqlite3_bind_int64(_sPutLog,3,(long long)OSUtils::now()); -		sqlite3_bind_int(_sPutLog,4,member.authorized ? 1 : 0); -		if ((clientMajorVersion > 0)||(clientMinorVersion > 0)||(clientRevision > 0)) { -			Utils::snprintf(ver,sizeof(ver),"%u.%u.%u",clientMajorVersion,clientMinorVersion,clientRevision); -			sqlite3_bind_text(_sPutLog,5,ver,-1,SQLITE_STATIC); -		} else sqlite3_bind_null(_sPutLog,5); -		if (fa.length() > 0) -			sqlite3_bind_text(_sPutLog,6,fa.c_str(),-1,SQLITE_STATIC); -		else sqlite3_bind_null(_sPutLog,6); -		sqlite3_step(_sPutLog); -	} - -	// Check member authorization - -	if (!member.authorized) -		return NetworkController::NETCONF_QUERY_ACCESS_DENIED; - -	// Create and sign netconf - -	netconf.clear(); -	{ -		char tss[24],rs[24]; -		Utils::snprintf(tss,sizeof(tss),"%.16llx",(unsigned long long)OSUtils::now()); -		Utils::snprintf(rs,sizeof(rs),"%.16llx",(unsigned long long)network.revision); -		netconf[ZT_NETWORKCONFIG_DICT_KEY_TIMESTAMP] = tss; -		netconf[ZT_NETWORKCONFIG_DICT_KEY_REVISION] = rs; -		netconf[ZT_NETWORKCONFIG_DICT_KEY_NETWORK_ID] = network.id; -		netconf[ZT_NETWORKCONFIG_DICT_KEY_ISSUED_TO] = member.nodeId; -		netconf[ZT_NETWORKCONFIG_DICT_KEY_PRIVATE] = network.isPrivate ? "1" : "0"; -		netconf[ZT_NETWORKCONFIG_DICT_KEY_NAME] = (network.name) ? network.name : ""; -		netconf[ZT_NETWORKCONFIG_DICT_KEY_ENABLE_BROADCAST] = network.enableBroadcast ? "1" : "0"; -		netconf[ZT_NETWORKCONFIG_DICT_KEY_ALLOW_PASSIVE_BRIDGING] = network.allowPassiveBridging ? "1" : "0"; - -		{	// TODO: right now only etherTypes are supported in rules -			std::vector<int> allowedEtherTypes; -			sqlite3_reset(_sGetEtherTypesFromRuleTable); -			sqlite3_bind_text(_sGetEtherTypesFromRuleTable,1,network.id,16,SQLITE_STATIC); -			while (sqlite3_step(_sGetEtherTypesFromRuleTable) == SQLITE_ROW) { -				if (sqlite3_column_type(_sGetEtherTypesFromRuleTable,0) == SQLITE_NULL) { -					allowedEtherTypes.clear(); -					allowedEtherTypes.push_back(0); // NULL 'allow' matches ANY -					break; -				} else { -					int et = sqlite3_column_int(_sGetEtherTypesFromRuleTable,0); -					if ((et >= 0)&&(et <= 0xffff)) -						allowedEtherTypes.push_back(et); -				} -			} -			std::sort(allowedEtherTypes.begin(),allowedEtherTypes.end()); -			allowedEtherTypes.erase(std::unique(allowedEtherTypes.begin(),allowedEtherTypes.end()),allowedEtherTypes.end()); -			std::string allowedEtherTypesCsv; -			for(std::vector<int>::const_iterator i(allowedEtherTypes.begin());i!=allowedEtherTypes.end();++i) { -				if (allowedEtherTypesCsv.length()) -					allowedEtherTypesCsv.push_back(','); -				char tmp[16]; -				Utils::snprintf(tmp,sizeof(tmp),"%.4x",(unsigned int)*i); -				allowedEtherTypesCsv.append(tmp); -			} -			netconf[ZT_NETWORKCONFIG_DICT_KEY_ALLOWED_ETHERNET_TYPES] = allowedEtherTypesCsv; -		} - -		if (network.multicastLimit > 0) { -			char ml[16]; -			Utils::snprintf(ml,sizeof(ml),"%lx",(unsigned long)network.multicastLimit); -			netconf[ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_LIMIT] = ml; -		} - -		{ -			std::string activeBridges; -			sqlite3_reset(_sGetActiveBridges); -			sqlite3_bind_text(_sGetActiveBridges,1,network.id,16,SQLITE_STATIC); -			while (sqlite3_step(_sGetActiveBridges) == SQLITE_ROW) { -				const char *ab = (const char *)sqlite3_column_text(_sGetActiveBridges,0); -				if ((ab)&&(strlen(ab) == 10)) { -					if (activeBridges.length()) -						activeBridges.push_back(','); -					activeBridges.append(ab); -				} -				if (activeBridges.length() > 1024) // sanity check -- you can't have too many active bridges at the moment -					break; -			} -			if (activeBridges.length()) -				netconf[ZT_NETWORKCONFIG_DICT_KEY_ACTIVE_BRIDGES] = activeBridges; -		} - -		{ -			std::string relays; -			sqlite3_reset(_sGetRelays); -			sqlite3_bind_text(_sGetRelays,1,network.id,16,SQLITE_STATIC); -			while (sqlite3_step(_sGetRelays) == SQLITE_ROW) { -				const char *n = (const char *)sqlite3_column_text(_sGetRelays,0); -				const char *a = (const char *)sqlite3_column_text(_sGetRelays,1); -				if ((n)&&(a)) { -					Address node(n); -					InetAddress addr(a); -					if ((node)&&(addr)) { -						if (relays.length()) -							relays.push_back(','); -						relays.append(node.toString()); -						relays.push_back(';'); -						relays.append(addr.toString()); -					} -				} -			} -			if (relays.length()) -				netconf[ZT_NETWORKCONFIG_DICT_KEY_RELAYS] = relays; -		} - -		{ -			char tmp[128]; -			std::string gateways; -			sqlite3_reset(_sGetGateways); -			sqlite3_bind_text(_sGetGateways,1,network.id,16,SQLITE_STATIC); -			while (sqlite3_step(_sGetGateways) == SQLITE_ROW) { -				const unsigned char *ip = (const unsigned char *)sqlite3_column_blob(_sGetGateways,0); -				switch(sqlite3_column_int(_sGetGateways,1)) { // ipVersion -					case 4: -						Utils::snprintf(tmp,sizeof(tmp),"%s%d.%d.%d.%d/%d", -							(gateways.length() > 0) ? "," : "", -							(int)ip[12], -							(int)ip[13], -							(int)ip[14], -							(int)ip[15], -							(int)sqlite3_column_int(_sGetGateways,2)); // metric -						gateways.append(tmp); -						break; -					case 6: -						Utils::snprintf(tmp,sizeof(tmp),"%s%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x/%d", -							(gateways.length() > 0) ? "," : "", -							(int)ip[0], -							(int)ip[1], -							(int)ip[2], -							(int)ip[3], -							(int)ip[4], -							(int)ip[5], -							(int)ip[6], -							(int)ip[7], -							(int)ip[8], -							(int)ip[9], -							(int)ip[10], -							(int)ip[11], -							(int)ip[12], -							(int)ip[13], -							(int)ip[14], -							(int)ip[15], -							(int)sqlite3_column_int(_sGetGateways,2)); // metric -						gateways.append(tmp); -						break; -				} -			} -			if (gateways.length()) -				netconf[ZT_NETWORKCONFIG_DICT_KEY_GATEWAYS] = gateways; -		} - -		if ((network.v4AssignMode)&&(!strcmp(network.v4AssignMode,"zt"))) { -			std::string v4s; - -			// Get existing IPv4 IP assignments and network routes -- keep routes in a -			// vector for use in auto-assign if we need them. -			std::vector< std::pair<uint32_t,int> > routedNetworks; -			bool haveStaticIpAssignment = false; -			sqlite3_reset(_sGetIpAssignmentsForNode); -			sqlite3_bind_text(_sGetIpAssignmentsForNode,1,network.id,16,SQLITE_STATIC); -			sqlite3_bind_text(_sGetIpAssignmentsForNode,2,member.nodeId,10,SQLITE_STATIC); -			sqlite3_bind_int(_sGetIpAssignmentsForNode,3,4); // 4 == IPv4 -			while (sqlite3_step(_sGetIpAssignmentsForNode) == SQLITE_ROW) { -				const unsigned char *ip = (const unsigned char *)sqlite3_column_blob(_sGetIpAssignmentsForNode,1); -				if ((!ip)||(sqlite3_column_bytes(_sGetIpAssignmentsForNode,1) != 16)) -					continue; -				int ipNetmaskBits = sqlite3_column_int(_sGetIpAssignmentsForNode,2); -				if ((ipNetmaskBits <= 0)||(ipNetmaskBits > 32)) -					continue; - -				const IpAssignmentType ipt = (IpAssignmentType)sqlite3_column_int(_sGetIpAssignmentsForNode,0); -				switch(ipt) { -					case ZT_IP_ASSIGNMENT_TYPE_ADDRESS: -						haveStaticIpAssignment = true; -						break; -					case ZT_IP_ASSIGNMENT_TYPE_NETWORK: -						routedNetworks.push_back(std::pair<uint32_t,int>(Utils::ntoh(*(reinterpret_cast<const uint32_t *>(ip + 12))),ipNetmaskBits)); -						break; -					default: -						continue; -				} - -				// 1.0.4 or newer clients support network routes in addition to IPs. -				// Older clients only support IP address / netmask entries. -				if ((clientIs104)||(ipt == ZT_IP_ASSIGNMENT_TYPE_ADDRESS)) { -					char tmp[32]; -					Utils::snprintf(tmp,sizeof(tmp),"%d.%d.%d.%d/%d",(int)ip[12],(int)ip[13],(int)ip[14],(int)ip[15],ipNetmaskBits); -					if (v4s.length()) -						v4s.push_back(','); -					v4s.append(tmp); -				} -			} - -			if (!haveStaticIpAssignment) { -				// Attempt to auto-assign an IPv4 address from an available routed pool -				sqlite3_reset(_sGetIpAssignmentPools); -				sqlite3_bind_text(_sGetIpAssignmentPools,1,network.id,16,SQLITE_STATIC); -				sqlite3_bind_int(_sGetIpAssignmentPools,2,4); // 4 == IPv4 - -				while (sqlite3_step(_sGetIpAssignmentPools) == SQLITE_ROW) { -					const unsigned char *ipRangeStartB = reinterpret_cast<const unsigned char *>(sqlite3_column_blob(_sGetIpAssignmentPools,0)); -					const unsigned char *ipRangeEndB = reinterpret_cast<const unsigned char *>(sqlite3_column_blob(_sGetIpAssignmentPools,1)); -					if ((!ipRangeStartB)||(!ipRangeEndB)||(sqlite3_column_bytes(_sGetIpAssignmentPools,0) != 16)||(sqlite3_column_bytes(_sGetIpAssignmentPools,1) != 16)) -						continue; - -					uint32_t ipRangeStart = Utils::ntoh(*(reinterpret_cast<const uint32_t *>(ipRangeStartB + 12))); -					uint32_t ipRangeEnd = Utils::ntoh(*(reinterpret_cast<const uint32_t *>(ipRangeEndB + 12))); -					if (ipRangeEnd < ipRangeStart) -						continue; -					uint32_t ipRangeLen = ipRangeEnd - ipRangeStart; - -					// Start with the LSB of the member's address -					uint32_t ipTrialCounter = (uint32_t)(identity.address().toInt() & 0xffffffff); - -					for(uint32_t k=ipRangeStart,l=0;(k<=ipRangeEnd)&&(l < 1000000);++k,++l) { -						uint32_t ip = (ipRangeLen > 0) ? (ipRangeStart + (ipTrialCounter % ipRangeLen)) : ipRangeStart; -						++ipTrialCounter; - -						for(std::vector< std::pair<uint32_t,int> >::const_iterator r(routedNetworks.begin());r!=routedNetworks.end();++r) { -							if ((ip & (0xffffffff << (32 - r->second))) == r->first) { -								// IP is included in a routed network, so check if it's allocated - -								uint32_t ipBlob[4]; -								ipBlob[0] = 0; ipBlob[1] = 0; ipBlob[2] = 0; ipBlob[3] = Utils::hton(ip); - -								sqlite3_reset(_sCheckIfIpIsAllocated); -								sqlite3_bind_text(_sCheckIfIpIsAllocated,1,network.id,16,SQLITE_STATIC); -								sqlite3_bind_blob(_sCheckIfIpIsAllocated,2,(const void *)ipBlob,16,SQLITE_STATIC); -								sqlite3_bind_int(_sCheckIfIpIsAllocated,3,4); // 4 == IPv4 -								sqlite3_bind_int(_sCheckIfIpIsAllocated,4,(int)ZT_IP_ASSIGNMENT_TYPE_ADDRESS); -								if (sqlite3_step(_sCheckIfIpIsAllocated) != SQLITE_ROW) { -									// No rows returned, so the IP is available -									sqlite3_reset(_sAllocateIp); -									sqlite3_bind_text(_sAllocateIp,1,network.id,16,SQLITE_STATIC); -									sqlite3_bind_text(_sAllocateIp,2,member.nodeId,10,SQLITE_STATIC); -									sqlite3_bind_int(_sAllocateIp,3,(int)ZT_IP_ASSIGNMENT_TYPE_ADDRESS); -									sqlite3_bind_blob(_sAllocateIp,4,(const void *)ipBlob,16,SQLITE_STATIC); -									sqlite3_bind_int(_sAllocateIp,5,r->second); // IP netmask bits from matching route -									sqlite3_bind_int(_sAllocateIp,6,4); // 4 == IPv4 -									if (sqlite3_step(_sAllocateIp) == SQLITE_DONE) { -										char tmp[32]; -										Utils::snprintf(tmp,sizeof(tmp),"%d.%d.%d.%d/%d",(int)((ip >> 24) & 0xff),(int)((ip >> 16) & 0xff),(int)((ip >> 8) & 0xff),(int)(ip & 0xff),r->second); -										if (v4s.length()) -											v4s.push_back(','); -										v4s.append(tmp); -										haveStaticIpAssignment = true; // break outer loop -									} -								} - -								break; // stop checking routed networks -							} -						} - -						if (haveStaticIpAssignment) -							break; -					} -				} -			} - -			if (v4s.length()) -				netconf[ZT_NETWORKCONFIG_DICT_KEY_IPV4_STATIC] = v4s; -		} - -		// TODO: IPv6 auto-assign once it's supported in UI - -		if (network.isPrivate) { -			CertificateOfMembership com(OSUtils::now(),ZT_NETWORK_AUTOCONF_DELAY + (ZT_NETWORK_AUTOCONF_DELAY / 2),nwid,identity.address()); -			if (com.sign(signingId)) // basically can't fail unless our identity is invalid -				netconf[ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATE_OF_MEMBERSHIP] = com.toString(); -			else { -				netconf["error"] = "unable to sign COM"; -				return NETCONF_QUERY_INTERNAL_SERVER_ERROR; -			} -		} - -		if (!netconf.sign(signingId,OSUtils::now())) { -			netconf["error"] = "unable to sign netconf dictionary"; -			return NETCONF_QUERY_INTERNAL_SERVER_ERROR; -		} -	} - -	return NetworkController::NETCONF_QUERY_OK; +	return _doNetworkConfigRequest(fromAddr,signingId,identity,nwid,metaData,netconf);  }  unsigned int SqliteNetworkController::handleControlPlaneHttpGET( @@ -870,6 +456,35 @@ unsigned int SqliteNetworkController::handleControlPlaneHttpPOST(  										}  										addToNetworkRevision = 1;  									} +								} else if (!strcmp(j->u.object.values[k].name,"identity")) { +									// Identity is technically an immutable field, but if the member's Node has +									// no identity we allow it to be populated. This is primarily for migrating +									// node data from another controller. +									json_value *idstr = j->u.object.values[k].value; +									if (idstr->type == json_string) { +										bool alreadyHaveIdentity = false; + +										sqlite3_reset(_sGetNodeIdentity); +										sqlite3_bind_text(_sGetNodeIdentity,1,addrs,10,SQLITE_STATIC); +										if (sqlite3_step(_sGetNodeIdentity) == SQLITE_ROW) { +											const char *tmp2 = (const char *)sqlite3_column_text(_sGetNodeIdentity,0); +											if ((tmp2)&&(tmp2[0])) +												alreadyHaveIdentity = true; +										} + +										if (!alreadyHaveIdentity) { +											try { +												Identity id2(idstr->u.string.ptr); +												if (id2) { +													std::string idstr2(id2.toString(false)); // object must persist until after sqlite3_step() for SQLITE_STATIC +													sqlite3_reset(_sCreateOrReplaceNode); +													sqlite3_bind_text(_sCreateOrReplaceNode,1,addrs,10,SQLITE_STATIC); +													sqlite3_bind_text(_sCreateOrReplaceNode,2,idstr2.c_str(),-1,SQLITE_STATIC); +													sqlite3_step(_sCreateOrReplaceNode); +												} +											} catch ( ... ) {} // ignore invalid identities +										} +									}  								}  							} @@ -1311,7 +926,12 @@ unsigned int SqliteNetworkController::handleControlPlaneHttpDELETE(  				sqlite3_reset(_sDeleteNetwork);  				sqlite3_bind_text(_sDeleteNetwork,1,nwids,16,SQLITE_STATIC); -				return ((sqlite3_step(_sDeleteNetwork) == SQLITE_DONE) ? 200 : 500); +				if (sqlite3_step(_sDeleteNetwork) == SQLITE_DONE) { +					sqlite3_reset(_sDeleteAllNetworkMembers); +					sqlite3_bind_text(_sDeleteAllNetworkMembers,1,nwids,16,SQLITE_STATIC); +					sqlite3_step(_sDeleteAllNetworkMembers); +					return 200; +				} else return 500;  			}  		} // else 404 @@ -1330,7 +950,7 @@ unsigned int SqliteNetworkController::_doCPGet(  	std::string &responseContentType)  {  	// Assumes _lock is locked -	char json[16384]; +	char json[65536];  	if ((path.size() > 0)&&(path[0] == "network")) { @@ -1355,8 +975,55 @@ unsigned int SqliteNetworkController::_doCPGet(  						sqlite3_bind_text(_sGetMember2,1,nwids,16,SQLITE_STATIC);  						sqlite3_bind_text(_sGetMember2,2,addrs,10,SQLITE_STATIC);  						if (sqlite3_step(_sGetMember2) == SQLITE_ROW) { +							const char *memberIdStr = (const char *)sqlite3_column_text(_sGetMember2,3); + +							// If testSingingId is included in the URL or X-ZT1-TestSigningId in the headers +							// and if it contains an identity with a secret portion, the resturned JSON +							// will contain an extra field called _testConf. This will contain several +							// fields that report the result of doNetworkConfigRequest() for this member. +							std::string testFields; +							{ +								Identity testOutputSigningId; +								std::map<std::string,std::string>::const_iterator sid(urlArgs.find("testSigningId")); +								if (sid != urlArgs.end()) { +									testOutputSigningId.fromString(sid->second.c_str()); +								} else { +									sid = headers.find("x-zt1-testsigningid"); +									if (sid != headers.end()) +										testOutputSigningId.fromString(sid->second.c_str()); +								} + +								if ((testOutputSigningId.hasPrivate())&&(memberIdStr)) { +									Dictionary testNetconf; +									NetworkController::ResultCode rc = this->_doNetworkConfigRequest( +										InetAddress(), +										testOutputSigningId, +										Identity(memberIdStr), +										nwid, +										Dictionary(), // TODO: allow passing of meta-data for testing +										testNetconf); +									char rcs[16]; +									Utils::snprintf(rcs,sizeof(rcs),"%d,\n",(int)rc); +									testFields.append("\t\"_test\": {\n"); +									testFields.append("\t\t\"resultCode\": "); testFields.append(rcs); +									testFields.append("\t\t\"result\": \""); testFields.append(_jsonEscape(testNetconf.toString().c_str()).c_str()); testFields.append("\",\n"); +									testFields.append("\t\t\"resultJson\": {\n"); +									for(Dictionary::const_iterator i(testNetconf.begin());i!=testNetconf.end();++i) { +										if (i != testNetconf.begin()) +											testFields.append(",\n"); +										testFields.append("\t\t\t\""); +										testFields.append(i->first); +										testFields.append("\": \""); +										testFields.append(_jsonEscape(i->second.c_str())); +										testFields.push_back('"'); +									} +									testFields.append("\n\t\t}\n"); +									testFields.append("\t},\n"); +								} +							} +  							Utils::snprintf(json,sizeof(json), -								"{\n" +								"{\n%s"  								"\t\"nwid\": \"%s\",\n"  								"\t\"address\": \"%s\",\n"  								"\t\"controllerInstanceId\": \"%s\",\n" @@ -1366,6 +1033,7 @@ unsigned int SqliteNetworkController::_doCPGet(  								"\t\"clock\": %llu,\n"  								"\t\"identity\": \"%s\",\n"  								"\t\"ipAssignments\": [", +								testFields.c_str(),  								nwids,  								addrs,  								_instanceId.c_str(), @@ -1373,7 +1041,7 @@ unsigned int SqliteNetworkController::_doCPGet(  								(sqlite3_column_int(_sGetMember2,1) > 0) ? "true" : "false",  								(unsigned long long)sqlite3_column_int64(_sGetMember2,2),  								(unsigned long long)OSUtils::now(), -								_jsonEscape((const char *)sqlite3_column_text(_sGetMember2,3)).c_str()); +								_jsonEscape(memberIdStr).c_str());  							responseBody = json;  							sqlite3_reset(_sGetIpAssignmentsForNode2); @@ -1398,28 +1066,35 @@ unsigned int SqliteNetworkController::_doCPGet(  							responseBody.append("],\n\t\"recentLog\": ["); -							sqlite3_reset(_sGetRecentMemberLog); -							sqlite3_bind_text(_sGetRecentMemberLog,1,nwids,16,SQLITE_STATIC); -							sqlite3_bind_text(_sGetRecentMemberLog,2,addrs,10,SQLITE_STATIC); -							bool firstLog = true; -							while (sqlite3_step(_sGetRecentMemberLog) == SQLITE_ROW) { -								responseBody.append(firstLog ? "{" : ",{"); -								firstLog = false; -								responseBody.append("\"ts\":"); -								responseBody.append(reinterpret_cast<const char *>(sqlite3_column_text(_sGetRecentMemberLog,0))); -								responseBody.append((sqlite3_column_int(_sGetRecentMemberLog,1) == 0) ? ",\"authorized\":false,\"version\":" : ",\"authorized\":true,\"version\":"); -								const char *ver = reinterpret_cast<const char *>(sqlite3_column_text(_sGetRecentMemberLog,2)); -								if ((ver)&&(ver[0])) { -									responseBody.push_back('"'); -									responseBody.append(_jsonEscape(ver)); -									responseBody.append("\",\"fromAddr\":"); -								} else responseBody.append("null,\"fromAddr\":"); -								const char *fa = reinterpret_cast<const char *>(sqlite3_column_text(_sGetRecentMemberLog,3)); -								if ((fa)&&(fa[0])) { -									responseBody.push_back('"'); -									responseBody.append(_jsonEscape(fa)); -									responseBody.append("\"}"); -								} else responseBody.append("null}"); +							{ +								std::map< std::pair<Address,uint64_t>,_LLEntry >::const_iterator lli(_lastLog.find(std::pair<Address,uint64_t>(Address(address),nwid))); +								if (lli != _lastLog.end()) { +									const _LLEntry &lastLogEntry = lli->second; +									uint64_t eptr = lastLogEntry.totalRequests; +									for(int k=0;k<ZT_SQLITENETWORKCONTROLLER_IN_MEMORY_LOG_SIZE;++k) { +										if (!eptr--) +											break; +										const unsigned long ptr = (unsigned long)eptr % ZT_SQLITENETWORKCONTROLLER_IN_MEMORY_LOG_SIZE; + +										char tsbuf[64]; +										Utils::snprintf(tsbuf,sizeof(tsbuf),"%llu",(unsigned long long)lastLogEntry.l[ptr].ts); + +										responseBody.append((k == 0) ? "{" : ",{"); +										responseBody.append("\"ts\":"); +										responseBody.append(tsbuf); +										responseBody.append(lastLogEntry.l[ptr].authorized ? ",\"authorized\":false,\"version\":" : ",\"authorized\":true,\"version\":"); +										if (lastLogEntry.l[ptr].version[0]) { +											responseBody.push_back('"'); +											responseBody.append(_jsonEscape(lastLogEntry.l[ptr].version)); +											responseBody.append("\",\"fromAddr\":"); +										} else responseBody.append("null,\"fromAddr\":"); +										if (lastLogEntry.l[ptr].fromAddr) { +											responseBody.push_back('"'); +											responseBody.append(_jsonEscape(lastLogEntry.l[ptr].fromAddr.toString())); +											responseBody.append("\"}"); +										} else responseBody.append("null}"); +									} +								}  							}  							responseBody.append("]\n}\n"); @@ -1732,4 +1407,409 @@ unsigned int SqliteNetworkController::_doCPGet(  	return 404;  } +NetworkController::ResultCode SqliteNetworkController::_doNetworkConfigRequest(const InetAddress &fromAddr,const Identity &signingId,const Identity &identity,uint64_t nwid,const Dictionary &metaData,Dictionary &netconf) +{ +	// Assumes _lock is locked + +	// Decode some stuff from metaData +	const unsigned int clientMajorVersion = (unsigned int)metaData.getHexUInt(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_MAJOR_VERSION,0); +	const unsigned int clientMinorVersion = (unsigned int)metaData.getHexUInt(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_MINOR_VERSION,0); +	const unsigned int clientRevision = (unsigned int)metaData.getHexUInt(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_REVISION,0); +	const bool clientIs104 = (Utils::compareVersion(clientMajorVersion,clientMinorVersion,clientRevision,1,0,4) >= 0); + +	// Note: we can't reuse prepared statements that return const char * pointers without +	// making our own copy in e.g. a std::string first. + +	if ((!signingId)||(!signingId.hasPrivate())) { +		netconf["error"] = "signing identity invalid or lacks private key"; +		return NetworkController::NETCONF_QUERY_INTERNAL_SERVER_ERROR; +	} +	if (signingId.address().toInt() != (nwid >> 24)) { +		netconf["error"] = "signing identity address does not match most significant 40 bits of network ID"; +		return NetworkController::NETCONF_QUERY_INTERNAL_SERVER_ERROR; +	} + +	// Check rate limit circuit breaker to prevent flooding +	const uint64_t now = OSUtils::now(); +	_LLEntry &lastLogEntry = _lastLog[std::pair<Address,uint64_t>(identity.address(),nwid)]; +	if ((now - lastLogEntry.lastRequestTime) <= ZT_NETCONF_MIN_REQUEST_PERIOD) +		return NetworkController::NETCONF_QUERY_IGNORE; +	lastLogEntry.lastRequestTime = now; + +	NetworkRecord network; +	memset(&network,0,sizeof(network)); +	Utils::snprintf(network.id,sizeof(network.id),"%.16llx",(unsigned long long)nwid); + +	MemberRecord member; +	memset(&member,0,sizeof(member)); +	Utils::snprintf(member.nodeId,sizeof(member.nodeId),"%.10llx",(unsigned long long)identity.address().toInt()); + +	// Create Node record or do full identity check if we already have one + +	sqlite3_reset(_sGetNodeIdentity); +	sqlite3_bind_text(_sGetNodeIdentity,1,member.nodeId,10,SQLITE_STATIC); +	if (sqlite3_step(_sGetNodeIdentity) == SQLITE_ROW) { +		try { +			Identity alreadyKnownIdentity((const char *)sqlite3_column_text(_sGetNodeIdentity,0)); +			if (alreadyKnownIdentity != identity) +				return NetworkController::NETCONF_QUERY_ACCESS_DENIED; +		} catch ( ... ) { // identity stored in database is not valid or is NULL +			return NetworkController::NETCONF_QUERY_ACCESS_DENIED; +		} +	} else { +		std::string idstr(identity.toString(false)); +		sqlite3_reset(_sCreateOrReplaceNode); +		sqlite3_bind_text(_sCreateOrReplaceNode,1,member.nodeId,10,SQLITE_STATIC); +		sqlite3_bind_text(_sCreateOrReplaceNode,2,idstr.c_str(),-1,SQLITE_STATIC); +		if (sqlite3_step(_sCreateOrReplaceNode) != SQLITE_DONE) { +			netconf["error"] = "unable to create new Node record"; +			return NetworkController::NETCONF_QUERY_INTERNAL_SERVER_ERROR; +		} +	} + +	// Fetch Network record + +	sqlite3_reset(_sGetNetworkById); +	sqlite3_bind_text(_sGetNetworkById,1,network.id,16,SQLITE_STATIC); +	if (sqlite3_step(_sGetNetworkById) == SQLITE_ROW) { +		network.name = (const char *)sqlite3_column_text(_sGetNetworkById,0); +		network.isPrivate = (sqlite3_column_int(_sGetNetworkById,1) > 0); +		network.enableBroadcast = (sqlite3_column_int(_sGetNetworkById,2) > 0); +		network.allowPassiveBridging = (sqlite3_column_int(_sGetNetworkById,3) > 0); +		network.v4AssignMode = (const char *)sqlite3_column_text(_sGetNetworkById,4); +		network.v6AssignMode = (const char *)sqlite3_column_text(_sGetNetworkById,5); +		network.multicastLimit = sqlite3_column_int(_sGetNetworkById,6); +		network.creationTime = (uint64_t)sqlite3_column_int64(_sGetNetworkById,7); +		network.revision = (uint64_t)sqlite3_column_int64(_sGetNetworkById,8); +		network.memberRevisionCounter = (uint64_t)sqlite3_column_int64(_sGetNetworkById,9); +	} else { +		return NetworkController::NETCONF_QUERY_OBJECT_NOT_FOUND; +	} + +	// Fetch Member record + +	bool foundMember = false; +	sqlite3_reset(_sGetMember); +	sqlite3_bind_text(_sGetMember,1,network.id,16,SQLITE_STATIC); +	sqlite3_bind_text(_sGetMember,2,member.nodeId,10,SQLITE_STATIC); +	if (sqlite3_step(_sGetMember) == SQLITE_ROW) { +		foundMember = true; +		member.rowid = (int64_t)sqlite3_column_int64(_sGetMember,0); +		member.authorized = (sqlite3_column_int(_sGetMember,1) > 0); +		member.activeBridge = (sqlite3_column_int(_sGetMember,2) > 0); +	} + +	// Create Member record for unknown nodes, auto-authorizing if network is public + +	if (!foundMember) { +		member.authorized = (network.isPrivate ? false : true); +		member.activeBridge = false; +		sqlite3_reset(_sCreateMember); +		sqlite3_bind_text(_sCreateMember,1,network.id,16,SQLITE_STATIC); +		sqlite3_bind_text(_sCreateMember,2,member.nodeId,10,SQLITE_STATIC); +		sqlite3_bind_int(_sCreateMember,3,(member.authorized ? 1 : 0)); +		sqlite3_bind_text(_sCreateMember,4,network.id,16,SQLITE_STATIC); +		if (sqlite3_step(_sCreateMember) != SQLITE_DONE) { +			netconf["error"] = "unable to create new member record"; +			return NetworkController::NETCONF_QUERY_INTERNAL_SERVER_ERROR; +		} +		member.rowid = (int64_t)sqlite3_last_insert_rowid(_db); + +		sqlite3_reset(_sIncrementMemberRevisionCounter); +		sqlite3_bind_text(_sIncrementMemberRevisionCounter,1,network.id,16,SQLITE_STATIC); +		sqlite3_step(_sIncrementMemberRevisionCounter); +	} + +	// Add log entry to in-memory circular log + +	{ +		const unsigned long ptr = (unsigned long)lastLogEntry.totalRequests % ZT_SQLITENETWORKCONTROLLER_IN_MEMORY_LOG_SIZE; +		lastLogEntry.l[ptr].ts = now; +		lastLogEntry.l[ptr].fromAddr = fromAddr; +		if ((clientMajorVersion > 0)||(clientMinorVersion > 0)||(clientRevision > 0)) +			Utils::snprintf(lastLogEntry.l[ptr].version,sizeof(lastLogEntry.l[ptr].version),"%u.%u.%u",clientMajorVersion,clientMinorVersion,clientRevision); +		else lastLogEntry.l[ptr].version[0] = (char)0; +		lastLogEntry.l[ptr].authorized = member.authorized; +		++lastLogEntry.totalRequests; +		// TODO: push or save these somewhere +	} + +	// Check member authorization + +	if (!member.authorized) +		return NetworkController::NETCONF_QUERY_ACCESS_DENIED; + +	// Create and sign netconf + +	netconf.clear(); +	{ +		char tss[24],rs[24]; +		Utils::snprintf(tss,sizeof(tss),"%.16llx",(unsigned long long)now); +		Utils::snprintf(rs,sizeof(rs),"%.16llx",(unsigned long long)network.revision); +		netconf[ZT_NETWORKCONFIG_DICT_KEY_TIMESTAMP] = tss; +		netconf[ZT_NETWORKCONFIG_DICT_KEY_REVISION] = rs; +		netconf[ZT_NETWORKCONFIG_DICT_KEY_NETWORK_ID] = network.id; +		netconf[ZT_NETWORKCONFIG_DICT_KEY_ISSUED_TO] = member.nodeId; +		netconf[ZT_NETWORKCONFIG_DICT_KEY_PRIVATE] = network.isPrivate ? "1" : "0"; +		netconf[ZT_NETWORKCONFIG_DICT_KEY_NAME] = (network.name) ? network.name : ""; +		netconf[ZT_NETWORKCONFIG_DICT_KEY_ENABLE_BROADCAST] = network.enableBroadcast ? "1" : "0"; +		netconf[ZT_NETWORKCONFIG_DICT_KEY_ALLOW_PASSIVE_BRIDGING] = network.allowPassiveBridging ? "1" : "0"; + +		{	// TODO: right now only etherTypes are supported in rules +			std::vector<int> allowedEtherTypes; +			sqlite3_reset(_sGetEtherTypesFromRuleTable); +			sqlite3_bind_text(_sGetEtherTypesFromRuleTable,1,network.id,16,SQLITE_STATIC); +			while (sqlite3_step(_sGetEtherTypesFromRuleTable) == SQLITE_ROW) { +				if (sqlite3_column_type(_sGetEtherTypesFromRuleTable,0) == SQLITE_NULL) { +					allowedEtherTypes.clear(); +					allowedEtherTypes.push_back(0); // NULL 'allow' matches ANY +					break; +				} else { +					int et = sqlite3_column_int(_sGetEtherTypesFromRuleTable,0); +					if ((et >= 0)&&(et <= 0xffff)) +						allowedEtherTypes.push_back(et); +				} +			} +			std::sort(allowedEtherTypes.begin(),allowedEtherTypes.end()); +			allowedEtherTypes.erase(std::unique(allowedEtherTypes.begin(),allowedEtherTypes.end()),allowedEtherTypes.end()); +			std::string allowedEtherTypesCsv; +			for(std::vector<int>::const_iterator i(allowedEtherTypes.begin());i!=allowedEtherTypes.end();++i) { +				if (allowedEtherTypesCsv.length()) +					allowedEtherTypesCsv.push_back(','); +				char tmp[16]; +				Utils::snprintf(tmp,sizeof(tmp),"%.4x",(unsigned int)*i); +				allowedEtherTypesCsv.append(tmp); +			} +			netconf[ZT_NETWORKCONFIG_DICT_KEY_ALLOWED_ETHERNET_TYPES] = allowedEtherTypesCsv; +		} + +		if (network.multicastLimit > 0) { +			char ml[16]; +			Utils::snprintf(ml,sizeof(ml),"%lx",(unsigned long)network.multicastLimit); +			netconf[ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_LIMIT] = ml; +		} + +		{ +			std::string activeBridges; +			sqlite3_reset(_sGetActiveBridges); +			sqlite3_bind_text(_sGetActiveBridges,1,network.id,16,SQLITE_STATIC); +			while (sqlite3_step(_sGetActiveBridges) == SQLITE_ROW) { +				const char *ab = (const char *)sqlite3_column_text(_sGetActiveBridges,0); +				if ((ab)&&(strlen(ab) == 10)) { +					if (activeBridges.length()) +						activeBridges.push_back(','); +					activeBridges.append(ab); +				} +				if (activeBridges.length() > 1024) // sanity check -- you can't have too many active bridges at the moment +					break; +			} +			if (activeBridges.length()) +				netconf[ZT_NETWORKCONFIG_DICT_KEY_ACTIVE_BRIDGES] = activeBridges; +		} + +		{ +			std::string relays; +			sqlite3_reset(_sGetRelays); +			sqlite3_bind_text(_sGetRelays,1,network.id,16,SQLITE_STATIC); +			while (sqlite3_step(_sGetRelays) == SQLITE_ROW) { +				const char *n = (const char *)sqlite3_column_text(_sGetRelays,0); +				const char *a = (const char *)sqlite3_column_text(_sGetRelays,1); +				if ((n)&&(a)) { +					Address node(n); +					InetAddress addr(a); +					if ((node)&&(addr)) { +						if (relays.length()) +							relays.push_back(','); +						relays.append(node.toString()); +						relays.push_back(';'); +						relays.append(addr.toString()); +					} +				} +			} +			if (relays.length()) +				netconf[ZT_NETWORKCONFIG_DICT_KEY_RELAYS] = relays; +		} + +		{ +			char tmp[128]; +			std::string gateways; +			sqlite3_reset(_sGetGateways); +			sqlite3_bind_text(_sGetGateways,1,network.id,16,SQLITE_STATIC); +			while (sqlite3_step(_sGetGateways) == SQLITE_ROW) { +				const unsigned char *ip = (const unsigned char *)sqlite3_column_blob(_sGetGateways,0); +				switch(sqlite3_column_int(_sGetGateways,1)) { // ipVersion +					case 4: +						Utils::snprintf(tmp,sizeof(tmp),"%s%d.%d.%d.%d/%d", +							(gateways.length() > 0) ? "," : "", +							(int)ip[12], +							(int)ip[13], +							(int)ip[14], +							(int)ip[15], +							(int)sqlite3_column_int(_sGetGateways,2)); // metric +						gateways.append(tmp); +						break; +					case 6: +						Utils::snprintf(tmp,sizeof(tmp),"%s%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x/%d", +							(gateways.length() > 0) ? "," : "", +							(int)ip[0], +							(int)ip[1], +							(int)ip[2], +							(int)ip[3], +							(int)ip[4], +							(int)ip[5], +							(int)ip[6], +							(int)ip[7], +							(int)ip[8], +							(int)ip[9], +							(int)ip[10], +							(int)ip[11], +							(int)ip[12], +							(int)ip[13], +							(int)ip[14], +							(int)ip[15], +							(int)sqlite3_column_int(_sGetGateways,2)); // metric +						gateways.append(tmp); +						break; +				} +			} +			if (gateways.length()) +				netconf[ZT_NETWORKCONFIG_DICT_KEY_GATEWAYS] = gateways; +		} + +		if ((network.v4AssignMode)&&(!strcmp(network.v4AssignMode,"zt"))) { +			std::string v4s; + +			// Get existing IPv4 IP assignments and network routes -- keep routes in a +			// vector for use in auto-assign if we need them. +			std::vector< std::pair<uint32_t,int> > routedNetworks; +			bool haveStaticIpAssignment = false; +			sqlite3_reset(_sGetIpAssignmentsForNode); +			sqlite3_bind_text(_sGetIpAssignmentsForNode,1,network.id,16,SQLITE_STATIC); +			sqlite3_bind_text(_sGetIpAssignmentsForNode,2,member.nodeId,10,SQLITE_STATIC); +			sqlite3_bind_int(_sGetIpAssignmentsForNode,3,4); // 4 == IPv4 +			while (sqlite3_step(_sGetIpAssignmentsForNode) == SQLITE_ROW) { +				const unsigned char *ip = (const unsigned char *)sqlite3_column_blob(_sGetIpAssignmentsForNode,1); +				if ((!ip)||(sqlite3_column_bytes(_sGetIpAssignmentsForNode,1) != 16)) +					continue; +				int ipNetmaskBits = sqlite3_column_int(_sGetIpAssignmentsForNode,2); +				if ((ipNetmaskBits <= 0)||(ipNetmaskBits > 32)) +					continue; + +				const IpAssignmentType ipt = (IpAssignmentType)sqlite3_column_int(_sGetIpAssignmentsForNode,0); +				switch(ipt) { +					case ZT_IP_ASSIGNMENT_TYPE_ADDRESS: +						haveStaticIpAssignment = true; +						break; +					case ZT_IP_ASSIGNMENT_TYPE_NETWORK: +						routedNetworks.push_back(std::pair<uint32_t,int>(Utils::ntoh(*(reinterpret_cast<const uint32_t *>(ip + 12))),ipNetmaskBits)); +						break; +					default: +						continue; +				} + +				// 1.0.4 or newer clients support network routes in addition to IPs. +				// Older clients only support IP address / netmask entries. +				if ((clientIs104)||(ipt == ZT_IP_ASSIGNMENT_TYPE_ADDRESS)) { +					char tmp[32]; +					Utils::snprintf(tmp,sizeof(tmp),"%d.%d.%d.%d/%d",(int)ip[12],(int)ip[13],(int)ip[14],(int)ip[15],ipNetmaskBits); +					if (v4s.length()) +						v4s.push_back(','); +					v4s.append(tmp); +				} +			} + +			if (!haveStaticIpAssignment) { +				// Attempt to auto-assign an IPv4 address from an available routed pool +				sqlite3_reset(_sGetIpAssignmentPools); +				sqlite3_bind_text(_sGetIpAssignmentPools,1,network.id,16,SQLITE_STATIC); +				sqlite3_bind_int(_sGetIpAssignmentPools,2,4); // 4 == IPv4 + +				while (sqlite3_step(_sGetIpAssignmentPools) == SQLITE_ROW) { +					const unsigned char *ipRangeStartB = reinterpret_cast<const unsigned char *>(sqlite3_column_blob(_sGetIpAssignmentPools,0)); +					const unsigned char *ipRangeEndB = reinterpret_cast<const unsigned char *>(sqlite3_column_blob(_sGetIpAssignmentPools,1)); +					if ((!ipRangeStartB)||(!ipRangeEndB)||(sqlite3_column_bytes(_sGetIpAssignmentPools,0) != 16)||(sqlite3_column_bytes(_sGetIpAssignmentPools,1) != 16)) +						continue; + +					uint32_t ipRangeStart = Utils::ntoh(*(reinterpret_cast<const uint32_t *>(ipRangeStartB + 12))); +					uint32_t ipRangeEnd = Utils::ntoh(*(reinterpret_cast<const uint32_t *>(ipRangeEndB + 12))); +					if (ipRangeEnd < ipRangeStart) +						continue; +					uint32_t ipRangeLen = ipRangeEnd - ipRangeStart; + +					// Start with the LSB of the member's address +					uint32_t ipTrialCounter = (uint32_t)(identity.address().toInt() & 0xffffffff); + +					for(uint32_t k=ipRangeStart,l=0;(k<=ipRangeEnd)&&(l < 1000000);++k,++l) { +						uint32_t ip = (ipRangeLen > 0) ? (ipRangeStart + (ipTrialCounter % ipRangeLen)) : ipRangeStart; +						++ipTrialCounter; + +						for(std::vector< std::pair<uint32_t,int> >::const_iterator r(routedNetworks.begin());r!=routedNetworks.end();++r) { +							if ((ip & (0xffffffff << (32 - r->second))) == r->first) { +								// IP is included in a routed network, so check if it's allocated + +								uint32_t ipBlob[4]; +								ipBlob[0] = 0; ipBlob[1] = 0; ipBlob[2] = 0; ipBlob[3] = Utils::hton(ip); + +								sqlite3_reset(_sCheckIfIpIsAllocated); +								sqlite3_bind_text(_sCheckIfIpIsAllocated,1,network.id,16,SQLITE_STATIC); +								sqlite3_bind_blob(_sCheckIfIpIsAllocated,2,(const void *)ipBlob,16,SQLITE_STATIC); +								sqlite3_bind_int(_sCheckIfIpIsAllocated,3,4); // 4 == IPv4 +								sqlite3_bind_int(_sCheckIfIpIsAllocated,4,(int)ZT_IP_ASSIGNMENT_TYPE_ADDRESS); +								if (sqlite3_step(_sCheckIfIpIsAllocated) != SQLITE_ROW) { +									// No rows returned, so the IP is available +									sqlite3_reset(_sAllocateIp); +									sqlite3_bind_text(_sAllocateIp,1,network.id,16,SQLITE_STATIC); +									sqlite3_bind_text(_sAllocateIp,2,member.nodeId,10,SQLITE_STATIC); +									sqlite3_bind_int(_sAllocateIp,3,(int)ZT_IP_ASSIGNMENT_TYPE_ADDRESS); +									sqlite3_bind_blob(_sAllocateIp,4,(const void *)ipBlob,16,SQLITE_STATIC); +									sqlite3_bind_int(_sAllocateIp,5,r->second); // IP netmask bits from matching route +									sqlite3_bind_int(_sAllocateIp,6,4); // 4 == IPv4 +									if (sqlite3_step(_sAllocateIp) == SQLITE_DONE) { +										char tmp[32]; +										Utils::snprintf(tmp,sizeof(tmp),"%d.%d.%d.%d/%d",(int)((ip >> 24) & 0xff),(int)((ip >> 16) & 0xff),(int)((ip >> 8) & 0xff),(int)(ip & 0xff),r->second); +										if (v4s.length()) +											v4s.push_back(','); +										v4s.append(tmp); +										haveStaticIpAssignment = true; // break outer loop +									} +								} + +								break; // stop checking routed networks +							} +						} + +						if (haveStaticIpAssignment) +							break; +					} +				} +			} + +			if (v4s.length()) +				netconf[ZT_NETWORKCONFIG_DICT_KEY_IPV4_STATIC] = v4s; +		} + +		if ((network.v6AssignMode)&&(!strcmp(network.v6AssignMode,"rfc4193"))) { +			InetAddress rfc4193Addr(InetAddress::makeIpv6rfc4193(nwid,identity.address().toInt())); +			netconf[ZT_NETWORKCONFIG_DICT_KEY_IPV6_STATIC] = rfc4193Addr.toString(); +		} + +		if (network.isPrivate) { +			CertificateOfMembership com(now,ZT_NETWORK_AUTOCONF_DELAY + (ZT_NETWORK_AUTOCONF_DELAY / 2),nwid,identity.address()); +			if (com.sign(signingId)) // basically can't fail unless our identity is invalid +				netconf[ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATE_OF_MEMBERSHIP] = com.toString(); +			else { +				netconf["error"] = "unable to sign COM"; +				return NETCONF_QUERY_INTERNAL_SERVER_ERROR; +			} +		} + +		if (!netconf.sign(signingId,now)) { +			netconf["error"] = "unable to sign netconf dictionary"; +			return NETCONF_QUERY_INTERNAL_SERVER_ERROR; +		} +	} + +	return NetworkController::NETCONF_QUERY_OK; +} +  } // namespace ZeroTier diff --git a/controller/SqliteNetworkController.hpp b/controller/SqliteNetworkController.hpp index adfe0991..f0b61c40 100644 --- a/controller/SqliteNetworkController.hpp +++ b/controller/SqliteNetworkController.hpp @@ -40,6 +40,9 @@  #include "../node/NetworkController.hpp"  #include "../node/Mutex.hpp" +// Number of in-memory last log entries to maintain per user +#define ZT_SQLITENETWORKCONTROLLER_IN_MEMORY_LOG_SIZE 32 +  namespace ZeroTier {  class SqliteNetworkController : public NetworkController @@ -54,7 +57,6 @@ public:  		const Identity &identity,  		uint64_t nwid,  		const Dictionary &metaData, -		uint64_t haveRevision,  		Dictionary &netconf);  	unsigned int handleControlPlaneHttpGET( @@ -94,11 +96,45 @@ private:  		const std::string &body,  		std::string &responseBody,  		std::string &responseContentType); +	NetworkController::ResultCode _doNetworkConfigRequest( +		const InetAddress &fromAddr, +		const Identity &signingId, +		const Identity &identity, +		uint64_t nwid, +		const Dictionary &metaData, +		Dictionary &netconf);  	std::string _dbPath;  	std::string _instanceId; -	std::map< std::pair<Address,uint64_t>,uint64_t > _lastRequestTime; +	// A circular buffer last log +	struct _LLEntry +	{ +		_LLEntry() +		{ +			for(long i=0;i<ZT_SQLITENETWORKCONTROLLER_IN_MEMORY_LOG_SIZE;++i) +				this->l[i].ts = 0; +			this->lastRequestTime = 0; +			this->totalRequests = 0; +		} + +		// Circular buffer of last log entries +		struct { +			uint64_t ts; // timestamp or 0 if circular buffer entry unused +			char version[64]; +			InetAddress fromAddr; +			bool authorized; +		} l[ZT_SQLITENETWORKCONTROLLER_IN_MEMORY_LOG_SIZE]; + +		// Time of last request whether successful or not +		uint64_t lastRequestTime; + +		// Total requests by this address / network ID pair (also serves mod IN_MEMORY_LOG_SIZE as circular buffer ptr) +		uint64_t totalRequests; +	}; + +	// Last log entries by address and network ID pair +	std::map< std::pair<Address,uint64_t>,_LLEntry > _lastLog;  	sqlite3 *_db; @@ -106,7 +142,7 @@ private:  	sqlite3_stmt *_sGetMember;  	sqlite3_stmt *_sCreateMember;  	sqlite3_stmt *_sGetNodeIdentity; -	sqlite3_stmt *_sCreateNode; +	sqlite3_stmt *_sCreateOrReplaceNode;  	sqlite3_stmt *_sUpdateNode;  	sqlite3_stmt *_sUpdateNode2;  	sqlite3_stmt *_sGetEtherTypesFromRuleTable; @@ -137,6 +173,7 @@ private:  	sqlite3_stmt *_sUpdateMemberAuthorized;  	sqlite3_stmt *_sUpdateMemberActiveBridge;  	sqlite3_stmt *_sDeleteMember; +	sqlite3_stmt *_sDeleteAllNetworkMembers;  	sqlite3_stmt *_sDeleteNetwork;  	sqlite3_stmt *_sGetGateways;  	sqlite3_stmt *_sDeleteGateways; @@ -144,9 +181,6 @@ private:  	sqlite3_stmt *_sIncrementMemberRevisionCounter;  	sqlite3_stmt *_sGetConfig;  	sqlite3_stmt *_sSetConfig; -	sqlite3_stmt *_sPutLog; -	sqlite3_stmt *_sGetMemberLog; -	sqlite3_stmt *_sGetRecentMemberLog;  	Mutex _lock;  }; diff --git a/controller/schema.sql b/controller/schema.sql index 48d5607c..b6db7fa4 100644 --- a/controller/schema.sql +++ b/controller/schema.sql @@ -77,19 +77,6 @@ CREATE TABLE Member (  CREATE INDEX Member_networkId_activeBridge ON Member(networkId, activeBridge);  CREATE INDEX Member_networkId_memberRevision ON Member(networkId, memberRevision); -CREATE TABLE Log ( -  networkId char(16) NOT NULL, -  nodeId char(10) NOT NULL, -  ts integer NOT NULL, -  authorized integer NOT NULL, -  authTokenId integer, -  version varchar(16), -  fromAddr varchar(64) -); - -CREATE INDEX Log_networkId_nodeId ON Log(networkId, nodeId); -CREATE INDEX Log_ts ON Log(ts); -  CREATE TABLE Relay (    networkId char(16) NOT NULL REFERENCES Network(id) ON DELETE CASCADE,    address char(10) NOT NULL, diff --git a/controller/schema.sql.c b/controller/schema.sql.c index 49c44548..a5b9130b 100644 --- a/controller/schema.sql.c +++ b/controller/schema.sql.c @@ -78,19 +78,6 @@  "CREATE INDEX Member_networkId_activeBridge ON Member(networkId, activeBridge);\n"\  "CREATE INDEX Member_networkId_memberRevision ON Member(networkId, memberRevision);\n"\  "\n"\ -"CREATE TABLE Log (\n"\ -"  networkId char(16) NOT NULL,\n"\ -"  nodeId char(10) NOT NULL,\n"\ -"  ts integer NOT NULL,\n"\ -"  authorized integer NOT NULL,\n"\ -"  authTokenId integer,\n"\ -"  version varchar(16),\n"\ -"  fromAddr varchar(64)\n"\ -");\n"\ -"\n"\ -"CREATE INDEX Log_networkId_nodeId ON Log(networkId, nodeId);\n"\ -"CREATE INDEX Log_ts ON Log(ts);\n"\ -"\n"\  "CREATE TABLE Relay (\n"\  "  networkId char(16) NOT NULL REFERENCES Network(id) ON DELETE CASCADE,\n"\  "  address char(10) NOT NULL,\n"\ | 
