فهرست منبع

Fix for ZTO-33 (Jira), only assign routes if there is a viable source IP.

Adam Ierymenko 5 سال پیش
والد
کامیت
90f18f7ee7
3فایلهای تغییر یافته به همراه73 افزوده شده و 11 حذف شده
  1. 46 0
      node/InetAddress.hpp
  2. 16 0
      node/Utils.hpp
  3. 11 11
      service/OneService.cpp

+ 46 - 0
node/InetAddress.hpp

@@ -453,6 +453,52 @@ struct InetAddress : public sockaddr_storage
 	 */
 	bool isNetwork() const;
 
+	/**
+	 * Find the total number of prefix bits that match between this IP and another
+	 * 
+	 * @param b Second IP to compare with
+	 * @return Number of matching prefix bits or 0 if none match or IPs are of different families (e.g. v4 and v6)
+	 */
+	inline unsigned int matchingPrefixBits(const InetAddress &b) const
+	{
+		unsigned int c = 0;
+		if (ss_family == b.ss_family) {
+			switch(ss_family) {
+				case AF_INET: {
+					uint32_t ip0 = Utils::ntoh((uint32_t)reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr);
+					uint32_t ip1 = Utils::ntoh((uint32_t)reinterpret_cast<const struct sockaddr_in *>(&b)->sin_addr.s_addr);
+					while ((ip0 >> 31) == (ip1 >> 31)) {
+						ip0 <<= 1;
+						ip1 <<= 1;
+						if (++c == 32)
+							break;
+					}
+				}	break;
+				case AF_INET6: {
+					const uint8_t *ip0 = reinterpret_cast<const uint8_t *>(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr);
+					const uint8_t *ip1 = reinterpret_cast<const uint8_t *>(reinterpret_cast<const struct sockaddr_in6 *>(&b)->sin6_addr.s6_addr);
+					for(unsigned int i=0;i<16;++i) {
+						if (ip0[i] == ip1[i]) {
+							c += 8;
+						} else {
+							uint8_t ip0b = ip0[i];
+							uint8_t ip1b = ip1[i];
+							uint8_t bit = 0x80;
+							while (bit != 0) {
+								if ((ip0b & bit) != (ip1b & bit))
+									break;
+								++c;
+								bit >>= 1;
+							}
+							break;
+						}
+					}
+				}	break;
+			}
+		}
+		return c;
+	}
+
 	/**
 	 * @return 14-bit (0-16383) hash of this IP's first 24 or 48 bits (for V4 or V6) for rate limiting code, or 0 if non-IP
 	 */

+ 16 - 0
node/Utils.hpp

@@ -94,6 +94,22 @@ public:
 	static const CPUIDRegisters CPUID;
 #endif
 
+	/**
+	 * Compute the log2 (most significant bit set) of a 32-bit integer
+	 * 
+	 * @param v Integer to compute
+	 * @return log2 or 0 if v is 0
+	 */
+	static inline unsigned int log2(uint32_t v)
+	{
+		uint32_t r = (v > 0xffff) << 4; v >>= r;
+		uint32_t shift = (v > 0xff) << 3; v >>= shift; r |= shift;
+		shift = (v > 0xf) << 2; v >>= shift; r |= shift;
+		shift = (v > 0x3) << 1; v >>= shift; r |= shift;
+		r |= (v >> 1);
+		return (unsigned int)r;
+	}
+
 	/**
 	 * Perform a time-invariant binary comparison
 	 *

+ 11 - 11
service/OneService.cpp

@@ -1949,21 +1949,21 @@ public:
 				const InetAddress *const target = reinterpret_cast<const InetAddress *>(&(n.config.routes[i].target));
 				const InetAddress *const via = reinterpret_cast<const InetAddress *>(&(n.config.routes[i].via));
 
+				// Make sure we are allowed to set this managed route.
+				if ( (!checkIfManagedIsAllowed(n,*target)) || ((via->ss_family == target->ss_family)&&(matchIpOnly(myIps,*via))) )
+					continue;
+
+				// Find an IP on the interface that can be a source IP, abort if no IPs assigned.
 				const InetAddress *src = NULL;
-				for (unsigned int j=0; j<n.config.assignedAddressCount; ++j) {
-					const InetAddress *const tmp = reinterpret_cast<const InetAddress *>(&(n.config.assignedAddresses[j]));
-					if (target->isV4() && tmp->isV4()) {
-						src = reinterpret_cast<InetAddress *>(&(n.config.assignedAddresses[j]));
-						break;
-					} else if (target->isV6() && tmp->isV6()) {
-						src = reinterpret_cast<InetAddress *>(&(n.config.assignedAddresses[j]));
-						break;
+				unsigned int mostMatchingPrefixBits = 0;
+				for(std::vector<InetAddress>::const_iterator i(myIps.begin());i!=myIps.end();++i) {
+					const unsigned int matchingPrefixBits = i->matchingPrefixBits(*target);
+					if (matchingPrefixBits >= mostMatchingPrefixBits) {
+						mostMatchingPrefixBits = matchingPrefixBits;
+						src = &(*i);
 					}
 				}
 				if (!src)
-					src = &NULL_INET_ADDR;
-
-				if ( (!checkIfManagedIsAllowed(n,*target)) || ((via->ss_family == target->ss_family)&&(matchIpOnly(myIps,*via))) )
 					continue;
 
 				// Ignore routes implied by local managed IPs since adding the IP adds the route