summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYu Watanabe <watanabe.yu+github@gmail.com>2021-06-21 21:08:19 +0200
committerYu Watanabe <watanabe.yu+github@gmail.com>2021-06-30 17:49:03 +0200
commit76a86ffdbee2dd9ef0f2b5338e14eb6ba7671456 (patch)
treeb4241501e3c103729fd4ace9222170a0dfd80895
parentnetwork: make request_drop() accept NULL (diff)
downloadsystemd-76a86ffdbee2dd9ef0f2b5338e14eb6ba7671456.tar.xz
systemd-76a86ffdbee2dd9ef0f2b5338e14eb6ba7671456.zip
network: ipv4acd: first probe address and then assign it
Previously, if IPv4 ACD is enabled on an address, then we first assign the address, and start sd-ipv4acd daemon for the address. This is not only RFC incompliant, but also the address is always dropped, as the daemon always considers the address is conflicted. This commit makes networkd first starts sd-ipv4acd daemon to probe the address, and then the address is configured if no conflict is detected. Fixes #17235.
-rw-r--r--src/network/meson.build2
-rw-r--r--src/network/networkd-address.c168
-rw-r--r--src/network/networkd-address.h9
-rw-r--r--src/network/networkd-dhcp4.c181
-rw-r--r--src/network/networkd-ipv4acd.c271
-rw-r--r--src/network/networkd-ipv4acd.h10
-rw-r--r--src/network/networkd-link.c24
-rw-r--r--src/network/networkd-link.h3
8 files changed, 326 insertions, 342 deletions
diff --git a/src/network/meson.build b/src/network/meson.build
index 5a3a23101d..4e137d7b9e 100644
--- a/src/network/meson.build
+++ b/src/network/meson.build
@@ -79,6 +79,8 @@ sources = files('''
networkd-dhcp4.h
networkd-dhcp6.c
networkd-dhcp6.h
+ networkd-ipv4acd.c
+ networkd-ipv4acd.h
networkd-ipv4ll.c
networkd-ipv4ll.h
networkd-ipv6-proxy-ndp.c
diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c
index a8f081ec81..35305aff99 100644
--- a/src/network/networkd-address.c
+++ b/src/network/networkd-address.c
@@ -9,6 +9,7 @@
#include "netlink-util.h"
#include "networkd-address-pool.h"
#include "networkd-address.h"
+#include "networkd-ipv4acd.h"
#include "networkd-manager.h"
#include "networkd-network.h"
#include "networkd-queue.h"
@@ -129,6 +130,7 @@ static int address_new_static(Network *network, const char *filename, unsigned s
address->network = network;
address->section = TAKE_PTR(n);
+ address->is_static = true;
r = ordered_hashmap_ensure_put(&network->addresses_by_section, &network_config_hash_ops, address->section, address);
if (r < 0)
@@ -152,6 +154,7 @@ Address *address_free(Address *address) {
set_remove(address->link->addresses, address);
set_remove(address->link->addresses_foreign, address);
+ set_remove(address->link->addresses_ipv4acd, address);
set_remove(address->link->static_addresses, address);
if (address->link->dhcp_address == address)
address->link->dhcp_address = NULL;
@@ -994,8 +997,6 @@ int address_configure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m,
return 1;
}
-static int ipv4_dad_configure(Address *address);
-
static int address_configure(
const Address *address,
Link *link,
@@ -1242,6 +1243,12 @@ static int address_is_ready_to_configure(Link *link, const Address *address) {
return log_link_warning_errno(link, SYNTHETIC_ERRNO(E2BIG),
"Too many addresses are configured, refusing: %m");
+ if (address->family == AF_INET &&
+ address->duplicate_address_detection & ADDRESS_FAMILY_IPV4 &&
+ link->hw_addr.length == ETH_ALEN &&
+ !ether_addr_is_null(&link->hw_addr.ether))
+ return ipv4acd_address_is_ready_to_configure(link, address);
+
r = address_add(link, address, NULL);
if (r < 0)
return log_link_warning_errno(link, r, "Could not add address: %m");;
@@ -1286,12 +1293,6 @@ int request_process_address(Request *req) {
if (r < 0)
log_link_warning_errno(link, r, "Could not enable IP masquerading, ignoring: %m");
- if (FLAGS_SET(a->duplicate_address_detection, ADDRESS_FAMILY_IPV4)) {
- r = ipv4_dad_configure(a);
- if (r < 0)
- log_link_warning_errno(link, r, "Failed to start IPv4ACD client, ignoring: %m");
- }
-
return 1;
}
@@ -1475,157 +1476,6 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message,
return 1;
}
-static void static_address_on_acd(sd_ipv4acd *acd, int event, void *userdata) {
- Address *address;
- Link *link;
- int r;
-
- assert(acd);
- assert(userdata);
-
- address = (Address *) userdata;
- link = address->link;
-
- assert(address->family == AF_INET);
-
- switch (event) {
- case SD_IPV4ACD_EVENT_STOP:
- log_link_debug(link, "Stopping ACD client...");
- return;
-
- case SD_IPV4ACD_EVENT_BIND:
- log_link_debug(link, "Successfully claimed address "IPV4_ADDRESS_FMT_STR,
- IPV4_ADDRESS_FMT_VAL(address->in_addr.in));
- link_check_ready(link);
- break;
-
- case SD_IPV4ACD_EVENT_CONFLICT:
- log_link_warning(link, "DAD conflict. Dropping address "IPV4_ADDRESS_FMT_STR,
- IPV4_ADDRESS_FMT_VAL(address->in_addr.in));
- r = address_remove(address, link);
- if (r < 0)
- log_link_error_errno(link, r, "Failed to drop DAD conflicted address "IPV4_ADDRESS_FMT_STR,
- IPV4_ADDRESS_FMT_VAL(address->in_addr.in));
-
- link_check_ready(link);
- break;
-
- default:
- assert_not_reached("Invalid IPv4ACD event.");
- }
-
- (void) sd_ipv4acd_stop(acd);
-
- return;
-}
-
-static int ipv4_dad_configure(Address *address) {
- int r;
-
- assert(address);
- assert(address->link);
-
- if (address->family != AF_INET)
- return 0;
-
- log_address_debug(address, "Starting IPv4ACD client. Probing", address->link);
-
- if (!address->acd) {
- r = sd_ipv4acd_new(&address->acd);
- if (r < 0)
- return r;
-
- r = sd_ipv4acd_attach_event(address->acd, address->link->manager->event, 0);
- if (r < 0)
- return r;
- }
-
- r = sd_ipv4acd_set_ifindex(address->acd, address->link->ifindex);
- if (r < 0)
- return r;
-
- r = sd_ipv4acd_set_mac(address->acd, &address->link->hw_addr.ether);
- if (r < 0)
- return r;
-
- r = sd_ipv4acd_set_address(address->acd, &address->in_addr.in);
- if (r < 0)
- return r;
-
- r = sd_ipv4acd_set_callback(address->acd, static_address_on_acd, address);
- if (r < 0)
- return r;
-
- return sd_ipv4acd_start(address->acd, true);
-}
-
-static int ipv4_dad_update_mac_one(Address *address) {
- bool running;
- int r;
-
- assert(address);
-
- if (!address->acd)
- return 0;
-
- running = sd_ipv4acd_is_running(address->acd);
-
- r = sd_ipv4acd_stop(address->acd);
- if (r < 0)
- return r;
-
- r = sd_ipv4acd_set_mac(address->acd, &address->link->hw_addr.ether);
- if (r < 0)
- return r;
-
- if (running) {
- r = sd_ipv4acd_start(address->acd, true);
- if (r < 0)
- return r;
- }
-
- return 0;
-}
-
-int ipv4_dad_update_mac(Link *link) {
- Address *address;
- int k, r = 0;
-
- assert(link);
-
- SET_FOREACH(address, link->addresses) {
- k = ipv4_dad_update_mac_one(address);
- if (k < 0 && r >= 0)
- r = k;
- }
-
- return r;
-}
-
-int ipv4_dad_stop(Link *link) {
- Address *address;
- int k, r = 0;
-
- assert(link);
-
- SET_FOREACH(address, link->addresses) {
- k = sd_ipv4acd_stop(address->acd);
- if (k < 0 && r >= 0)
- r = k;
- }
-
- return r;
-}
-
-void ipv4_dad_unref(Link *link) {
- Address *address;
-
- assert(link);
-
- SET_FOREACH(address, link->addresses)
- address->acd = sd_ipv4acd_unref(address->acd);
-}
-
int config_parse_broadcast(
const char *unit,
const char *filename,
diff --git a/src/network/networkd-address.h b/src/network/networkd-address.h
index a24320fca2..ff3d46abdd 100644
--- a/src/network/networkd-address.h
+++ b/src/network/networkd-address.h
@@ -41,12 +41,13 @@ typedef struct Address {
bool scope_set:1;
bool ip_masquerade_done:1;
+ bool is_static:1; /* currently only used by IPv4ACD */
+ bool acd_announced:1;
AddressFamily duplicate_address_detection;
+ sd_ipv4acd *acd;
/* Called when address become ready */
address_ready_callback_t callback;
-
- sd_ipv4acd *acd;
} Address;
int address_new(Address **ret);
@@ -71,10 +72,6 @@ int link_get_ipv6_address(Link *link, const struct in6_addr *address, Address **
int link_get_ipv4_address(Link *link, const struct in_addr *address, unsigned char prefixlen, Address **ret);
int manager_has_address(Manager *manager, int family, const union in_addr_union *address, bool check_ready);
-void ipv4_dad_unref(Link *link);
-int ipv4_dad_stop(Link *link);
-int ipv4_dad_update_mac(Link *link);
-
int link_request_address(
Link *link,
Address *address,
diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c
index 1aaeda98e2..f80adcdbcf 100644
--- a/src/network/networkd-dhcp4.c
+++ b/src/network/networkd-dhcp4.c
@@ -14,6 +14,7 @@
#include "network-internal.h"
#include "networkd-address.h"
#include "networkd-dhcp4.h"
+#include "networkd-ipv4acd.h"
#include "networkd-link.h"
#include "networkd-manager.h"
#include "networkd-network.h"
@@ -84,9 +85,6 @@ static int dhcp4_release_old_lease(Link *link) {
static void dhcp4_check_ready(Link *link) {
int r;
- if (link->network->dhcp_send_decline && !link->dhcp4_address_bind)
- return;
-
if (link->dhcp4_messages > 0) {
log_link_debug(link, "%s(): DHCPv4 address and routes are not set.", __func__);
return;
@@ -768,159 +766,32 @@ int dhcp4_lease_lost(Link *link) {
link->dhcp_lease = sd_dhcp_lease_unref(link->dhcp_lease);
link_dirty(link);
- (void) sd_ipv4acd_stop(link->dhcp_acd);
-
- if (r < 0)
- return r;
-
- r = link_request_static_nexthops(link, true);
- if (r < 0)
- return r;
-
- return link_request_static_routes(link, true);
-}
-
-static void dhcp_address_on_acd(sd_ipv4acd *acd, int event, void *userdata) {
- struct in_addr address;
- Link *link;
- int r;
-
- assert(acd);
- assert(userdata);
-
- link = userdata;
-
- switch (event) {
- case SD_IPV4ACD_EVENT_STOP:
- log_link_debug(link, "Stopping ACD client for DHCPv4 address.");
- return;
-
- case SD_IPV4ACD_EVENT_BIND:
- if (DEBUG_LOGGING) {
- (void) sd_dhcp_lease_get_address(link->dhcp_lease, &address);
- log_link_debug(link, "Successfully claimed DHCPv4 address "IPV4_ADDRESS_FMT_STR, IPV4_ADDRESS_FMT_VAL(address));
- }
- link->dhcp4_address_bind = true;
- dhcp4_check_ready(link);
- break;
-
- case SD_IPV4ACD_EVENT_CONFLICT:
- (void) sd_dhcp_lease_get_address(link->dhcp_lease, &address);
- log_link_warning(link, "DAD conflict. Dropping DHCPv4 address "IPV4_ADDRESS_FMT_STR, IPV4_ADDRESS_FMT_VAL(address));
-
- r = sd_dhcp_client_send_decline(link->dhcp_client);
- if (r < 0)
- log_link_warning_errno(link, r, "Failed to send DHCP DECLINE, ignoring: %m");
-
- if (link->dhcp_lease) {
- r = dhcp4_lease_lost(link);
- if (r < 0)
- link_enter_failed(link);
- }
- break;
-
- default:
- assert_not_reached("Invalid IPv4ACD event.");
- }
-
- (void) sd_ipv4acd_stop(acd);
-
- return;
-}
-
-static int dhcp4_configure_dad(Link *link) {
- int r;
-
- assert(link);
- assert(link->manager);
- assert(link->network);
-
- if (!link->network->dhcp_send_decline)
- return 0;
-
- if (!link->dhcp_acd) {
- r = sd_ipv4acd_new(&link->dhcp_acd);
- if (r < 0)
- return r;
-
- r = sd_ipv4acd_attach_event(link->dhcp_acd, link->manager->event, 0);
- if (r < 0)
- return r;
- }
-
- r = sd_ipv4acd_set_ifindex(link->dhcp_acd, link->ifindex);
- if (r < 0)
- return r;
-
- r = sd_ipv4acd_set_mac(link->dhcp_acd, &link->hw_addr.ether);
- if (r < 0)
- return r;
-
- return 0;
-}
-
-static int dhcp4_dad_update_mac(Link *link) {
- bool running;
- int r;
-
- assert(link);
-
- if (!link->dhcp_acd)
- return 0;
-
- running = sd_ipv4acd_is_running(link->dhcp_acd);
+ if (link->network->dhcp_send_decline) {
+ Address *a;
- r = sd_ipv4acd_stop(link->dhcp_acd);
- if (r < 0)
- return r;
+ /* The acquired address may be still ARP probing and not configured. */
- r = sd_ipv4acd_set_mac(link->dhcp_acd, &link->hw_addr.ether);
- if (r < 0)
- return r;
+ SET_FOREACH(a, link->addresses_ipv4acd)
+ if (!a->is_static && address_get(link, a, NULL) < 0) {
+ Request req = {
+ .link = link,
+ .address = a,
+ };
- if (running) {
- r = sd_ipv4acd_start(link->dhcp_acd, true);
- if (r < 0)
- return r;
+ log_link_debug(link, "Canceling the request to configure DHCPv4 address "IPV4_ADDRESS_FMT_STR,
+ IPV4_ADDRESS_FMT_VAL(a->in_addr.in));
+ request_drop(ordered_set_get(link->manager->request_queue, &req));
+ }
}
- return 0;
-}
-
-static int dhcp4_start_acd(Link *link) {
- struct in_addr addr, old;
- int r;
-
- if (!link->network->dhcp_send_decline)
- return 0;
-
- if (!link->dhcp_lease)
- return 0;
-
- (void) sd_ipv4acd_stop(link->dhcp_acd);
-
- link->dhcp4_address_bind = false;
-
- r = sd_dhcp_lease_get_address(link->dhcp_lease, &addr);
- if (r < 0)
- return r;
-
- r = sd_ipv4acd_get_address(link->dhcp_acd, &old);
- if (r < 0)
- return r;
-
- r = sd_ipv4acd_set_address(link->dhcp_acd, &addr);
if (r < 0)
return r;
- r = sd_ipv4acd_set_callback(link->dhcp_acd, dhcp_address_on_acd, link);
+ r = link_request_static_nexthops(link, true);
if (r < 0)
return r;
- log_link_debug(link, "Starting IPv4ACD client. Probing DHCPv4 address "IPV4_ADDRESS_FMT_STR,
- IPV4_ADDRESS_FMT_VAL(addr));
-
- return sd_ipv4acd_start(link->dhcp_acd, !in4_addr_equal(&addr, &old));
+ return link_request_static_routes(link, true);
}
static int dhcp4_address_ready_callback(Address *address) {
@@ -957,11 +828,6 @@ static int dhcp4_after_address_configure(Request *req, void *object) {
}
link->dhcp_address = address;
-
- r = dhcp4_start_acd(link);
- if (r < 0)
- return log_link_error_errno(link, r, "Failed to start IPv4ACD for DHCPv4 address: %m");
-
return 0;
}
@@ -1057,6 +923,7 @@ static int dhcp4_request_address(Link *link, bool announce) {
addr->broadcast.s_addr = address.s_addr | ~netmask.s_addr;
SET_FLAG(addr->flags, IFA_F_NOPREFIXROUTE, !link_prefixroute(link));
addr->route_metric = link->network->dhcp_route_metric;
+ addr->duplicate_address_detection = link->network->dhcp_send_decline ? ADDRESS_FAMILY_IPV4 : ADDRESS_FAMILY_NO;
if (address_get(link, addr, NULL) < 0)
link->dhcp4_configured = false;
@@ -1687,10 +1554,6 @@ int dhcp4_configure(Link *link) {
if (r < 0)
return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set initial DHCPv4 address: %m");
- r = dhcp4_configure_dad(link);
- if (r < 0)
- return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to configure service type: %m");
-
return dhcp4_set_client_identifier(link);
}
@@ -1708,15 +1571,7 @@ int dhcp4_update_mac(Link *link) {
if (r < 0)
return r;
- r = dhcp4_set_client_identifier(link);
- if (r < 0)
- return r;
-
- r = dhcp4_dad_update_mac(link);
- if (r < 0)
- return r;
-
- return 0;
+ return dhcp4_set_client_identifier(link);
}
int dhcp4_start(Link *link) {
diff --git a/src/network/networkd-ipv4acd.c b/src/network/networkd-ipv4acd.c
new file mode 100644
index 0000000000..06dfa2d540
--- /dev/null
+++ b/src/network/networkd-ipv4acd.c
@@ -0,0 +1,271 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "sd-dhcp-client.h"
+#include "sd-ipv4acd.h"
+
+#include "networkd-address.h"
+#include "networkd-dhcp4.h"
+#include "networkd-ipv4acd.h"
+#include "networkd-link.h"
+#include "networkd-manager.h"
+
+static int static_address_on_stop(Link *link, Address *address) {
+ int r;
+
+ assert(link);
+ assert(address);
+
+ if (address_get(link, address, NULL) < 0)
+ return 0;
+
+ log_link_debug(link, "Removing address "IPV4_ADDRESS_FMT_STR", as the ACD client is stopped.",
+ IPV4_ADDRESS_FMT_VAL(address->in_addr.in));
+
+ r = address_remove(address, link);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to remove address "IPV4_ADDRESS_FMT_STR": %m",
+ IPV4_ADDRESS_FMT_VAL(address->in_addr.in));
+
+ return 0;
+}
+
+static int static_address_on_conflict(Link *link, Address *address) {
+ int r;
+
+ assert(link);
+ assert(address);
+
+ if (address_get(link, address, NULL) < 0) {
+ log_link_warning(link, "Cannot configure requested address "IPV4_ADDRESS_FMT_STR", as an address conflict is detected.",
+ IPV4_ADDRESS_FMT_VAL(address->in_addr.in));
+ return 0;
+ }
+
+ log_link_warning(link, "Dropping address "IPV4_ADDRESS_FMT_STR", as an address conflict is detected.",
+ IPV4_ADDRESS_FMT_VAL(address->in_addr.in));
+
+ r = address_remove(address, link);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to remove address "IPV4_ADDRESS_FMT_STR": %m",
+ IPV4_ADDRESS_FMT_VAL(address->in_addr.in));
+
+ return 0;
+}
+
+static int dhcp4_address_on_conflict(Link *link) {
+ int r;
+
+ assert(link);
+ assert(link->dhcp_client);
+
+ r = sd_dhcp_client_send_decline(link->dhcp_client);
+ if (r < 0)
+ log_link_warning_errno(link, r, "Failed to send DHCP DECLINE, ignoring: %m");
+
+ if (!link->dhcp_lease)
+ /* Unlikely, but during probing the address, the lease may be lost. */
+ return 0;
+
+ log_link_warning(link, "Dropping DHCPv4 lease, as an address conflict is detected.");
+ r = dhcp4_lease_lost(link);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to drop DHCPv4 lease: %m");
+
+ /* It is not necessary to call address_remove() here, as dhcp4_lease_lost() removes it. */
+ return 0;
+}
+
+static void on_acd(sd_ipv4acd *acd, int event, void *userdata, bool is_static) {
+ Address *address = userdata;
+ Link *link;
+ int r;
+
+ assert(acd);
+ assert(address);
+ assert(address->acd == acd);
+ assert(address->link);
+ assert(address->family == AF_INET);
+
+ link = address->link;
+
+ switch (event) {
+ case SD_IPV4ACD_EVENT_STOP:
+ if (is_static) {
+ r = static_address_on_stop(link, address);
+ if (r < 0)
+ link_enter_failed(link);
+ }
+
+ /* We have nothing to do for DHCPv4 lease here, as the dhcp client is already stopped
+ * when stopping the ipv4acd client. See link_stop_engines(). */
+ break;
+
+ case SD_IPV4ACD_EVENT_BIND:
+ log_link_debug(link, "Successfully claimed address "IPV4_ADDRESS_FMT_STR,
+ IPV4_ADDRESS_FMT_VAL(address->in_addr.in));
+
+ address->acd_announced = true;
+ break;
+
+ case SD_IPV4ACD_EVENT_CONFLICT:
+ if (is_static)
+ r = static_address_on_conflict(link, address);
+ else
+ r = dhcp4_address_on_conflict(link);
+ if (r < 0)
+ link_enter_failed(link);
+ break;
+
+ default:
+ assert_not_reached("Invalid IPv4ACD event.");
+ }
+}
+
+static void static_address_on_acd(sd_ipv4acd *acd, int event, void *userdata) {
+ on_acd(acd, event, userdata, true);
+}
+
+static void dhcp4_address_on_acd(sd_ipv4acd *acd, int event, void *userdata) {
+ on_acd(acd, event, userdata, false);
+}
+
+static int ipv4acd_configure(Link *link, const Address *a) {
+ _cleanup_(address_freep) Address *address = NULL;
+ int r;
+
+ assert(link);
+ assert(a);
+ assert(a->family == AF_INET);
+
+ log_link_debug(link, "Configuring IPv4ACD for address "IPV4_ADDRESS_FMT_STR,
+ IPV4_ADDRESS_FMT_VAL(a->in_addr.in));
+
+ r = address_dup(a, &address);
+ if (r < 0)
+ return r;
+
+ r = set_ensure_put(&link->addresses_ipv4acd, &address_hash_ops, address);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -EEXIST;
+ address->link = link;
+
+ r = sd_ipv4acd_new(&address->acd);
+ if (r < 0)
+ return r;
+
+ r = sd_ipv4acd_attach_event(address->acd, link->manager->event, 0);
+ if (r < 0)
+ return r;
+
+ r = sd_ipv4acd_set_ifindex(address->acd, link->ifindex);
+ if (r < 0)
+ return r;
+
+ r = sd_ipv4acd_set_mac(address->acd, &link->hw_addr.ether);
+ if (r < 0)
+ return r;
+
+ r = sd_ipv4acd_set_address(address->acd, &address->in_addr.in);
+ if (r < 0)
+ return r;
+
+ r = sd_ipv4acd_set_callback(address->acd,
+ address->is_static ? static_address_on_acd : dhcp4_address_on_acd,
+ address);
+ if (r < 0)
+ return r;
+
+ if (link_has_carrier(link)) {
+ r = sd_ipv4acd_start(address->acd, true);
+ if (r < 0)
+ return r;
+ }
+
+ TAKE_PTR(address);
+ return 0;
+}
+
+int ipv4acd_address_is_ready_to_configure(Link *link, const Address *address) {
+ Address *acd_address;
+ int r;
+
+ acd_address = set_get(link->addresses_ipv4acd, address);
+ if (!acd_address) {
+ r = ipv4acd_configure(link, address);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to configure IPv4ACD client: %m");
+
+ return false;
+ }
+
+ if (!acd_address->acd_announced)
+ return false;
+
+ r = set_ensure_put(&link->addresses, &address_hash_ops, acd_address);
+ if (r < 0)
+ return log_oom();
+ if (r == 0)
+ return log_link_warning_errno(link, SYNTHETIC_ERRNO(EEXIST), "Address already exists.");
+
+ acd_address->flags |= IFA_F_TENTATIVE;
+ return true;
+}
+
+int ipv4acd_update_mac(Link *link) {
+ Address *address;
+ int k, r = 0;
+
+ assert(link);
+
+ if (link->hw_addr.length != ETH_ALEN)
+ return 0;
+ if (ether_addr_is_null(&link->hw_addr.ether))
+ return 0;
+
+ SET_FOREACH(address, link->addresses_ipv4acd) {
+ assert(address->acd);
+
+ k = sd_ipv4acd_set_mac(address->acd, &link->hw_addr.ether);
+ if (k < 0)
+ r = k;
+ }
+ if (r < 0)
+ link_enter_failed(link);
+
+ return r;
+}
+
+int ipv4acd_start(Link *link) {
+ Address *address;
+ int r;
+
+ assert(link);
+
+ SET_FOREACH(address, link->addresses_ipv4acd) {
+ if (sd_ipv4acd_is_running(address->acd))
+ continue;
+
+ r = sd_ipv4acd_start(address->acd, true);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+int ipv4acd_stop(Link *link) {
+ Address *address;
+ int k, r = 0;
+
+ assert(link);
+
+ SET_FOREACH(address, link->addresses_ipv4acd) {
+ k = sd_ipv4acd_stop(address->acd);
+ if (k < 0)
+ r = k;
+ }
+
+ return r;
+}
diff --git a/src/network/networkd-ipv4acd.h b/src/network/networkd-ipv4acd.h
new file mode 100644
index 0000000000..d2b4ff775f
--- /dev/null
+++ b/src/network/networkd-ipv4acd.h
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+typedef struct Address Address;
+typedef struct Link Link;
+
+int ipv4acd_address_is_ready_to_configure(Link *link, const Address *address);
+int ipv4acd_update_mac(Link *link);
+int ipv4acd_start(Link *link);
+int ipv4acd_stop(Link *link);
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index ba1bd283df..4efe74ad41 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -33,6 +33,7 @@
#include "networkd-dhcp-server.h"
#include "networkd-dhcp4.h"
#include "networkd-dhcp6.h"
+#include "networkd-ipv4acd.h"
#include "networkd-ipv4ll.h"
#include "networkd-ipv6-proxy-ndp.h"
#include "networkd-link-bus.h"
@@ -204,7 +205,6 @@ static void link_free_engines(Link *link) {
link->dhcp_server = sd_dhcp_server_unref(link->dhcp_server);
link->dhcp_client = sd_dhcp_client_unref(link->dhcp_client);
link->dhcp_lease = sd_dhcp_lease_unref(link->dhcp_lease);
- link->dhcp_acd = sd_ipv4acd_unref(link->dhcp_acd);
link->lldp = sd_lldp_unref(link->lldp);
link_lldp_emit_stop(link);
@@ -216,8 +216,6 @@ static void link_free_engines(Link *link) {
link->dhcp6_lease = sd_dhcp6_lease_unref(link->dhcp6_lease);
link->ndisc = sd_ndisc_unref(link->ndisc);
link->radv = sd_radv_unref(link->radv);
-
- ipv4_dad_unref(link);
}
static Link *link_free(Link *link) {
@@ -244,6 +242,7 @@ static Link *link_free(Link *link) {
link->addresses = set_free(link->addresses);
link->addresses_foreign = set_free(link->addresses_foreign);
+ link->addresses_ipv4acd = set_free(link->addresses_ipv4acd);
link->pool_addresses = set_free(link->pool_addresses);
link->static_addresses = set_free(link->static_addresses);
link->dhcp6_addresses = set_free(link->dhcp6_addresses);
@@ -361,6 +360,7 @@ int link_stop_engines(Link *link, bool may_keep_dhcp) {
bool keep_dhcp = may_keep_dhcp &&
link->network &&
+ !link->network->dhcp_send_decline && /* IPv4 ACD for the DHCPv4 address is running. */
(link->manager->restarting ||
FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP_ON_STOP));
@@ -370,10 +370,6 @@ int link_stop_engines(Link *link, bool may_keep_dhcp) {
r = log_link_warning_errno(link, k, "Could not stop DHCPv4 client: %m");
}
- k = sd_ipv4acd_stop(link->dhcp_acd);
- if (k < 0)
- r = log_link_warning_errno(link, k, "Could not stop IPv4 ACD client for DHCPv4: %m");
-
k = sd_dhcp_server_stop(link->dhcp_server);
if (k < 0)
r = log_link_warning_errno(link, k, "Could not stop DHCPv4 server: %m");
@@ -386,7 +382,7 @@ int link_stop_engines(Link *link, bool may_keep_dhcp) {
if (k < 0)
r = log_link_warning_errno(link, k, "Could not stop IPv4 link-local: %m");
- k = ipv4_dad_stop(link);
+ k = ipv4acd_stop(link);
if (k < 0)
r = log_link_warning_errno(link, k, "Could not stop IPv4 ACD client: %m");
@@ -668,6 +664,10 @@ static int link_acquire_dynamic_ipv4_conf(Link *link) {
return log_link_warning_errno(link, r, "Could not start DHCP server: %m");
}
+ r = ipv4acd_start(link);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Could not start IPv4 ACD client: %m");
+
return 0;
}
@@ -2039,6 +2039,10 @@ static int link_update_hardware_address(Link *link, sd_netlink_message *message)
r = ipv4ll_update_mac(link);
if (r < 0)
+ return log_link_debug_errno(link, r, "Could not update MAC address in IPv4 ACD client: %m");
+
+ r = ipv4ll_update_mac(link);
+ if (r < 0)
return log_link_debug_errno(link, r, "Could not update MAC address in IPv4LL client: %m");
r = dhcp4_update_mac(link);
@@ -2065,10 +2069,6 @@ static int link_update_hardware_address(Link *link, sd_netlink_message *message)
return log_link_debug_errno(link, r, "Could not update MAC address for LLDP: %m");
}
- r = ipv4_dad_update_mac(link);
- if (r < 0)
- return log_link_debug_errno(link, r, "Could not update MAC address in IPv4 ACD client: %m");
-
return 0;
}
diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h
index 28c2821ee6..4077ccaf09 100644
--- a/src/network/networkd-link.h
+++ b/src/network/networkd-link.h
@@ -104,6 +104,7 @@ typedef struct Link {
Set *addresses;
Set *addresses_foreign;
+ Set *addresses_ipv4acd;
Set *pool_addresses;
Set *static_addresses;
Set *neighbors;
@@ -119,11 +120,9 @@ typedef struct Link {
Set *dhcp_routes, *dhcp_routes_old;
char *lease_file;
unsigned dhcp4_messages;
- sd_ipv4acd *dhcp_acd;
bool dhcp4_route_failed:1;
bool dhcp4_route_retrying:1;
bool dhcp4_configured:1;
- bool dhcp4_address_bind:1;
sd_ipv4ll *ipv4ll;
bool ipv4ll_address_configured:1;