Browse Source

Sanity checks on array sizes and fix a bug in IPv4 auto-assign.

Adam Ierymenko 7 years ago
parent
commit
4e689998f9
4 changed files with 82 additions and 48 deletions
  1. 2 0
      controller/DB.cpp
  2. 73 42
      controller/EmbeddedNetworkController.cpp
  3. 6 5
      controller/RethinkDB.cpp
  4. 1 1
      controller/RethinkDB.hpp

+ 2 - 0
controller/DB.cpp

@@ -373,8 +373,10 @@ void DB::_fillSummaryInfo(const std::shared_ptr<_Network> &nw,NetworkSummaryInfo
 {
 {
 	for(auto ab=nw->activeBridgeMembers.begin();ab!=nw->activeBridgeMembers.end();++ab)
 	for(auto ab=nw->activeBridgeMembers.begin();ab!=nw->activeBridgeMembers.end();++ab)
 		info.activeBridges.push_back(Address(*ab));
 		info.activeBridges.push_back(Address(*ab));
+	std::sort(info.activeBridges.begin(),info.activeBridges.end());
 	for(auto ip=nw->allocatedIps.begin();ip!=nw->allocatedIps.end();++ip)
 	for(auto ip=nw->allocatedIps.begin();ip!=nw->allocatedIps.end();++ip)
 		info.allocatedIps.push_back(*ip);
 		info.allocatedIps.push_back(*ip);
+	std::sort(info.allocatedIps.begin(),info.allocatedIps.end());
 	info.authorizedMemberCount = (unsigned long)nw->authorizedMembers.size();
 	info.authorizedMemberCount = (unsigned long)nw->authorizedMembers.size();
 	info.totalMemberCount = (unsigned long)nw->members.size();
 	info.totalMemberCount = (unsigned long)nw->members.size();
 	info.mostRecentDeauthTime = nw->mostRecentDeauthTime;
 	info.mostRecentDeauthTime = nw->mostRecentDeauthTime;

+ 73 - 42
controller/EmbeddedNetworkController.cpp

@@ -53,6 +53,9 @@ using json = nlohmann::json;
 // Min duration between requests for an address/nwid combo to prevent floods
 // Min duration between requests for an address/nwid combo to prevent floods
 #define ZT_NETCONF_MIN_REQUEST_PERIOD 1000
 #define ZT_NETCONF_MIN_REQUEST_PERIOD 1000
 
 
+// Global maximum size of arrays in JSON objects
+#define ZT_CONTROLLER_MAX_ARRAY_SIZE 16384
+
 namespace ZeroTier {
 namespace ZeroTier {
 
 
 namespace {
 namespace {
@@ -686,6 +689,8 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST(
 									if ((ip.ss_family == AF_INET)||(ip.ss_family == AF_INET6)) {
 									if ((ip.ss_family == AF_INET)||(ip.ss_family == AF_INET6)) {
 										char tmpip[64];
 										char tmpip[64];
 										mipa.push_back(ip.toIpString(tmpip));
 										mipa.push_back(ip.toIpString(tmpip));
+										if (mipa.size() >= ZT_CONTROLLER_MAX_ARRAY_SIZE)
+											break;
 									}
 									}
 								}
 								}
 								member["ipAssignments"] = mipa;
 								member["ipAssignments"] = mipa;
@@ -707,6 +712,8 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST(
 									ta.push_back(t->first);
 									ta.push_back(t->first);
 									ta.push_back(t->second);
 									ta.push_back(t->second);
 									mtagsa.push_back(ta);
 									mtagsa.push_back(ta);
+									if (mtagsa.size() >= ZT_CONTROLLER_MAX_ARRAY_SIZE)
+										break;
 								}
 								}
 								member["tags"] = mtagsa;
 								member["tags"] = mtagsa;
 							}
 							}
@@ -718,6 +725,8 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST(
 								json mcaps = json::array();
 								json mcaps = json::array();
 								for(unsigned long i=0;i<capabilities.size();++i) {
 								for(unsigned long i=0;i<capabilities.size();++i) {
 									mcaps.push_back(OSUtils::jsonInt(capabilities[i],0ULL));
 									mcaps.push_back(OSUtils::jsonInt(capabilities[i],0ULL));
+									if (mcaps.size() >= ZT_CONTROLLER_MAX_ARRAY_SIZE)
+										break;
 								}
 								}
 								std::sort(mcaps.begin(),mcaps.end());
 								std::sort(mcaps.begin(),mcaps.end());
 								mcaps.erase(std::unique(mcaps.begin(),mcaps.end()),mcaps.end());
 								mcaps.erase(std::unique(mcaps.begin(),mcaps.end()),mcaps.end());
@@ -850,6 +859,8 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST(
 												tmp["via"] = v.toIpString(tmp2);
 												tmp["via"] = v.toIpString(tmp2);
 											else tmp["via"] = json();
 											else tmp["via"] = json();
 											nrts.push_back(tmp);
 											nrts.push_back(tmp);
+											if (nrts.size() >= ZT_CONTROLLER_MAX_ARRAY_SIZE)
+												break;
 										}
 										}
 									}
 									}
 								}
 								}
@@ -873,6 +884,8 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST(
 										tmp["ipRangeStart"] = f.toIpString(tmp2);
 										tmp["ipRangeStart"] = f.toIpString(tmp2);
 										tmp["ipRangeEnd"] = t.toIpString(tmp2);
 										tmp["ipRangeEnd"] = t.toIpString(tmp2);
 										nipp.push_back(tmp);
 										nipp.push_back(tmp);
+										if (nipp.size() >= ZT_CONTROLLER_MAX_ARRAY_SIZE)
+											break;
 									}
 									}
 								}
 								}
 							}
 							}
@@ -888,8 +901,11 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST(
 								json &rule = rules[i];
 								json &rule = rules[i];
 								if (rule.is_object()) {
 								if (rule.is_object()) {
 									ZT_VirtualNetworkRule ztr;
 									ZT_VirtualNetworkRule ztr;
-									if (_parseRule(rule,ztr))
+									if (_parseRule(rule,ztr)) {
 										nrules.push_back(_renderRule(ztr));
 										nrules.push_back(_renderRule(ztr));
+										if (nrules.size() >= ZT_CONTROLLER_MAX_ARRAY_SIZE)
+											break;
+									}
 								}
 								}
 							}
 							}
 							network["rules"] = nrules;
 							network["rules"] = nrules;
@@ -929,8 +945,11 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST(
 											json &rule = rules[i];
 											json &rule = rules[i];
 											if (rule.is_object()) {
 											if (rule.is_object()) {
 												ZT_VirtualNetworkRule ztr;
 												ZT_VirtualNetworkRule ztr;
-												if (_parseRule(rule,ztr))
+												if (_parseRule(rule,ztr)) {
 													nrules.push_back(_renderRule(ztr));
 													nrules.push_back(_renderRule(ztr));
+													if (nrules.size() >= ZT_CONTROLLER_MAX_ARRAY_SIZE)
+														break;
+												}
 											}
 											}
 										}
 										}
 									}
 									}
@@ -941,8 +960,11 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST(
 							}
 							}
 
 
 							json ncapsa = json::array();
 							json ncapsa = json::array();
-							for(std::map< uint64_t,json >::iterator c(ncaps.begin());c!=ncaps.end();++c)
+							for(std::map< uint64_t,json >::iterator c(ncaps.begin());c!=ncaps.end();++c) {
 								ncapsa.push_back(c->second);
 								ncapsa.push_back(c->second);
+								if (ncapsa.size() >= ZT_CONTROLLER_MAX_ARRAY_SIZE)
+									break;
+							}
 							network["capabilities"] = ncapsa;
 							network["capabilities"] = ncapsa;
 						}
 						}
 					}
 					}
@@ -966,8 +988,11 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST(
 							}
 							}
 
 
 							json ntagsa = json::array();
 							json ntagsa = json::array();
-							for(std::map< uint64_t,json >::iterator t(ntags.begin());t!=ntags.end();++t)
+							for(std::map< uint64_t,json >::iterator t(ntags.begin());t!=ntags.end();++t) {
 								ntagsa.push_back(t->second);
 								ntagsa.push_back(t->second);
+								if (ntagsa.size() >= ZT_CONTROLLER_MAX_ARRAY_SIZE)
+									break;
+							}
 							network["tags"] = ntagsa;
 							network["tags"] = ntagsa;
 						}
 						}
 					}
 					}
@@ -1304,7 +1329,7 @@ void EmbeddedNetworkController::_request(
 		}
 		}
 	}
 	}
 
 
-	std::auto_ptr<NetworkConfig> nc(new NetworkConfig());
+	std::unique_ptr<NetworkConfig> nc(new NetworkConfig());
 
 
 	nc->networkId = nwid;
 	nc->networkId = nwid;
 	nc->type = OSUtils::jsonBool(network["private"],true) ? ZT_NETWORK_TYPE_PRIVATE : ZT_NETWORK_TYPE_PUBLIC;
 	nc->type = OSUtils::jsonBool(network["private"],true) ? ZT_NETWORK_TYPE_PRIVATE : ZT_NETWORK_TYPE_PUBLIC;
@@ -1483,29 +1508,29 @@ void EmbeddedNetworkController::_request(
 	json ipAssignments = member["ipAssignments"]; // we want to make a copy
 	json ipAssignments = member["ipAssignments"]; // we want to make a copy
 	if (ipAssignments.is_array()) {
 	if (ipAssignments.is_array()) {
 		for(unsigned long i=0;i<ipAssignments.size();++i) {
 		for(unsigned long i=0;i<ipAssignments.size();++i) {
-			if (!ipAssignments[i].is_string())
-				continue;
-			std::string ips = ipAssignments[i];
-			InetAddress ip(ips.c_str());
-
-			// IP assignments are only pushed if there is a corresponding local route. We also now get the netmask bits from
-			// this route, ignoring the netmask bits field of the assigned IP itself. Using that was worthless and a source
-			// of user error / poor UX.
-			int routedNetmaskBits = 0;
-			for(unsigned int rk=0;rk<nc->routeCount;++rk) {
-				if ( (!nc->routes[rk].via.ss_family) && (reinterpret_cast<const InetAddress *>(&(nc->routes[rk].target))->containsAddress(ip)) )
-					routedNetmaskBits = reinterpret_cast<const InetAddress *>(&(nc->routes[rk].target))->netmaskBits();
-			}
+			if (ipAssignments[i].is_string()) {
+				const std::string ips = ipAssignments[i];
+				InetAddress ip(ips.c_str());
+
+				// IP assignments are only pushed if there is a corresponding local route. We also now get the netmask bits from
+				// this route, ignoring the netmask bits field of the assigned IP itself. Using that was worthless and a source
+				// of user error / poor UX.
+				int routedNetmaskBits = -1;
+				for(unsigned int rk=0;rk<nc->routeCount;++rk) {
+					if ( (!nc->routes[rk].via.ss_family) && (reinterpret_cast<const InetAddress *>(&(nc->routes[rk].target))->containsAddress(ip)) )
+						routedNetmaskBits = reinterpret_cast<const InetAddress *>(&(nc->routes[rk].target))->netmaskBits();
+				}
 
 
-			if (routedNetmaskBits > 0) {
-				if (nc->staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES) {
-					ip.setPort(routedNetmaskBits);
-					nc->staticIps[nc->staticIpCount++] = ip;
+				if (routedNetmaskBits >= 0) {
+					if (nc->staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES) {
+						ip.setPort(routedNetmaskBits);
+						nc->staticIps[nc->staticIpCount++] = ip;
+					}
+					if (ip.ss_family == AF_INET)
+						haveManagedIpv4AutoAssignment = true;
+					else if (ip.ss_family == AF_INET6)
+						haveManagedIpv6AutoAssignment = true;
 				}
 				}
-				if (ip.ss_family == AF_INET)
-					haveManagedIpv4AutoAssignment = true;
-				else if (ip.ss_family == AF_INET6)
-					haveManagedIpv6AutoAssignment = true;
 			}
 			}
 		}
 		}
 	} else {
 	} else {
@@ -1559,13 +1584,16 @@ void EmbeddedNetworkController::_request(
 						// If it's routed, then try to claim and assign it and if successful end loop
 						// If it's routed, then try to claim and assign it and if successful end loop
 						if ( (routedNetmaskBits > 0) && (!std::binary_search(ns.allocatedIps.begin(),ns.allocatedIps.end(),ip6)) ) {
 						if ( (routedNetmaskBits > 0) && (!std::binary_search(ns.allocatedIps.begin(),ns.allocatedIps.end(),ip6)) ) {
 							char tmpip[64];
 							char tmpip[64];
-							ipAssignments.push_back(ip6.toIpString(tmpip));
-							member["ipAssignments"] = ipAssignments;
-							ip6.setPort((unsigned int)routedNetmaskBits);
-							if (nc->staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES)
-								nc->staticIps[nc->staticIpCount++] = ip6;
-							haveManagedIpv6AutoAssignment = true;
-							break;
+							const std::string ipStr(ip6.toIpString(tmpip));
+							if (std::find(ipAssignments.begin(),ipAssignments.end(),ipStr) == ipAssignments.end()) {
+								ipAssignments.push_back(ipStr);
+								member["ipAssignments"] = ipAssignments;
+								ip6.setPort((unsigned int)routedNetmaskBits);
+								if (nc->staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES)
+									nc->staticIps[nc->staticIpCount++] = ip6;
+								haveManagedIpv6AutoAssignment = true;
+								break;
+							}
 						}
 						}
 					}
 					}
 				}
 				}
@@ -1612,16 +1640,19 @@ void EmbeddedNetworkController::_request(
 						const InetAddress ip4(Utils::hton(ip),0);
 						const InetAddress ip4(Utils::hton(ip),0);
 						if ( (routedNetmaskBits > 0) && (!std::binary_search(ns.allocatedIps.begin(),ns.allocatedIps.end(),ip4)) ) {
 						if ( (routedNetmaskBits > 0) && (!std::binary_search(ns.allocatedIps.begin(),ns.allocatedIps.end(),ip4)) ) {
 							char tmpip[64];
 							char tmpip[64];
-							ipAssignments.push_back(ip4.toIpString(tmpip));
-							member["ipAssignments"] = ipAssignments;
-							if (nc->staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES) {
-								struct sockaddr_in *const v4ip = reinterpret_cast<struct sockaddr_in *>(&(nc->staticIps[nc->staticIpCount++]));
-								v4ip->sin_family = AF_INET;
-								v4ip->sin_port = Utils::hton((uint16_t)routedNetmaskBits);
-								v4ip->sin_addr.s_addr = Utils::hton(ip);
+							const std::string ipStr(ip4.toIpString(tmpip));
+							if (std::find(ipAssignments.begin(),ipAssignments.end(),ipStr) == ipAssignments.end()) {
+								ipAssignments.push_back(ipStr);
+								member["ipAssignments"] = ipAssignments;
+								if (nc->staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES) {
+									struct sockaddr_in *const v4ip = reinterpret_cast<struct sockaddr_in *>(&(nc->staticIps[nc->staticIpCount++]));
+									v4ip->sin_family = AF_INET;
+									v4ip->sin_port = Utils::hton((uint16_t)routedNetmaskBits);
+									v4ip->sin_addr.s_addr = Utils::hton(ip);
+								}
+								haveManagedIpv4AutoAssignment = true;
+								break;
 							}
 							}
-							haveManagedIpv4AutoAssignment = true;
-							break;
 						}
 						}
 					}
 					}
 				}
 				}

+ 6 - 5
controller/RethinkDB.cpp

@@ -212,6 +212,7 @@ RethinkDB::RethinkDB(EmbeddedNetworkController *const nc,const Identity &myId,co
 					delete config;
 					delete config;
 					if (!table)
 					if (!table)
 						continue;
 						continue;
+					const std::string jdump(OSUtils::jsonDump(record,-1));
 
 
 					while (_run == 1) {
 					while (_run == 1) {
 						try {
 						try {
@@ -223,7 +224,7 @@ RethinkDB::RethinkDB(EmbeddedNetworkController *const nc,const Identity &myId,co
 									R::db(this->_db).table(table).get(deleteId).delete_().run(*rdb);
 									R::db(this->_db).table(table).get(deleteId).delete_().run(*rdb);
 								} else {
 								} else {
 									//printf("UPSERT: %s" ZT_EOL_S,record.dump().c_str());
 									//printf("UPSERT: %s" ZT_EOL_S,record.dump().c_str());
-									R::db(this->_db).table(table).insert(R::Datum::from_json(OSUtils::jsonDump(record,-1)),R::optargs("conflict","update","return_changes",false)).run(*rdb);
+									R::db(this->_db).table(table).insert(R::Datum::from_json(jdump),R::optargs("conflict","update","return_changes",false)).run(*rdb);
 								}
 								}
 								break;
 								break;
 							} else {
 							} else {
@@ -231,13 +232,13 @@ RethinkDB::RethinkDB(EmbeddedNetworkController *const nc,const Identity &myId,co
 								rdb.reset();
 								rdb.reset();
 							}
 							}
 						} catch (std::exception &e) {
 						} catch (std::exception &e) {
-							fprintf(stderr,"[%s] ERROR: %.10llx controller RethinkDB (insert/update): %s" ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt(),e.what());
+							fprintf(stderr,"[%s] ERROR: %.10llx controller RethinkDB (insert/update): %s [%s]" ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt(),e.what(),jdump.c_str());
 							rdb.reset();
 							rdb.reset();
 						} catch (R::Error &e) {
 						} catch (R::Error &e) {
-							fprintf(stderr,"[%s] ERROR: %.10llx controller RethinkDB (insert/update): %s" ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt(),e.message.c_str());
+							fprintf(stderr,"[%s] ERROR: %.10llx controller RethinkDB (insert/update): %s [%s]" ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt(),e.message.c_str(),jdump.c_str());
 							rdb.reset();
 							rdb.reset();
 						} catch ( ... ) {
 						} catch ( ... ) {
-							fprintf(stderr,"[%s] ERROR: %.10llx controller RethinkDB (insert/update): unknown exception" ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt());
+							fprintf(stderr,"[%s] ERROR: %.10llx controller RethinkDB (insert/update): unknown exception [%s]" ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt(),jdump.c_str());
 							rdb.reset();
 							rdb.reset();
 						}
 						}
 						std::this_thread::sleep_for(std::chrono::milliseconds(250));
 						std::this_thread::sleep_for(std::chrono::milliseconds(250));
@@ -280,7 +281,7 @@ RethinkDB::RethinkDB(EmbeddedNetworkController *const nc,const Identity &myId,co
 							tmpobj["ts"] = i->second.first;
 							tmpobj["ts"] = i->second.first;
 							tmpobj["phy"] = i->second.second.toIpString(tmp2);
 							tmpobj["phy"] = i->second.second.toIpString(tmp2);
 							batch.emplace_back(tmpobj);
 							batch.emplace_back(tmpobj);
-							if (batch.size() >= 256) {
+							if (batch.size() >= 1024) {
 								R::db(this->_db).table("MemberStatus",R::optargs("read_mode","outdated")).insert(batch,R::optargs("conflict","update")).run(*rdb);
 								R::db(this->_db).table("MemberStatus",R::optargs("read_mode","outdated")).insert(batch,R::optargs("conflict","update")).run(*rdb);
 								batch.clear();
 								batch.clear();
 							}
 							}

+ 1 - 1
controller/RethinkDB.hpp

@@ -23,7 +23,7 @@
 
 
 #include "DB.hpp"
 #include "DB.hpp"
 
 
-#define ZT_CONTROLLER_RETHINKDB_COMMIT_THREADS 8
+#define ZT_CONTROLLER_RETHINKDB_COMMIT_THREADS 4
 
 
 namespace ZeroTier
 namespace ZeroTier
 {
 {