]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
network: ipv4acd: first probe address and then assign it
authorYu Watanabe <watanabe.yu+github@gmail.com>
Mon, 21 Jun 2021 19:08:19 +0000 (04:08 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Wed, 30 Jun 2021 15:49:03 +0000 (00:49 +0900)
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.

src/network/meson.build
src/network/networkd-address.c
src/network/networkd-address.h
src/network/networkd-dhcp4.c
src/network/networkd-ipv4acd.c [new file with mode: 0644]
src/network/networkd-ipv4acd.h [new file with mode: 0644]
src/network/networkd-link.c
src/network/networkd-link.h

index 5a3a23101d7a296fc9c0fa2227f484c6b393a3b3..4e137d7b9e2e93f81f859b5d637cbb86685221f5 100644 (file)
@@ -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
index a8f081ec8184f24eb52d01e59baffdddae1c4d41..35305aff99f9c49412d15edca1d0299b88d1ebbe 100644 (file)
@@ -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,
index a24320fca22cefbfebe8f57133a234cb0d4b0691..ff3d46abdddad05aeb064343d2ec873a01479015 100644 (file)
@@ -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,
index 1aaeda98e286104e8d623f3484852a72290c7532..f80adcdbcfef2289457906e07d22aadb692df3b6 100644 (file)
@@ -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 (file)
index 0000000..06dfa2d
--- /dev/null
@@ -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 (file)
index 0000000..d2b4ff7
--- /dev/null
@@ -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);
index ba1bd283df797d6c93b36550f26065a856e289ca..4efe74ad4121631cd605d70b18c003d17d2e4463 100644 (file)
@@ -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;
 }
 
@@ -2037,6 +2037,10 @@ static int link_update_hardware_address(Link *link, sd_netlink_message *message)
                         log_link_debug_errno(link, r, "Failed to manage link by its new hardware address, ignoring: %m");
         }
 
+        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");
@@ -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;
 }
 
index 28c2821ee6d322e115f780eae5380c2083f493b1..4077ccaf09c5b99ba32b702834052873d826a128 100644 (file)
@@ -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;