network: ignore broadcast address for /31 or /32 addresses

As they do not have broadcast address.
See https://tools.ietf.org/html/rfc3021
This commit is contained in:
Yu Watanabe 2020-12-03 10:19:35 +09:00
parent 05a7023d24
commit 2a236f9fc0
1 changed files with 52 additions and 45 deletions

View File

@ -144,29 +144,35 @@ Address *address_free(Address *address) {
return mfree(address); return mfree(address);
} }
static bool address_may_have_broadcast(const Address *a) {
assert(a);
/* A /31 or /32 IPv4 address does not have a broadcast address.
* See https://tools.ietf.org/html/rfc3021 */
return a->family == AF_INET && in4_addr_is_null(&a->in_addr_peer.in) && a->prefixlen <= 30;
}
void address_hash_func(const Address *a, struct siphash *state) { void address_hash_func(const Address *a, struct siphash *state) {
assert(a); assert(a);
siphash24_compress(&a->family, sizeof(a->family), state); siphash24_compress(&a->family, sizeof(a->family), state);
switch (a->family) { if (!IN_SET(a->family, AF_INET, AF_INET6))
case AF_INET: /* treat non-IPv4 or IPv6 address family as AF_UNSPEC */
siphash24_compress(&a->broadcast, sizeof(a->broadcast), state); return;
if (a->family == AF_INET)
siphash24_compress_string(a->label, state); siphash24_compress_string(a->label, state);
_fallthrough_; siphash24_compress(&a->prefixlen, sizeof(a->prefixlen), state);
case AF_INET6: /* local address */
siphash24_compress(&a->prefixlen, sizeof(a->prefixlen), state); siphash24_compress(&a->in_addr, FAMILY_ADDRESS_SIZE(a->family), state);
/* local address */ /* peer address */
siphash24_compress(&a->in_addr, FAMILY_ADDRESS_SIZE(a->family), state); siphash24_compress(&a->in_addr_peer, FAMILY_ADDRESS_SIZE(a->family), state);
/* peer address */
siphash24_compress(&a->in_addr_peer, FAMILY_ADDRESS_SIZE(a->family), state);
break; if (address_may_have_broadcast(a))
default: siphash24_compress(&a->broadcast, sizeof(a->broadcast), state);
/* treat any other address family as AF_UNSPEC */
break;
}
} }
int address_compare_func(const Address *a1, const Address *a2) { int address_compare_func(const Address *a1, const Address *a2) {
@ -176,32 +182,32 @@ int address_compare_func(const Address *a1, const Address *a2) {
if (r != 0) if (r != 0)
return r; return r;
switch (a1->family) { if (!IN_SET(a1->family, AF_INET, AF_INET6))
/* use the same notion of equality as the kernel does */ /* treat non-IPv4 or IPv6 address family as AF_UNSPEC */
case AF_INET: return 0;
r = CMP(a1->broadcast.s_addr, a2->broadcast.s_addr);
if (r != 0)
return r;
if (a1->family == AF_INET) {
r = strcmp_ptr(a1->label, a2->label); r = strcmp_ptr(a1->label, a2->label);
if (r != 0) if (r != 0)
return r; return r;
_fallthrough_;
case AF_INET6:
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;
return memcmp(&a1->in_addr_peer, &a2->in_addr_peer, 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); DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(address_hash_ops, Address, address_hash_func, address_compare_func, address_free);
@ -222,18 +228,21 @@ static int address_copy(Address *dest, const Address *src) {
assert(dest); assert(dest);
assert(src); assert(src);
r = free_and_strdup(&dest->label, src->label); if (src->family == AF_INET) {
if (r < 0) r = free_and_strdup(&dest->label, src->label);
return r; if (r < 0)
return r;
}
dest->family = src->family; dest->family = src->family;
dest->prefixlen = src->prefixlen; dest->prefixlen = src->prefixlen;
dest->scope = src->scope; dest->scope = src->scope;
dest->flags = src->flags; dest->flags = src->flags;
dest->broadcast = src->broadcast;
dest->cinfo = src->cinfo; dest->cinfo = src->cinfo;
dest->in_addr = src->in_addr; dest->in_addr = src->in_addr;
dest->in_addr_peer = src->in_addr_peer; dest->in_addr_peer = src->in_addr_peer;
if (address_may_have_broadcast(src))
dest->broadcast = src->broadcast;
dest->duplicate_address_detection = src->duplicate_address_detection; dest->duplicate_address_detection = src->duplicate_address_detection;
return 0; return 0;
@ -840,13 +849,13 @@ int address_configure(
r = netlink_message_append_in_addr_union(req, IFA_ADDRESS, address->family, &address->in_addr_peer); r = netlink_message_append_in_addr_union(req, IFA_ADDRESS, address->family, &address->in_addr_peer);
if (r < 0) if (r < 0)
return log_link_error_errno(link, r, "Could not append IFA_ADDRESS attribute: %m"); return log_link_error_errno(link, r, "Could not append IFA_ADDRESS attribute: %m");
} else if (address->family == AF_INET && address->prefixlen <= 30) { } else if (address_may_have_broadcast(address)) {
r = sd_netlink_message_append_in_addr(req, IFA_BROADCAST, &address->broadcast); r = sd_netlink_message_append_in_addr(req, IFA_BROADCAST, &address->broadcast);
if (r < 0) if (r < 0)
return log_link_error_errno(link, r, "Could not append IFA_BROADCAST attribute: %m"); return log_link_error_errno(link, r, "Could not append IFA_BROADCAST attribute: %m");
} }
if (address->label) { if (address->family == AF_INET && address->label) {
r = sd_netlink_message_append_string(req, IFA_LABEL, address->label); r = sd_netlink_message_append_string(req, IFA_LABEL, address->label);
if (r < 0) if (r < 0)
return log_link_error_errno(link, r, "Could not append IFA_LABEL attribute: %m"); return log_link_error_errno(link, r, "Could not append IFA_LABEL attribute: %m");
@ -1809,9 +1818,7 @@ static int address_section_verify(Address *address) {
address->section->filename, address->section->line); address->section->filename, address->section->line);
} }
if (address->family == AF_INET && if (address_may_have_broadcast(address)) {
in_addr_is_null(address->family, &address->in_addr_peer) &&
address->prefixlen <= 30) {
if (address->broadcast.s_addr == 0) if (address->broadcast.s_addr == 0)
address->broadcast.s_addr = address->in_addr.in.s_addr | htobe32(0xfffffffflu >> address->prefixlen); address->broadcast.s_addr = address->in_addr.in.s_addr | htobe32(0xfffffffflu >> address->prefixlen);
} else if (address->broadcast.s_addr != 0) { } else if (address->broadcast.s_addr != 0) {