return a->family == AF_INET && in4_addr_is_null(&a->in_addr_peer.in) && a->prefixlen <= 30;
}
+static uint32_t address_prefix(const Address *a) {
+ assert(a);
+
+ /* make sure we don't try to shift by 32.
+ * See ISO/IEC 9899:TC3 ยง 6.5.7.3. */
+ if (a->prefixlen == 0)
+ return 0;
+
+ if (a->in_addr_peer.in.s_addr != 0)
+ return be32toh(a->in_addr_peer.in.s_addr) >> (32 - a->prefixlen);
+ else
+ return be32toh(a->in_addr.in.s_addr) >> (32 - a->prefixlen);
+}
+
void address_hash_func(const Address *a, struct siphash *state) {
assert(a);
siphash24_compress(&a->family, sizeof(a->family), state);
- if (!IN_SET(a->family, AF_INET, AF_INET6))
- /* treat non-IPv4 or IPv6 address family as AF_UNSPEC */
- return;
-
- if (a->family == AF_INET)
- siphash24_compress_string(a->label, state);
+ switch (a->family) {
+ case AF_INET:
+ siphash24_compress(&a->prefixlen, sizeof(a->prefixlen), state);
- siphash24_compress(&a->prefixlen, sizeof(a->prefixlen), state);
- /* local address */
- siphash24_compress(&a->in_addr, FAMILY_ADDRESS_SIZE(a->family), state);
- /* peer address */
- siphash24_compress(&a->in_addr_peer, FAMILY_ADDRESS_SIZE(a->family), state);
+ uint32_t prefix = address_prefix(a);
+ siphash24_compress(&prefix, sizeof(prefix), state);
- if (address_may_have_broadcast(a))
- siphash24_compress(&a->broadcast, sizeof(a->broadcast), state);
+ _fallthrough_;
+ case AF_INET6:
+ siphash24_compress(&a->in_addr, FAMILY_ADDRESS_SIZE(a->family), state);
+ break;
+ default:
+ /* treat any other address family as AF_UNSPEC */
+ break;
+ }
}
int address_compare_func(const Address *a1, const Address *a2) {
if (r != 0)
return r;
- if (!IN_SET(a1->family, AF_INET, AF_INET6))
- /* treat non-IPv4 or IPv6 address family as AF_UNSPEC */
- return 0;
-
- if (a1->family == AF_INET) {
- r = strcmp_ptr(a1->label, a2->label);
+ switch (a1->family) {
+ case AF_INET:
+ /* See kernel's find_matching_ifa() in net/ipv4/devinet.c */
+ r = CMP(a1->prefixlen, a2->prefixlen);
if (r != 0)
return r;
- }
- r = CMP(a1->prefixlen, a2->prefixlen);
- if (r != 0)
- return r;
-
- r = memcmp(&a1->in_addr, &a2->in_addr, FAMILY_ADDRESS_SIZE(a1->family));
- if (r != 0)
- return r;
-
- r = memcmp(&a1->in_addr_peer, &a2->in_addr_peer, FAMILY_ADDRESS_SIZE(a1->family));
- if (r != 0)
- return r;
-
- if (address_may_have_broadcast(a1))
- return CMP(a1->broadcast.s_addr, a2->broadcast.s_addr);
+ r = CMP(address_prefix(a1), address_prefix(a2));
+ if (r != 0)
+ return r;
- return 0;
+ _fallthrough_;
+ case AF_INET6:
+ /* See kernel's ipv6_get_ifaddr() in net/ipv6/addrconf.c */
+ return memcmp(&a1->in_addr, &a2->in_addr, FAMILY_ADDRESS_SIZE(a1->family));
+ default:
+ /* treat any other address family as AF_UNSPEC */
+ return 0;
+ }
}
DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(address_hash_ops, Address, address_hash_func, address_compare_func, address_free);
assert_se(in_addr_from_string(AF_INET, "192.168.3.9", &a2->in_addr) >= 0);
assert_se(address_equal(a1, a2));
assert_se(in_addr_from_string(AF_INET, "192.168.3.10", &a1->in_addr_peer) >= 0);
- assert_se(!address_equal(a1, a2));
+ assert_se(address_equal(a1, a2));
assert_se(in_addr_from_string(AF_INET, "192.168.3.11", &a2->in_addr_peer) >= 0);
- assert_se(!address_equal(a1, a2));
- a2->in_addr_peer = a1->in_addr_peer;
assert_se(address_equal(a1, a2));
a1->prefixlen = 10;
assert_se(!address_equal(a1, a2));
assert_se(!address_equal(a1, a2));
a2->family = AF_INET6;
- a1->in_addr_peer = a2->in_addr_peer = IN_ADDR_NULL;
assert_se(in_addr_from_string(AF_INET6, "2001:4ca0:4f01::2", &a1->in_addr) >= 0);
assert_se(in_addr_from_string(AF_INET6, "2001:4ca0:4f01::2", &a2->in_addr) >= 0);
assert_se(address_equal(a1, a2));
- a1->prefixlen = 8;
- assert_se(!address_equal(a1, a2));
a2->prefixlen = 8;
assert_se(address_equal(a1, a2));