]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
network: make Token=prefixstable optionally take secret key
authorYu Watanabe <watanabe.yu+github@gmail.com>
Sat, 13 Nov 2021 04:13:48 +0000 (13:13 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Sun, 14 Nov 2021 04:29:40 +0000 (13:29 +0900)
Closes #21345.

man/systemd.network.xml
src/network/networkd-address-generation.c

index eecdeb3e41e6df7b349df9de44e425add87cba12..1dd4d150103bf56540da105ae547f3693b241726 100644 (file)
@@ -2223,7 +2223,7 @@ Table=1234</programlisting></para>
               </listitem>
             </varlistentry>
             <varlistentry>
-              <term><option>prefixstable[:<replaceable>ADDRESS</replaceable>]</option></term>
+              <term><option>prefixstable[:<replaceable>ADDRESS</replaceable>][,<replaceable>UUID</replaceable>]</option></term>
               <listitem>
                 <para>
                   The algorithm specified in
@@ -2233,6 +2233,19 @@ Table=1234</programlisting></para>
                   then an interface identifier is generated only when a prefix received in an RA
                   message matches the supplied address.
                 </para>
+                <para>
+                  This mode can also optionally take a non-null UUID in the format which
+                  <function>sd_id128_from_string()</function> accepts, e.g.
+                  <literal>86b123b969ba4b7eb8b3d8605123525a</literal> or
+                  <literal>86b123b9-69ba-4b7e-b8b3-d8605123525a</literal>. 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
+                  <citerefentry><refentrytitle>sd-id128</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+                  <citerefentry><refentrytitle>sd_id128_from_string</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+                  and
+                  <citerefentry><refentrytitle>sd_id128_get_machine</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+                </para>
                 <para>
                   Note that the <literal>prefixstable</literal> algorithm uses both the interface
                   name and MAC address as input to the hash to compute the interface identifier, so
index 2d574ee78bc7e9618a5e2c9e9aa96da9b829738e..0d7d3ee6534a349e533bc39e0e2ed48238f98cc0 100644 (file)
@@ -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();