network/ipv4acd: manage ACD engines with Address object

IPv4 addresses are managed with local and peer addresses and prefix
length. So, potentially, the same address with different prefix length
can be assigned on a link, e.g. 192.168.0.1/24 and 192.168.0.1/26.
If one of the address is configured with ACD but the other is not,
then previously ACD might be unexpectedly disabled or enabled on them,
as we managed ACD engines with only local addresses.

This makes ACD engines managed with the corresponding Address objects.
This commit is contained in:
Yu Watanabe
2024-07-31 05:35:20 +09:00
parent a55d1b29a4
commit d9ef1cc407
3 changed files with 44 additions and 31 deletions
+2 -4
View File
@@ -135,8 +135,6 @@ void link_get_address_states(
*ret_all = address_state_from_scope(MIN(ipv4_scope, ipv6_scope));
}
static void address_hash_func(const Address *a, struct siphash *state);
static int address_compare_func(const Address *a1, const Address *a2);
static void address_detach(Address *address);
DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
@@ -446,7 +444,7 @@ static int address_ipv4_prefix(const Address *a, struct in_addr *ret) {
return 0;
}
static void address_hash_func(const Address *a, struct siphash *state) {
void address_hash_func(const Address *a, struct siphash *state) {
assert(a);
siphash24_compress_typesafe(a->family, state);
@@ -476,7 +474,7 @@ static void address_hash_func(const Address *a, struct siphash *state) {
}
}
static int address_compare_func(const Address *a1, const Address *a2) {
int address_compare_func(const Address *a1, const Address *a2) {
int r;
r = CMP(a1->family, a2->family);
+2 -4
View File
@@ -88,6 +88,8 @@ void link_get_address_states(
LinkAddressState *ret_ipv6,
LinkAddressState *ret_all);
void address_hash_func(const Address *a, struct siphash *state);
int address_compare_func(const Address *a1, const Address *a2);
extern const struct hash_ops address_hash_ops;
bool address_can_update(const Address *existing, const Address *requesting);
@@ -118,10 +120,6 @@ static inline int link_get_ipv6_address(Link *link, const struct in6_addr *addre
assert(address);
return link_get_address(link, AF_INET6, &(union in_addr_union) { .in6 = *address }, prefixlen, ret);
}
static inline int link_get_ipv4_address(Link *link, const struct in_addr *address, unsigned char prefixlen, Address **ret) {
assert(address);
return link_get_address(link, AF_INET, &(union in_addr_union) { .in = *address }, prefixlen, ret);
}
int manager_get_address(Manager *manager, int family, const union in_addr_union *address, unsigned char prefixlen, Address **ret);
bool manager_has_address(Manager *manager, int family, const union in_addr_union *address);
+40 -23
View File
@@ -13,10 +13,14 @@
#include "networkd-link.h"
#include "networkd-manager.h"
DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
DEFINE_PRIVATE_HASH_OPS_FULL(
ipv4acd_hash_ops,
void, trivial_hash_func, trivial_compare_func,
sd_ipv4acd, sd_ipv4acd_unref);
Address,
address_hash_func,
address_compare_func,
address_unref,
sd_ipv4acd,
sd_ipv4acd_unref);
bool link_ipv4acd_supported(Link *link) {
assert(link);
@@ -71,7 +75,7 @@ bool ipv4acd_bound(Link *link, const Address *address) {
if (address->family != AF_INET)
return true;
acd = hashmap_get(link->ipv4acd_by_address, IN4_ADDR_TO_PTR(&address->in_addr.in));
acd = hashmap_get(link->ipv4acd_by_address, address);
if (!acd)
return true;
@@ -127,24 +131,22 @@ static int dhcp4_address_on_conflict(Link *link) {
static void on_acd(sd_ipv4acd *acd, int event, void *userdata) {
Link *link = ASSERT_PTR(userdata);
Address *address = NULL;
struct in_addr a;
int r;
assert(acd);
r = sd_ipv4acd_get_address(acd, &a);
if (r < 0) {
log_link_warning_errno(link, r, "Failed to get address from IPv4ACD: %m");
link_enter_failed(link);
}
void *val, *key;
HASHMAP_FOREACH_KEY(val, key, link->ipv4acd_by_address)
if (val == acd) {
(void) address_get(link, key, &address);
break;
}
(void) link_get_ipv4_address(link, &a, 0, &address);
if (!address)
return;
switch (event) {
case SD_IPV4ACD_EVENT_STOP:
if (!address)
break;
if (address->source == NETWORK_CONFIG_SOURCE_STATIC) {
r = static_ipv4acd_address_remove(link, address, /* on_conflict = */ false);
if (r < 0)
@@ -156,14 +158,11 @@ static void on_acd(sd_ipv4acd *acd, int event, void *userdata) {
break;
case SD_IPV4ACD_EVENT_BIND:
log_link_debug(link, "Successfully claimed address %s", IN4_ADDR_TO_STRING(&a));
log_link_debug(link, "Successfully claimed address %s", IN4_ADDR_TO_STRING(&address->in_addr.in));
break;
case SD_IPV4ACD_EVENT_CONFLICT:
if (!address)
break;
log_link_warning(link, "Dropping address %s, as an address conflict was detected.", IN4_ADDR_TO_STRING(&a));
log_link_warning(link, "Dropping address %s, as an address conflict was detected.", IN4_ADDR_TO_STRING(&address->in_addr.in));
if (address->source == NETWORK_CONFIG_SOURCE_STATIC)
r = static_ipv4acd_address_remove(link, address, /* on_conflict = */ true);
@@ -207,6 +206,7 @@ static int ipv4acd_start_one(Link *link, sd_ipv4acd *acd) {
int ipv4acd_configure(Link *link, const Address *address) {
_cleanup_(sd_ipv4acd_unrefp) sd_ipv4acd *acd = NULL;
_cleanup_(address_unrefp) Address *a = NULL;
sd_ipv4acd *existing;
int r;
@@ -217,7 +217,7 @@ int ipv4acd_configure(Link *link, const Address *address) {
if (address->family != AF_INET)
return 0;
existing = hashmap_get(link->ipv4acd_by_address, IN4_ADDR_TO_PTR(&address->in_addr.in));
existing = hashmap_get(link->ipv4acd_by_address, address);
if (!address_ipv4acd_enabled(link, address))
return sd_ipv4acd_stop(existing);
@@ -227,6 +227,15 @@ int ipv4acd_configure(Link *link, const Address *address) {
log_link_debug(link, "Configuring IPv4ACD for address %s.", IN4_ADDR_TO_STRING(&address->in_addr.in));
r = address_new(&a);
if (r < 0)
return r;
a->family = AF_INET;
a->in_addr = address->in_addr;
a->in_addr_peer = address->in_addr_peer;
a->prefixlen = address->prefixlen;
r = sd_ipv4acd_new(&acd);
if (r < 0)
return r;
@@ -255,11 +264,17 @@ int ipv4acd_configure(Link *link, const Address *address) {
if (r < 0)
return r;
r = hashmap_ensure_put(&link->ipv4acd_by_address, &ipv4acd_hash_ops, IN4_ADDR_TO_PTR(&address->in_addr.in), acd);
r = ipv4acd_start_one(link, acd);
if (r < 0)
return r;
return ipv4acd_start_one(link, TAKE_PTR(acd));
r = hashmap_ensure_put(&link->ipv4acd_by_address, &ipv4acd_hash_ops, a, acd);
if (r < 0)
return r;
TAKE_PTR(a);
TAKE_PTR(acd);
return 0;
}
void ipv4acd_detach(Link *link, const Address *address) {
@@ -269,7 +284,9 @@ void ipv4acd_detach(Link *link, const Address *address) {
if (address->family != AF_INET)
return;
sd_ipv4acd_unref(hashmap_remove(link->ipv4acd_by_address, IN4_ADDR_TO_PTR(&address->in_addr.in)));
Address *a;
sd_ipv4acd_unref(hashmap_remove2(link->ipv4acd_by_address, address, (void**) &a));
address_unref(a);
}
int ipv4acd_update_mac(Link *link) {