From: Yu Watanabe Date: Tue, 9 Apr 2024 02:27:41 +0000 (+0900) Subject: network/address-generation: regenerate IPv6 prefix stable address on conflict X-Git-Tag: v256-rc1~233^2~1 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e14679ff76ad4410d5d0a611a15a3d48c9f2e0eb;p=thirdparty%2Fsystemd.git network/address-generation: regenerate IPv6 prefix stable address on conflict If a generated address with IPv6Token=prefixstable conflicts with the one on another node or interface, let's generate another address and try to assign it. This improves support of RFC 7217. Fixes #31605. --- diff --git a/src/network/networkd-address-generation.c b/src/network/networkd-address-generation.c index d28094d53cc..816cdf713ac 100644 --- a/src/network/networkd-address-generation.c +++ b/src/network/networkd-address-generation.c @@ -390,6 +390,39 @@ int radv_generate_addresses(Link *link, Set *tokens, const struct in6_addr *pref return generate_addresses(link, tokens, &RADV_APP_ID, prefix, prefixlen, ret); } +int regenerate_address(Address *address, Link *link) { + struct in6_addr masked; + sd_id128_t app_id; + + assert(link); + assert(address); + assert(address->family == AF_INET6); + assert(!address->link && !address->network); + + if (!address->token || + address->token->type != ADDRESS_GENERATION_PREFIXSTABLE) + return 0; + + switch (address->source) { + case NETWORK_CONFIG_SOURCE_STATIC: + app_id = RADV_APP_ID; + break; + case NETWORK_CONFIG_SOURCE_DHCP_PD: + app_id = DHCP_PD_APP_ID; + break; + case NETWORK_CONFIG_SOURCE_NDISC: + app_id = NDISC_APP_ID; + break; + default: + assert_not_reached(); + } + + masked = address->in_addr.in6; + in6_addr_mask(&masked, address->prefixlen); + + return generate_stable_private_address(link, &app_id, &address->token->secret_key, &masked, &address->in_addr.in6, &address->in_addr.in6); +} + int config_parse_address_generation_type( const char *unit, const char *filename, diff --git a/src/network/networkd-address-generation.h b/src/network/networkd-address-generation.h index 68828b44ca0..2c6091321ae 100644 --- a/src/network/networkd-address-generation.h +++ b/src/network/networkd-address-generation.h @@ -5,6 +5,7 @@ #include "hashmap.h" #include "in-addr-util.h" +typedef struct Address Address; typedef struct IPv6Token IPv6Token; typedef struct Link Link; @@ -15,4 +16,6 @@ int dhcp_pd_generate_addresses(Link *link, const struct in6_addr *prefix, Hashma int ndisc_generate_addresses(Link *link, const struct in6_addr *prefix, uint8_t prefixlen, Hashmap **ret); int radv_generate_addresses(Link *link, Set *tokens, const struct in6_addr *prefix, uint8_t prefixlen, Hashmap **ret); +int regenerate_address(Address *address, Link *link); + CONFIG_PARSER_PROTOTYPE(config_parse_address_generation_type); diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c index df9d0c03886..b4ac0bc41b6 100644 --- a/src/network/networkd-address.c +++ b/src/network/networkd-address.c @@ -10,9 +10,11 @@ #include "netlink-util.h" #include "networkd-address-pool.h" #include "networkd-address.h" +#include "networkd-dhcp-prefix-delegation.h" #include "networkd-dhcp-server.h" #include "networkd-ipv4acd.h" #include "networkd-manager.h" +#include "networkd-ndisc.h" #include "networkd-netlabel.h" #include "networkd-network.h" #include "networkd-queue.h" @@ -802,8 +804,49 @@ static int address_update(Address *address) { return 0; } -static int address_drop(Address *address) { - Link *link = ASSERT_PTR(ASSERT_PTR(address)->link); +static int address_removed_maybe_kernel_dad(Link *link, Address *address) { + int r; + + assert(link); + assert(address); + + if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED)) + return 0; + + if (address->family != AF_INET6) + return 0; + + if (!FLAGS_SET(address->flags, IFA_F_TENTATIVE)) + return 0; + + log_link_info(link, "Address %s with tentative flag is removed, maybe a duplicated address is assigned on another node or link?", + IN6_ADDR_TO_STRING(&address->in_addr.in6)); + + /* Reset the address state, as the object may be reused in the below. */ + address->state = 0; + + switch (address->source) { + case NETWORK_CONFIG_SOURCE_STATIC: + r = link_reconfigure_radv_address(address, link); + break; + case NETWORK_CONFIG_SOURCE_DHCP_PD: + r = dhcp_pd_reconfigure_address(address, link); + break; + case NETWORK_CONFIG_SOURCE_NDISC: + r = ndisc_reconfigure_address(address, link); + break; + default: + r = 0; + } + if (r < 0) + return log_link_warning_errno(link, r, "Failed to configure an alternative address: %m"); + + return 0; +} + +static int address_drop(Address *in, bool removed_by_us) { + _cleanup_(address_unrefp) Address *address = address_ref(ASSERT_PTR(in)); + Link *link = ASSERT_PTR(address->link); int r; r = address_set_masquerade(address, /* add = */ false); @@ -823,6 +866,14 @@ static int address_drop(Address *address) { address_detach(address); + if (!removed_by_us) { + r = address_removed_maybe_kernel_dad(link, address); + if (r < 0) { + link_enter_failed(link); + return r; + } + } + link_update_operstate(link, /* also_update_master = */ true); link_check_ready(link); return 0; @@ -1137,7 +1188,7 @@ static int address_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Remov if (address_get_request(link, address, &req) >= 0) address_enter_removed(req->userdata); - (void) address_drop(address); + (void) address_drop(address, /* removed_by_us = */ true); } } @@ -1847,9 +1898,11 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message, if (type == RTM_DELADDR) { if (address) { + bool removed_by_us = FLAGS_SET(address->state, NETWORK_CONFIG_STATE_REMOVING); + address_enter_removed(address); log_address_debug(address, "Forgetting removed", link); - (void) address_drop(address); + (void) address_drop(address, removed_by_us); } else log_address_debug(tmp, "Kernel removed unknown", link); diff --git a/src/network/networkd-dhcp-prefix-delegation.c b/src/network/networkd-dhcp-prefix-delegation.c index 6e83163b047..2e660b77631 100644 --- a/src/network/networkd-dhcp-prefix-delegation.c +++ b/src/network/networkd-dhcp-prefix-delegation.c @@ -369,6 +369,28 @@ static int dhcp_pd_request_address_one(Address *address, Link *link) { return link_request_address(link, address, &link->dhcp_pd_messages, dhcp_pd_address_handler, NULL); } +int dhcp_pd_reconfigure_address(Address *address, Link *link) { + int r; + + assert(address); + assert(address->source == NETWORK_CONFIG_SOURCE_DHCP_PD); + assert(link); + + r = regenerate_address(address, link); + if (r <= 0) + return r; + + r = dhcp_pd_request_address_one(address, link); + if (r < 0) + return r; + + if (!link->dhcp_pd_configured) + link_set_state(link, LINK_STATE_CONFIGURING); + + link_check_ready(link); + return 0; +} + static int dhcp_pd_request_address( Link *link, const struct in6_addr *prefix, diff --git a/src/network/networkd-dhcp-prefix-delegation.h b/src/network/networkd-dhcp-prefix-delegation.h index e591b8ae2e0..4a8cca92b6d 100644 --- a/src/network/networkd-dhcp-prefix-delegation.h +++ b/src/network/networkd-dhcp-prefix-delegation.h @@ -8,6 +8,7 @@ #include "conf-parser.h" +typedef struct Address Address; typedef struct Link Link; bool link_dhcp_pd_is_enabled(Link *link); @@ -19,5 +20,6 @@ int dhcp4_pd_prefix_acquired(Link *uplink); int dhcp6_pd_prefix_acquired(Link *uplink); void dhcp_pd_prefix_lost(Link *uplink); void dhcp4_pd_prefix_lost(Link *uplink); +int dhcp_pd_reconfigure_address(Address *address, Link *link); CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_pd_subnet_id); diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c index e160014c5ab..2b039bed968 100644 --- a/src/network/networkd-ndisc.c +++ b/src/network/networkd-ndisc.c @@ -377,6 +377,28 @@ static int ndisc_request_address(Address *address, Link *link, sd_ndisc_router * return 0; } +int ndisc_reconfigure_address(Address *address, Link *link) { + int r; + + assert(address); + assert(address->source == NETWORK_CONFIG_SOURCE_NDISC); + assert(link); + + r = regenerate_address(address, link); + if (r <= 0) + return r; + + r = ndisc_request_address(address, link, NULL); + if (r < 0) + return r; + + if (!link->ndisc_configured) + link_set_state(link, LINK_STATE_CONFIGURING); + + link_check_ready(link); + return 0; +} + static int ndisc_redirect_route_new(sd_ndisc_redirect *rd, Route **ret) { _cleanup_(route_unrefp) Route *route = NULL; struct in6_addr gateway, destination; diff --git a/src/network/networkd-ndisc.h b/src/network/networkd-ndisc.h index 2766f5e4350..019fe4da425 100644 --- a/src/network/networkd-ndisc.h +++ b/src/network/networkd-ndisc.h @@ -4,6 +4,7 @@ #include "conf-parser.h" #include "time-util.h" +typedef struct Address Address; typedef struct Link Link; typedef struct Network Network; @@ -61,6 +62,7 @@ int ndisc_stop(Link *link); void ndisc_flush(Link *link); int link_request_ndisc(Link *link); +int ndisc_reconfigure_address(Address *address, Link *link); CONFIG_PARSER_PROTOTYPE(config_parse_ndisc_start_dhcp6_client); CONFIG_PARSER_PROTOTYPE(config_parse_ndisc_use_domains); diff --git a/src/network/networkd-radv.c b/src/network/networkd-radv.c index 279ea8d043b..6ff09119e33 100644 --- a/src/network/networkd-radv.c +++ b/src/network/networkd-radv.c @@ -280,6 +280,29 @@ int link_request_radv_addresses(Link *link) { return 0; } +int link_reconfigure_radv_address(Address *address, Link *link) { + int r; + + assert(address); + assert(address->source == NETWORK_CONFIG_SOURCE_STATIC); + assert(link); + + r = regenerate_address(address, link); + if (r <= 0) + return r; + + r = link_request_static_address(link, address); + if (r < 0) + return r; + + if (link->static_address_messages != 0) { + link->static_addresses_configured = false; + link_set_state(link, LINK_STATE_CONFIGURING); + } + + return 0; +} + static int radv_set_prefix(Link *link, Prefix *prefix) { _cleanup_(sd_radv_prefix_unrefp) sd_radv_prefix *p = NULL; int r; diff --git a/src/network/networkd-radv.h b/src/network/networkd-radv.h index 48677b50de4..94834e77a8e 100644 --- a/src/network/networkd-radv.h +++ b/src/network/networkd-radv.h @@ -71,6 +71,7 @@ void network_drop_invalid_pref64_prefixes(Network *network); void network_adjust_radv(Network *network); int link_request_radv_addresses(Link *link); +int link_reconfigure_radv_address(Address *address, Link *link); bool link_radv_enabled(Link *link); int radv_start(Link *link);