From 08fdea122d3c3679eacdfb99311a3891912a8992 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Fri, 23 Aug 2019 10:39:22 +0200 Subject: [PATCH 1/2] ipv6: set neighbor parameters from RAs IPv6 router advertisement messages contain the following parameters (RFC 4861): - Reachable time: 32-bit unsigned integer. The time, in milliseconds, that a node assumes a neighbor is reachable after having received a reachability confirmation. Used by the Neighbor Unreachability Detection algorithm. A value of zero means unspecified (by this router). - Retrans Timer: 32-bit unsigned integer. The time, in milliseconds, between retransmitted Neighbor Solicitation messages. Used by address resolution and the Neighbor Unreachability Detection algorithm. A value of zero means unspecified (by this router). Currently NM ignores them; however, since it leaves accept_ra=1, the kernel parses RAs and applies those parameters for us [1]. In the next commit kernel handling of RAs will be disabled, so let NM set those neighbor-related parameters. [1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv6/ndisc.c?h=v5.2#n1353 (cherry picked from commit 5f0c6f8d3be548ee8880e21d96088fef36d74848) --- src/devices/nm-device.c | 12 ++++++++++ src/ndisc/nm-lndp-ndisc.c | 13 +++++++++++ src/ndisc/nm-ndisc.c | 8 +++++++ src/ndisc/nm-ndisc.h | 4 ++++ src/nm-iface-helper.c | 12 ++++++++++ src/platform/nm-platform.c | 48 ++++++++++++++++++++++++++++++++++++++ src/platform/nm-platform.h | 6 +++++ 7 files changed, 103 insertions(+) diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index ce7202968c..1e7a415e47 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -9667,6 +9667,18 @@ ndisc_config_changed (NMNDisc *ndisc, const NMNDiscData *rdata, guint changed_in if (changed & NM_NDISC_CONFIG_HOP_LIMIT) nm_platform_sysctl_ip_conf_set_ipv6_hop_limit_safe (nm_device_get_platform (self), nm_device_get_ip_iface (self), rdata->hop_limit); + if (changed & NM_NDISC_CONFIG_REACHABLE_TIME) { + nm_platform_sysctl_ip_neigh_set_ipv6_reachable_time (nm_device_get_platform (self), + nm_device_get_ip_iface (self), + rdata->reachable_time_ms); + } + + if (changed & NM_NDISC_CONFIG_RETRANS_TIMER) { + nm_platform_sysctl_ip_neigh_set_ipv6_retrans_time (nm_device_get_platform (self), + nm_device_get_ip_iface (self), + rdata->retrans_timer_ms); + } + if (changed & NM_NDISC_CONFIG_MTU) { if (priv->ip6_mtu != rdata->mtu) { _LOGD (LOGD_DEVICE, "mtu: set IPv6 MTU to %u", (guint) rdata->mtu); diff --git a/src/ndisc/nm-lndp-ndisc.c b/src/ndisc/nm-lndp-ndisc.c index 5643c19286..9352a75352 100644 --- a/src/ndisc/nm-lndp-ndisc.c +++ b/src/ndisc/nm-lndp-ndisc.c @@ -116,6 +116,7 @@ receive_ra (struct ndp *ndp, struct ndp_msg *msg, gpointer user_data) gint32 now = nm_utils_get_monotonic_timestamp_s (); int offset; int hop_limit; + guint32 val; /* Router discovery is subject to the following RFC documents: * @@ -294,6 +295,18 @@ receive_ra (struct ndp *ndp, struct ndp_msg *msg, gpointer user_data) changed |= NM_NDISC_CONFIG_HOP_LIMIT; } + val = ndp_msgra_reachable_time (msgra); + if (val && rdata->public.reachable_time_ms != val) { + rdata->public.reachable_time_ms = val; + changed |= NM_NDISC_CONFIG_REACHABLE_TIME; + } + + val = ndp_msgra_retransmit_time (msgra); + if (val && rdata->public.retrans_timer_ms != val) { + rdata->public.retrans_timer_ms = val; + changed |= NM_NDISC_CONFIG_RETRANS_TIMER; + } + /* MTU */ ndp_msg_opt_for_each_offset(offset, msg, NDP_MSG_OPT_MTU) { guint32 mtu = ndp_msg_opt_mtu(msg, offset); diff --git a/src/ndisc/nm-ndisc.c b/src/ndisc/nm-ndisc.c index 1acf5ba9e6..a8fabb5634 100644 --- a/src/ndisc/nm-ndisc.c +++ b/src/ndisc/nm-ndisc.c @@ -1039,6 +1039,14 @@ _config_changed_log (NMNDisc *ndisc, NMNDiscConfigMap changed) config_map_to_string (changed, changedstr); _LOGD ("neighbor discovery configuration changed [%s]:", changedstr); _LOGD (" dhcp-level %s", dhcp_level_to_string (priv->rdata.public.dhcp_level)); + + if (rdata->public.hop_limit) + _LOGD (" hop limit : %d", rdata->public.hop_limit); + if (rdata->public.reachable_time_ms) + _LOGD (" reachable time : %u", (guint) rdata->public.reachable_time_ms); + if (rdata->public.retrans_timer_ms) + _LOGD (" retrans timer : %u", (guint) rdata->public.retrans_timer_ms); + for (i = 0; i < rdata->gateways->len; i++) { NMNDiscGateway *gateway = &g_array_index (rdata->gateways, NMNDiscGateway, i); diff --git a/src/ndisc/nm-ndisc.h b/src/ndisc/nm-ndisc.h index 766d4ab9e4..8e07ca41de 100644 --- a/src/ndisc/nm-ndisc.h +++ b/src/ndisc/nm-ndisc.h @@ -112,6 +112,8 @@ typedef enum { NM_NDISC_CONFIG_DNS_DOMAINS = 1 << 5, NM_NDISC_CONFIG_HOP_LIMIT = 1 << 6, NM_NDISC_CONFIG_MTU = 1 << 7, + NM_NDISC_CONFIG_REACHABLE_TIME = 1 << 8, + NM_NDISC_CONFIG_RETRANS_TIMER = 1 << 9, } NMNDiscConfigMap; typedef enum { @@ -137,6 +139,8 @@ typedef struct { NMNDiscDHCPLevel dhcp_level; guint32 mtu; int hop_limit; + guint32 reachable_time_ms; + guint32 retrans_timer_ms; guint gateways_n; guint addresses_n; diff --git a/src/nm-iface-helper.c b/src/nm-iface-helper.c index dd5bb327a0..1f3f3a0c41 100644 --- a/src/nm-iface-helper.c +++ b/src/nm-iface-helper.c @@ -220,6 +220,18 @@ ndisc_config_changed (NMNDisc *ndisc, const NMNDiscData *rdata, guint changed_in if (changed & NM_NDISC_CONFIG_HOP_LIMIT) nm_platform_sysctl_ip_conf_set_ipv6_hop_limit_safe (NM_PLATFORM_GET, global_opt.ifname, rdata->hop_limit); + if (changed & NM_NDISC_CONFIG_REACHABLE_TIME) { + nm_platform_sysctl_ip_neigh_set_ipv6_reachable_time (NM_PLATFORM_GET, + global_opt.ifname, + rdata->reachable_time_ms); + } + + if (changed & NM_NDISC_CONFIG_RETRANS_TIMER) { + nm_platform_sysctl_ip_neigh_set_ipv6_retrans_time (NM_PLATFORM_GET, + global_opt.ifname, + rdata->retrans_timer_ms); + } + if (changed & NM_NDISC_CONFIG_MTU) { nm_platform_sysctl_ip_conf_set_int64 (NM_PLATFORM_GET, AF_INET6, diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index 6ead15ed35..0a7acba9ab 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -610,6 +610,54 @@ nm_platform_sysctl_ip_conf_set_ipv6_hop_limit_safe (NMPlatform *self, return TRUE; } +gboolean +nm_platform_sysctl_ip_neigh_set_ipv6_reachable_time (NMPlatform *self, + const char *iface, + guint value_ms) +{ + char path[NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE]; + char str[128]; + guint clamped; + + _CHECK_SELF (self, klass, FALSE); + + if (!value_ms) + return TRUE; + + /* RFC 4861 says the value can't be greater than one hour. + * Also use a reasonable lower threshold. */ + clamped = NM_CLAMP (value_ms, 100, 3600000); + nm_sprintf_buf (path, "/proc/sys/net/ipv6/neigh/%s/base_reachable_time_ms", iface); + nm_sprintf_buf (str, "%u", clamped); + if (!nm_platform_sysctl_set (self, NMP_SYSCTL_PATHID_ABSOLUTE (path), str)) + return FALSE; + + /* Set stale time in the same way as kernel */ + nm_sprintf_buf (path, "/proc/sys/net/ipv6/neigh/%s/gc_stale_time", iface); + nm_sprintf_buf (str, "%u", clamped * 3 / 1000); + + return nm_platform_sysctl_set (self, NMP_SYSCTL_PATHID_ABSOLUTE (path), str); +} + +gboolean +nm_platform_sysctl_ip_neigh_set_ipv6_retrans_time (NMPlatform *self, + const char *iface, + guint value_ms) +{ + char path[NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE]; + char str[128]; + + _CHECK_SELF (self, klass, FALSE); + + if (!value_ms) + return TRUE; + + nm_sprintf_buf (path, "/proc/sys/net/ipv6/neigh/%s/retrans_time_ms", iface); + nm_sprintf_buf (str, "%u", NM_CLAMP (value_ms, 10, 3600000)); + + return nm_platform_sysctl_set (self, NMP_SYSCTL_PATHID_ABSOLUTE (path), str); +} + /** * nm_platform_sysctl_get: * @self: platform instance diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index 248eca44f9..44733809f0 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -1353,6 +1353,12 @@ gboolean nm_platform_sysctl_ip_conf_set_int64 (NMPlatform *self, gboolean nm_platform_sysctl_ip_conf_set_ipv6_hop_limit_safe (NMPlatform *self, const char *iface, int value); +gboolean nm_platform_sysctl_ip_neigh_set_ipv6_reachable_time (NMPlatform *self, + const char *iface, + guint value_ms); +gboolean nm_platform_sysctl_ip_neigh_set_ipv6_retrans_time (NMPlatform *self, + const char *iface, + guint value_ms); int nm_platform_sysctl_ip_conf_get_rp_filter_ipv4 (NMPlatform *platform, const char *iface, gboolean consider_all, From d1c7c381e40aba5c1c5f7b99d555b0c5ac2723fe Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Fri, 23 Aug 2019 11:40:33 +0200 Subject: [PATCH 2/2] ipv6: disable kernel handling of RAs (accept_ra) With accept_ra set to 1, kernel sends its own router solicitation messages and parses the advertisements. This duplicates what NM already does in userspace and has unwanted consequences like [1] and [2]. The only reason why accept_ra was re-enabled in the past was to apply RA parameters like ReachableTime and RetransTimer [3]; but now NM supports them and so accept_ra can be turned off again. Also, note that previously the option was set in addrconf6_start_with_link_ready(), and so this was done only when the method was 'auto'. Instead, now we clear it for all methods except 'ignore'. [1] https://mail.gnome.org/archives/networkmanager-list/2019-June/msg00027.html [2] https://bugzilla.redhat.com/show_bug.cgi?id=1734470 [3] https://bugzilla.redhat.com/show_bug.cgi?id=1068673 (cherry picked from commit 5a534529e24edc54f8a2608587208647e63e63da) --- src/devices/nm-device.c | 24 +++--------------------- src/nm-iface-helper.c | 5 +---- 2 files changed, 4 insertions(+), 25 deletions(-) diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 1e7a415e47..0a62c47946 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -9740,24 +9740,11 @@ addrconf6_start_with_link_ready (NMDevice *self) if (!ip_config_merge_and_apply (self, AF_INET6, TRUE)) _LOGW (LOGD_IP6, "failed to apply manual IPv6 configuration"); - /* FIXME: These sysctls would probably be better set by the lndp ndisc itself. */ - switch (nm_ndisc_get_node_type (priv->ndisc)) { - case NM_NDISC_NODE_TYPE_HOST: - /* Accepting prefixes from discovered routers. */ - nm_device_sysctl_ip_conf_set (self, AF_INET6, "accept_ra", "1"); - nm_device_sysctl_ip_conf_set (self, AF_INET6, "accept_ra_defrtr", "0"); - nm_device_sysctl_ip_conf_set (self, AF_INET6, "accept_ra_pinfo", "0"); - nm_device_sysctl_ip_conf_set (self, AF_INET6, "accept_ra_rtr_pref", "0"); - break; - case NM_NDISC_NODE_TYPE_ROUTER: - /* We're the router. */ + if (nm_ndisc_get_node_type (priv->ndisc) == NM_NDISC_NODE_TYPE_ROUTER) { nm_device_sysctl_ip_conf_set (self, AF_INET6, "forwarding", "1"); nm_device_activate_schedule_ip_config_result (self, AF_INET6, NULL); priv->needs_ip6_subnet = TRUE; g_signal_emit (self, signals[IP6_SUBNET_NEEDED], 0); - break; - default: - g_assert_not_reached (); } priv->ndisc_changed_id = g_signal_connect (priv->ndisc, @@ -9868,9 +9855,6 @@ save_ip6_properties (NMDevice *self) { static const char *const ip6_properties_to_save[] = { "accept_ra", - "accept_ra_defrtr", - "accept_ra_pinfo", - "accept_ra_rtr_pref", "forwarding", "disable_ipv6", "hop_limit", @@ -10195,6 +10179,7 @@ act_stage3_ip_config_start (NMDevice *self, set_nm_ipv6ll (self, TRUE); /* Re-enable IPv6 on the interface */ + nm_device_sysctl_ip_conf_set (self, AF_INET6, "accept_ra", "0"); set_disable_ipv6 (self, "0"); /* Synchronize external IPv6 configuration with kernel, since @@ -14823,7 +14808,6 @@ nm_device_cleanup (NMDevice *self, NMDeviceStateReason reason, CleanupType clean /* Turn off kernel IPv6 */ if (cleanup_type == CLEANUP_TYPE_DECONFIGURE) { set_disable_ipv6 (self, "1"); - nm_device_sysctl_ip_conf_set (self, AF_INET6, "accept_ra", "0"); nm_device_sysctl_ip_conf_set (self, AF_INET6, "use_tempaddr", "0"); } @@ -15115,9 +15099,7 @@ ip6_managed_setup (NMDevice *self) { set_nm_ipv6ll (self, TRUE); set_disable_ipv6 (self, "1"); - nm_device_sysctl_ip_conf_set (self, AF_INET6, "accept_ra_defrtr", "0"); - nm_device_sysctl_ip_conf_set (self, AF_INET6, "accept_ra_pinfo", "0"); - nm_device_sysctl_ip_conf_set (self, AF_INET6, "accept_ra_rtr_pref", "0"); + nm_device_sysctl_ip_conf_set (self, AF_INET6, "accept_ra", "0"); nm_device_sysctl_ip_conf_set (self, AF_INET6, "use_tempaddr", "0"); nm_device_sysctl_ip_conf_set (self, AF_INET6, "forwarding", "0"); } diff --git a/src/nm-iface-helper.c b/src/nm-iface-helper.c index 1f3f3a0c41..afe3fc5d1e 100644 --- a/src/nm-iface-helper.c +++ b/src/nm-iface-helper.c @@ -569,10 +569,7 @@ main (int argc, char *argv[]) if (iid) nm_ndisc_set_iid (ndisc, *iid); - nm_platform_sysctl_ip_conf_set (NM_PLATFORM_GET, AF_INET6, global_opt.ifname, "accept_ra", "1"); - nm_platform_sysctl_ip_conf_set (NM_PLATFORM_GET, AF_INET6, global_opt.ifname, "accept_ra_defrtr", "0"); - nm_platform_sysctl_ip_conf_set (NM_PLATFORM_GET, AF_INET6, global_opt.ifname, "accept_ra_pinfo", "0"); - nm_platform_sysctl_ip_conf_set (NM_PLATFORM_GET, AF_INET6, global_opt.ifname, "accept_ra_rtr_pref", "0"); + nm_platform_sysctl_ip_conf_set (NM_PLATFORM_GET, AF_INET6, global_opt.ifname, "accept_ra", "0"); g_signal_connect (NM_PLATFORM_GET, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED,