network: revert previous changes to address_compare_func()

This partially reverts fe841414ef and
2a236f9fc0.

For IPv4, kernel compares the local address, prefix, and prefixlen.
For IPv6, kernel compares only the local address.
Let's follow the kernel's comparison way.

Fixes #17831.
This commit is contained in:
Yu Watanabe 2020-12-04 16:41:08 +09:00
parent 77613416e0
commit 1d30fc5cb6
2 changed files with 44 additions and 42 deletions

View File

@ -153,26 +153,40 @@ static bool address_may_have_broadcast(const Address *a) {
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;
switch (a->family) {
case AF_INET:
siphash24_compress(&a->prefixlen, sizeof(a->prefixlen), state);
if (a->family == AF_INET)
siphash24_compress_string(a->label, state);
uint32_t prefix = address_prefix(a);
siphash24_compress(&prefix, sizeof(prefix), 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);
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) {
@ -182,32 +196,25 @@ 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(address_prefix(a1), address_prefix(a2));
if (r != 0)
return r;
_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;
}
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);
return 0;
}
DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(address_hash_ops, Address, address_hash_func, address_compare_func, address_free);

View File

@ -159,10 +159,8 @@ static void test_address_equality(void) {
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));
@ -173,13 +171,10 @@ static void test_address_equality(void) {
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));