diff --git a/man/systemd.network.xml b/man/systemd.network.xml
index 1aa9b132df7..f9e8fa5c258 100644
--- a/man/systemd.network.xml
+++ b/man/systemd.network.xml
@@ -193,6 +193,16 @@
This happens when multicast routing is enabled.
+
+ IPv6LinkLocalAddressGenerationMode=
+
+ Specifies how IPv6 link local address is generated. Takes one of eui64,
+ none, stable-privacy and random.
+ When unset, the kernel's default will be used. Note that if LinkLocalAdressing=
+ not configured as ipv6 then IPv6LinkLocalAddressGenerationMode=
+ is ignored.
+
+
Unmanaged=
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index 32fe86045d6..59e37baa6bf 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -1692,12 +1692,18 @@ static int link_configure_addrgen_mode(Link *link) {
if (!link_ipv6ll_enabled(link))
ipv6ll_mode = IN6_ADDR_GEN_MODE_NONE;
- else if (sysctl_read_ip_property(AF_INET6, link->ifname, "stable_secret", NULL) < 0)
- /* The file may not exist. And even if it exists, when stable_secret is unset,
- * reading the file fails with EIO. */
- ipv6ll_mode = IN6_ADDR_GEN_MODE_EUI64;
- else
- ipv6ll_mode = IN6_ADDR_GEN_MODE_STABLE_PRIVACY;
+ else if (link->network->ipv6_address_gen_mode < 0) {
+ r = sysctl_read_ip_property(AF_INET6, link->ifname, "stable_secret", NULL);
+ if (r < 0) {
+ /* The file may not exist. And even if it exists, when stable_secret is unset,
+ * reading the file fails with EIO. */
+ log_link_debug_errno(link, r, "Failed to read sysctl property stable_secret: %m");
+
+ ipv6ll_mode = IN6_ADDR_GEN_MODE_EUI64;
+ } else
+ ipv6ll_mode = IN6_ADDR_GEN_MODE_STABLE_PRIVACY;
+ } else
+ ipv6ll_mode = link->network->ipv6_address_gen_mode;
r = sd_netlink_message_append_u8(req, IFLA_INET6_ADDR_GEN_MODE, ipv6ll_mode);
if (r < 0)
@@ -4434,6 +4440,16 @@ void link_clean(Link *link) {
link_unref(set_remove(link->manager->dirty_links, link));
}
+static const char* const link_ipv6_address_gen_mode_table[_LINK_IPV6_ADDRESS_GEN_MODE_MAX] = {
+ [LINK_IPV6_ADDRESSS_GEN_MODE_EUI64] = "eui64",
+ [LINK_IPV6_ADDRESSS_GEN_MODE_NONE] = "none",
+ [LINK_IPV6_ADDRESSS_GEN_MODE_STABLE_PRIVACY] = "stable-privacy",
+ [LINK_IPV6_ADDRESSS_GEN_MODE_RANDOM] = "random",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(link_ipv6_address_gen_mode, LinkIPv6AddressGenMode);
+DEFINE_CONFIG_PARSE_ENUM(config_parse_link_ipv6_address_gen_mode, link_ipv6_address_gen_mode, LinkIPv6AddressGenMode, "Failed to parse link IPv6 address generation mode");
+
static const char* const link_state_table[_LINK_STATE_MAX] = {
[LINK_STATE_PENDING] = "pending",
[LINK_STATE_INITIALIZED] = "initialized",
diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h
index 3d07d882cda..e8d67e0d64f 100644
--- a/src/network/networkd-link.h
+++ b/src/network/networkd-link.h
@@ -35,6 +35,15 @@ typedef enum LinkState {
_LINK_STATE_INVALID = -1
} LinkState;
+typedef enum LinkIPv6AddressGenMode {
+ LINK_IPV6_ADDRESSS_GEN_MODE_EUI64 = IN6_ADDR_GEN_MODE_EUI64,
+ LINK_IPV6_ADDRESSS_GEN_MODE_NONE = IN6_ADDR_GEN_MODE_NONE,
+ LINK_IPV6_ADDRESSS_GEN_MODE_STABLE_PRIVACY = IN6_ADDR_GEN_MODE_STABLE_PRIVACY,
+ LINK_IPV6_ADDRESSS_GEN_MODE_RANDOM = IN6_ADDR_GEN_MODE_RANDOM,
+ _LINK_IPV6_ADDRESS_GEN_MODE_MAX,
+ _LINK_IPV6_ADDRESS_GEN_MODE_INVALID = -1
+} LinkIPv6AddressGenMode;
+
typedef struct Manager Manager;
typedef struct Network Network;
typedef struct Address Address;
@@ -209,6 +218,9 @@ int link_stop_clients(Link *link, bool may_keep_dhcp);
const char* link_state_to_string(LinkState s) _const_;
LinkState link_state_from_string(const char *s) _pure_;
+const char* link_ipv6_address_gen_mode_to_string(LinkIPv6AddressGenMode s) _const_;
+LinkIPv6AddressGenMode link_ipv6_address_gen_mode_from_string(const char *s) _pure_;
+
uint32_t link_get_vrf_table(Link *link);
uint32_t link_get_dhcp_route_table(Link *link);
uint32_t link_get_ipv6_accept_ra_route_table(Link *link);
@@ -217,6 +229,8 @@ int link_request_set_nexthop(Link *link);
int link_reconfigure(Link *link, bool force);
+CONFIG_PARSER_PROTOTYPE(config_parse_link_ipv6_address_gen_mode);
+
int log_link_message_full_errno(Link *link, sd_netlink_message *m, int level, int err, const char *msg);
#define log_link_message_error_errno(link, m, err, msg) log_link_message_full_errno(link, m, LOG_ERR, err, msg)
#define log_link_message_warning_errno(link, m, err, msg) log_link_message_full_errno(link, m, LOG_WARNING, err, msg)
diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf
index 3749ff6b8d9..a2dd5479b21 100644
--- a/src/network/networkd-network-gperf.gperf
+++ b/src/network/networkd-network-gperf.gperf
@@ -51,6 +51,7 @@ Link.ARP, config_parse_tristate,
Link.Multicast, config_parse_tristate, 0, offsetof(Network, multicast)
Link.AllMulticast, config_parse_tristate, 0, offsetof(Network, allmulticast)
Link.Unmanaged, config_parse_bool, 0, offsetof(Network, unmanaged)
+Link.IPv6LinkLocalAddressGenerationMode, config_parse_link_ipv6_address_gen_mode, 0, offsetof(Network, ipv6_address_gen_mode)
Link.RequiredForOnline, config_parse_required_for_online, 0, 0
Network.Description, config_parse_string, 0, offsetof(Network, description)
Network.Bridge, config_parse_ifname, 0, offsetof(Network, bridge_name)
diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c
index cd57df6d3db..1b4083febf2 100644
--- a/src/network/networkd-network.c
+++ b/src/network/networkd-network.c
@@ -461,7 +461,7 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
.ipv6_accept_ra_start_dhcp6_client = true,
.keep_configuration = _KEEP_CONFIGURATION_INVALID,
-
+ .ipv6_address_gen_mode = _LINK_IPV6_ADDRESS_GEN_MODE_INVALID,
.can_triple_sampling = -1,
.can_termination = -1,
.ip_service_type = -1,
diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h
index 6811658fcc2..362ef6508f7 100644
--- a/src/network/networkd-network.h
+++ b/src/network/networkd-network.h
@@ -259,6 +259,7 @@ struct Network {
bool configure_without_carrier;
bool ignore_carrier_loss;
KeepConfiguration keep_configuration;
+ LinkIPv6AddressGenMode ipv6_address_gen_mode;
uint32_t iaid;
DUID duid;
diff --git a/test/fuzz/fuzz-network-parser/directives.network b/test/fuzz/fuzz-network-parser/directives.network
index 625842a9ba1..2b951926725 100644
--- a/test/fuzz/fuzz-network-parser/directives.network
+++ b/test/fuzz/fuzz-network-parser/directives.network
@@ -38,6 +38,7 @@ MTUBytes=
Multicast=
MACAddress=
Group=
+IPv6LinkLocalAddressGenerationMode=
[BridgeFDB]
VLANId=
MACAddress=