From 03f0658460964bcc63b5194b69885b1dbf33f77d Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sat, 21 Jun 2025 03:48:51 +0900 Subject: [PATCH 1/2] network/dhcp6: refactoring for verify_dhcp6_address() No functional change. Preparation for later changes. --- src/network/networkd-dhcp6.c | 53 +++++++++++++++--------------------- 1 file changed, 22 insertions(+), 31 deletions(-) diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c index 4806980993..e8b9bc1057 100644 --- a/src/network/networkd-dhcp6.c +++ b/src/network/networkd-dhcp6.c @@ -155,46 +155,37 @@ static int dhcp6_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Reques } static int verify_dhcp6_address(Link *link, const Address *address) { - bool by_ndisc = false; - Address *existing; - int log_level; - assert(link); assert(address); assert(address->family == AF_INET6); - const char *pretty = IN6_ADDR_TO_STRING(&address->in_addr.in6); + Address *existing = NULL; + (void) address_get_harder(link, address, &existing); - if (address_get_harder(link, address, &existing) < 0) { - /* New address. */ - log_level = LOG_INFO; - goto simple_log; - } else - log_level = LOG_DEBUG; + if (!existing || address_can_update(existing, address)) { + log_link_full(link, + /* Downgrade the log level if the address already exists or has been requested. */ + existing ? LOG_DEBUG : LOG_INFO, + "DHCPv6 address %s (valid %s, preferred %s)", + IN6_ADDR_PREFIX_TO_STRING(&address->in_addr.in6, address->prefixlen), + FORMAT_LIFETIME(address->lifetime_valid_usec), + FORMAT_LIFETIME(address->lifetime_preferred_usec)); + return 0; + } - if (address_can_update(existing, address)) - goto simple_log; + bool by_ndisc = existing->source == NETWORK_CONFIG_SOURCE_NDISC; - if (existing->source == NETWORK_CONFIG_SOURCE_NDISC) - by_ndisc = true; - - log_link_warning(link, "Ignoring DHCPv6 address %s/%u (valid %s, preferred %s) which conflicts with %s/%u%s.", - pretty, address->prefixlen, - FORMAT_LIFETIME(address->lifetime_valid_usec), - FORMAT_LIFETIME(address->lifetime_preferred_usec), - pretty, existing->prefixlen, - by_ndisc ? " assigned by NDisc" : ""); - if (by_ndisc) - log_link_warning(link, "Hint: use IPv6Token= setting to change the address generated by NDisc or set UseAutonomousPrefix=no."); + log_link_full(link, + LOG_WARNING, + "Ignoring DHCPv6 address %s (valid %s, preferred %s) which conflicts with %s%s.%s", + IN6_ADDR_PREFIX_TO_STRING(&address->in_addr.in6, address->prefixlen), + FORMAT_LIFETIME(address->lifetime_valid_usec), + FORMAT_LIFETIME(address->lifetime_preferred_usec), + IN6_ADDR_PREFIX_TO_STRING(&existing->in_addr.in6, existing->prefixlen), + by_ndisc ? " assigned by NDisc" : "", + by_ndisc ? "\nHint: use IPv6Token= setting to change the address generated by NDisc or set UseAutonomousPrefix=no." : ""); return -EEXIST; - -simple_log: - log_link_full(link, log_level, "DHCPv6 address %s/%u (valid %s, preferred %s)", - pretty, address->prefixlen, - FORMAT_LIFETIME(address->lifetime_valid_usec), - FORMAT_LIFETIME(address->lifetime_preferred_usec)); - return 0; } static int dhcp6_request_address( From 4c4fe8cd71da64506705736fda0ab7f7154f28d2 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sat, 21 Jun 2025 03:51:30 +0900 Subject: [PATCH 2/2] network/dhcp6: consider the DHCPv6 protocol as finished when conflict addresses exist Replaces #37891. --- src/network/networkd-address.c | 22 ++++++++++++++++++++++ src/network/networkd-address.h | 4 ++++ src/network/networkd-dhcp6.c | 4 +++- 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c index 406c40babb..15c5f883e1 100644 --- a/src/network/networkd-address.c +++ b/src/network/networkd-address.c @@ -326,6 +326,27 @@ bool link_check_addresses_ready(Link *link, NetworkConfigSource source) { has = true; } + if (has || source != NETWORK_CONFIG_SOURCE_DHCP6) + return has; + + /* If there is no DHCPv6 addresses, but all conflicting addresses are successfully configured, then + * let's handle the DHCPv6 client successfully configured addresses. Otherwise, if the conflicted + * addresses are static or foreign, and there is no other dynamic addressing protocol enabled, then + * the link will never enter the configured state. See also link_check_ready(). */ + SET_FOREACH(a, link->addresses) { + if (source >= 0 && a->source == NETWORK_CONFIG_SOURCE_DHCP6) + continue; + if (!a->also_requested_by_dhcp6) + continue; + if (address_is_marked(a)) + continue; + if (!address_exists(a)) + continue; + if (!address_is_ready(a)) + return false; + has = true; + } + return has; } @@ -1948,6 +1969,7 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message, nft_set_context_clear(&address->nft_set_context); (void) nft_set_context_dup(&a->nft_set_context, &address->nft_set_context); address->requested_as_null = a->requested_as_null; + address->also_requested_by_dhcp6 = a->also_requested_by_dhcp6; address->callback = a->callback; ipv6_token_ref(a->token); diff --git a/src/network/networkd-address.h b/src/network/networkd-address.h index c68ecb5ec0..0edac279fa 100644 --- a/src/network/networkd-address.h +++ b/src/network/networkd-address.h @@ -47,6 +47,10 @@ typedef struct Address { bool ip_masquerade_done:1; bool requested_as_null:1; bool used_by_dhcp_server:1; + /* If this address is configured by non-DHCPv6 protocol (i.e. statically, NDISC, or any foreign + * methods), and the DHCPv6 client requests the same address, set this flag. + * See link_check_addresses_ready() and verify_dhcp6_address(). */ + bool also_requested_by_dhcp6:1; /* duplicate_address_detection is only used by static or IPv4 dynamic addresses. * To control DAD for IPv6 dynamic addresses, set IFA_F_NODAD to flags. */ diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c index e8b9bc1057..fbdc4cf445 100644 --- a/src/network/networkd-dhcp6.c +++ b/src/network/networkd-dhcp6.c @@ -176,7 +176,8 @@ static int verify_dhcp6_address(Link *link, const Address *address) { bool by_ndisc = existing->source == NETWORK_CONFIG_SOURCE_NDISC; log_link_full(link, - LOG_WARNING, + /* Downgrade the log level if we have logged about the conflict previously. */ + existing->also_requested_by_dhcp6 ? LOG_DEBUG : LOG_WARNING, "Ignoring DHCPv6 address %s (valid %s, preferred %s) which conflicts with %s%s.%s", IN6_ADDR_PREFIX_TO_STRING(&address->in_addr.in6, address->prefixlen), FORMAT_LIFETIME(address->lifetime_valid_usec), @@ -185,6 +186,7 @@ static int verify_dhcp6_address(Link *link, const Address *address) { by_ndisc ? " assigned by NDisc" : "", by_ndisc ? "\nHint: use IPv6Token= setting to change the address generated by NDisc or set UseAutonomousPrefix=no." : ""); + existing->also_requested_by_dhcp6 = true; return -EEXIST; }