mirror of
https://github.com/systemd/systemd
synced 2024-10-15 12:34:37 +00:00
Merge pull request #32166 from yuwata/network-rfc7217-dad
network: generate another address on conflict
This commit is contained in:
commit
bf5e509840
|
@ -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))
|
||||
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(
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 (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;
|
||||
}
|
||||
|
||||
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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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')
|
||||
|
|
Loading…
Reference in a new issue