diff --git a/man/systemd.network.xml b/man/systemd.network.xml
index 1459ba83d9..77ed77918c 100644
--- a/man/systemd.network.xml
+++ b/man/systemd.network.xml
@@ -2729,6 +2729,16 @@ Token=prefixstable:2002:da8:1::
+
+ Token=
+
+ Specifies an optional address generation mode for assigning an address in each
+ prefix. This accepts the same syntax as Token= in the [IPv6AcceptRA]
+ section. If Assign= is set to false, then this setting will be ignored.
+ Defaults to unset, which means the EUI-64 algorithm will be used.
+
+
+
RouteMetric=
diff --git a/src/network/networkd-address-generation.c b/src/network/networkd-address-generation.c
index bea765a3b0..04b80dee86 100644
--- a/src/network/networkd-address-generation.c
+++ b/src/network/networkd-address-generation.c
@@ -22,6 +22,7 @@
#define DHCP6PD_APP_ID SD_ID128_MAKE(fb,b9,37,ca,4a,ed,4a,4d,b0,70,7f,aa,71,c0,c9,85)
#define NDISC_APP_ID SD_ID128_MAKE(13,ac,81,a7,d5,3f,49,78,92,79,5d,0c,29,3a,bc,7e)
+#define RADV_APP_ID SD_ID128_MAKE(1f,1e,90,c8,5c,78,4f,dc,8e,61,2d,59,0d,53,c1,25)
typedef enum AddressGenerationType {
ADDRESS_GENERATION_EUI64,
@@ -236,6 +237,10 @@ int ndisc_generate_addresses(Link *link, const struct in6_addr *prefix, uint8_t
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) {
+ 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);
diff --git a/src/network/networkd-address-generation.h b/src/network/networkd-address-generation.h
index 7e8b01e0a8..8aa5d4d3f5 100644
--- a/src/network/networkd-address-generation.h
+++ b/src/network/networkd-address-generation.h
@@ -11,5 +11,6 @@ void generate_eui64_address(const Link *link, const struct in6_addr *prefix, str
int dhcp6_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);
CONFIG_PARSER_PROTOTYPE(config_parse_address_generation_type);
diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf
index c8b1521d90..b670038d2a 100644
--- a/src/network/networkd-network-gperf.gperf
+++ b/src/network/networkd-network-gperf.gperf
@@ -348,6 +348,7 @@ IPv6Prefix.ValidLifetimeSec, config_parse_prefix_lifetime,
IPv6Prefix.PreferredLifetimeSec, config_parse_prefix_lifetime, 0, 0
IPv6Prefix.Assign, config_parse_prefix_assign, 0, 0
IPv6Prefix.RouteMetric, config_parse_prefix_metric, 0, 0
+IPv6Prefix.Token, config_parse_prefix_token, 0, 0
IPv6RoutePrefix.Route, config_parse_route_prefix, 0, 0
IPv6RoutePrefix.LifetimeSec, config_parse_route_prefix_lifetime, 0, 0
LLDP.MUDURL, config_parse_mud_url, 0, offsetof(Network, lldp_mudurl)
diff --git a/src/network/networkd-radv.c b/src/network/networkd-radv.c
index b9ba0ff9ba..34e6a41c34 100644
--- a/src/network/networkd-radv.c
+++ b/src/network/networkd-radv.c
@@ -69,6 +69,7 @@ Prefix *prefix_free(Prefix *prefix) {
network_config_section_free(prefix->section);
sd_radv_prefix_unref(prefix->radv_prefix);
+ set_free(prefix->tokens);
return mfree(prefix);
}
@@ -204,8 +205,8 @@ int link_request_radv_addresses(Link *link) {
return 0;
HASHMAP_FOREACH(p, link->network->prefixes_by_section) {
- _cleanup_(address_freep) Address *address = NULL;
- struct in6_addr prefix;
+ _cleanup_set_free_ Set *addresses = NULL;
+ struct in6_addr prefix, *a;
uint8_t prefixlen;
if (!p->assign)
@@ -215,7 +216,7 @@ int link_request_radv_addresses(Link *link) {
if (r < 0)
return r;
- /* generate_eui64_address() below requires the prefix length <= 64. */
+ /* radv_generate_addresses() below requires the prefix length <= 64. */
if (prefixlen > 64) {
_cleanup_free_ char *str = NULL;
@@ -226,20 +227,27 @@ int link_request_radv_addresses(Link *link) {
continue;
}
- r = address_new(&address);
- if (r < 0)
- return log_oom();
-
- generate_eui64_address(link, &prefix, &address->in_addr.in6);
-
- address->source = NETWORK_CONFIG_SOURCE_STATIC;
- address->family = AF_INET6;
- address->prefixlen = prefixlen;
- address->route_metric = p->route_metric;
-
- r = link_request_static_address(link, TAKE_PTR(address), true);
+ r = radv_generate_addresses(link, p->tokens, &prefix, prefixlen, &addresses);
if (r < 0)
return r;
+
+ SET_FOREACH(a, addresses) {
+ _cleanup_(address_freep) Address *address = NULL;
+
+ r = address_new(&address);
+ if (r < 0)
+ return -ENOMEM;
+
+ address->source = NETWORK_CONFIG_SOURCE_STATIC;
+ address->family = AF_INET6;
+ address->in_addr.in6 = *a;
+ address->prefixlen = prefixlen;
+ address->route_metric = p->route_metric;
+
+ r = link_request_static_address(link, TAKE_PTR(address), true);
+ if (r < 0)
+ return r;
+ }
}
return 0;
@@ -875,6 +883,41 @@ int config_parse_prefix_metric(
return 0;
}
+int config_parse_prefix_token(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ _cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL;
+ Network *network = userdata;
+ int r;
+
+ assert(filename);
+ assert(section);
+ assert(lvalue);
+ assert(rvalue);
+ assert(userdata);
+
+ r = prefix_new_static(network, filename, section_line, &p);
+ if (r < 0)
+ return log_oom();
+
+ r = config_parse_address_generation_type(unit, filename, line, section, section_line,
+ lvalue, ltype, rvalue, &p->tokens, userdata);
+ if (r < 0)
+ return r;
+
+ TAKE_PTR(p);
+ return 0;
+}
+
int config_parse_route_prefix(
const char *unit,
const char *filename,
diff --git a/src/network/networkd-radv.h b/src/network/networkd-radv.h
index aec3d3386f..5c869d8623 100644
--- a/src/network/networkd-radv.h
+++ b/src/network/networkd-radv.h
@@ -35,6 +35,7 @@ typedef struct Prefix {
bool assign;
uint32_t route_metric;
+ Set *tokens;
} Prefix;
typedef struct RoutePrefix {
@@ -70,6 +71,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_prefix_flags);
CONFIG_PARSER_PROTOTYPE(config_parse_prefix_lifetime);
CONFIG_PARSER_PROTOTYPE(config_parse_prefix_assign);
CONFIG_PARSER_PROTOTYPE(config_parse_prefix_metric);
+CONFIG_PARSER_PROTOTYPE(config_parse_prefix_token);
CONFIG_PARSER_PROTOTYPE(config_parse_radv_dns);
CONFIG_PARSER_PROTOTYPE(config_parse_radv_search_domains);
CONFIG_PARSER_PROTOTYPE(config_parse_route_prefix);
diff --git a/test/fuzz/fuzz-network-parser/directives.network b/test/fuzz/fuzz-network-parser/directives.network
index 9b5ecccedb..ea02d4c640 100644
--- a/test/fuzz/fuzz-network-parser/directives.network
+++ b/test/fuzz/fuzz-network-parser/directives.network
@@ -247,6 +247,7 @@ PreferredLifetimeSec=
AddressAutoconfiguration=
ValidLifetimeSec=
Assign=
+Token=
RouteMetric=
[IPv6RoutePrefix]
Route=