Merge pull request #32166 from yuwata/network-rfc7217-dad

network: generate another address on conflict
This commit is contained in:
Luca Boccassi 2024-04-10 22:59:01 +01:00 committed by GitHub
commit bf5e509840
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 417 additions and 117 deletions

View file

@ -34,11 +34,125 @@ typedef enum AddressGenerationType {
_ADDRESS_GENERATION_TYPE_INVALID = -EINVAL,
} AddressGenerationType;
typedef struct IPv6Token {
struct IPv6Token {
unsigned n_ref;
AddressGenerationType type;
struct in6_addr address;
sd_id128_t secret_key;
} IPv6Token;
};
DEFINE_TRIVIAL_REF_UNREF_FUNC(IPv6Token, ipv6_token, mfree);
DEFINE_TRIVIAL_CLEANUP_FUNC(IPv6Token*, ipv6_token_unref);
static void ipv6_token_hash_func(const IPv6Token *p, struct siphash *state) {
siphash24_compress_typesafe(p->type, state);
siphash24_compress_typesafe(p->address, state);
id128_hash_func(&p->secret_key, state);
}
static int ipv6_token_compare_func(const IPv6Token *a, const IPv6Token *b) {
int r;
r = CMP(a->type, b->type);
if (r != 0)
return r;
r = memcmp(&a->address, &b->address, sizeof(struct in6_addr));
if (r != 0)
return r;
return id128_compare_func(&a->secret_key, &b->secret_key);
}
DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
ipv6_token_hash_ops,
IPv6Token,
ipv6_token_hash_func,
ipv6_token_compare_func,
ipv6_token_unref);
DEFINE_PRIVATE_HASH_OPS_FULL(
ipv6_token_by_addr_hash_ops,
struct in6_addr,
in6_addr_hash_func,
in6_addr_compare_func,
free,
IPv6Token,
ipv6_token_unref);
static int ipv6_token_new(AddressGenerationType type, const struct in6_addr *addr, const sd_id128_t *secret_key, IPv6Token **ret) {
IPv6Token *p;
assert(type >= 0 && type < _ADDRESS_GENERATION_TYPE_MAX);
assert(addr);
assert(secret_key);
assert(ret);
p = new(IPv6Token, 1);
if (!p)
return -ENOMEM;
*p = (IPv6Token) {
.n_ref = 1,
.type = type,
.address = *addr,
.secret_key = *secret_key,
};
*ret = p;
return 0;
}
static int ipv6_token_add(Set **tokens, AddressGenerationType type, const struct in6_addr *addr, const sd_id128_t *secret_key) {
IPv6Token *p;
int r;
assert(tokens);
r = ipv6_token_new(type, addr, secret_key, &p);
if (r < 0)
return r;
return set_ensure_consume(tokens, &ipv6_token_hash_ops, p);
}
static int ipv6_token_put_by_addr(Hashmap **tokens_by_address, const struct in6_addr *addr, IPv6Token *token) {
_cleanup_free_ struct in6_addr *copy = NULL;
int r;
assert(tokens_by_address);
assert(addr);
assert(token);
copy = newdup(struct in6_addr, addr, 1);
if (!copy)
return -ENOMEM;
r = hashmap_ensure_put(tokens_by_address, &ipv6_token_by_addr_hash_ops, copy, token);
if (r == -EEXIST)
return 0;
if (r < 0)
return r;
TAKE_PTR(copy);
ipv6_token_ref(token);
return 1;
}
static int ipv6_token_type_put_by_addr(Hashmap **tokens_by_addr, const struct in6_addr *addr, AddressGenerationType type) {
_cleanup_(ipv6_token_unrefp) IPv6Token *token = NULL;
int r;
assert(tokens_by_addr);
assert(addr);
r = ipv6_token_new(type, &(struct in6_addr) {}, &SD_ID128_NULL, &token);
if (r < 0)
return r;
return ipv6_token_put_by_addr(tokens_by_addr, addr, token);
}
static int generate_eui64_address(const Link *link, const struct in6_addr *prefix, struct in6_addr *ret) {
assert(link);
@ -134,10 +248,12 @@ static int generate_stable_private_address(
const sd_id128_t *app_id,
const sd_id128_t *secret_key,
const struct in6_addr *prefix,
const struct in6_addr *previous,
struct in6_addr *ret) {
sd_id128_t secret_machine_key;
struct in6_addr addr;
bool found = false;
uint8_t i;
int r;
@ -162,16 +278,29 @@ static int generate_stable_private_address(
for (i = 0; i < DAD_CONFLICTS_IDGEN_RETRIES_RFC7217; i++) {
generate_stable_private_address_one(link, secret_key, prefix, i, &addr);
if (stable_private_address_is_valid(&addr))
break;
if (!stable_private_address_is_valid(&addr))
continue;
/* When 'previous' is non-NULL, then this is called after DAD in the kernel triggered.
* Let's increment the counter and provide the next address. */
if (previous && !found) {
found = in6_addr_equal(previous, &addr);
continue;
}
break;
}
if (i >= DAD_CONFLICTS_IDGEN_RETRIES_RFC7217)
if (i >= DAD_CONFLICTS_IDGEN_RETRIES_RFC7217) {
/* propagate recognizable errors. */
return log_link_debug_errno(link, SYNTHETIC_ERRNO(ENOANO),
if (previous && !found)
return -EADDRNOTAVAIL;
return log_link_debug_errno(link, SYNTHETIC_ERRNO(EADDRINUSE),
"Failed to generate stable private address.");
}
*ret = addr;
return 0;
return 1;
}
static int generate_addresses(
@ -180,10 +309,10 @@ static int generate_addresses(
const sd_id128_t *app_id,
const struct in6_addr *prefix,
uint8_t prefixlen,
Set **ret) {
Hashmap **ret) {
_cleanup_set_free_ Set *addresses = NULL;
struct in6_addr masked;
_cleanup_hashmap_free_ Hashmap *tokens_by_address = NULL;
struct in6_addr masked, addr;
IPv6Token *j;
int r;
@ -197,8 +326,6 @@ static int generate_addresses(
in6_addr_mask(&masked, prefixlen);
SET_FOREACH(j, tokens) {
struct in6_addr addr, *copy;
switch (j->type) {
case ADDRESS_GENERATION_EUI64:
if (generate_eui64_address(link, &masked, &addr) < 0)
@ -214,7 +341,7 @@ static int generate_addresses(
if (in6_addr_is_set(&j->address) && !in6_addr_equal(&j->address, &masked))
continue;
if (generate_stable_private_address(link, app_id, &j->secret_key, &masked, &addr) < 0)
if (generate_stable_private_address(link, app_id, &j->secret_key, &masked, /* previous = */ NULL, &addr) < 0)
continue;
break;
@ -223,97 +350,77 @@ static int generate_addresses(
assert_not_reached();
}
copy = newdup(struct in6_addr, &addr, 1);
if (!copy)
return -ENOMEM;
r = set_ensure_consume(&addresses, &in6_addr_hash_ops_free, copy);
r = ipv6_token_put_by_addr(&tokens_by_address, &addr, j);
if (r < 0)
return r;
}
/* fall back to EUI-64 if no token is provided */
if (set_isempty(addresses)) {
_cleanup_free_ struct in6_addr *addr = NULL;
if (hashmap_isempty(tokens_by_address)) {
AddressGenerationType type;
addr = new(struct in6_addr, 1);
if (!addr)
return -ENOMEM;
if (IN_SET(link->iftype, ARPHRD_ETHER, ARPHRD_INFINIBAND))
r = generate_eui64_address(link, &masked, addr);
else
r = generate_stable_private_address(link, app_id, &SD_ID128_NULL, &masked, addr);
if (IN_SET(link->iftype, ARPHRD_ETHER, ARPHRD_INFINIBAND)) {
type = ADDRESS_GENERATION_EUI64;
r = generate_eui64_address(link, &masked, &addr);
} else {
type = ADDRESS_GENERATION_PREFIXSTABLE;
r = generate_stable_private_address(link, app_id, &SD_ID128_NULL, &masked, /* previous = */ NULL, &addr);
}
if (r < 0)
return r;
r = set_ensure_consume(&addresses, &in6_addr_hash_ops_free, TAKE_PTR(addr));
r = ipv6_token_type_put_by_addr(&tokens_by_address, &addr, type);
if (r < 0)
return r;
}
*ret = TAKE_PTR(addresses);
*ret = TAKE_PTR(tokens_by_address);
return 0;
}
int dhcp_pd_generate_addresses(Link *link, const struct in6_addr *prefix, Set **ret) {
int dhcp_pd_generate_addresses(Link *link, const struct in6_addr *prefix, Hashmap **ret) {
return generate_addresses(link, link->network->dhcp_pd_tokens, &DHCP_PD_APP_ID, prefix, 64, ret);
}
int ndisc_generate_addresses(Link *link, const struct in6_addr *prefix, uint8_t prefixlen, Set **ret) {
int ndisc_generate_addresses(Link *link, const struct in6_addr *prefix, uint8_t prefixlen, Hashmap **ret) {
return generate_addresses(link, link->network->ndisc_tokens, &NDISC_APP_ID, prefix, prefixlen, ret);
}
int radv_generate_addresses(Link *link, Set *tokens, const struct in6_addr *prefix, uint8_t prefixlen, Set **ret) {
int radv_generate_addresses(Link *link, Set *tokens, const struct in6_addr *prefix, uint8_t prefixlen, Hashmap **ret) {
return generate_addresses(link, tokens, &RADV_APP_ID, prefix, prefixlen, ret);
}
static void ipv6_token_hash_func(const IPv6Token *p, struct siphash *state) {
siphash24_compress_typesafe(p->type, state);
siphash24_compress_typesafe(p->address, state);
id128_hash_func(&p->secret_key, state);
}
int regenerate_address(Address *address, Link *link) {
struct in6_addr masked;
sd_id128_t app_id;
static int ipv6_token_compare_func(const IPv6Token *a, const IPv6Token *b) {
int r;
assert(link);
assert(address);
assert(address->family == AF_INET6);
assert(!address->link && !address->network);
r = CMP(a->type, b->type);
if (r != 0)
return r;
if (!address->token ||
address->token->type != ADDRESS_GENERATION_PREFIXSTABLE)
return 0;
r = memcmp(&a->address, &b->address, sizeof(struct in6_addr));
if (r != 0)
return r;
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();
}
return id128_compare_func(&a->secret_key, &b->secret_key);
}
masked = address->in_addr.in6;
in6_addr_mask(&masked, address->prefixlen);
DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
ipv6_token_hash_ops,
IPv6Token,
ipv6_token_hash_func,
ipv6_token_compare_func,
free);
static int ipv6_token_add(Set **tokens, AddressGenerationType type, const struct in6_addr *addr, const sd_id128_t *secret_key) {
IPv6Token *p;
assert(tokens);
assert(type >= 0 && type < _ADDRESS_GENERATION_TYPE_MAX);
assert(addr);
assert(secret_key);
p = new(IPv6Token, 1);
if (!p)
return -ENOMEM;
*p = (IPv6Token) {
.type = type,
.address = *addr,
.secret_key = *secret_key,
};
return set_ensure_consume(tokens, &ipv6_token_hash_ops, p);
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(

View file

@ -2,13 +2,20 @@
#pragma once
#include "conf-parser.h"
#include "hashmap.h"
#include "in-addr-util.h"
#include "set.h"
typedef struct Address Address;
typedef struct IPv6Token IPv6Token;
typedef struct Link Link;
int dhcp_pd_generate_addresses(Link *link, const struct in6_addr *prefix, Set **ret);
int ndisc_generate_addresses(Link *link, const struct in6_addr *prefix, uint8_t prefixlen, Set **ret);
int radv_generate_addresses(Link *link, Set *tokens, const struct in6_addr *prefix, uint8_t prefixlen, Set **ret);
IPv6Token* ipv6_token_ref(IPv6Token *token);
IPv6Token* ipv6_token_unref(IPv6Token *token);
int dhcp_pd_generate_addresses(Link *link, const struct in6_addr *prefix, Hashmap **ret);
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);

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"
@ -260,6 +262,7 @@ static Address* address_free(Address *address) {
config_section_free(address->section);
free(address->label);
free(address->netlabel);
ipv6_token_unref(address->token);
nft_set_context_clear(&address->nft_set_context);
return mfree(address);
}
@ -608,6 +611,7 @@ int address_dup(const Address *src, Address **ret) {
dest->section = NULL;
dest->link = NULL;
dest->label = NULL;
dest->token = ipv6_token_ref(src->token);
dest->netlabel = NULL;
dest->nft_set_context.sets = NULL;
dest->nft_set_context.n_sets = 0;
@ -800,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);
@ -821,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;
@ -1135,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);
}
}
@ -1845,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);
@ -1886,6 +1941,10 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message,
(void) nft_set_context_dup(&a->nft_set_context, &address->nft_set_context);
address->requested_as_null = a->requested_as_null;
address->callback = a->callback;
ipv6_token_ref(a->token);
ipv6_token_unref(address->token);
address->token = a->token;
}
/* Then, update miscellaneous info. */

View file

@ -10,6 +10,7 @@
#include "hash-funcs.h"
#include "in-addr-util.h"
#include "network-util.h"
#include "networkd-address-generation.h"
#include "networkd-link.h"
#include "networkd-util.h"
#include "time-util.h"
@ -63,6 +64,9 @@ struct Address {
* To control DAD for IPv6 dynamic addresses, set IFA_F_NODAD to flags. */
AddressFamily duplicate_address_detection;
/* Used by address generator. */
IPv6Token *token;
/* Called when address become ready */
address_ready_callback_t callback;

View file

@ -353,14 +353,50 @@ static void log_dhcp_pd_address(Link *link, const Address *address) {
FORMAT_LIFETIME(address->lifetime_preferred_usec));
}
static int dhcp_pd_request_address_one(Address *address, Link *link) {
Address *existing;
assert(address);
assert(link);
log_dhcp_pd_address(link, address);
if (address_get(link, address, &existing) < 0)
link->dhcp_pd_configured = false;
else
address_unmark(existing);
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,
usec_t lifetime_preferred_usec,
usec_t lifetime_valid_usec) {
_cleanup_set_free_ Set *addresses = NULL;
struct in6_addr *a;
int r;
assert(link);
@ -370,13 +406,15 @@ static int dhcp_pd_request_address(
if (!link->network->dhcp_pd_assign)
return 0;
r = dhcp_pd_generate_addresses(link, prefix, &addresses);
_cleanup_hashmap_free_ Hashmap *tokens_by_address = NULL;
r = dhcp_pd_generate_addresses(link, prefix, &tokens_by_address);
if (r < 0)
return log_link_warning_errno(link, r, "Failed to generate addresses for acquired DHCP delegated prefix: %m");
SET_FOREACH(a, addresses) {
IPv6Token *token;
struct in6_addr *a;
HASHMAP_FOREACH_KEY(token, a, tokens_by_address) {
_cleanup_(address_unrefp) Address *address = NULL;
Address *existing;
r = address_new(&address);
if (r < 0)
@ -390,20 +428,13 @@ static int dhcp_pd_request_address(
address->lifetime_valid_usec = lifetime_valid_usec;
SET_FLAG(address->flags, IFA_F_MANAGETEMPADDR, link->network->dhcp_pd_manage_temporary_address);
address->route_metric = link->network->dhcp_pd_route_metric;
log_dhcp_pd_address(link, address);
address->token = ipv6_token_ref(token);
r = free_and_strdup_warn(&address->netlabel, link->network->dhcp_pd_netlabel);
if (r < 0)
return r;
if (address_get(link, address, &existing) < 0)
link->dhcp_pd_configured = false;
else
address_unmark(existing);
r = link_request_address(link, address, &link->dhcp_pd_messages,
dhcp_pd_address_handler, NULL);
r = dhcp_pd_request_address_one(address, link);
if (r < 0)
return log_link_error_errno(link, r, "Failed to request DHCP delegated prefix address: %m");
}

View file

@ -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);

View file

@ -328,20 +328,19 @@ static int ndisc_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Reques
}
static int ndisc_request_address(Address *address, Link *link, sd_ndisc_router *rt) {
struct in6_addr router;
bool is_new;
int r;
assert(address);
assert(link);
assert(rt);
r = sd_ndisc_router_get_sender_address(rt, &router);
if (r < 0)
return r;
if (rt) {
r = sd_ndisc_router_get_sender_address(rt, &address->provider.in6);
if (r < 0)
return r;
address->source = NETWORK_CONFIG_SOURCE_NDISC;
address->provider.in6 = router;
address->source = NETWORK_CONFIG_SOURCE_NDISC;
}
r = free_and_strdup_warn(&address->netlabel, link->network->ndisc_netlabel);
if (r < 0)
@ -378,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;
@ -1029,8 +1050,7 @@ static int ndisc_router_process_hop_limit(Link *link, sd_ndisc_router *rt) {
static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *rt) {
usec_t lifetime_valid_usec, lifetime_preferred_usec;
_cleanup_set_free_ Set *addresses = NULL;
struct in6_addr prefix, *a;
struct in6_addr prefix;
uint8_t prefixlen;
int r;
@ -1068,11 +1088,14 @@ static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *r
if (lifetime_preferred_usec > lifetime_valid_usec)
return 0;
r = ndisc_generate_addresses(link, &prefix, prefixlen, &addresses);
_cleanup_hashmap_free_ Hashmap *tokens_by_address = NULL;
r = ndisc_generate_addresses(link, &prefix, prefixlen, &tokens_by_address);
if (r < 0)
return log_link_warning_errno(link, r, "Failed to generate SLAAC addresses: %m");
SET_FOREACH(a, addresses) {
IPv6Token *token;
struct in6_addr *a;
HASHMAP_FOREACH_KEY(token, a, tokens_by_address) {
_cleanup_(address_unrefp) Address *address = NULL;
r = address_new(&address);
@ -1085,6 +1108,7 @@ static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *r
address->flags = IFA_F_NOPREFIXROUTE|IFA_F_MANAGETEMPADDR;
address->lifetime_valid_usec = lifetime_valid_usec;
address->lifetime_preferred_usec = lifetime_preferred_usec;
address->token = ipv6_token_ref(token);
/* draft-ietf-6man-slaac-renum-07 section 4.2
* https://datatracker.ietf.org/doc/html/draft-ietf-6man-slaac-renum-07#section-4.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);

View file

@ -243,9 +243,6 @@ int link_request_radv_addresses(Link *link) {
return 0;
HASHMAP_FOREACH(p, link->network->prefixes_by_section) {
_cleanup_set_free_ Set *addresses = NULL;
struct in6_addr *a;
if (!p->assign)
continue;
@ -253,11 +250,14 @@ int link_request_radv_addresses(Link *link) {
if (p->prefixlen > 64)
continue;
r = radv_generate_addresses(link, p->tokens, &p->prefix, p->prefixlen, &addresses);
_cleanup_hashmap_free_ Hashmap *tokens_by_address = NULL;
r = radv_generate_addresses(link, p->tokens, &p->prefix, p->prefixlen, &tokens_by_address);
if (r < 0)
return r;
SET_FOREACH(a, addresses) {
IPv6Token *token;
struct in6_addr *a;
HASHMAP_FOREACH_KEY(token, a, tokens_by_address) {
_cleanup_(address_unrefp) Address *address = NULL;
r = address_new(&address);
@ -269,6 +269,7 @@ int link_request_radv_addresses(Link *link) {
address->in_addr.in6 = *a;
address->prefixlen = p->prefixlen;
address->route_metric = p->route_metric;
address->token = ipv6_token_ref(token);
r = link_request_static_address(link, address);
if (r < 0)
@ -279,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;

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);

View file

@ -6,8 +6,6 @@ Name=veth99
IPv6AcceptRA=true
[IPv6AcceptRA]
Token=prefixstable:2002:da8:1::
Token=prefixstable:2002:da8:1::,86b123b969ba4b7eb8b3d8605123525a
# invalid tokens
Token=prefixstable:2002:da8:1::,00000000000000000000000000000000
Token=prefixstable:2002:da8:1::,
@ -17,3 +15,10 @@ Token=prefixstable@
Token=static
Token=static:
Token=static:::
# valid token
Token=prefixstable:2002:da8:1::
Token=prefixstable:2002:da8:1::,86b123b969ba4b7eb8b3d8605123525a
# reset token
Token=
# set token again
Token=prefixstable:2002:da8:1::,86b123b969ba4b7eb8b3d8605123525a

View file

@ -5577,10 +5577,44 @@ class NetworkdRATests(unittest.TestCase, Utilities):
start_networkd()
self.wait_online('veth99:routable', 'veth-peer:degraded')
output = networkctl_status('veth99')
output = check_output('ip -6 address show dev veth99')
print(output)
self.assertIn('2002:da8:1:0:b47e:7975:fc7a:7d6e', output)
self.assertIn('2002:da8:2:0:1034:56ff:fe78:9abc', output) # EUI64
self.assertIn('2002:da8:1:0:b47e:7975:fc7a:7d6e/64', output) # the 1st prefixstable
self.assertIn('2002:da8:2:0:1034:56ff:fe78:9abc/64', output) # EUI64
with open(os.path.join(network_unit_dir, '25-ipv6-prefix-veth-token-prefixstable.network'), mode='a', encoding='utf-8') as f:
f.write('\n[IPv6AcceptRA]\nPrefixAllowList=2002:da8:1:0::/64\n')
networkctl_reload()
self.wait_online('veth99:routable')
output = check_output('ip -6 address show dev veth99')
print(output)
self.assertIn('2002:da8:1:0:b47e:7975:fc7a:7d6e/64', output) # the 1st prefixstable
self.assertNotIn('2002:da8:2:0:1034:56ff:fe78:9abc/64', output) # EUI64
check_output('ip address del 2002:da8:1:0:b47e:7975:fc7a:7d6e/64 dev veth99')
check_output('ip address add 2002:da8:1:0:b47e:7975:fc7a:7d6e/64 dev veth-peer nodad')
networkctl_reconfigure('veth99')
self.wait_online('veth99:routable')
output = check_output('ip -6 address show dev veth99')
print(output)
self.assertNotIn('2002:da8:1:0:b47e:7975:fc7a:7d6e/64', output) # the 1st prefixstable
self.assertIn('2002:da8:1:0:da5d:e50a:43fd:5d0f/64', output) # the 2nd prefixstable
check_output('ip address del 2002:da8:1:0:da5d:e50a:43fd:5d0f/64 dev veth99')
check_output('ip address add 2002:da8:1:0:da5d:e50a:43fd:5d0f/64 dev veth-peer nodad')
networkctl_reconfigure('veth99')
self.wait_online('veth99:routable')
output = check_output('ip -6 address show dev veth99')
print(output)
self.assertNotIn('2002:da8:1:0:b47e:7975:fc7a:7d6e/64', output) # the 1st prefixstable
self.assertNotIn('2002:da8:1:0:da5d:e50a:43fd:5d0f/64', output) # the 2nd prefixstable
self.assertIn('2002:da8:1:0:c7e4:77ec:eb31:1b0d/64', output) # the 3rd prefixstable
def test_ipv6_token_prefixstable_without_address(self):
copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-prefixstable-without-address.network')