#include "networkd-link.h"
#include "networkd-manager.h"
+DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
+ ipv4acd_hash_ops,
+ void, trivial_hash_func, trivial_compare_func,
+ sd_ipv4acd, sd_ipv4acd_unref);
+
bool link_ipv4acd_supported(Link *link) {
assert(link);
return true;
}
-static bool address_ipv4acd_enabled(Address *address) {
+static bool address_ipv4acd_enabled(Link *link, const Address *address) {
+ assert(link);
assert(address);
- assert(address->link);
if (address->family != AF_INET)
return false;
if (!IN_SET(address->source, NETWORK_CONFIG_SOURCE_STATIC, NETWORK_CONFIG_SOURCE_DHCP4))
return false;
- if (!link_ipv4acd_supported(address->link))
- return false;
-
- return true;
+ return link_ipv4acd_supported(link);
}
-bool ipv4acd_bound(const Address *address) {
+bool ipv4acd_bound(Link *link, const Address *address) {
+ sd_ipv4acd *acd;
+
+ assert(link);
assert(address);
- if (!address->acd)
+ if (address->family != AF_INET)
+ return true;
+
+ acd = hashmap_get(link->ipv4acd_by_address, IN4_ADDR_TO_PTR(&address->in_addr.in));
+ if (!acd)
return true;
- return address->acd_bound;
+ return sd_ipv4acd_is_bound(acd) > 0;
}
static int static_ipv4acd_address_remove(Link *link, Address *address, bool on_conflict) {
}
static void on_acd(sd_ipv4acd *acd, int event, void *userdata) {
- Address *address = ASSERT_PTR(userdata);
+ Link *link = ASSERT_PTR(userdata);
+ Address *address = NULL;
struct in_addr a;
- Link *link;
int r;
assert(acd);
- assert(address->acd == acd);
- assert(address->link);
- assert(address->family == AF_INET);
- assert(IN_SET(address->source, NETWORK_CONFIG_SOURCE_STATIC, NETWORK_CONFIG_SOURCE_DHCP4));
- link = address->link;
- a = address->in_addr.in;
+ 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) link_get_ipv4_address(link, &a, 0, &address);
switch (event) {
case SD_IPV4ACD_EVENT_STOP:
- address->acd_bound = false;
+ if (!address)
+ break;
if (address->source == NETWORK_CONFIG_SOURCE_STATIC) {
r = static_ipv4acd_address_remove(link, address, /* on_conflict = */ false);
break;
case SD_IPV4ACD_EVENT_BIND:
- address->acd_bound = true;
-
log_link_debug(link, "Successfully claimed address %s", IN4_ADDR_TO_STRING(&a));
break;
case SD_IPV4ACD_EVENT_CONFLICT:
- address->acd_bound = false;
+ if (!address)
+ break;
log_link_warning(link, "Dropping address %s, as an address conflict was detected.", IN4_ADDR_TO_STRING(&a));
return link_get_by_hw_addr(m, &hw_addr, NULL) >= 0;
}
-static int address_ipv4acd_start(Address *address) {
- assert(address);
- assert(address->link);
-
- if (!address->acd)
- return 0;
+static int ipv4acd_start_one(Link *link, sd_ipv4acd *acd) {
+ assert(link);
+ assert(acd);
- if (sd_ipv4acd_is_running(address->acd))
+ if (sd_ipv4acd_is_running(acd))
return 0;
- if (!link_has_carrier(address->link))
+ if (!link_has_carrier(link))
return 0;
- return sd_ipv4acd_start(address->acd, true);
+ return sd_ipv4acd_start(acd, /* reset_conflicts = */ true);
}
-int ipv4acd_configure(Address *address) {
- Link *link;
+int ipv4acd_configure(Link *link, const Address *address) {
+ _cleanup_(sd_ipv4acd_unrefp) sd_ipv4acd *acd = NULL;
+ sd_ipv4acd *existing;
int r;
+ assert(link);
+ assert(link->manager);
assert(address);
- link = ASSERT_PTR(address->link);
-
- if (!address_ipv4acd_enabled(address)) {
- address->acd = sd_ipv4acd_unref(address->acd);
- address->acd_bound = false;
+ if (address->family != AF_INET)
return 0;
- }
- if (address->acd)
- return address_ipv4acd_start(address);
+ existing = hashmap_get(link->ipv4acd_by_address, IN4_ADDR_TO_PTR(&address->in_addr.in));
+
+ if (!address_ipv4acd_enabled(link, address))
+ return sd_ipv4acd_stop(existing);
+
+ if (existing)
+ return ipv4acd_start_one(link, existing);
log_link_debug(link, "Configuring IPv4ACD for address %s.", IN4_ADDR_TO_STRING(&address->in_addr.in));
- r = sd_ipv4acd_new(&address->acd);
+ r = sd_ipv4acd_new(&acd);
+ if (r < 0)
+ return r;
+
+ r = sd_ipv4acd_attach_event(acd, link->manager->event, 0);
if (r < 0)
return r;
- r = sd_ipv4acd_attach_event(address->acd, link->manager->event, 0);
+ r = sd_ipv4acd_set_ifindex(acd, link->ifindex);
if (r < 0)
return r;
- r = sd_ipv4acd_set_ifindex(address->acd, link->ifindex);
+ r = sd_ipv4acd_set_mac(acd, &link->hw_addr.ether);
if (r < 0)
return r;
- r = sd_ipv4acd_set_mac(address->acd, &link->hw_addr.ether);
+ r = sd_ipv4acd_set_address(acd, &address->in_addr.in);
if (r < 0)
return r;
- r = sd_ipv4acd_set_address(address->acd, &address->in_addr.in);
+ r = sd_ipv4acd_set_callback(acd, on_acd, link);
if (r < 0)
return r;
- r = sd_ipv4acd_set_callback(address->acd, on_acd, address);
+ r = sd_ipv4acd_set_check_mac_callback(acd, ipv4acd_check_mac, link->manager);
if (r < 0)
return r;
- r = sd_ipv4acd_set_check_mac_callback(address->acd, ipv4acd_check_mac, link->manager);
+ r = hashmap_ensure_put(&link->ipv4acd_by_address, &ipv4acd_hash_ops, IN4_ADDR_TO_PTR(&address->in_addr.in), acd);
if (r < 0)
return r;
- return address_ipv4acd_start(address);
+ return ipv4acd_start_one(link, TAKE_PTR(acd));
+}
+
+void ipv4acd_detach(Link *link, const Address *address) {
+ assert(link);
+ assert(address);
+
+ if (address->family != AF_INET)
+ return;
+
+ sd_ipv4acd_unref(hashmap_remove(link->ipv4acd_by_address, IN4_ADDR_TO_PTR(&address->in_addr.in)));
}
int ipv4acd_update_mac(Link *link) {
- Address *address;
- int k, r = 0;
+ sd_ipv4acd *acd;
+ int r;
assert(link);
if (ether_addr_is_null(&link->hw_addr.ether))
return 0;
- SET_FOREACH(address, link->addresses) {
- if (!address->acd)
- continue;
-
- k = sd_ipv4acd_set_mac(address->acd, &link->hw_addr.ether);
- if (k < 0)
- r = k;
+ HASHMAP_FOREACH(acd, link->ipv4acd_by_address) {
+ r = sd_ipv4acd_set_mac(acd, &link->hw_addr.ether);
+ if (r < 0)
+ return r;
}
- if (r < 0)
- link_enter_failed(link);
- return r;
+ return 0;
}
int ipv4acd_start(Link *link) {
- Address *address;
+ sd_ipv4acd *acd;
int r;
assert(link);
- SET_FOREACH(address, link->addresses) {
- r = address_ipv4acd_start(address);
+ HASHMAP_FOREACH(acd, link->ipv4acd_by_address) {
+ r = ipv4acd_start_one(link, acd);
if (r < 0)
return r;
}
}
int ipv4acd_stop(Link *link) {
- Address *address;
+ sd_ipv4acd *acd;
int k, r = 0;
assert(link);
- SET_FOREACH(address, link->addresses) {
- if (!address->acd)
- continue;
-
- k = sd_ipv4acd_stop(address->acd);
+ HASHMAP_FOREACH(acd, link->ipv4acd_by_address) {
+ k = sd_ipv4acd_stop(acd);
if (k < 0)
r = k;
}
}
int ipv4acd_set_ifname(Link *link) {
- Address *address;
+ sd_ipv4acd *acd;
int r;
assert(link);
- SET_FOREACH(address, link->addresses) {
- if (!address->acd)
- continue;
-
- r = sd_ipv4acd_set_ifname(address->acd, link->ifname);
+ HASHMAP_FOREACH(acd, link->ipv4acd_by_address) {
+ r = sd_ipv4acd_set_ifname(acd, link->ifname);
if (r < 0)
return r;
}