]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
network: attempt to trigger kernel IPv6LL address generation
authorJoerie de Gram <j.de.gram@gmail.com>
Tue, 8 Feb 2022 13:56:26 +0000 (22:56 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Thu, 10 Feb 2022 08:35:41 +0000 (17:35 +0900)
Try to ensure kernel IPv6 link local address generation occurs by
setting the per-if addr_gen_mode sysctl when the link is already up,
instead of the netlink interface (IFLA_INET6_ADDR_GEN_MODE).

The netlink setting is sufficient in cases where the interface is not
yet up when networkd configures an interface - bringing the interface
up will trigger in-kernel address generation.

If the interface is already up, yet the interface has no IPv6LL assigned
setting IFLA_INET6_ADDR_GEN_MODE has no effect.

Writing the addr_gen_mode sysctl is a best effort attempt at triggering
address generation regardless of interface state because it also works
in cases where the interface is already up.

Fixes #22424.

src/network/networkd-ipv6ll.c
src/network/networkd-ipv6ll.h
src/network/networkd-setlink.c

index 992be2fca68deac6b836a2a6413c872f69e00563..79cf679daf5660888a28f2e713e497254dc8715b 100644 (file)
@@ -223,6 +223,16 @@ int link_set_ipv6ll_stable_secret(Link *link) {
         return sysctl_write_ip_property(AF_INET6, link->ifname, "stable_secret", str);
 }
 
+int link_set_ipv6ll_addrgen_mode(Link *link, IPv6LinkLocalAddressGenMode mode) {
+        assert(link);
+        assert(mode >= 0 && mode < _IPV6_LINK_LOCAL_ADDRESS_GEN_MODE_MAX);
+
+        if (mode == link->ipv6ll_address_gen_mode)
+                return 0;
+
+        return sysctl_write_ip_property_uint32(AF_INET6, link->ifname, "addr_gen_mode", mode);
+}
+
 static const char* const ipv6_link_local_address_gen_mode_table[_IPV6_LINK_LOCAL_ADDRESS_GEN_MODE_MAX] = {
         [IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_EUI64]          = "eui64",
         [IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_NONE]           = "none",
index ac7a7d3e3e247a22a71efc6e40c792ebc910e0ee..a9763debb1abf6efcbd9b373df8ae209804ddfeb 100644 (file)
@@ -29,6 +29,7 @@ int ipv6ll_addrgen_mode_fill_message(sd_netlink_message *message, IPv6LinkLocalA
 int link_update_ipv6ll_addrgen_mode(Link *link, sd_netlink_message *message);
 
 int link_set_ipv6ll_stable_secret(Link *link);
+int link_set_ipv6ll_addrgen_mode(Link *link, IPv6LinkLocalAddressGenMode mode);
 
 const char* ipv6_link_local_address_gen_mode_to_string(IPv6LinkLocalAddressGenMode s) _const_;
 IPv6LinkLocalAddressGenMode ipv6_link_local_address_gen_mode_from_string(const char *s) _pure_;
index 7540f6f60fadb432bd72db219524002b7bac621f..846528eb44583ca92a01a830b351a211a902f328 100644 (file)
@@ -688,6 +688,19 @@ int link_request_to_set_addrgen_mode(Link *link) {
         if (mode == link->ipv6ll_address_gen_mode)
                 return 0;
 
+        /* If the link is already up, then changing the mode by netlink does not take effect until the
+         * link goes down. Hence, we need to reset the interface. However, setting the mode by sysctl
+         * does not need that. Let's use the sysctl interface when the link is already up.
+         * See also issue #22424. */
+        if (mode != IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_NONE &&
+            FLAGS_SET(link->flags, IFF_UP)) {
+                r = link_set_ipv6ll_addrgen_mode(link, mode);
+                if (r < 0)
+                        log_link_warning_errno(link, r, "Cannot set IPv6 address generation mode, ignoring: %m");
+
+                return 0;
+        }
+
         r = link_request_set_link(link, SET_LINK_ADDRESS_GENERATION_MODE, link_set_addrgen_mode_handler, &req);
         if (r < 0)
                 return r;