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:
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user