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.
This commit is contained in:
Yu Watanabe
2024-04-10 04:36:03 +09:00
parent 0ac03e004c
commit e14679ff76
9 changed files with 165 additions and 4 deletions
+33
View File
@@ -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,
@@ -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);
+57 -4
View File
@@ -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);
@@ -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,
@@ -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);
+22
View File
@@ -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;
+2
View File
@@ -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);
+23
View File
@@ -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;
+1
View File
@@ -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);