network/dhcp6: handle conflict addresses gracefully (#37916)

This commit is contained in:
Luca Boccassi
2025-06-23 22:23:35 +01:00
committed by GitHub
3 changed files with 51 additions and 32 deletions
+22
View File
@@ -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);
+4
View File
@@ -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. */
+25 -32
View File
@@ -155,46 +155,39 @@ 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.");
return -EEXIST;
simple_log:
log_link_full(link, log_level, "DHCPv6 address %s/%u (valid %s, preferred %s)",
pretty, address->prefixlen,
log_link_full(link,
/* 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),
FORMAT_LIFETIME(address->lifetime_preferred_usec));
return 0;
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." : "");
existing->also_requested_by_dhcp6 = true;
return -EEXIST;
}
static int dhcp6_request_address(