From 03e1cc96a5dc3d9a9b86f60810a330332c484a94 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 19 Sep 2017 09:40:13 +0200 Subject: [PATCH] core: fix handling IPv6 device-route and use correct route metric Before commit 6698bf58bb53fb07838c52ca67293dd5352ec31c, we would rely on kernel to add the device-route for manual IPv6 routes. We broke that and now kernel would still add the device-route, however nm_platform_ip_route_sync() would delete it immediately after. That is because previously nm_platform_ip_route_sync() would ignore routes with rtm_protocol RTPRO_KERNEL. Now, it will sync and delete those too. Fix that by adding the device-route like we do it for IPv4. This also fixes an actual issue where the automatically added route always had route-metric 256. Instead, we now use the metric from ipv6.route-metric setting. Fixes: 6698bf58bb53fb07838c52ca67293dd5352ec31c --- src/devices/nm-device.c | 9 +++- src/nm-iface-helper.c | 2 + src/nm-ip4-config.c | 29 ++++++++---- src/nm-ip6-config.c | 92 +++++++++++++++++++++++++++++++++++++ src/nm-ip6-config.h | 4 ++ src/platform/nm-platform.c | 8 +++- src/vpn/nm-vpn-connection.c | 3 ++ 7 files changed, 134 insertions(+), 13 deletions(-) diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 7b2e92171d..79f67760ae 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -6290,6 +6290,7 @@ ip6_config_merge_and_apply (NMDevice *self, NMConnection *connection; gboolean success; NMIP6Config *composite; + const guint32 default_route_metric = nm_device_get_ip6_route_metric (self); const struct in6_addr *gateway; gboolean connection_has_default_route, connection_is_never_default; gboolean ignore_auto_routes = FALSE; @@ -6423,8 +6424,7 @@ ip6_config_merge_and_apply (NMDevice *self, memset (&default_route, 0, sizeof (default_route)); default_route.rt_source = NM_IP_CONFIG_SOURCE_USER; default_route.gateway = *gateway; - default_route.metric = route_metric_with_penalty (self, - nm_device_get_ip6_route_metric (self)); + default_route.metric = route_metric_with_penalty (self, default_route_metric); default_route.mss = nm_ip6_config_get_mss (composite); nm_clear_nmp_object (&priv->default_route6); nm_ip6_config_add_route (composite, &default_route, &priv->default_route6); @@ -6447,6 +6447,11 @@ END_ADD_DEFAULT_ROUTE: nm_ip6_config_add_route (composite, NMP_OBJECT_CAST_IP6_ROUTE (priv->default_routegw6), NULL); } + if (commit) { + nm_ip6_config_add_device_routes (composite, + default_route_metric); + } + /* Allow setting MTU etc */ if (commit) { NMUtilsIPv6IfaceId iid; diff --git a/src/nm-iface-helper.c b/src/nm-iface-helper.c index 43c2ae478b..873b6c9668 100644 --- a/src/nm-iface-helper.c +++ b/src/nm-iface-helper.c @@ -226,6 +226,8 @@ ndisc_config_changed (NMNDisc *ndisc, const NMNDiscData *rdata, guint changed_in } nm_ip6_config_merge (existing, ndisc_config, NM_IP_CONFIG_MERGE_DEFAULT); + nm_ip6_config_add_device_routes (existing, + global_opt.priority_v6); if (!nm_ip6_config_commit (existing, NM_PLATFORM_GET, NULL)) _LOGW (LOGD_IP6, "failed to apply IPv6 config"); } diff --git a/src/nm-ip4-config.c b/src/nm-ip4-config.c index 90407e34a1..56b7dfb5b1 100644 --- a/src/nm-ip4-config.c +++ b/src/nm-ip4-config.c @@ -245,13 +245,23 @@ _nm_ip_config_lookup_ip_route (const NMDedupMultiIndex *multi_idx, if (!entry) return NULL; - if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID) - nm_assert (nm_platform_ip4_route_cmp (NMP_OBJECT_CAST_IP4_ROUTE (entry->obj), NMP_OBJECT_CAST_IP4_ROUTE (needle), cmp_type) == 0); - else { - if (nm_platform_ip4_route_cmp (NMP_OBJECT_CAST_IP4_ROUTE (entry->obj), - NMP_OBJECT_CAST_IP4_ROUTE (needle), - cmp_type) != 0) - return NULL; + if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID) { + nm_assert ( ( NMP_OBJECT_GET_TYPE (needle) == NMP_OBJECT_TYPE_IP4_ROUTE + && nm_platform_ip4_route_cmp (NMP_OBJECT_CAST_IP4_ROUTE (entry->obj), NMP_OBJECT_CAST_IP4_ROUTE (needle), cmp_type) == 0) + || ( NMP_OBJECT_GET_TYPE (needle) == NMP_OBJECT_TYPE_IP6_ROUTE + && nm_platform_ip6_route_cmp (NMP_OBJECT_CAST_IP6_ROUTE (entry->obj), NMP_OBJECT_CAST_IP6_ROUTE (needle), cmp_type) == 0)); + } else { + if (NMP_OBJECT_GET_TYPE (needle) == NMP_OBJECT_TYPE_IP4_ROUTE) { + if (nm_platform_ip4_route_cmp (NMP_OBJECT_CAST_IP4_ROUTE (entry->obj), + NMP_OBJECT_CAST_IP4_ROUTE (needle), + cmp_type) != 0) + return NULL; + } else { + if (nm_platform_ip6_route_cmp (NMP_OBJECT_CAST_IP6_ROUTE (entry->obj), + NMP_OBJECT_CAST_IP6_ROUTE (needle), + cmp_type) != 0) + return NULL; + } } return entry; } @@ -740,9 +750,8 @@ nm_ip4_config_add_device_routes (NMIP4Config *self, ifindex = nm_ip4_config_get_ifindex (self); g_return_if_fail (ifindex > 0); - /* For IPv6, we explicitly add the device-routes (onlink) to NMIP6Config. - * As we don't do that for IPv4, add it here shortly before syncing - * the routes. */ + /* For IPv6 slaac, we explicitly add the device-routes (onlink) to NMIP6Config. + * As we don't do that for IPv4 (and manual IPv6 addresses), add them explicitly. */ nm_ip_config_iter_ip4_address_for_each (&iter, self, &addr) { nm_auto_nmpobj NMPObject *r = NULL; diff --git a/src/nm-ip6-config.c b/src/nm-ip6-config.c index acb9ccb646..fce5fa8e90 100644 --- a/src/nm-ip6-config.c +++ b/src/nm-ip6-config.c @@ -115,6 +115,9 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMIP6Config, static void _add_address (NMIP6Config *self, const NMPObject *obj_new, const NMPlatformIP6Address *new); static void _add_route (NMIP6Config *self, const NMPObject *obj_new, const NMPlatformIP6Route *new, const NMPObject **out_obj_new); +static const NMDedupMultiEntry *_lookup_route (const NMIP6Config *self, + const NMPObject *needle, + NMPlatformIPRouteCmpType cmp_type); /*****************************************************************************/ @@ -527,6 +530,77 @@ nm_ip6_config_capture (NMDedupMultiIndex *multi_idx, NMPlatform *platform, int i return self; } +void +nm_ip6_config_add_device_routes (NMIP6Config *self, + guint32 default_route_metric) +{ + const NMIP6ConfigPrivate *priv; + const NMPlatformIP6Address *addr; + int ifindex; + NMDedupMultiIter iter; + + g_return_if_fail (NM_IS_IP6_CONFIG (self)); + + priv = NM_IP6_CONFIG_GET_PRIVATE (self); + + ifindex = nm_ip6_config_get_ifindex (self); + g_return_if_fail (ifindex > 0); + + /* For IPv6 addresses received via SLAAC/autoconf, we explicitly add the + * device-routes (onlink) to NMIP6Config. + * + * For manually added IPv6 routes, add the device routes explicitly. */ + + nm_ip_config_iter_ip6_address_for_each (&iter, self, &addr) { + NMPObject *r; + NMPlatformIP6Route *route; + gboolean has_peer; + int routes_n, routes_i; + + if (NM_FLAGS_HAS (addr->n_ifa_flags, IFA_F_NOPREFIXROUTE)) + continue; + + has_peer = !IN6_IS_ADDR_UNSPECIFIED (&addr->peer_address); + + /* If we have an IPv6 peer, we add two /128 routes + * (unless, both addresses are identical). */ + routes_n = ( has_peer + && !IN6_ARE_ADDR_EQUAL (&addr->address, &addr->peer_address)) + ? 2 : 1; + + for (routes_i = 0; routes_i < routes_n; routes_i++) { + + r = nmp_object_new (NMP_OBJECT_TYPE_IP6_ROUTE, NULL); + route = NMP_OBJECT_CAST_IP6_ROUTE (r); + + route->ifindex = ifindex; + route->rt_source = NM_IP_CONFIG_SOURCE_KERNEL; + route->metric = default_route_metric; + + if (has_peer) { + if (routes_i == 0) + route->network = addr->address; + else + route->network = addr->peer_address; + route->plen = 128; + } else { + nm_utils_ip6_address_clear_host_address (&route->network, &addr->address, addr->plen); + route->plen = addr->plen; + } + + nm_platform_ip_route_normalize (AF_INET6, (NMPlatformIPRoute *) route); + + if (_lookup_route (self, + r, + NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID)) { + /* we already track this route. Don't add it again. */ + nmp_object_unref (r); + } else + _add_route (self, r, NULL, NULL); + } + } +} + gboolean nm_ip6_config_commit (const NMIP6Config *self, NMPlatform *platform, @@ -1801,6 +1875,24 @@ nm_ip6_config_has_any_dad_pending (const NMIP6Config *self, /*****************************************************************************/ +static const NMDedupMultiEntry * +_lookup_route (const NMIP6Config *self, + const NMPObject *needle, + NMPlatformIPRouteCmpType cmp_type) +{ + const NMIP6ConfigPrivate *priv; + + nm_assert (NM_IS_IP6_CONFIG (self)); + nm_assert (NMP_OBJECT_GET_TYPE (needle) == NMP_OBJECT_TYPE_IP6_ROUTE); + + priv = NM_IP6_CONFIG_GET_PRIVATE (self); + + return _nm_ip_config_lookup_ip_route (priv->multi_idx, + &priv->idx_ip6_routes_, + needle, + cmp_type); +} + void nm_ip6_config_reset_routes_ndisc (NMIP6Config *self, const NMNDiscRoute *routes, diff --git a/src/nm-ip6-config.h b/src/nm-ip6-config.h index 71ae1e469c..03dee47b27 100644 --- a/src/nm-ip6-config.h +++ b/src/nm-ip6-config.h @@ -107,6 +107,10 @@ struct _NMDedupMultiIndex *nm_ip6_config_get_multi_idx (const NMIP6Config *self) NMIP6Config *nm_ip6_config_capture (struct _NMDedupMultiIndex *multi_idx, NMPlatform *platform, int ifindex, gboolean capture_resolv_conf, NMSettingIP6ConfigPrivacy use_temporary); + +void nm_ip6_config_add_device_routes (NMIP6Config *self, + guint32 default_route_metric); + gboolean nm_ip6_config_commit (const NMIP6Config *self, NMPlatform *platform, GPtrArray **out_temporary_not_available); diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index c3b123b79f..73cf1c553c 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -3479,6 +3479,7 @@ nm_platform_ip6_address_sync (NMPlatform *self, gint32 now = nm_utils_get_monotonic_timestamp_s (); guint i; NMPLookup lookup; + guint32 ifa_flags; /* Delete unknown addresses */ plat_addresses = nm_platform_lookup_clone (self, @@ -3502,6 +3503,10 @@ nm_platform_ip6_address_sync (NMPlatform *self, if (!known_addresses) return TRUE; + ifa_flags = nm_platform_check_support_kernel_extended_ifa_flags (self) + ? IFA_F_NOPREFIXROUTE + : 0; + /* Add missing addresses */ for (i = 0; i < known_addresses->len; i++) { const NMPlatformIP6Address *known_address = NMP_OBJECT_CAST_IP6_ADDRESS (known_addresses->pdata[i]); @@ -3518,7 +3523,8 @@ nm_platform_ip6_address_sync (NMPlatform *self, if (!nm_platform_ip6_address_add (self, ifindex, known_address->address, known_address->plen, known_address->peer_address, - lifetime, preferred, known_address->n_ifa_flags)) + lifetime, preferred, + ifa_flags | known_address->n_ifa_flags)) return FALSE; } diff --git a/src/vpn/nm-vpn-connection.c b/src/vpn/nm-vpn-connection.c index 0006cf3cc2..e0f6698ef3 100644 --- a/src/vpn/nm-vpn-connection.c +++ b/src/vpn/nm-vpn-connection.c @@ -1786,6 +1786,9 @@ next: nm_ip6_config_add_route (config, &r, NULL); } + nm_ip6_config_add_device_routes (config, + nm_vpn_connection_get_ip6_route_metric (self)); + if (priv->ip6_config) { nm_ip6_config_replace (priv->ip6_config, config, NULL); g_object_unref (config);