diff --git a/src/basic/sysctl-util.c b/src/basic/sysctl-util.c index 8a73dc6b3e..2feb4917d7 100644 --- a/src/basic/sysctl-util.c +++ b/src/basic/sysctl-util.c @@ -10,6 +10,7 @@ #include "fileio.h" #include "log.h" #include "macro.h" +#include "parse-util.h" #include "path-util.h" #include "socket-util.h" #include "string-util.h" @@ -193,3 +194,29 @@ int sysctl_read_ip_property(int af, const char *ifname, const char *property, ch return sysctl_read(p, ret); } + +int sysctl_read_ip_property_int(int af, const char *ifname, const char *property, int *ret) { + _cleanup_free_ char *s = NULL; + int r; + + assert(ret); + + r = sysctl_read_ip_property(af, ifname, property, &s); + if (r < 0) + return r; + + return safe_atoi(s, ret); +} + +int sysctl_read_ip_property_uint32(int af, const char *ifname, const char *property, uint32_t *ret) { + _cleanup_free_ char *s = NULL; + int r; + + assert(ret); + + r = sysctl_read_ip_property(af, ifname, property, &s); + if (r < 0) + return r; + + return safe_atou32(s, ret); +} diff --git a/src/basic/sysctl-util.h b/src/basic/sysctl-util.h index 041292f693..bbfc4ce1ea 100644 --- a/src/basic/sysctl-util.h +++ b/src/basic/sysctl-util.h @@ -17,6 +17,8 @@ static inline int sysctl_write(const char *property, const char *value) { } int sysctl_read_ip_property(int af, const char *ifname, const char *property, char **ret); +int sysctl_read_ip_property_int(int af, const char *ifname, const char *property, int *ret); +int sysctl_read_ip_property_uint32(int af, const char *ifname, const char *property, uint32_t *ret); int sysctl_write_ip_property(int af, const char *ifname, const char *property, const char *value, Hashmap **shadow); static inline int sysctl_write_ip_property_boolean(int af, const char *ifname, const char *property, bool value, Hashmap **shadow) { return sysctl_write_ip_property(af, ifname, property, one_zero(value), shadow); diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 234e08680b..59240bbc36 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -288,6 +288,7 @@ static Link *link_free(Link *link) { network_unref(link->network); sd_event_source_disable_unref(link->carrier_lost_timer); + sd_event_source_disable_unref(link->ipv6_mtu_wait_synced_event_source); return mfree(link); } @@ -1869,8 +1870,6 @@ static int link_carrier_lost(Link *link) { } static int link_admin_state_up(Link *link) { - int r; - assert(link); /* This is called every time an interface admin state changes to up; @@ -1886,9 +1885,7 @@ static int link_admin_state_up(Link *link) { /* We set the ipv6 mtu after the device mtu, but the kernel resets * ipv6 mtu on NETDEV_UP, so we need to reset it. */ - r = link_set_ipv6_mtu(link, LOG_INFO); - if (r < 0) - log_link_warning_errno(link, r, "Cannot set IPv6 MTU, ignoring: %m"); + (void) link_set_ipv6_mtu(link, LOG_INFO); return 0; } @@ -2439,12 +2436,9 @@ static int link_update_mtu(Link *link, sd_netlink_message *message) { link->mtu = mtu; - if (IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED)) { + if (IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED)) /* The kernel resets IPv6 MTU after changing device MTU. So, we need to re-set IPv6 MTU again. */ - r = link_set_ipv6_mtu(link, LOG_INFO); - if (r < 0) - log_link_warning_errno(link, r, "Failed to set IPv6 MTU, ignoring: %m"); - } + (void) link_set_ipv6_mtu_async(link); if (link->dhcp_client) { r = sd_dhcp_client_set_mtu(link->dhcp_client, link->mtu); diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h index 27bc299bc5..e86839af8e 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -74,6 +74,9 @@ typedef struct Link { sd_device *dev; char *driver; + sd_event_source *ipv6_mtu_wait_synced_event_source; + unsigned ipv6_mtu_wait_trial_count; + /* bridge vlan */ uint16_t bridge_vlan_pvid; bool bridge_vlan_pvid_is_untagged; diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c index 529311f4a4..fc06e5c38b 100644 --- a/src/network/networkd-ndisc.c +++ b/src/network/networkd-ndisc.c @@ -1083,9 +1083,7 @@ static int ndisc_router_process_mtu(Link *link, sd_ndisc_router *rt) { link->ndisc_mtu = mtu; - r = link_set_ipv6_mtu(link, LOG_DEBUG); - if (r < 0) - log_link_warning_errno(link, r, "Failed to apply IPv6 MTU (%"PRIu32"), ignoring: %m", mtu); + (void) link_set_ipv6_mtu(link, LOG_DEBUG); return 0; } diff --git a/src/network/networkd-route-util.c b/src/network/networkd-route-util.c index 9bb1e0f707..dc9663e24b 100644 --- a/src/network/networkd-route-util.c +++ b/src/network/networkd-route-util.c @@ -16,23 +16,26 @@ #include "strv.h" #include "sysctl-util.h" -#define ROUTES_DEFAULT_MAX_PER_FAMILY 4096U +#define ROUTES_DEFAULT_MAX_PER_FAMILY 4096 unsigned routes_max(void) { static thread_local unsigned cached = 0; - _cleanup_free_ char *s4 = NULL, *s6 = NULL; - unsigned val4 = ROUTES_DEFAULT_MAX_PER_FAMILY, val6 = ROUTES_DEFAULT_MAX_PER_FAMILY; + int val4 = ROUTES_DEFAULT_MAX_PER_FAMILY, val6 = ROUTES_DEFAULT_MAX_PER_FAMILY; if (cached > 0) return cached; - if (sysctl_read_ip_property(AF_INET, NULL, "route/max_size", &s4) >= 0) - if (safe_atou(s4, &val4) >= 0 && val4 == 2147483647U) + /* The kernel internally stores these maximum size in int. */ + + if (sysctl_read_ip_property_int(AF_INET, /* ifname = */ NULL, "route/max_size", &val4) >= 0) + if (val4 == INT_MAX) /* This is the default "no limit" value in the kernel */ val4 = ROUTES_DEFAULT_MAX_PER_FAMILY; - if (sysctl_read_ip_property(AF_INET6, NULL, "route/max_size", &s6) >= 0) - (void) safe_atou(s6, &val6); + if (sysctl_read_ip_property_int(AF_INET6, /* ifname = */ NULL, "route/max_size", &val6) >= 0) + if (val6 == INT_MAX) + /* This is the default "no limit" value in the kernel */ + val6 = ROUTES_DEFAULT_MAX_PER_FAMILY; cached = MAX(ROUTES_DEFAULT_MAX_PER_FAMILY, val4) + MAX(ROUTES_DEFAULT_MAX_PER_FAMILY, val6); diff --git a/src/network/networkd-sysctl.c b/src/network/networkd-sysctl.c index 7330c2cc76..c0762b9b70 100644 --- a/src/network/networkd-sysctl.c +++ b/src/network/networkd-sysctl.c @@ -8,6 +8,7 @@ #include "af-list.h" #include "cgroup-util.h" +#include "event-util.h" #include "fd-util.h" #include "format-util.h" #include "missing_network.h" @@ -503,6 +504,7 @@ static int link_set_ipv6_proxy_ndp(Link *link) { int link_set_ipv6_mtu(Link *link, int log_level) { uint32_t mtu = 0; + int r; assert(link); assert(link->manager); @@ -510,6 +512,14 @@ int link_set_ipv6_mtu(Link *link, int log_level) { if (!link_is_configured_for_family(link, AF_INET6)) return 0; + if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED)) + return 0; + + if (sd_event_source_get_enabled(link->ipv6_mtu_wait_synced_event_source, /* ret = */ NULL) > 0) { + log_link_debug(link, "Waiting for IPv6 MTU is synced to link MTU, delaying to set IPv6 MTU."); + return 0; + } + assert(link->network); if (link->network->ndisc_use_mtu) @@ -526,7 +536,88 @@ int link_set_ipv6_mtu(Link *link, int log_level) { mtu = link->mtu; } - return sysctl_write_ip_property_uint32(AF_INET6, link->ifname, "mtu", mtu, manager_get_sysctl_shadow(link->manager)); + r = sysctl_write_ip_property_uint32(AF_INET6, link->ifname, "mtu", mtu, manager_get_sysctl_shadow(link->manager)); + if (r < 0) + return log_link_warning_errno(link, r, "Failed to set IPv6 MTU to %"PRIu32": %m", mtu); + + return 0; +} + +static int ipv6_mtu_wait_synced_handler(sd_event_source *s, uint64_t usec, void *userdata); + +static int link_set_ipv6_mtu_async_impl(Link *link) { + uint32_t current_mtu; + int r; + + assert(link); + + /* When the link MTU is updated, it seems that the kernel IPv6 MTU of the interface is asynchronously + * reset to the link MTU. Hence, we need to check if it is already reset, and wait for a while if not. */ + + if (++link->ipv6_mtu_wait_trial_count >= 10) { + log_link_debug(link, "Timed out waiting for IPv6 MTU being synced to link MTU, proceeding anyway."); + r = link_set_ipv6_mtu(link, LOG_INFO); + if (r < 0) + return r; + + return 1; /* done */ + } + + /* Check if IPv6 MTU is synced. */ + r = sysctl_read_ip_property_uint32(AF_INET6, link->ifname, "mtu", ¤t_mtu); + if (r < 0) + return log_link_warning_errno(link, r, "Failed to read IPv6 MTU: %m"); + + if (current_mtu == link->mtu) { + /* Already synced. Update IPv6 MTU now. */ + r = link_set_ipv6_mtu(link, LOG_INFO); + if (r < 0) + return r; + + return 1; /* done */ + } + + /* If not, set up a timer event source. */ + r = event_reset_time_relative( + link->manager->event, &link->ipv6_mtu_wait_synced_event_source, + CLOCK_BOOTTIME, 100 * USEC_PER_MSEC, 0, + ipv6_mtu_wait_synced_handler, link, + /* priority = */ 0, "ipv6-mtu-wait-synced", /* force = */ true); + if (r < 0) + return log_link_warning_errno(link, r, "Failed to configure timer event source for waiting for IPv6 MTU being synced: %m"); + + /* Check again. */ + r = sysctl_read_ip_property_uint32(AF_INET6, link->ifname, "mtu", ¤t_mtu); + if (r < 0) + return log_link_warning_errno(link, r, "Failed to read IPv6 MTU: %m"); + + if (current_mtu == link->mtu) { + /* Synced while setting up the timer event source. Disable it and update IPv6 MTU now. */ + r = sd_event_source_set_enabled(link->ipv6_mtu_wait_synced_event_source, SD_EVENT_OFF); + if (r < 0) + log_link_debug_errno(link, r, "Failed to disable timer event source for IPv6 MTU, ignoring: %m"); + + r = link_set_ipv6_mtu(link, LOG_INFO); + if (r < 0) + return r; + + return 1; /* done */ + } + + log_link_debug(link, "IPv6 MTU is not synced to the link MTU after it is changed. Waiting for a while."); + return 0; /* waiting */ +} + +static int ipv6_mtu_wait_synced_handler(sd_event_source *s, uint64_t usec, void *userdata) { + (void) link_set_ipv6_mtu_async_impl(ASSERT_PTR(userdata)); + return 0; +} + +int link_set_ipv6_mtu_async(Link *link) { + assert(link); + + link->ipv6_mtu_wait_trial_count = 0; + return link_set_ipv6_mtu_async_impl(link); } static int link_set_ipv4_accept_local(Link *link) { @@ -616,9 +707,7 @@ int link_set_sysctl(Link *link) { if (r < 0) log_link_warning_errno(link, r, "Cannot set IPv6 proxy NDP, ignoring: %m"); - r = link_set_ipv6_mtu(link, LOG_INFO); - if (r < 0) - log_link_warning_errno(link, r, "Cannot set IPv6 MTU, ignoring: %m"); + (void) link_set_ipv6_mtu(link, LOG_INFO); r = link_set_ipv6ll_stable_secret(link); if (r < 0) diff --git a/src/network/networkd-sysctl.h b/src/network/networkd-sysctl.h index 446b835555..1c19fbd2d6 100644 --- a/src/network/networkd-sysctl.h +++ b/src/network/networkd-sysctl.h @@ -42,6 +42,7 @@ void manager_set_sysctl(Manager *manager); int link_get_ip_forwarding(Link *link, int family); int link_set_sysctl(Link *link); int link_set_ipv6_mtu(Link *link, int log_level); +int link_set_ipv6_mtu_async(Link *link); const char* ipv6_privacy_extensions_to_string(IPv6PrivacyExtensions i) _const_; IPv6PrivacyExtensions ipv6_privacy_extensions_from_string(const char *s) _pure_;