summaryrefslogtreecommitdiffstats
path: root/src/network/networkd-address-generation.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/network/networkd-address-generation.c')
-rw-r--r--src/network/networkd-address-generation.c257
1 files changed, 182 insertions, 75 deletions
diff --git a/src/network/networkd-address-generation.c b/src/network/networkd-address-generation.c
index 65f0009..816cdf7 100644
--- a/src/network/networkd-address-generation.c
+++ b/src/network/networkd-address-generation.c
@@ -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);
@@ -121,7 +235,7 @@ static void generate_stable_private_address_one(
if (link->ssid)
siphash24_compress_string(link->ssid, &state);
- siphash24_compress(&dad_counter, sizeof(uint8_t), &state);
+ siphash24_compress_typesafe(dad_counter, &state);
rid = htole64(siphash24_finalize(&state));
@@ -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;
-
- addr = new(struct in6_addr, 1);
- if (!addr)
- return -ENOMEM;
+ if (hashmap_isempty(tokens_by_address)) {
+ AddressGenerationType type;
- 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(&p->type, sizeof(p->type), state);
- siphash24_compress(&p->address, sizeof(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,
- free);
+int regenerate_address(Address *address, Link *link) {
+ struct in6_addr masked;
+ sd_id128_t app_id;
-static int ipv6_token_add(Set **tokens, AddressGenerationType type, const struct in6_addr *addr, const sd_id128_t *secret_key) {
- IPv6Token *p;
+ assert(link);
+ assert(address);
+ assert(address->family == AF_INET6);
+ assert(!address->link && !address->network);
- assert(tokens);
- assert(type >= 0 && type < _ADDRESS_GENERATION_TYPE_MAX);
- assert(addr);
- assert(secret_key);
+ if (!address->token ||
+ address->token->type != ADDRESS_GENERATION_PREFIXSTABLE)
+ return 0;
- p = new(IPv6Token, 1);
- if (!p)
- return -ENOMEM;
+ 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();
+ }
- *p = (IPv6Token) {
- .type = type,
- .address = *addr,
- .secret_key = *secret_key,
- };
+ masked = address->in_addr.in6;
+ in6_addr_mask(&masked, address->prefixlen);
- 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(