From de2f372ea2d3f01131b684814580dec2d28a606a Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 8 Feb 2022 21:56:02 +0900 Subject: [PATCH 1/8] network: move ipv6ll related functions to networkd-ipv6ll.[ch] --- src/network/meson.build | 2 + src/network/networkd-ipv6ll.c | 89 ++++++++++++++++++++++++ src/network/networkd-ipv6ll.h | 28 ++++++++ src/network/networkd-link.c | 61 ---------------- src/network/networkd-link.h | 3 +- src/network/networkd-network-gperf.gperf | 1 + src/network/networkd-network.c | 10 --- src/network/networkd-network.h | 14 +--- 8 files changed, 122 insertions(+), 86 deletions(-) create mode 100644 src/network/networkd-ipv6ll.c create mode 100644 src/network/networkd-ipv6ll.h diff --git a/src/network/meson.build b/src/network/meson.build index 456599c5d9..35adad42c0 100644 --- a/src/network/meson.build +++ b/src/network/meson.build @@ -93,6 +93,8 @@ sources = files(''' networkd-ipv4ll.h networkd-ipv6-proxy-ndp.c networkd-ipv6-proxy-ndp.h + networkd-ipv6ll.c + networkd-ipv6ll.h networkd-json.c networkd-json.h networkd-link-bus.c diff --git a/src/network/networkd-ipv6ll.c b/src/network/networkd-ipv6ll.c new file mode 100644 index 0000000000..016df4a1b0 --- /dev/null +++ b/src/network/networkd-ipv6ll.c @@ -0,0 +1,89 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include +#include + +#include "in-addr-util.h" +#include "networkd-address.h" +#include "networkd-ipv6ll.h" +#include "networkd-link.h" +#include "networkd-network.h" +#include "networkd-util.h" +#include "socket-util.h" +#include "string-table.h" +#include "strv.h" + +bool link_ipv6ll_enabled(Link *link) { + assert(link); + + if (!socket_ipv6_is_supported()) + return false; + + if (link->flags & IFF_LOOPBACK) + return false; + + if (!link->network) + return false; + + if (link->iftype == ARPHRD_CAN) + return false; + + if (STRPTR_IN_SET(link->kind, "vrf", "wireguard", "ipip", "gre", "sit", "vti", "nlmon")) + return false; + + if (link->network->bond) + return false; + + return link->network->link_local & ADDRESS_FAMILY_IPV6; +} + +bool link_may_have_ipv6ll(Link *link) { + assert(link); + + /* + * This is equivalent to link_ipv6ll_enabled() for non-WireGuard interfaces. + * + * For WireGuard interface, the kernel does not assign any IPv6LL addresses, but we can assign + * it manually. It is necessary to set an IPv6LL address manually to run NDisc or RADV on + * WireGuard interface. Note, also Multicast=yes must be set. See #17380. + * + * TODO: May be better to introduce GenerateIPv6LinkLocalAddress= setting, and use algorithms + * used in networkd-address-generation.c + */ + + if (link_ipv6ll_enabled(link)) + return true; + + /* IPv6LL address can be manually assigned on WireGuard interface. */ + if (streq_ptr(link->kind, "wireguard")) { + Address *a; + + if (!link->network) + return false; + + ORDERED_HASHMAP_FOREACH(a, link->network->addresses_by_section) { + if (a->family != AF_INET6) + continue; + if (in6_addr_is_set(&a->in_addr_peer.in6)) + continue; + if (in6_addr_is_link_local(&a->in_addr.in6)) + return true; + } + } + + return false; +} + +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", + [IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_STABLE_PRIVACY] = "stable-privacy", + [IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_RANDOM] = "random", +}; + +DEFINE_STRING_TABLE_LOOKUP(ipv6_link_local_address_gen_mode, IPv6LinkLocalAddressGenMode); +DEFINE_CONFIG_PARSE_ENUM( + config_parse_ipv6_link_local_address_gen_mode, + ipv6_link_local_address_gen_mode, + IPv6LinkLocalAddressGenMode, + "Failed to parse IPv6 link local address generation mode"); diff --git a/src/network/networkd-ipv6ll.h b/src/network/networkd-ipv6ll.h new file mode 100644 index 0000000000..0a1c3da5fd --- /dev/null +++ b/src/network/networkd-ipv6ll.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include +#include +#include + +#include "conf-parser.h" +#include "macro.h" + +typedef struct Link Link; + +typedef enum IPv6LinkLocalAddressGenMode { + IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_EUI64 = IN6_ADDR_GEN_MODE_EUI64, + IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_NONE = IN6_ADDR_GEN_MODE_NONE, + IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_STABLE_PRIVACY = IN6_ADDR_GEN_MODE_STABLE_PRIVACY, + IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_RANDOM = IN6_ADDR_GEN_MODE_RANDOM, + _IPV6_LINK_LOCAL_ADDRESS_GEN_MODE_MAX, + _IPV6_LINK_LOCAL_ADDRESS_GEN_MODE_INVALID = -EINVAL, +} IPv6LinkLocalAddressGenMode; + +bool link_ipv6ll_enabled(Link *link); +bool link_may_have_ipv6ll(Link *link); + +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_; + +CONFIG_PARSER_PROTOTYPE(config_parse_ipv6_link_local_address_gen_mode); diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index addf0d4afe..5f5f413fff 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -105,67 +105,6 @@ bool link_ipv4ll_enabled(Link *link) { return link->network->link_local & ADDRESS_FAMILY_IPV4; } -bool link_ipv6ll_enabled(Link *link) { - assert(link); - - if (!socket_ipv6_is_supported()) - return false; - - if (link->flags & IFF_LOOPBACK) - return false; - - if (!link->network) - return false; - - if (link->iftype == ARPHRD_CAN) - return false; - - if (STRPTR_IN_SET(link->kind, "vrf", "wireguard", "ipip", "gre", "sit", "vti", "nlmon")) - return false; - - if (link->network->bond) - return false; - - return link->network->link_local & ADDRESS_FAMILY_IPV6; -} - -bool link_may_have_ipv6ll(Link *link) { - assert(link); - - /* - * This is equivalent to link_ipv6ll_enabled() for non-WireGuard interfaces. - * - * For WireGuard interface, the kernel does not assign any IPv6LL addresses, but we can assign - * it manually. It is necessary to set an IPv6LL address manually to run NDisc or RADV on - * WireGuard interface. Note, also Multicast=yes must be set. See #17380. - * - * TODO: May be better to introduce GenerateIPv6LinkLocalAddress= setting, and use algorithms - * used in networkd-address-generation.c - */ - - if (link_ipv6ll_enabled(link)) - return true; - - /* IPv6LL address can be manually assigned on WireGuard interface. */ - if (streq_ptr(link->kind, "wireguard")) { - Address *a; - - if (!link->network) - return false; - - ORDERED_HASHMAP_FOREACH(a, link->network->addresses_by_section) { - if (a->family != AF_INET6) - continue; - if (in6_addr_is_set(&a->in_addr_peer.in6)) - continue; - if (in6_addr_is_link_local(&a->in_addr.in6)) - return true; - } - } - - return false; -} - bool link_ipv6_enabled(Link *link) { assert(link); diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h index 7ccb31df79..8b3f109657 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -21,6 +21,7 @@ #include "log-link.h" #include "netif-util.h" #include "network-util.h" +#include "networkd-ipv6ll.h" #include "networkd-util.h" #include "ordered-set.h" #include "resolve-util.h" @@ -219,8 +220,6 @@ static inline bool link_has_carrier(Link *link) { } bool link_ipv6_enabled(Link *link); -bool link_ipv6ll_enabled(Link *link); -bool link_may_have_ipv6ll(Link *link); int link_ipv6ll_gained(Link *link); bool link_ipv4ll_enabled(Link *link); diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index 08e3f13f5a..f66631a7db 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -22,6 +22,7 @@ _Pragma("GCC diagnostic ignored \"-Wimplicit-fallthrough\"") #include "networkd-dhcp6.h" #include "networkd-ipv4ll.h" #include "networkd-ipv6-proxy-ndp.h" +#include "networkd-ipv6ll.h" #include "networkd-lldp-tx.h" #include "networkd-ndisc.h" #include "networkd-network.h" diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index edcd68d616..faed708818 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -1385,16 +1385,6 @@ static const char* const keep_configuration_table[_KEEP_CONFIGURATION_MAX] = { DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(keep_configuration, KeepConfiguration, KEEP_CONFIGURATION_YES); -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", - [IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_STABLE_PRIVACY] = "stable-privacy", - [IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_RANDOM] = "random", -}; - -DEFINE_STRING_TABLE_LOOKUP(ipv6_link_local_address_gen_mode, IPv6LinkLocalAddressGenMode); -DEFINE_CONFIG_PARSE_ENUM(config_parse_ipv6_link_local_address_gen_mode, ipv6_link_local_address_gen_mode, IPv6LinkLocalAddressGenMode, "Failed to parse IPv6 link local address generation mode"); - static const char* const activation_policy_table[_ACTIVATION_POLICY_MAX] = { [ACTIVATION_POLICY_UP] = "up", [ACTIVATION_POLICY_ALWAYS_UP] = "always-up", diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index f7eb37aced..19d73ed825 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -18,6 +18,7 @@ #include "networkd-dhcp-common.h" #include "networkd-dhcp4.h" #include "networkd-dhcp6.h" +#include "networkd-ipv6ll.h" #include "networkd-lldp-rx.h" #include "networkd-ndisc.h" #include "networkd-radv.h" @@ -38,15 +39,6 @@ typedef enum KeepConfiguration { _KEEP_CONFIGURATION_INVALID = -EINVAL, } KeepConfiguration; -typedef enum IPv6LinkLocalAddressGenMode { - IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_EUI64 = IN6_ADDR_GEN_MODE_EUI64, - IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_NONE = IN6_ADDR_GEN_MODE_NONE, - IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_STABLE_PRIVACY = IN6_ADDR_GEN_MODE_STABLE_PRIVACY, - IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_RANDOM = IN6_ADDR_GEN_MODE_RANDOM, - _IPV6_LINK_LOCAL_ADDRESS_GEN_MODE_MAX, - _IPV6_LINK_LOCAL_ADDRESS_GEN_MODE_INVALID = -EINVAL, -} IPv6LinkLocalAddressGenMode; - typedef enum ActivationPolicy { ACTIVATION_POLICY_UP, ACTIVATION_POLICY_ALWAYS_UP, @@ -385,7 +377,6 @@ CONFIG_PARSER_PROTOTYPE(config_parse_ntp); CONFIG_PARSER_PROTOTYPE(config_parse_required_for_online); CONFIG_PARSER_PROTOTYPE(config_parse_required_family_for_online); CONFIG_PARSER_PROTOTYPE(config_parse_keep_configuration); -CONFIG_PARSER_PROTOTYPE(config_parse_ipv6_link_local_address_gen_mode); CONFIG_PARSER_PROTOTYPE(config_parse_activation_policy); CONFIG_PARSER_PROTOTYPE(config_parse_link_group); CONFIG_PARSER_PROTOTYPE(config_parse_ignore_carrier_loss); @@ -395,8 +386,5 @@ const struct ConfigPerfItem* network_network_gperf_lookup(const char *key, GPERF const char* keep_configuration_to_string(KeepConfiguration i) _const_; KeepConfiguration keep_configuration_from_string(const char *s) _pure_; -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_; - const char* activation_policy_to_string(ActivationPolicy i) _const_; ActivationPolicy activation_policy_from_string(const char *s) _pure_; From 80f2647dfbc694a17dbbd1afc1392fc7ed6600c1 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 8 Feb 2022 22:16:38 +0900 Subject: [PATCH 2/8] network: introduce two helper functions for setting IPv6LL address generation mode --- src/network/networkd-ipv6ll.c | 44 ++++++++++++++++++++++++++++++++++ src/network/networkd-ipv6ll.h | 5 ++++ src/network/networkd-setlink.c | 29 +++------------------- 3 files changed, 52 insertions(+), 26 deletions(-) diff --git a/src/network/networkd-ipv6ll.c b/src/network/networkd-ipv6ll.c index 016df4a1b0..0ff28761f2 100644 --- a/src/network/networkd-ipv6ll.c +++ b/src/network/networkd-ipv6ll.c @@ -74,6 +74,50 @@ bool link_may_have_ipv6ll(Link *link) { return false; } +IPv6LinkLocalAddressGenMode link_get_ipv6ll_addrgen_mode(Link *link) { + assert(link); + + if (!link_ipv6ll_enabled(link)) + return IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_NONE; + + if (link->network->ipv6ll_address_gen_mode >= 0) + return link->network->ipv6ll_address_gen_mode; + + if (in6_addr_is_set(&link->network->ipv6ll_stable_secret)) + return IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_STABLE_PRIVACY; + + return IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_EUI64; +} + +int ipv6ll_addrgen_mode_fill_message(sd_netlink_message *message, IPv6LinkLocalAddressGenMode mode) { + int r; + + assert(message); + assert(mode >= 0 && mode < _IPV6_LINK_LOCAL_ADDRESS_GEN_MODE_MAX); + + r = sd_netlink_message_open_container(message, IFLA_AF_SPEC); + if (r < 0) + return r; + + r = sd_netlink_message_open_container(message, AF_INET6); + if (r < 0) + return r; + + r = sd_netlink_message_append_u8(message, IFLA_INET6_ADDR_GEN_MODE, mode); + if (r < 0) + return r; + + r = sd_netlink_message_close_container(message); + if (r < 0) + return r; + + r = sd_netlink_message_close_container(message); + if (r < 0) + return r; + + return 0; +} + 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", diff --git a/src/network/networkd-ipv6ll.h b/src/network/networkd-ipv6ll.h index 0a1c3da5fd..4f63c71fb5 100644 --- a/src/network/networkd-ipv6ll.h +++ b/src/network/networkd-ipv6ll.h @@ -5,6 +5,8 @@ #include #include +#include "sd-netlink.h" + #include "conf-parser.h" #include "macro.h" @@ -22,6 +24,9 @@ typedef enum IPv6LinkLocalAddressGenMode { bool link_ipv6ll_enabled(Link *link); bool link_may_have_ipv6ll(Link *link); +IPv6LinkLocalAddressGenMode link_get_ipv6ll_addrgen_mode(Link *link); +int ipv6ll_addrgen_mode_fill_message(sd_netlink_message *message, 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_; diff --git a/src/network/networkd-setlink.c b/src/network/networkd-setlink.c index 3fbc910aa2..514833e420 100644 --- a/src/network/networkd-setlink.c +++ b/src/network/networkd-setlink.c @@ -228,23 +228,7 @@ static int link_configure_fill_message( switch (op) { case SET_LINK_ADDRESS_GENERATION_MODE: - r = sd_netlink_message_open_container(req, IFLA_AF_SPEC); - if (r < 0) - return r; - - r = sd_netlink_message_open_container(req, AF_INET6); - if (r < 0) - return r; - - r = sd_netlink_message_append_u8(req, IFLA_INET6_ADDR_GEN_MODE, PTR_TO_UINT8(userdata)); - if (r < 0) - return r; - - r = sd_netlink_message_close_container(req); - if (r < 0) - return r; - - r = sd_netlink_message_close_container(req); + r = ipv6ll_addrgen_mode_fill_message(req, PTR_TO_UINT8(userdata)); if (r < 0) return r; break; @@ -689,8 +673,8 @@ static int link_request_set_link( } int link_request_to_set_addrgen_mode(Link *link) { + IPv6LinkLocalAddressGenMode mode; Request *req; - uint8_t mode; int r; assert(link); @@ -699,14 +683,7 @@ int link_request_to_set_addrgen_mode(Link *link) { if (!socket_ipv6_is_supported()) return 0; - if (!link_ipv6ll_enabled(link)) - mode = IN6_ADDR_GEN_MODE_NONE; - else if (link->network->ipv6ll_address_gen_mode >= 0) - mode = link->network->ipv6ll_address_gen_mode; - else if (in6_addr_is_set(&link->network->ipv6ll_stable_secret)) - mode = IN6_ADDR_GEN_MODE_STABLE_PRIVACY; - else - mode = IN6_ADDR_GEN_MODE_EUI64; + mode = link_get_ipv6ll_addrgen_mode(link); r = link_request_set_link(link, SET_LINK_ADDRESS_GENERATION_MODE, link_set_addrgen_mode_handler, &req); if (r < 0) From 7d0d832d67e62c4129ea7e65a1aa0597ca3309f4 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 8 Feb 2022 22:26:23 +0900 Subject: [PATCH 3/8] network: monitor current IPv6LL address generation mode --- src/network/networkd-ipv6ll.c | 59 +++++++++++++++++++++++++++++++++++ src/network/networkd-ipv6ll.h | 1 + src/network/networkd-link.c | 6 ++++ src/network/networkd-link.h | 3 ++ 4 files changed, 69 insertions(+) diff --git a/src/network/networkd-ipv6ll.c b/src/network/networkd-ipv6ll.c index 0ff28761f2..1db0856bc4 100644 --- a/src/network/networkd-ipv6ll.c +++ b/src/network/networkd-ipv6ll.c @@ -118,6 +118,65 @@ int ipv6ll_addrgen_mode_fill_message(sd_netlink_message *message, IPv6LinkLocalA return 0; } +int link_update_ipv6ll_addrgen_mode(Link *link, sd_netlink_message *message) { + uint8_t mode; + int family, r; + + assert(link); + assert(message); + + r = sd_rtnl_message_get_family(message, &family); + if (r < 0) + return r; + + if (family != AF_UNSPEC) + return 0; + + r = sd_netlink_message_enter_container(message, IFLA_AF_SPEC); + if (r == -ENODATA) + return 0; + if (r < 0) + return r; + + r = sd_netlink_message_enter_container(message, AF_INET6); + if (r == -ENODATA) + return sd_netlink_message_exit_container(message); + if (r < 0) + return r; + + mode = (uint8_t) link->ipv6ll_address_gen_mode; + r = sd_netlink_message_read_u8(message, IFLA_INET6_ADDR_GEN_MODE, &mode); + if (r < 0 && r != -ENODATA) + return r; + + r = sd_netlink_message_exit_container(message); + if (r < 0) + return r; + + r = sd_netlink_message_exit_container(message); + if (r < 0) + return r; + + if (mode == (uint8_t) link->ipv6ll_address_gen_mode) + return 0; + + if (mode >= _IPV6_LINK_LOCAL_ADDRESS_GEN_MODE_MAX) { + log_link_debug(link, "Received invalid IPv6 link-local address generation mode (%u), ignoring.", mode); + return 0; + } + + if (link->ipv6ll_address_gen_mode < 0) + log_link_debug(link, "Saved IPv6 link-local address generation mode: %s", + ipv6_link_local_address_gen_mode_to_string(mode)); + else + log_link_debug(link, "IPv6 link-local address generation mode is changed: %s -> %s", + ipv6_link_local_address_gen_mode_to_string(link->ipv6ll_address_gen_mode), + ipv6_link_local_address_gen_mode_to_string(mode)); + + link->ipv6ll_address_gen_mode = mode; + return 0; +} + 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", diff --git a/src/network/networkd-ipv6ll.h b/src/network/networkd-ipv6ll.h index 4f63c71fb5..9de9a75d89 100644 --- a/src/network/networkd-ipv6ll.h +++ b/src/network/networkd-ipv6ll.h @@ -26,6 +26,7 @@ bool link_may_have_ipv6ll(Link *link); IPv6LinkLocalAddressGenMode link_get_ipv6ll_addrgen_mode(Link *link); int ipv6ll_addrgen_mode_fill_message(sd_netlink_message *message, IPv6LinkLocalAddressGenMode mode); +int link_update_ipv6ll_addrgen_mode(Link *link, sd_netlink_message *message); 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_; diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 5f5f413fff..5a4b0b5a55 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -2318,6 +2318,10 @@ static int link_update(Link *link, sd_netlink_message *message) { if (r < 0) return r; + r = link_update_ipv6ll_addrgen_mode(link, message); + if (r < 0) + return r; + return link_update_flags(link, message); } @@ -2391,6 +2395,8 @@ static int link_new(Manager *manager, sd_netlink_message *message, Link **ret) { .ifname = TAKE_PTR(ifname), .kind = TAKE_PTR(kind), + .ipv6ll_address_gen_mode = _IPV6_LINK_LOCAL_ADDRESS_GEN_MODE_INVALID, + .state_file = TAKE_PTR(state_file), .lease_file = TAKE_PTR(lease_file), .lldp_file = TAKE_PTR(lldp_file), diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h index 8b3f109657..47e8538d70 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -68,6 +68,9 @@ typedef struct Link { sd_device *sd_device; char *driver; + /* link local addressing */ + IPv6LinkLocalAddressGenMode ipv6ll_address_gen_mode; + /* wlan */ enum nl80211_iftype wlan_iftype; char *ssid; From 6614116458d53d739811af26c70b5f73931e32fa Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 9 Feb 2022 02:14:12 +0900 Subject: [PATCH 4/8] network: use log_link_warning_errno() or friends where applicable --- src/network/networkd-link.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 5a4b0b5a55..e4cb30debc 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -2516,7 +2516,7 @@ int manager_rtnl_process_link(sd_netlink *rtnl, sd_netlink_message *message, Man /* netdev exists, so make sure the ifindex matches */ r = netdev_set_ifindex(netdev, message); if (r < 0) { - log_warning_errno(r, "Could not process new link message for netdev, ignoring: %m"); + log_netdev_warning_errno(netdev, r, "Could not process new link message for netdev, ignoring: %m"); return 0; } } @@ -2531,32 +2531,30 @@ int manager_rtnl_process_link(sd_netlink *rtnl, sd_netlink_message *message, Man r = link_update(link, message); if (r < 0) { - log_warning_errno(r, "Could not process link message: %m"); + log_link_warning_errno(link, r, "Could not process link message: %m"); link_enter_failed(link); return 0; } r = link_check_initialized(link); if (r < 0) { - log_warning_errno(r, "Failed to check link is initialized: %m"); + log_link_warning_errno(link, r, "Failed to check link is initialized: %m"); link_enter_failed(link); return 0; } } else { r = link_update(link, message); if (r < 0) { - log_warning_errno(r, "Could not process link message: %m"); + log_link_warning_errno(link, r, "Could not process link message: %m"); link_enter_failed(link); return 0; } } - break; case RTM_DELLINK: link_drop(link); netdev_drop(netdev); - break; default: From 05b8fc498ee6d5593559b242c1061f94d378f59b Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 8 Feb 2022 22:27:17 +0900 Subject: [PATCH 5/8] network: skip to set IPv6LL address generation mode if the requested mode is already set --- src/network/networkd-setlink.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/network/networkd-setlink.c b/src/network/networkd-setlink.c index 514833e420..7540f6f60f 100644 --- a/src/network/networkd-setlink.c +++ b/src/network/networkd-setlink.c @@ -685,6 +685,9 @@ int link_request_to_set_addrgen_mode(Link *link) { mode = link_get_ipv6ll_addrgen_mode(link); + if (mode == link->ipv6ll_address_gen_mode) + return 0; + r = link_request_set_link(link, SET_LINK_ADDRESS_GENERATION_MODE, link_set_addrgen_mode_handler, &req); if (r < 0) return r; From 2b25f4eb23194feb224eef7f5ef4b6a23d3dc032 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 8 Feb 2022 22:53:33 +0900 Subject: [PATCH 6/8] network: move link_set_ipv6ll_stable_secret() to networkd-ipv6ll.c --- src/network/networkd-ipv6ll.c | 46 +++++++++++++++++++++++++++++++++++ src/network/networkd-ipv6ll.h | 2 ++ src/network/networkd-sysctl.c | 45 ---------------------------------- 3 files changed, 48 insertions(+), 45 deletions(-) diff --git a/src/network/networkd-ipv6ll.c b/src/network/networkd-ipv6ll.c index 1db0856bc4..992be2fca6 100644 --- a/src/network/networkd-ipv6ll.c +++ b/src/network/networkd-ipv6ll.c @@ -12,6 +12,7 @@ #include "socket-util.h" #include "string-table.h" #include "strv.h" +#include "sysctl-util.h" bool link_ipv6ll_enabled(Link *link) { assert(link); @@ -177,6 +178,51 @@ int link_update_ipv6ll_addrgen_mode(Link *link, sd_netlink_message *message) { return 0; } +#define STABLE_SECRET_APP_ID_1 SD_ID128_MAKE(aa,05,1d,94,43,68,45,07,b9,73,f1,e8,e4,b7,34,52) +#define STABLE_SECRET_APP_ID_2 SD_ID128_MAKE(52,c4,40,a0,9f,2f,48,58,a9,3a,f6,29,25,ba,7a,7d) + +int link_set_ipv6ll_stable_secret(Link *link) { + _cleanup_free_ char *str = NULL; + struct in6_addr a; + int r; + + assert(link); + assert(link->network); + + if (link->network->ipv6ll_address_gen_mode != IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_STABLE_PRIVACY) + return 0; + + if (in6_addr_is_set(&link->network->ipv6ll_stable_secret)) + a = link->network->ipv6ll_stable_secret; + else { + sd_id128_t key; + le64_t v; + + /* Generate a stable secret address from machine-ID and the interface name. */ + + r = sd_id128_get_machine_app_specific(STABLE_SECRET_APP_ID_1, &key); + if (r < 0) + return log_link_debug_errno(link, r, "Failed to generate key: %m"); + + v = htole64(siphash24_string(link->ifname, key.bytes)); + memcpy(a.s6_addr, &v, sizeof(v)); + + r = sd_id128_get_machine_app_specific(STABLE_SECRET_APP_ID_2, &key); + if (r < 0) + return log_link_debug_errno(link, r, "Failed to generate key: %m"); + + v = htole64(siphash24_string(link->ifname, key.bytes)); + assert_cc(sizeof(v) * 2 == sizeof(a.s6_addr)); + memcpy(a.s6_addr + sizeof(v), &v, sizeof(v)); + } + + r = in6_addr_to_string(&a, &str); + if (r < 0) + return r; + + return sysctl_write_ip_property(AF_INET6, link->ifname, "stable_secret", str); +} + 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", diff --git a/src/network/networkd-ipv6ll.h b/src/network/networkd-ipv6ll.h index 9de9a75d89..ac7a7d3e3e 100644 --- a/src/network/networkd-ipv6ll.h +++ b/src/network/networkd-ipv6ll.h @@ -28,6 +28,8 @@ IPv6LinkLocalAddressGenMode link_get_ipv6ll_addrgen_mode(Link *link); int ipv6ll_addrgen_mode_fill_message(sd_netlink_message *message, IPv6LinkLocalAddressGenMode mode); int link_update_ipv6ll_addrgen_mode(Link *link, sd_netlink_message *message); +int link_set_ipv6ll_stable_secret(Link *link); + 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_; diff --git a/src/network/networkd-sysctl.c b/src/network/networkd-sysctl.c index 6c7a606dfb..4e4b285f08 100644 --- a/src/network/networkd-sysctl.c +++ b/src/network/networkd-sysctl.c @@ -11,9 +11,6 @@ #include "string-table.h" #include "sysctl-util.h" -#define STABLE_SECRET_APP_ID_1 SD_ID128_MAKE(aa,05,1d,94,43,68,45,07,b9,73,f1,e8,e4,b7,34,52) -#define STABLE_SECRET_APP_ID_2 SD_ID128_MAKE(52,c4,40,a0,9f,2f,48,58,a9,3a,f6,29,25,ba,7a,7d) - static int link_update_ipv6_sysctl(Link *link) { assert(link); @@ -214,48 +211,6 @@ int link_set_ipv6_mtu(Link *link) { return sysctl_write_ip_property_uint32(AF_INET6, link->ifname, "mtu", mtu); } -static int link_set_ipv6ll_stable_secret(Link *link) { - _cleanup_free_ char *str = NULL; - struct in6_addr a; - int r; - - assert(link); - assert(link->network); - - if (link->network->ipv6ll_address_gen_mode != IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_STABLE_PRIVACY) - return 0; - - if (in6_addr_is_set(&link->network->ipv6ll_stable_secret)) - a = link->network->ipv6ll_stable_secret; - else { - sd_id128_t key; - le64_t v; - - /* Generate a stable secret address from machine-ID and the interface name. */ - - r = sd_id128_get_machine_app_specific(STABLE_SECRET_APP_ID_1, &key); - if (r < 0) - return log_link_debug_errno(link, r, "Failed to generate key: %m"); - - v = htole64(siphash24_string(link->ifname, key.bytes)); - memcpy(a.s6_addr, &v, sizeof(v)); - - r = sd_id128_get_machine_app_specific(STABLE_SECRET_APP_ID_2, &key); - if (r < 0) - return log_link_debug_errno(link, r, "Failed to generate key: %m"); - - v = htole64(siphash24_string(link->ifname, key.bytes)); - assert_cc(sizeof(v) * 2 == sizeof(a.s6_addr)); - memcpy(a.s6_addr + sizeof(v), &v, sizeof(v)); - } - - r = in6_addr_to_string(&a, &str); - if (r < 0) - return r; - - return sysctl_write_ip_property(AF_INET6, link->ifname, "stable_secret", str); -} - static int link_set_ipv4_accept_local(Link *link) { assert(link); From 77d65e5659b681209f15ca914cdc7a40e5ead48e Mon Sep 17 00:00:00 2001 From: Joerie de Gram Date: Tue, 8 Feb 2022 22:56:26 +0900 Subject: [PATCH 7/8] network: attempt to trigger kernel IPv6LL address generation 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 | 10 ++++++++++ src/network/networkd-ipv6ll.h | 1 + src/network/networkd-setlink.c | 13 +++++++++++++ 3 files changed, 24 insertions(+) diff --git a/src/network/networkd-ipv6ll.c b/src/network/networkd-ipv6ll.c index 992be2fca6..79cf679daf 100644 --- a/src/network/networkd-ipv6ll.c +++ b/src/network/networkd-ipv6ll.c @@ -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", diff --git a/src/network/networkd-ipv6ll.h b/src/network/networkd-ipv6ll.h index ac7a7d3e3e..a9763debb1 100644 --- a/src/network/networkd-ipv6ll.h +++ b/src/network/networkd-ipv6ll.h @@ -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_; diff --git a/src/network/networkd-setlink.c b/src/network/networkd-setlink.c index 7540f6f60f..846528eb44 100644 --- a/src/network/networkd-setlink.c +++ b/src/network/networkd-setlink.c @@ -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; From 2becdbccd1a8d377c67f3f00c6e34cf3e53e87c7 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 8 Feb 2022 23:53:43 +0900 Subject: [PATCH 8/8] test-network: add testcase for re-generating IPv6LL address --- test/test-network/systemd-networkd-tests.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py index 5f64933cf9..d9089d273e 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -2952,22 +2952,35 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): self.assertEqual(read_ipv6_sysctl_attr('test1', 'addr_gen_mode'), '2') self.assertEqual(read_ipv6_sysctl_attr('dummy98', 'addr_gen_mode'), '1') - def test_link_local_addressing_remove_ipv6ll(self): + def test_link_local_addressing_ipv6ll(self): copy_unit_to_networkd_unit_path('26-link-local-addressing-ipv6.network', '12-dummy.netdev') start_networkd() self.wait_online(['dummy98:degraded']) + # An IPv6LL address exists by default. output = check_output('ip address show dev dummy98') print(output) self.assertRegex(output, 'inet6 .* scope link') copy_unit_to_networkd_unit_path('25-link-local-addressing-no.network') - restart_networkd(1) + check_output(*networkctl_cmd, 'reload', env=env) + time.sleep(1) self.wait_online(['dummy98:carrier']) + # Check if the IPv6LL address is removed. output = check_output('ip address show dev dummy98') print(output) - self.assertNotRegex(output, 'inet6* .* scope link') + self.assertNotRegex(output, 'inet6 .* scope link') + + remove_unit_from_networkd_path(['25-link-local-addressing-no.network']) + check_output(*networkctl_cmd, 'reload', env=env) + time.sleep(1) + self.wait_online(['dummy98:degraded']) + + # Check if a new IPv6LL address is assigned. + output = check_output('ip address show dev dummy98') + print(output) + self.assertRegex(output, 'inet6 .* scope link') def test_sysctl(self): copy_unit_to_networkd_unit_path('25-sysctl.network', '12-dummy.netdev')