diff --git a/node/InetAddress.hpp b/node/InetAddress.hpp index 67f70d2a9..a9a35dd20 100644 --- a/node/InetAddress.hpp +++ b/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(this)->sin_addr.s_addr); + uint32_t ip1 = Utils::ntoh((uint32_t)reinterpret_cast(&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(reinterpret_cast(this)->sin6_addr.s6_addr); + const uint8_t *ip1 = reinterpret_cast(reinterpret_cast(&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 */ diff --git a/node/Utils.hpp b/node/Utils.hpp index ec898fc0a..685fdf591 100644 --- a/node/Utils.hpp +++ b/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 * diff --git a/service/OneService.cpp b/service/OneService.cpp index 7cf28d3fd..30f241196 100644 --- a/service/OneService.cpp +++ b/service/OneService.cpp @@ -1949,21 +1949,21 @@ public: const InetAddress *const target = reinterpret_cast(&(n.config.routes[i].target)); const InetAddress *const via = reinterpret_cast(&(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.assignedAddresses[j])); - if (target->isV4() && tmp->isV4()) { - src = reinterpret_cast(&(n.config.assignedAddresses[j])); - break; - } else if (target->isV6() && tmp->isV6()) { - src = reinterpret_cast(&(n.config.assignedAddresses[j])); - break; + unsigned int mostMatchingPrefixBits = 0; + for(std::vector::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