From f2a3a133ecda7b6b6727f5836fe73f0b2a52401e Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sat, 13 Nov 2021 13:13:48 +0900 Subject: [PATCH] network: make Token=prefixstable optionally take secret key Closes #21345. --- man/systemd.network.xml | 15 +++- src/network/networkd-address-generation.c | 92 +++++++++++++++++------ 2 files changed, 84 insertions(+), 23 deletions(-) diff --git a/man/systemd.network.xml b/man/systemd.network.xml index eecdeb3e41..1dd4d15010 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -2223,7 +2223,7 @@ Table=1234 - + The algorithm specified in @@ -2233,6 +2233,19 @@ Table=1234 then an interface identifier is generated only when a prefix received in an RA message matches the supplied address. + + This mode can also optionally take a non-null UUID in the format which + sd_id128_from_string() accepts, e.g. + 86b123b969ba4b7eb8b3d8605123525a or + 86b123b9-69ba-4b7e-b8b3-d8605123525a. If a UUID is specified, the + value is used as the secret key to generate interface identifiers. If not specified, + then an application specific ID generated with the system's machine-ID will be used + as the secret key. See + sd-id1283, + sd_id128_from_string3, + and + sd_id128_get_machine3, + Note that the prefixstable algorithm uses both the interface name and MAC address as input to the hash to compute the interface identifier, so diff --git a/src/network/networkd-address-generation.c b/src/network/networkd-address-generation.c index 2d574ee78b..0d7d3ee653 100644 --- a/src/network/networkd-address-generation.c +++ b/src/network/networkd-address-generation.c @@ -4,6 +4,7 @@ #include "sd-id128.h" +#include "id128-util.h" #include "memory-util.h" #include "networkd-address-generation.h" #include "networkd-link.h" @@ -35,6 +36,7 @@ typedef enum AddressGenerationType { typedef struct IPv6Token { AddressGenerationType type; struct in6_addr address; + sd_id128_t secret_key; } IPv6Token; static void generate_eui64_address(const Link *link, const struct in6_addr *prefix, struct in6_addr *ret) { @@ -117,29 +119,35 @@ static void generate_stable_private_address_one( static int generate_stable_private_address( Link *link, const sd_id128_t *app_id, + const sd_id128_t *secret_key, const struct in6_addr *prefix, struct in6_addr *ret) { + sd_id128_t secret_machine_key; struct in6_addr addr; - sd_id128_t secret_key; uint8_t i; int r; assert(link); assert(app_id); + assert(secret_key); assert(prefix); assert(ret); - r = sd_id128_get_machine_app_specific(*app_id, &secret_key); - if (r < 0) - return log_link_debug_errno(link, r, "Failed to generate secret key for IPv6 stable private address: %m"); + if (sd_id128_is_null(*secret_key)) { + r = sd_id128_get_machine_app_specific(*app_id, &secret_machine_key); + if (r < 0) + return log_link_debug_errno(link, r, "Failed to generate secret key for IPv6 stable private address: %m"); + + secret_key = &secret_machine_key; + } /* While this loop uses dad_counter and a retry limit as specified in RFC 7217, the loop does * not actually attempt Duplicate Address Detection; the counter will be incremented only when * the address generation algorithm produces an invalid address, and the loop may exit with an * address which ends up being unusable due to duplication on the link. */ for (i = 0; i < DAD_CONFLICTS_IDGEN_RETRIES_RFC7217; i++) { - generate_stable_private_address_one(link, &secret_key, prefix, i, &addr); + generate_stable_private_address_one(link, secret_key, prefix, i, &addr); if (stable_private_address_is_valid(&addr)) break; @@ -192,7 +200,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, &masked, &addr) < 0) + if (generate_stable_private_address(link, app_id, &j->secret_key, &masked, &addr) < 0) continue; break; @@ -244,6 +252,7 @@ int radv_generate_addresses(Link *link, Set *tokens, const struct in6_addr *pref 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) { @@ -253,7 +262,11 @@ static int ipv6_token_compare_func(const IPv6Token *a, const IPv6Token *b) { if (r != 0) return r; - return memcmp(&a->address, &b->address, sizeof(struct in6_addr)); + 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( @@ -263,12 +276,13 @@ DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR( ipv6_token_compare_func, free); -static int ipv6_token_add(Set **tokens, AddressGenerationType type, const struct in6_addr *addr) { +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) @@ -277,6 +291,7 @@ static int ipv6_token_add(Set **tokens, AddressGenerationType type, const struct *p = (IPv6Token) { .type = type, .address = *addr, + .secret_key = *secret_key, }; return set_ensure_consume(tokens, &ipv6_token_hash_ops, p); @@ -294,10 +309,12 @@ int config_parse_address_generation_type( void *data, void *userdata) { + _cleanup_free_ char *addr_alloc = NULL; + sd_id128_t secret_key = SD_ID128_NULL; union in_addr_union buffer = {}; AddressGenerationType type; Set **tokens = data; - const char *p; + const char *addr; int r; assert(filename); @@ -310,33 +327,64 @@ int config_parse_address_generation_type( return 0; } - if ((p = startswith(rvalue, "prefixstable"))) { + if ((addr = startswith(rvalue, "prefixstable"))) { + const char *comma; + type = ADDRESS_GENERATION_PREFIXSTABLE; - if (*p == ':') - p++; - else if (*p == '\0') - p = NULL; - else { + if (*addr == ':') { + addr++; + + comma = strchr(addr, ','); + if (comma) { + addr_alloc = strndup(addr, comma - addr); + if (!addr_alloc) + return log_oom(); + + addr = addr_alloc; + } + } else if (*addr == ',') { + comma = addr; + addr = NULL; + } else if (*addr == '\0') { + comma = NULL; + addr = NULL; + } else { log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid IPv6 token mode in %s=, ignoring assignment: %s", lvalue, rvalue); return 0; } + if (comma) { + r = sd_id128_from_string(comma + 1, &secret_key); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse secret key in %s=, ignoring assignment: %s", + lvalue, rvalue); + return 0; + } + if (sd_id128_is_null(secret_key)) { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "Secret key in %s= cannot be null, ignoring assignment: %s", + lvalue, rvalue); + return 0; + } + } + } else if (streq(rvalue, "eui64")) { type = ADDRESS_GENERATION_EUI64; - p = NULL; + addr = NULL; } else { type = ADDRESS_GENERATION_STATIC; - p = startswith(rvalue, "static:"); - if (!p) - p = rvalue; + addr = startswith(rvalue, "static:"); + if (!addr) + addr = rvalue; } - if (p) { - r = in_addr_from_string(AF_INET6, p, &buffer); + if (addr) { + r = in_addr_from_string(AF_INET6, addr, &buffer); if (r < 0) { log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse IP address in %s=, ignoring assignment: %s", @@ -371,7 +419,7 @@ int config_parse_address_generation_type( assert_not_reached(); } - r = ipv6_token_add(tokens, type, &buffer.in6); + r = ipv6_token_add(tokens, type, &buffer.in6, &secret_key); if (r < 0) return log_oom();