From 0918b4914d03ec14f71b3e56178c44a6ec648b34 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Thu, 31 Aug 2017 13:39:04 +0200 Subject: [PATCH 1/2] core: support tracking default-route in NMIP4Config/NMIP6Config Default-routes are for the most part like regular routes. Add support to track them like regular routes in NMIP4Config/NMIP6Config. One thing is, sometimes we need to figure out whether an ip-config instance has a default-route. For that, keep track of the best default-route (there might be multiple) and expose it. That is the most complicated part of this patch, because there are so many places where the list of routes gets modified (replace, intersect, subtract, merge, add), and they all need to take care of updating the best default-route. In a next patch, NMDefaultRouteManager will be dropped and default-routes will be tracked by NMIP4Config/NMIP6Config. --- src/dns/nm-dns-dnsmasq.c | 12 +- src/nm-dispatcher.c | 4 + src/nm-ip4-config.c | 257 ++++++++++++++++++++++++++++++++----- src/nm-ip4-config.h | 27 ++++ src/nm-ip6-config.c | 196 +++++++++++++++++++++++----- src/nm-ip6-config.h | 8 ++ src/nm-pacrunner-manager.c | 4 + 7 files changed, 439 insertions(+), 69 deletions(-) diff --git a/src/dns/nm-dns-dnsmasq.c b/src/dns/nm-dns-dnsmasq.c index cb311dffda..3ec1fd2de8 100644 --- a/src/dns/nm-dns-dnsmasq.c +++ b/src/dns/nm-dns-dnsmasq.c @@ -91,8 +91,10 @@ get_ip4_rdns_domains (NMIP4Config *ip4) nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, ip4, &address) nm_utils_get_reverse_dns_domains_ip4 (address->address, address->plen, domains); - nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, ip4, &route) - nm_utils_get_reverse_dns_domains_ip4 (route->network, route->plen, domains); + nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, ip4, &route) { + if (!NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route)) + nm_utils_get_reverse_dns_domains_ip4 (route->network, route->plen, domains); + } /* Terminating NULL so we can use g_strfreev() to free it */ g_ptr_array_add (domains, NULL); @@ -119,8 +121,10 @@ get_ip6_rdns_domains (NMIP6Config *ip6) nm_ip_config_iter_ip6_address_for_each (&ipconf_iter, ip6, &address) nm_utils_get_reverse_dns_domains_ip6 (&address->address, address->plen, domains); - nm_ip_config_iter_ip6_route_for_each (&ipconf_iter, ip6, &route) - nm_utils_get_reverse_dns_domains_ip6 (&route->network, route->plen, domains); + nm_ip_config_iter_ip6_route_for_each (&ipconf_iter, ip6, &route) { + if (!NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route)) + nm_utils_get_reverse_dns_domains_ip6 (&route->network, route->plen, domains); + } /* Terminating NULL so we can use g_strfreev() to free it */ g_ptr_array_add (domains, NULL); diff --git a/src/nm-dispatcher.c b/src/nm-dispatcher.c index cb7bb3c5a3..b5b11812e0 100644 --- a/src/nm-dispatcher.c +++ b/src/nm-dispatcher.c @@ -166,6 +166,8 @@ dump_ip4_to_props (NMIP4Config *ip4, GVariantBuilder *builder) /* Static routes */ g_variant_builder_init (&int_builder, G_VARIANT_TYPE ("aau")); nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, ip4, &route) { + if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route)) + continue; array[0] = route->network; array[1] = route->plen; array[2] = route->gateway; @@ -235,6 +237,8 @@ dump_ip6_to_props (NMIP6Config *ip6, GVariantBuilder *builder) /* Static routes */ g_variant_builder_init (&int_builder, G_VARIANT_TYPE ("a(ayuayu)")); nm_ip_config_iter_ip6_route_for_each (&ipconf_iter, ip6, &route) { + if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route)) + continue; ip = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, &route->network, sizeof (struct in6_addr), 1); diff --git a/src/nm-ip4-config.c b/src/nm-ip4-config.c index 9037c2d6f2..cf8435414a 100644 --- a/src/nm-ip4-config.c +++ b/src/nm-ip4-config.c @@ -377,6 +377,7 @@ typedef struct { GVariant *route_data_variant; GVariant *routes_variant; NMDedupMultiIndex *multi_idx; + const NMPObject *best_default_route; union { NMIPConfigDedupMultiIdxType idx_ip4_addresses_; NMDedupMultiIdxType idx_ip4_addresses; @@ -471,6 +472,95 @@ nm_ip_config_iter_ip4_route_init (NMDedupMultiIter *ipconf_iter, const NMIP4Conf /*****************************************************************************/ +const NMPObject * +_nm_ip_config_best_default_route_find_better (const NMPObject *obj_cur, const NMPObject *obj_cmp) +{ + int addr_family; + int c; + guint metric_cur, metric_cmp; + + nm_assert ( !obj_cur + || NM_IN_SET (NMP_OBJECT_GET_TYPE (obj_cur), NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)); + nm_assert ( !obj_cmp + || ( !obj_cur + && NM_IN_SET (NMP_OBJECT_GET_TYPE (obj_cmp), NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)) + || NMP_OBJECT_GET_TYPE (obj_cur) == NMP_OBJECT_GET_TYPE (obj_cmp)); + nm_assert ( !obj_cur + || nm_ip_config_best_default_route_is (obj_cur)); + + /* assumes that @obj_cur is already the best default route (or NULL). It checks whether + * @obj_cmp is also a default route and returns the best of both. */ + if ( obj_cmp + && nm_ip_config_best_default_route_is (obj_cmp)) { + if (!obj_cur) + return obj_cmp; + + addr_family = NMP_OBJECT_GET_CLASS (obj_cmp)->addr_family; + metric_cur = nm_utils_ip_route_metric_normalize (addr_family, NMP_OBJECT_CAST_IP_ROUTE (obj_cur)->metric); + metric_cmp = nm_utils_ip_route_metric_normalize (addr_family, NMP_OBJECT_CAST_IP_ROUTE (obj_cmp)->metric); + + if (metric_cmp < metric_cur) + return obj_cmp; + + if (metric_cmp == metric_cur) { + /* Routes have the same metric. We still want to deterministically + * prefer one or the other. It's important to consistently choose one + * or the other, so that the order doesn't matter how routes are added + * (and merged). */ + c = nmp_object_cmp (obj_cur, obj_cmp); + if (c != 0) + return c < 0 ? obj_cur : obj_cmp; + + /* as last resort, compare pointers. */ + if (obj_cmp < obj_cur) + return obj_cmp; + } + } + return obj_cur; +} + +gboolean +_nm_ip_config_best_default_route_set (const NMPObject **best_default_route, const NMPObject *new_candidate) +{ + if (new_candidate == *best_default_route) + return FALSE; + nmp_object_ref (new_candidate); + nm_clear_nmp_object (best_default_route); + *best_default_route = new_candidate; + return TRUE; +} + +gboolean +_nm_ip_config_best_default_route_merge (const NMPObject **best_default_route, const NMPObject *new_candidate) +{ + new_candidate = _nm_ip_config_best_default_route_find_better (*best_default_route, + new_candidate); + return _nm_ip_config_best_default_route_set (best_default_route, new_candidate); +} + +const NMPObject * +nm_ip4_config_best_default_route_get (const NMIP4Config *self) +{ + g_return_val_if_fail (NM_IS_IP4_CONFIG (self), NULL); + + return NM_IP4_CONFIG_GET_PRIVATE (self)->best_default_route; +} + +const NMPObject * +_nm_ip4_config_best_default_route_find (const NMIP4Config *self) +{ + NMDedupMultiIter ipconf_iter; + const NMPObject *new_best_default_route = NULL; + + nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, self, NULL) { + new_best_default_route = _nm_ip_config_best_default_route_find_better (new_best_default_route, + ipconf_iter.current->obj); + } + return new_best_default_route; +} + +/*****************************************************************************/ + static void _notify_addresses (NMIP4Config *self) { @@ -487,6 +577,7 @@ _notify_routes (NMIP4Config *self) { NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); + nm_assert (priv->best_default_route == _nm_ip4_config_best_default_route_find (self)); nm_clear_g_variant (&priv->route_data_variant); nm_clear_g_variant (&priv->routes_variant); _notify (self, PROP_ROUTE_DATA); @@ -1056,8 +1147,7 @@ nm_ip4_config_create_setting (const NMIP4Config *self) nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, self, &route) { NMIPRoute *s_route; - /* Ignore default route. */ - if (!route->plen) + if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route)) continue; /* Ignore routes provided by external sources */ @@ -1133,10 +1223,8 @@ nm_ip4_config_merge (NMIP4Config *dst, const NMIP4Config *src, NMIPConfigMergeFl /* routes */ if (!NM_FLAGS_HAS (merge_flags, NM_IP_CONFIG_MERGE_NO_ROUTES)) { - const NMPlatformIP4Route *route; - - nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, src, &route) - _add_route (dst, NMP_OBJECT_UP_CAST (route), NULL, NULL); + nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, src, NULL) + _add_route (dst, ipconf_iter.current->obj, NULL, NULL); } if (dst_priv->route_metric == -1) @@ -1310,6 +1398,7 @@ nm_ip4_config_subtract (NMIP4Config *dst, const NMIP4Config *src) const NMPlatformIP4Route *r; NMDedupMultiIter ipconf_iter; gboolean changed; + gboolean changed_default_route; g_return_if_fail (src != NULL); g_return_if_fail (dst != NULL); @@ -1349,12 +1438,24 @@ nm_ip4_config_subtract (NMIP4Config *dst, const NMIP4Config *src) /* routes */ changed = FALSE; + changed_default_route = FALSE; nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, src, &r) { + nm_auto_nmpobj const NMPObject *obj_old = NULL; + if (nm_dedup_multi_index_remove_obj (dst_priv->multi_idx, &dst_priv->idx_ip4_routes, NMP_OBJECT_UP_CAST (r), - NULL)) + (gconstpointer *) &obj_old)) { + if (dst_priv->best_default_route == obj_old) { + nm_clear_nmp_object (&dst_priv->best_default_route); + changed_default_route = TRUE; + } changed = TRUE; + } + } + if (changed_default_route) { + _nm_ip_config_best_default_route_set (&dst_priv->best_default_route, + _nm_ip4_config_best_default_route_find (dst)); } if (changed) _notify_routes (dst); @@ -1421,16 +1522,17 @@ nm_ip4_config_intersect (NMIP4Config *dst, const NMIP4Config *src) NMDedupMultiIter ipconf_iter; const NMPlatformIP4Address *a; const NMPlatformIP4Route *r; + const NMPObject *new_best_default_route; gboolean changed; g_return_if_fail (src); g_return_if_fail (dst); - g_object_freeze_notify (G_OBJECT (dst)); - dst_priv = NM_IP4_CONFIG_GET_PRIVATE (dst); src_priv = NM_IP4_CONFIG_GET_PRIVATE (src); + g_object_freeze_notify (G_OBJECT (dst)); + /* addresses */ changed = FALSE; nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, dst, &a) { @@ -1459,17 +1561,24 @@ nm_ip4_config_intersect (NMIP4Config *dst, const NMIP4Config *src) /* routes */ changed = FALSE; + new_best_default_route = NULL; nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, dst, &r) { + const NMPObject *o = NMP_OBJECT_UP_CAST (r); + if (nm_dedup_multi_index_lookup_obj (src_priv->multi_idx, &src_priv->idx_ip4_routes, - NMP_OBJECT_UP_CAST (r))) + o)) { + new_best_default_route = _nm_ip_config_best_default_route_find_better (new_best_default_route, o); continue; + } if (nm_dedup_multi_index_remove_entry (dst_priv->multi_idx, ipconf_iter.current) != 1) nm_assert_not_reached (); changed = TRUE; } + if (_nm_ip_config_best_default_route_set (&dst_priv->best_default_route, new_best_default_route)) + nm_assert (changed); if (changed) _notify_routes (dst); @@ -1509,6 +1618,7 @@ nm_ip4_config_replace (NMIP4Config *dst, const NMIP4Config *src, gboolean *relev const NMIP4ConfigPrivate *src_priv; NMDedupMultiIter ipconf_iter_src, ipconf_iter_dst; const NMDedupMultiHeadEntry *head_entry_src; + const NMPObject *new_best_default_route; g_return_val_if_fail (src != NULL, FALSE); g_return_val_if_fail (dst != NULL, FALSE); @@ -1627,19 +1737,25 @@ nm_ip4_config_replace (NMIP4Config *dst, const NMIP4Config *src, gboolean *relev } if (!are_equal) { has_minor_changes = TRUE; + new_best_default_route = NULL; nm_dedup_multi_index_dirty_set_idx (dst_priv->multi_idx, &dst_priv->idx_ip4_routes); nm_dedup_multi_iter_for_each (&ipconf_iter_src, head_entry_src) { + const NMPObject *o = ipconf_iter_src.current->obj; + const NMPObject *obj_new; + _nm_ip_config_add_obj (dst_priv->multi_idx, &dst_priv->idx_ip4_routes_, dst_priv->ifindex, - ipconf_iter_src.current->obj, + o, NULL, FALSE, TRUE, NULL, - NULL); + &obj_new); + new_best_default_route = _nm_ip_config_best_default_route_find_better (new_best_default_route, obj_new); } nm_dedup_multi_index_dirty_remove_idx (dst_priv->multi_idx, &dst_priv->idx_ip4_routes, FALSE); + _nm_ip_config_best_default_route_set (&dst_priv->best_default_route, new_best_default_route); _notify_routes (dst); } @@ -2028,18 +2144,12 @@ nm_ip4_config_add_address (NMIP4Config *self, const NMPlatformIP4Address *new) void _nmtst_ip4_config_del_address (NMIP4Config *self, guint i) { - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); const NMPlatformIP4Address *a; a = _nmtst_ip4_config_get_address (self, i); - g_return_if_fail (a); - - if (nm_dedup_multi_index_remove_obj (priv->multi_idx, - &priv->idx_ip4_addresses, - NMP_OBJECT_UP_CAST (a), - NULL) != 1) - g_return_if_reached (); - _notify_addresses (self); + if (!nm_ip4_config_nmpobj_remove (self, + NMP_OBJECT_UP_CAST (a))) + g_assert_not_reached (); } guint @@ -2121,8 +2231,10 @@ nm_ip4_config_reset_routes (NMIP4Config *self) NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); if (nm_dedup_multi_index_remove_idx (priv->multi_idx, - &priv->idx_ip4_routes) > 0) + &priv->idx_ip4_routes) > 0) { + nm_clear_nmp_object (&priv->best_default_route); _notify_routes (self); + } } static void @@ -2132,6 +2244,7 @@ _add_route (NMIP4Config *self, const NMPObject **out_obj_new) { NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); + nm_auto_nmpobj const NMPObject *obj_old = NULL; const NMPObject *obj_new_2; nm_assert ((!new) != (!obj_new)); @@ -2145,8 +2258,12 @@ _add_route (NMIP4Config *self, (const NMPlatformObject *) new, TRUE, FALSE, - NULL, + &obj_old, &obj_new_2)) { + if ( priv->best_default_route == obj_old + && obj_old != obj_new_2) + nm_clear_nmp_object (&priv->best_default_route); + _nm_ip_config_best_default_route_merge (&priv->best_default_route, obj_new_2); NM_SET_OUT (out_obj_new, nmp_object_ref (obj_new_2)); _notify_routes (self); } else @@ -2172,7 +2289,7 @@ nm_ip4_config_add_route (NMIP4Config *self, { g_return_if_fail (self); g_return_if_fail (new); - g_return_if_fail (new->plen > 0 && new->plen <= 32); + g_return_if_fail (new->plen <= 32); g_return_if_fail (NM_IP4_CONFIG_GET_PRIVATE (self)->ifindex > 0); _add_route (self, NULL, new, out_obj_new); @@ -2181,18 +2298,12 @@ nm_ip4_config_add_route (NMIP4Config *self, void _nmtst_ip4_config_del_route (NMIP4Config *self, guint i) { - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); const NMPlatformIP4Route *r; r = _nmtst_ip4_config_get_route (self, i); - g_return_if_fail (r); - - if (nm_dedup_multi_index_remove_obj (priv->multi_idx, - &priv->idx_ip4_routes, - NMP_OBJECT_UP_CAST (r), - NULL) != 1) - g_return_if_reached (); - _notify_routes (self); + if (!nm_ip4_config_nmpobj_remove (self, + NMP_OBJECT_UP_CAST (r))) + g_assert_not_reached (); } guint @@ -2701,6 +2812,84 @@ nm_ip4_config_get_metered (const NMIP4Config *self) /*****************************************************************************/ +const NMPObject * +nm_ip4_config_nmpobj_lookup (const NMIP4Config *self, const NMPObject *needle) +{ + const NMIP4ConfigPrivate *priv; + const NMDedupMultiIdxType *idx_type; + + g_return_val_if_fail (NM_IS_IP4_CONFIG (self), NULL); + + priv = NM_IP4_CONFIG_GET_PRIVATE (self); + switch (NMP_OBJECT_GET_TYPE (needle)) { + case NMP_OBJECT_TYPE_IP4_ADDRESS: + idx_type = &priv->idx_ip4_addresses; + break; + case NMP_OBJECT_TYPE_IP4_ROUTE: + idx_type = &priv->idx_ip4_routes; + break; + default: + g_return_val_if_reached (NULL); + } + + return nm_dedup_multi_entry_get_obj (nm_dedup_multi_index_lookup_obj (priv->multi_idx, + idx_type, + needle)); +} + +gboolean +nm_ip4_config_nmpobj_remove (NMIP4Config *self, + const NMPObject *needle) +{ + NMIP4ConfigPrivate *priv; + NMDedupMultiIdxType *idx_type; + nm_auto_nmpobj const NMPObject *obj_old = NULL; + guint n; + + g_return_val_if_fail (NM_IS_IP4_CONFIG (self), FALSE); + + priv = NM_IP4_CONFIG_GET_PRIVATE (self); + switch (NMP_OBJECT_GET_TYPE (needle)) { + case NMP_OBJECT_TYPE_IP4_ADDRESS: + idx_type = &priv->idx_ip4_addresses; + break; + case NMP_OBJECT_TYPE_IP4_ROUTE: + idx_type = &priv->idx_ip4_routes; + break; + default: + g_return_val_if_reached (FALSE); + } + + n = nm_dedup_multi_index_remove_obj (priv->multi_idx, + idx_type, + needle, + (gconstpointer *) &obj_old); + if (n != 1) { + nm_assert (n == 0); + return FALSE; + } + + nm_assert (NMP_OBJECT_GET_TYPE (obj_old) == NMP_OBJECT_GET_TYPE (needle)); + + switch (NMP_OBJECT_GET_TYPE (obj_old)) { + case NMP_OBJECT_TYPE_IP4_ADDRESS: + _notify_addresses (self); + break; + case NMP_OBJECT_TYPE_IP4_ROUTE: + if (priv->best_default_route == obj_old) { + _nm_ip_config_best_default_route_set (&priv->best_default_route, + _nm_ip4_config_best_default_route_find (self)); + } + _notify_routes (self); + break; + default: + nm_assert_not_reached (); + } + return TRUE; +} + +/*****************************************************************************/ + static inline void hash_u32 (GChecksum *sum, guint32 n) { @@ -3056,6 +3245,8 @@ finalize (GObject *object) NMIP4Config *self = NM_IP4_CONFIG (object); NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); + nm_clear_nmp_object (&priv->best_default_route); + nm_dedup_multi_index_remove_idx (priv->multi_idx, &priv->idx_ip4_addresses); nm_dedup_multi_index_remove_idx (priv->multi_idx, &priv->idx_ip4_routes); diff --git a/src/nm-ip4-config.h b/src/nm-ip4-config.h index 1113c9928e..7db858b7b5 100644 --- a/src/nm-ip4-config.h +++ b/src/nm-ip4-config.h @@ -75,6 +75,25 @@ nm_ip_config_iter_ip4_route_next (NMDedupMultiIter *ipconf_iter, const NMPlatfor /*****************************************************************************/ +static inline gboolean +nm_ip_config_best_default_route_is (const NMPObject *obj) +{ + const NMPlatformIPRoute *r = NMP_OBJECT_CAST_IP_ROUTE (obj); + + /* return whether @obj is considered a default-route, that is, a route + * as added by NetworkManager. E.g. if the route is not in the main-table, + * it's considered just like a regular route. */ + return r + && !r->table_coerced + && NM_PLATFORM_IP_ROUTE_IS_DEFAULT (r); +} + +const NMPObject *_nm_ip_config_best_default_route_find_better (const NMPObject *obj_cur, const NMPObject *obj_cmp); +gboolean _nm_ip_config_best_default_route_set (const NMPObject **best_default_route, const NMPObject *new_candidate); +gboolean _nm_ip_config_best_default_route_merge (const NMPObject **best_default_route, const NMPObject *new_candidate); + +/*****************************************************************************/ + gboolean nm_ip_config_obj_id_equal_ip4_address (const NMPlatformIP4Address *a, const NMPlatformIP4Address *b); gboolean nm_ip_config_obj_id_equal_ip6_address (const NMPlatformIP6Address *a, @@ -163,6 +182,9 @@ gboolean nm_ip4_config_has_gateway (const NMIP4Config *self); guint32 nm_ip4_config_get_gateway (const NMIP4Config *self); gint64 nm_ip4_config_get_route_metric (const NMIP4Config *self); +const NMPObject *nm_ip4_config_best_default_route_get (const NMIP4Config *self); +const NMPObject *_nm_ip4_config_best_default_route_find (const NMIP4Config *self); + const NMDedupMultiHeadEntry *nm_ip4_config_lookup_addresses (const NMIP4Config *self); void nm_ip4_config_reset_addresses (NMIP4Config *self); void nm_ip4_config_add_address (NMIP4Config *self, const NMPlatformIP4Address *address); @@ -234,6 +256,11 @@ NMIPConfigSource nm_ip4_config_get_mtu_source (const NMIP4Config *self); void nm_ip4_config_set_metered (NMIP4Config *self, gboolean metered); gboolean nm_ip4_config_get_metered (const NMIP4Config *self); +const NMPObject *nm_ip4_config_nmpobj_lookup (const NMIP4Config *self, + const NMPObject *needle); +gboolean nm_ip4_config_nmpobj_remove (NMIP4Config *self, + const NMPObject *needle); + void nm_ip4_config_hash (const NMIP4Config *self, GChecksum *sum, gboolean dns_only); gboolean nm_ip4_config_equal (const NMIP4Config *a, const NMIP4Config *b); diff --git a/src/nm-ip6-config.c b/src/nm-ip6-config.c index 641e734340..0aad49d54d 100644 --- a/src/nm-ip6-config.c +++ b/src/nm-ip6-config.c @@ -72,6 +72,7 @@ typedef struct { GVariant *route_data_variant; GVariant *routes_variant; NMDedupMultiIndex *multi_idx; + const NMPObject *best_default_route; union { NMIPConfigDedupMultiIdxType idx_ip6_addresses_; NMDedupMultiIdxType idx_ip6_addresses; @@ -177,6 +178,29 @@ nm_ip_config_iter_ip6_route_init (NMDedupMultiIter *ipconf_iter, const NMIP6Conf /*****************************************************************************/ +const NMPObject * +nm_ip6_config_best_default_route_get (const NMIP6Config *self) +{ + g_return_val_if_fail (NM_IS_IP6_CONFIG (self), NULL); + + return NM_IP6_CONFIG_GET_PRIVATE (self)->best_default_route; +} + +const NMPObject * +_nm_ip6_config_best_default_route_find (const NMIP6Config *self) +{ + NMDedupMultiIter ipconf_iter; + const NMPObject *new_best_default_route = NULL; + + nm_ip_config_iter_ip6_route_for_each (&ipconf_iter, self, NULL) { + new_best_default_route = _nm_ip_config_best_default_route_find_better (new_best_default_route, + ipconf_iter.current->obj); + } + return new_best_default_route; +} + +/*****************************************************************************/ + static void _notify_addresses (NMIP6Config *self) { @@ -193,6 +217,7 @@ _notify_routes (NMIP6Config *self) { NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self); + nm_assert (priv->best_default_route == _nm_ip6_config_best_default_route_find (self)); nm_clear_g_variant (&priv->route_data_variant); nm_clear_g_variant (&priv->routes_variant); _notify (self, PROP_ROUTE_DATA); @@ -781,8 +806,7 @@ nm_ip6_config_create_setting (const NMIP6Config *self) if (IN6_IS_ADDR_LINKLOCAL (&route->network)) continue; - /* Ignore default route. */ - if (!route->plen) + if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route)) continue; /* Ignore routes provided by external sources */ @@ -857,10 +881,8 @@ nm_ip6_config_merge (NMIP6Config *dst, const NMIP6Config *src, NMIPConfigMergeFl /* routes */ if (!NM_FLAGS_HAS (merge_flags, NM_IP_CONFIG_MERGE_NO_ROUTES)) { - const NMPlatformIP6Route *route; - - nm_ip_config_iter_ip6_route_for_each (&ipconf_iter, src, &route) - _add_route (dst, NMP_OBJECT_UP_CAST (route), NULL, NULL); + nm_ip_config_iter_ip6_route_for_each (&ipconf_iter, src, NULL) + _add_route (dst, ipconf_iter.current->obj, NULL, NULL); } if (dst_priv->route_metric == -1) @@ -997,6 +1019,7 @@ nm_ip6_config_subtract (NMIP6Config *dst, const NMIP6Config *src) NMDedupMultiIter ipconf_iter; const struct in6_addr *dst_tmp, *src_tmp; gboolean changed; + gboolean changed_default_route; g_return_if_fail (src != NULL); g_return_if_fail (dst != NULL); @@ -1037,12 +1060,24 @@ nm_ip6_config_subtract (NMIP6Config *dst, const NMIP6Config *src) /* routes */ changed = FALSE; + changed_default_route = FALSE; nm_ip_config_iter_ip6_route_for_each (&ipconf_iter, src, &r) { + nm_auto_nmpobj const NMPObject *obj_old = NULL; + if (nm_dedup_multi_index_remove_obj (dst_priv->multi_idx, &dst_priv->idx_ip6_routes, NMP_OBJECT_UP_CAST (r), - NULL)) + (gconstpointer *) &obj_old)) { + if (dst_priv->best_default_route == obj_old) { + nm_clear_nmp_object (&dst_priv->best_default_route); + changed_default_route = TRUE; + } changed = TRUE; + } + } + if (changed_default_route) { + _nm_ip_config_best_default_route_set (&dst_priv->best_default_route, + _nm_ip6_config_best_default_route_find (dst)); } if (changed) _notify_routes (dst); @@ -1088,6 +1123,7 @@ nm_ip6_config_intersect (NMIP6Config *dst, const NMIP6Config *src) const NMPlatformIP6Address *a; const NMPlatformIP6Route *r; gboolean changed; + const NMPObject *new_best_default_route; g_return_if_fail (src); g_return_if_fail (dst); @@ -1128,17 +1164,24 @@ nm_ip6_config_intersect (NMIP6Config *dst, const NMIP6Config *src) /* routes */ changed = FALSE; + new_best_default_route = NULL; nm_ip_config_iter_ip6_route_for_each (&ipconf_iter, dst, &r) { + const NMPObject *o = NMP_OBJECT_UP_CAST (r); + if (nm_dedup_multi_index_lookup_obj (src_priv->multi_idx, &src_priv->idx_ip6_routes, - NMP_OBJECT_UP_CAST (r))) + o)) { + new_best_default_route = _nm_ip_config_best_default_route_find_better (new_best_default_route, o); continue; + } if (nm_dedup_multi_index_remove_entry (dst_priv->multi_idx, ipconf_iter.current) != 1) nm_assert_not_reached (); changed = TRUE; } + if (_nm_ip_config_best_default_route_set (&dst_priv->best_default_route, new_best_default_route)) + nm_assert (changed); if (changed) _notify_routes (dst); @@ -1175,6 +1218,7 @@ nm_ip6_config_replace (NMIP6Config *dst, const NMIP6Config *src, gboolean *relev const NMIP6ConfigPrivate *src_priv; NMDedupMultiIter ipconf_iter_src, ipconf_iter_dst; const NMDedupMultiHeadEntry *head_entry_src; + const NMPObject *new_best_default_route; g_return_val_if_fail (NM_IS_IP6_CONFIG (src), FALSE); g_return_val_if_fail (NM_IS_IP6_CONFIG (dst), FALSE); @@ -1293,19 +1337,25 @@ nm_ip6_config_replace (NMIP6Config *dst, const NMIP6Config *src, gboolean *relev } if (!are_equal) { has_minor_changes = TRUE; + new_best_default_route = NULL; nm_dedup_multi_index_dirty_set_idx (dst_priv->multi_idx, &dst_priv->idx_ip6_routes); nm_dedup_multi_iter_for_each (&ipconf_iter_src, head_entry_src) { + const NMPObject *o = ipconf_iter_src.current->obj; + const NMPObject *obj_new; + _nm_ip_config_add_obj (dst_priv->multi_idx, &dst_priv->idx_ip6_routes_, dst_priv->ifindex, - ipconf_iter_src.current->obj, + o, NULL, FALSE, TRUE, NULL, - NULL); + &obj_new); + new_best_default_route = _nm_ip_config_best_default_route_find_better (new_best_default_route, obj_new); } nm_dedup_multi_index_dirty_remove_idx (dst_priv->multi_idx, &dst_priv->idx_ip6_routes, FALSE); + _nm_ip_config_best_default_route_set (&dst_priv->best_default_route, new_best_default_route); _notify_routes (dst); } @@ -1631,18 +1681,12 @@ nm_ip6_config_add_address (NMIP6Config *self, const NMPlatformIP6Address *new) void _nmtst_ip6_config_del_address (NMIP6Config *self, guint i) { - NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self); const NMPlatformIP6Address *a; a = _nmtst_ip6_config_get_address (self, i); - g_return_if_fail (a); - - if (nm_dedup_multi_index_remove_obj (priv->multi_idx, - &priv->idx_ip6_addresses, - NMP_OBJECT_UP_CAST (a), - NULL) != 1) - g_return_if_reached (); - _notify_addresses (self); + if (!nm_ip6_config_nmpobj_remove (self, + NMP_OBJECT_UP_CAST (a))) + g_assert_not_reached (); } guint @@ -1767,6 +1811,7 @@ nm_ip6_config_reset_routes_ndisc (NMIP6Config *self, NMIP6ConfigPrivate *priv; guint i; gboolean changed = FALSE; + const NMPObject *new_best_default_route; g_return_if_fail (NM_IS_IP6_CONFIG (self)); @@ -1776,9 +1821,11 @@ nm_ip6_config_reset_routes_ndisc (NMIP6Config *self, nm_dedup_multi_index_dirty_set_idx (priv->multi_idx, &priv->idx_ip6_routes); + new_best_default_route = NULL; for (i = 0; i < routes_n; i++) { const NMNDiscRoute *ndisc_route = &routes[i]; NMPObject obj; + const NMPObject *obj_new; NMPlatformIP6Route *r; nmp_object_stackinit (&obj, NMP_OBJECT_TYPE_IP6_ROUTE, NULL); @@ -1798,10 +1845,14 @@ nm_ip6_config_reset_routes_ndisc (NMIP6Config *self, FALSE, TRUE, NULL, - NULL)) + &obj_new)) changed = TRUE; + new_best_default_route = _nm_ip_config_best_default_route_find_better (new_best_default_route, obj_new); } + if (_nm_ip_config_best_default_route_set (&priv->best_default_route, new_best_default_route)) + changed = TRUE; + if (nm_dedup_multi_index_dirty_remove_idx (priv->multi_idx, &priv->idx_ip6_routes, FALSE) > 0) changed = TRUE; @@ -1815,8 +1866,10 @@ nm_ip6_config_reset_routes (NMIP6Config *self) NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self); if (nm_dedup_multi_index_remove_idx (priv->multi_idx, - &priv->idx_ip6_routes) > 0) + &priv->idx_ip6_routes) > 0) { + nm_clear_nmp_object (&priv->best_default_route); _notify_routes (self); + } } static void @@ -1826,6 +1879,7 @@ _add_route (NMIP6Config *self, const NMPObject **out_obj_new) { NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self); + nm_auto_nmpobj const NMPObject *obj_old = NULL; const NMPObject *obj_new_2; nm_assert ((!new) != (!obj_new)); @@ -1839,8 +1893,12 @@ _add_route (NMIP6Config *self, (const NMPlatformObject *) new, TRUE, FALSE, - NULL, + &obj_old, &obj_new_2)) { + if ( priv->best_default_route == obj_old + && obj_old != obj_new_2) + nm_clear_nmp_object (&priv->best_default_route); + _nm_ip_config_best_default_route_merge (&priv->best_default_route, obj_new_2); NM_SET_OUT (out_obj_new, nmp_object_ref (obj_new_2)); _notify_routes (self); } else @@ -1866,7 +1924,7 @@ nm_ip6_config_add_route (NMIP6Config *self, { g_return_if_fail (self); g_return_if_fail (new); - g_return_if_fail (new->plen > 0 && new->plen <= 128); + g_return_if_fail (new->plen <= 128); g_return_if_fail (NM_IP6_CONFIG_GET_PRIVATE (self)->ifindex > 0); _add_route (self, NULL, new, out_obj_new); @@ -1875,18 +1933,12 @@ nm_ip6_config_add_route (NMIP6Config *self, void _nmtst_ip6_config_del_route (NMIP6Config *self, guint i) { - NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self); const NMPlatformIP6Route *r; r = _nmtst_ip6_config_get_route (self, i); - g_return_if_fail (r); - - if (nm_dedup_multi_index_remove_obj (priv->multi_idx, - &priv->idx_ip6_routes, - NMP_OBJECT_UP_CAST (r), - NULL) != 1) - g_return_if_reached (); - _notify_routes (self); + if (!nm_ip6_config_nmpobj_remove (self, + NMP_OBJECT_UP_CAST (r))) + g_assert_not_reached (); } guint @@ -2251,6 +2303,84 @@ nm_ip6_config_get_mss (const NMIP6Config *self) /*****************************************************************************/ +const NMPObject * +nm_ip6_config_nmpobj_lookup (const NMIP6Config *self, const NMPObject *needle) +{ + const NMIP6ConfigPrivate *priv; + const NMDedupMultiIdxType *idx_type; + + g_return_val_if_fail (NM_IS_IP6_CONFIG (self), NULL); + + priv = NM_IP6_CONFIG_GET_PRIVATE (self); + switch (NMP_OBJECT_GET_TYPE (needle)) { + case NMP_OBJECT_TYPE_IP6_ADDRESS: + idx_type = &priv->idx_ip6_addresses; + break; + case NMP_OBJECT_TYPE_IP6_ROUTE: + idx_type = &priv->idx_ip6_routes; + break; + default: + g_return_val_if_reached (NULL); + } + + return nm_dedup_multi_entry_get_obj (nm_dedup_multi_index_lookup_obj (priv->multi_idx, + idx_type, + needle)); +} + +gboolean +nm_ip6_config_nmpobj_remove (NMIP6Config *self, + const NMPObject *needle) +{ + NMIP6ConfigPrivate *priv; + NMDedupMultiIdxType *idx_type; + nm_auto_nmpobj const NMPObject *obj_old = NULL; + guint n; + + g_return_val_if_fail (NM_IS_IP6_CONFIG (self), FALSE); + + priv = NM_IP6_CONFIG_GET_PRIVATE (self); + switch (NMP_OBJECT_GET_TYPE (needle)) { + case NMP_OBJECT_TYPE_IP6_ADDRESS: + idx_type = &priv->idx_ip6_addresses; + break; + case NMP_OBJECT_TYPE_IP6_ROUTE: + idx_type = &priv->idx_ip6_routes; + break; + default: + g_return_val_if_reached (FALSE); + } + + n = nm_dedup_multi_index_remove_obj (priv->multi_idx, + idx_type, + needle, + (gconstpointer *) &obj_old); + if (n != 1) { + nm_assert (n == 0); + return FALSE; + } + + nm_assert (NMP_OBJECT_GET_TYPE (obj_old) == NMP_OBJECT_GET_TYPE (needle)); + + switch (NMP_OBJECT_GET_TYPE (obj_old)) { + case NMP_OBJECT_TYPE_IP6_ADDRESS: + _notify_addresses (self); + break; + case NMP_OBJECT_TYPE_IP6_ROUTE: + if (priv->best_default_route == obj_old) { + _nm_ip_config_best_default_route_set (&priv->best_default_route, + _nm_ip6_config_best_default_route_find (self)); + } + _notify_routes (self); + break; + default: + nm_assert_not_reached (); + } + return TRUE; +} + +/*****************************************************************************/ + static inline void hash_u32 (GChecksum *sum, guint32 n) { @@ -2611,6 +2741,8 @@ finalize (GObject *object) NMIP6Config *self = NM_IP6_CONFIG (object); NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self); + nm_clear_nmp_object (&priv->best_default_route); + nm_dedup_multi_index_remove_idx (priv->multi_idx, &priv->idx_ip6_addresses); nm_dedup_multi_index_remove_idx (priv->multi_idx, &priv->idx_ip6_routes); diff --git a/src/nm-ip6-config.h b/src/nm-ip6-config.h index 0a7fe17498..ec8bee881d 100644 --- a/src/nm-ip6-config.h +++ b/src/nm-ip6-config.h @@ -127,6 +127,9 @@ void nm_ip6_config_set_gateway (NMIP6Config *self, const struct in6_addr *); const struct in6_addr *nm_ip6_config_get_gateway (const NMIP6Config *self); gint64 nm_ip6_config_get_route_metric (const NMIP6Config *self); +const NMPObject *nm_ip6_config_best_default_route_get (const NMIP6Config *self); +const NMPObject *_nm_ip6_config_best_default_route_find (const NMIP6Config *self); + const NMDedupMultiHeadEntry *nm_ip6_config_lookup_addresses (const NMIP6Config *self); void nm_ip6_config_reset_addresses (NMIP6Config *self); void nm_ip6_config_add_address (NMIP6Config *self, const NMPlatformIP6Address *address); @@ -184,6 +187,11 @@ gint nm_ip6_config_get_dns_priority (const NMIP6Config *self); void nm_ip6_config_set_mss (NMIP6Config *self, guint32 mss); guint32 nm_ip6_config_get_mss (const NMIP6Config *self); +const NMPObject *nm_ip6_config_nmpobj_lookup (const NMIP6Config *self, + const NMPObject *needle); +gboolean nm_ip6_config_nmpobj_remove (NMIP6Config *self, + const NMPObject *needle); + void nm_ip6_config_hash (const NMIP6Config *self, GChecksum *sum, gboolean dns_only); gboolean nm_ip6_config_equal (const NMIP6Config *a, const NMIP6Config *b); diff --git a/src/nm-pacrunner-manager.c b/src/nm-pacrunner-manager.c index 2558ca0f30..08e10e40db 100644 --- a/src/nm-pacrunner-manager.c +++ b/src/nm-pacrunner-manager.c @@ -192,6 +192,8 @@ get_ip4_domains (GPtrArray *domains, NMIP4Config *ip4) } nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, ip4, &routes) { + if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (routes)) + continue; cidr = g_strdup_printf ("%s/%u", nm_utils_inet4_ntop (routes->network, NULL), routes->plen); @@ -225,6 +227,8 @@ get_ip6_domains (GPtrArray *domains, NMIP6Config *ip6) } nm_ip_config_iter_ip6_route_for_each (&ipconf_iter, ip6, &routes) { + if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (routes)) + continue; cidr = g_strdup_printf ("%s/%u", nm_utils_inet6_ntop (&routes->network, NULL), routes->plen); From 77ec302714795f905301d500b9aab6c88001f32e Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Wed, 30 Aug 2017 11:46:42 +0200 Subject: [PATCH 2/2] core: rework handling of default-routes and drop NMDefaultRouteManager Remove NMDefaultRouteManager. Instead, add the default-route to the NMIP4Config/NMIP6Config instance. This basically reverts commit e8824f6a5205ffcf761abd3e0897a22b254c7797. We added NMDefaultRouteManager because we used the corresponding to `ip route replace` when configuring routes. That would replace default-routes on other interfaces so we needed a central manager to coordinate routes. Now, we use the corresponding of `ip route append` to configure routes, and each interface can configure routes indepdentently. In NMDevice, when creating the default-route, ignore @auto_method for external devices. We shall not touch these devices. Especially the code in NMPolicy regarding selection of the best-device seems wrong. It probably needs further adjustments in the future. Especially get_best_ip_config() should be replaced, because this distinction VPN vs. devices seems wrong to me. Thereby, remove the @ignore_never_default argument. It was added by commit bb750260045239ab85574366bae8102eff8058cc, I don't think it's needed anymore. This brings another change. Now that we track default-routes in NMIP4Config/NMIP6Config, they are also exposed on D-Bus like regular routes. I think that makes sense, but it is a change in behavior, as previously such routes were not exposed there. --- Makefile.am | 2 - src/devices/nm-device.c | 376 +++----- src/devices/nm-device.h | 4 +- src/nm-default-route-manager.c | 1561 -------------------------------- src/nm-default-route-manager.h | 64 -- src/nm-ip4-config.c | 2 - src/nm-ip6-config.c | 2 - src/nm-netns.c | 10 - src/nm-netns.h | 1 - src/nm-policy.c | 202 ++++- src/nm-types.h | 1 - src/platform/nm-platform.c | 5 - src/vpn/nm-vpn-connection.c | 31 +- 13 files changed, 283 insertions(+), 1978 deletions(-) delete mode 100644 src/nm-default-route-manager.c delete mode 100644 src/nm-default-route-manager.h diff --git a/Makefile.am b/Makefile.am index 60b1adf175..2d6e21b922 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1494,8 +1494,6 @@ src_libNetworkManager_la_SOURCES = \ src/nm-dcb.h \ src/nm-netns.c \ src/nm-netns.h \ - src/nm-default-route-manager.c \ - src/nm-default-route-manager.h \ src/nm-dhcp4-config.c \ src/nm-dhcp4-config.h \ src/nm-dhcp6-config.c \ diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 364f142a72..aadd2cd6cf 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -65,7 +65,6 @@ #include "nm-utils/c-list.h" #include "dns/nm-dns-manager.h" #include "nm-core-internal.h" -#include "nm-default-route-manager.h" #include "systemd/nm-sd.h" #include "nm-lldp-listener.h" #include "nm-audit-manager.h" @@ -360,14 +359,12 @@ typedef struct _NMDevicePrivate { NMIP4Config * ext_ip4_config; /* Stuff added outside NM */ NMIP4Config * wwan_ip4_config; /* WWAN configuration */ GSList * vpn4_configs; /* VPNs which use this device */ - struct { - bool v4_has; - bool v4_is_assumed; - bool v6_has; - bool v6_is_assumed; - NMPlatformIP4Route v4; - NMPlatformIP6Route v6; - } default_route; + + const NMPObject *default_route4; + const NMPObject *default_route6; + const NMPObject *default_routegw4; + const NMPObject *default_routegw6; + bool v4_has_shadowed_routes; const char *ip4_rp_filter; @@ -1691,62 +1688,33 @@ out: return nm_utils_ip_route_metric_normalize (addr_family, route_metric); } -static void -_update_default_route (NMDevice *self, int addr_family, gboolean has, gboolean is_assumed) +const NMPObject * +nm_device_get_best_default_route (NMDevice *self, + int addr_family) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - bool *p_has, *p_is_assumed; - nm_assert (NM_IN_SET (addr_family, 0, AF_INET, AF_INET6)); - - if (addr_family == AF_INET) { - p_has = &priv->default_route.v4_has; - p_is_assumed = &priv->default_route.v4_is_assumed; - } else { - p_has = &priv->default_route.v6_has; - p_is_assumed = &priv->default_route.v6_is_assumed; + /* Prefer the best default-route we have in ipx_config. + * + * Otherwise, use priv->default_routeX. Usually, we would + * expect that if ipx_config has no default route, then + * also priv->default_routeX is unset. This is just to cover + * a case I cannot imagine now. */ + switch (addr_family) { + case AF_INET: + return (priv->ip4_config ? nm_ip4_config_best_default_route_get (priv->ip4_config) : NULL) + ?: priv->default_route4; + case AF_INET6: + return (priv->ip6_config ? nm_ip6_config_best_default_route_get (priv->ip6_config) : NULL) + ?: priv->default_route6; + case AF_UNSPEC: + return (priv->ip4_config ? nm_ip4_config_best_default_route_get (priv->ip4_config) : NULL) + ?: priv->default_route4 + ?: (priv->ip6_config ? nm_ip6_config_best_default_route_get (priv->ip6_config) : NULL) + ?: priv->default_route6; + default: + g_return_val_if_reached (NULL); } - - if (*p_has == has && *p_is_assumed == is_assumed) - return; - - *p_has = has; - *p_is_assumed = is_assumed; - - if (addr_family == AF_INET) - nm_default_route_manager_ip4_update_default_route (nm_netns_get_default_route_manager (priv->netns), self); - else - nm_default_route_manager_ip6_update_default_route (nm_netns_get_default_route_manager (priv->netns), self); -} - -const NMPlatformIP4Route * -nm_device_get_ip4_default_route (NMDevice *self, gboolean *out_is_assumed) -{ - NMDevicePrivate *priv; - - g_return_val_if_fail (NM_IS_DEVICE (self), NULL); - - priv = NM_DEVICE_GET_PRIVATE (self); - - if (out_is_assumed) - *out_is_assumed = priv->default_route.v4_is_assumed; - - return priv->default_route.v4_has ? &priv->default_route.v4 : NULL; -} - -const NMPlatformIP6Route * -nm_device_get_ip6_default_route (NMDevice *self, gboolean *out_is_assumed) -{ - NMDevicePrivate *priv; - - g_return_val_if_fail (NM_IS_DEVICE (self), NULL); - - priv = NM_DEVICE_GET_PRIVATE (self); - - if (out_is_assumed) - *out_is_assumed = priv->default_route.v6_is_assumed; - - return priv->default_route.v6_has ? &priv->default_route.v6 : NULL; } const char * @@ -1863,7 +1831,7 @@ update_connectivity_state (NMDevice *self, NMConnectivityState state) /* If the connectivity check is disabled, make an optimistic guess. */ if (state == NM_CONNECTIVITY_UNKNOWN) { if (priv->state == NM_DEVICE_STATE_ACTIVATED) { - if (priv->default_route.v4_has || priv->default_route.v6_has) + if (nm_device_get_best_default_route (self, AF_UNSPEC)) state = NM_CONNECTIVITY_FULL; else state = NM_CONNECTIVITY_LIMITED; @@ -1883,12 +1851,12 @@ update_connectivity_state (NMDevice *self, NMConnectivityState state) if ( priv->state == NM_DEVICE_STATE_ACTIVATED && !nm_device_sys_iface_state_is_external (self)) { - if ( priv->default_route.v4_has + if ( nm_device_get_best_default_route (self, AF_INET) && !ip4_config_merge_and_apply (self, NULL, TRUE)) - _LOGW (LOGD_IP4, "Failed to update IPv4 default route metric"); - if ( priv->default_route.v6_has + _LOGW (LOGD_IP4, "Failed to update IPv4 route metric"); + if ( nm_device_get_best_default_route (self, AF_INET6) && !ip6_config_merge_and_apply (self, TRUE)) - _LOGW (LOGD_IP6, "Failed to update IPv6 default route metric"); + _LOGW (LOGD_IP6, "Failed to update IPv6 route metric"); } } } @@ -2002,7 +1970,7 @@ concheck_periodic_update (NMDevice *self) gboolean check_enable; check_enable = (priv->state == NM_DEVICE_STATE_ACTIVATED) - && (priv->default_route.v4_has || priv->default_route.v6_has); + && nm_device_get_best_default_route (self, AF_UNSPEC); if (check_enable && !priv->concheck_periodic_id) { /* We just gained a default route. Enable periodic checking. */ @@ -2864,7 +2832,7 @@ ip4_rp_filter_update (NMDevice *self) const char *ip4_rp_filter; if ( priv->v4_has_shadowed_routes - || priv->default_route.v4_has) { + || nm_device_get_best_default_route (self, AF_INET)) { if (nm_device_ipv4_sysctl_get_uint32 (self, "rp_filter", 0) != 1) { /* Don't touch the rp_filter if it's not strict. */ return; @@ -3910,15 +3878,6 @@ nm_device_removed (NMDevice *self, gboolean unconfigure_ip_config) if (!unconfigure_ip_config) return; - /* Clean up IP configs; this does not actually deconfigure the - * interface, it just clears the configuration to which policy - * is reacting via NM_DEVICE_IP4_CONFIG_CHANGED/NM_DEVICE_IP6_CONFIG_CHANGED - * signal. As NMPolicy registered the NMIPxConfig instances in NMDnsManager, - * these would be leaked otherwise. */ - _update_default_route (self, AF_INET, priv->default_route.v4_has, TRUE); - _update_default_route (self, AF_INET6, priv->default_route.v6_has, TRUE); - _update_default_route (self, AF_INET, FALSE, TRUE); - _update_default_route (self, AF_INET6, FALSE, TRUE); nm_device_set_ip4_config (self, NULL, 0, FALSE); nm_device_set_ip6_config (self, NULL, FALSE); } @@ -5516,49 +5475,6 @@ ipv4ll_start (NMDevice *self) /*****************************************************************************/ -static gboolean -_device_get_default_route_from_platform (NMDevice *self, int addr_family, NMPlatformIPRoute *out_route) -{ - int ifindex = nm_device_get_ip_ifindex (self); - const NMDedupMultiHeadEntry *pl_head_entry; - NMDedupMultiIter iter; - const NMPObject *plobj = NULL; - const NMPlatformIPRoute *route = NULL; - guint32 route_metric = G_MAXUINT32; - - pl_head_entry = nm_platform_lookup_route_default (nm_device_get_platform (self), - addr_family == AF_INET - ? NMP_OBJECT_TYPE_IP4_ROUTE - : NMP_OBJECT_TYPE_IP6_ROUTE); - nmp_cache_iter_for_each (&iter, pl_head_entry, &plobj) { - guint32 m; - const NMPlatformIPRoute *r = NMP_OBJECT_CAST_IP_ROUTE (plobj); - - if ( r->ifindex != ifindex - || r->rt_source == NM_IP_CONFIG_SOURCE_RTPROT_KERNEL - || r->table_coerced) - continue; - - /* if there are several default routes, find the one with the best metric */ - m = nm_utils_ip_route_metric_normalize (addr_family, r->metric); - if (!route || m < route_metric) { - route = NMP_OBJECT_CAST_IP_ROUTE (plobj); - route_metric = m; - } - } - - if (route) { - if (addr_family == AF_INET) - *((NMPlatformIP4Route *) out_route) = *((NMPlatformIP4Route *) route); - else - *((NMPlatformIP6Route *) out_route) = *((NMPlatformIP6Route *) route); - return TRUE; - } - return FALSE; -} - -/*****************************************************************************/ - static void ensure_con_ip4_config (NMDevice *self) { @@ -5648,14 +5564,13 @@ ip4_config_merge_and_apply (NMDevice *self, NMConnection *connection; gboolean success; NMIP4Config *composite; - gboolean has_direct_route; const guint32 default_route_metric = nm_device_get_ip4_route_metric (self); guint32 gateway; gboolean connection_has_default_route, connection_is_never_default; gboolean ignore_auto_routes = FALSE; gboolean ignore_auto_dns = FALSE; - gboolean auto_method = FALSE; GSList *iter; + NMPlatformIP4Route default_route; /* Merge all the configs into the composite config */ if (config) { @@ -5671,10 +5586,6 @@ ip4_config_merge_and_apply (NMDevice *self, if (s_ip4) { ignore_auto_routes = nm_setting_ip_config_get_ignore_auto_routes (s_ip4); ignore_auto_dns = nm_setting_ip_config_get_ignore_auto_dns (s_ip4); - - if (nm_streq0 (nm_setting_ip_config_get_method (s_ip4), - NM_SETTING_IP4_CONFIG_METHOD_AUTO)) - auto_method = TRUE; } } @@ -5718,40 +5629,17 @@ ip4_config_merge_and_apply (NMDevice *self, if (priv->con_ip4_config) nm_ip4_config_merge (composite, priv->con_ip4_config, NM_IP_CONFIG_MERGE_DEFAULT); - /* Add the default route. - * - * We keep track of the default route of a device in a private field. - * NMDevice needs to know the default route at this point, because the gateway - * might require a direct route (see below). - * - * But also, we don't want to add the default route to priv->ip4_config, - * because the default route from the setting might not be the same that - * NMDefaultRouteManager eventually configures (because the it might - * tweak the effective metric). - */ - - /* unless we come to a different conclusion below, we have no default route and - * the route is assumed. */ - priv->default_route.v4_has = FALSE; - priv->default_route.v4_is_assumed = TRUE; + /* Add the default route... */ if (!commit) { /* during a non-commit event, we always pickup whatever is configured. */ goto END_ADD_DEFAULT_ROUTE; } - /* a generated-assumed connection detects the default route from the platform, - * but if the IP method is automatic we need to update the default route to - * maintain connectivity. - */ - if (nm_device_sys_iface_state_is_external (self) && !auto_method) + /* for external connections, we always pickup whatever is configured. */ + if (nm_device_sys_iface_state_is_external (self)) goto END_ADD_DEFAULT_ROUTE; - /* At this point, we treat assumed and non-assumed connections alike. - * For assumed connections we do that because we still manage RA and DHCP - * leases for them, so we must extend/update the default route on commits. - */ - connection_has_default_route = nm_utils_connection_has_default_route (connection, AF_INET, &connection_is_never_default); @@ -5763,9 +5651,8 @@ ip4_config_merge_and_apply (NMDevice *self, goto END_ADD_DEFAULT_ROUTE; } - /* we are about to commit (for a non-assumed connection). Enforce whatever we have - * configured. */ - priv->default_route.v4_is_assumed = FALSE; + nm_clear_nmp_object (&priv->default_route4); + nm_clear_nmp_object (&priv->default_routegw4); if (!connection_has_default_route) goto END_ADD_DEFAULT_ROUTE; @@ -5780,38 +5667,26 @@ ip4_config_merge_and_apply (NMDevice *self, && nm_device_get_device_type (self) != NM_DEVICE_TYPE_MODEM) goto END_ADD_DEFAULT_ROUTE; - has_direct_route = ( gateway == 0 - || nm_ip4_config_destination_is_direct (composite, gateway, 32) - || nm_ip4_config_get_direct_route_for_host (composite, gateway)); - - priv->default_route.v4_has = TRUE; - memset (&priv->default_route.v4, 0, sizeof (priv->default_route.v4)); - priv->default_route.v4.rt_source = NM_IP_CONFIG_SOURCE_USER; - priv->default_route.v4.gateway = gateway; - priv->default_route.v4.metric = route_metric_with_penalty (self, default_route_metric); - priv->default_route.v4.mss = nm_ip4_config_get_mss (composite); - - if (!has_direct_route) { - NMPlatformIP4Route r = priv->default_route.v4; + 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, default_route_metric); + default_route.mss = nm_ip4_config_get_mss (composite); + nm_clear_nmp_object (&priv->default_route4); + nm_ip4_config_add_route (composite, &default_route, &priv->default_route4); + if (!( gateway == 0 + || nm_ip4_config_destination_is_direct (composite, gateway, 32) + || nm_ip4_config_get_direct_route_for_host (composite, gateway))) { /* add a direct route to the gateway */ - r.network = gateway; - r.plen = 32; - r.gateway = 0; - nm_ip4_config_add_route (composite, &r, NULL); + default_route.network = gateway; + default_route.plen = 32; + default_route.gateway = 0; + nm_clear_nmp_object (&priv->default_routegw4); + nm_ip4_config_add_route (composite, &default_route, &priv->default_routegw4); } END_ADD_DEFAULT_ROUTE: - - if (priv->default_route.v4_is_assumed) { - /* If above does not explicitly assign a default route, we always pick up the - * default route based on what is currently configured. - * That means that even managed connections with never-default, can - * get a default route (if configured externally). - */ - priv->default_route.v4_has = _device_get_default_route_from_platform (self, AF_INET, (NMPlatformIPRoute *) &priv->default_route.v4); - } - if (commit) { if (NM_DEVICE_GET_CLASS (self)->ip4_config_pre_commit) NM_DEVICE_GET_CLASS (self)->ip4_config_pre_commit (self, composite); @@ -6369,14 +6244,13 @@ ip6_config_merge_and_apply (NMDevice *self, NMConnection *connection; gboolean success; NMIP6Config *composite; - gboolean has_direct_route; const struct in6_addr *gateway; gboolean connection_has_default_route, connection_is_never_default; gboolean ignore_auto_routes = FALSE; gboolean ignore_auto_dns = FALSE; - gboolean auto_method = FALSE; const char *token = NULL; GSList *iter; + NMPlatformIP6Route default_route; /* Apply ignore-auto-routes and ignore-auto-dns settings */ connection = nm_device_get_applied_connection (self); @@ -6391,11 +6265,6 @@ ip6_config_merge_and_apply (NMDevice *self, if (nm_setting_ip6_config_get_addr_gen_mode (ip6) == NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64) token = nm_setting_ip6_config_get_token (ip6); - - if (NM_IN_STRSET (nm_setting_ip_config_get_method (s_ip6), - NM_SETTING_IP6_CONFIG_METHOD_AUTO, - NM_SETTING_IP6_CONFIG_METHOD_DHCP)) - auto_method = TRUE; } } @@ -6453,40 +6322,17 @@ ip6_config_merge_and_apply (NMDevice *self, if (priv->con_ip6_config) nm_ip6_config_merge (composite, priv->con_ip6_config, NM_IP_CONFIG_MERGE_DEFAULT); - /* Add the default route. - * - * We keep track of the default route of a device in a private field. - * NMDevice needs to know the default route at this point, because the gateway - * might require a direct route (see below). - * - * But also, we don't want to add the default route to priv->ip6_config, - * because the default route from the setting might not be the same that - * NMDefaultRouteManager eventually configures (because the it might - * tweak the effective metric). - */ - - /* unless we come to a different conclusion below, we have no default route and - * the route is assumed. */ - priv->default_route.v6_has = FALSE; - priv->default_route.v6_is_assumed = TRUE; + /* Add the default route... */ if (!commit) { /* during a non-commit event, we always pickup whatever is configured. */ goto END_ADD_DEFAULT_ROUTE; } - /* a generated-assumed connection detects the default route from the platform, - * but if the IP method is automatic we need to update the default route to - * maintain connectivity. - */ - if (nm_device_sys_iface_state_is_external (self) && !auto_method) + /* for external connections, we always pickup whatever is configured. */ + if (nm_device_sys_iface_state_is_external (self)) goto END_ADD_DEFAULT_ROUTE; - /* At this point, we treat assumed and non-assumed connections alike. - * For assumed connections we do that because we still manage RA and DHCP - * leases for them, so we must extend/update the default route on commits. - */ - connection_has_default_route = nm_utils_connection_has_default_route (connection, AF_INET6, &connection_is_never_default); @@ -6498,9 +6344,8 @@ ip6_config_merge_and_apply (NMDevice *self, goto END_ADD_DEFAULT_ROUTE; } - /* we are about to commit (for a non-assumed connection). Enforce whatever we have - * configured. */ - priv->default_route.v6_is_assumed = FALSE; + nm_clear_nmp_object (&priv->default_route6); + nm_clear_nmp_object (&priv->default_routegw6); if (!connection_has_default_route) goto END_ADD_DEFAULT_ROUTE; @@ -6514,40 +6359,25 @@ ip6_config_merge_and_apply (NMDevice *self, if (!gateway) goto END_ADD_DEFAULT_ROUTE; + 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.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); - has_direct_route = nm_ip6_config_get_direct_route_for_host (composite, gateway) != NULL; - - - - priv->default_route.v6_has = TRUE; - memset (&priv->default_route.v6, 0, sizeof (priv->default_route.v6)); - priv->default_route.v6.rt_source = NM_IP_CONFIG_SOURCE_USER; - priv->default_route.v6.gateway = *gateway; - priv->default_route.v6.metric = route_metric_with_penalty (self, - nm_device_get_ip6_route_metric (self)); - priv->default_route.v6.mss = nm_ip6_config_get_mss (composite); - - if (!has_direct_route) { - NMPlatformIP6Route r = priv->default_route.v6; - + if (!nm_ip6_config_get_direct_route_for_host (composite, gateway)) { /* add a direct route to the gateway */ - r.network = *gateway; - r.plen = 128; - r.gateway = in6addr_any; - nm_ip6_config_add_route (composite, &r, NULL); + default_route.network = *gateway; + default_route.plen = 128; + default_route.gateway = in6addr_any; + nm_clear_nmp_object (&priv->default_routegw6); + nm_ip6_config_add_route (composite, &default_route, &priv->default_routegw6); } END_ADD_DEFAULT_ROUTE: - - if (priv->default_route.v6_is_assumed) { - /* If above does not explicitly assign a default route, we always pick up the - * default route based on what is currently configured. - * That means that even managed connections with never-default, can - * get a default route (if configured externally). - */ - priv->default_route.v6_has = _device_get_default_route_from_platform (self, AF_INET6, (NMPlatformIPRoute *) &priv->default_route.v6); - } - /* Allow setting MTU etc */ if (commit) { NMUtilsIPv6IfaceId iid; @@ -8914,6 +8744,8 @@ _cleanup_ip4_pre (NMDevice *self, CleanupType cleanup_type) _LOGD (LOGD_DEVICE, "clearing queued IP4 config change"); priv->queued_ip4_config_pending = FALSE; + nm_clear_nmp_object (&priv->default_route4); + nm_clear_nmp_object (&priv->default_routegw4); dhcp4_cleanup (self, cleanup_type, FALSE); arp_cleanup (self); dnsmasq_cleanup (self); @@ -8931,6 +8763,8 @@ _cleanup_ip6_pre (NMDevice *self, CleanupType cleanup_type) _LOGD (LOGD_DEVICE, "clearing queued IP6 config change"); priv->queued_ip6_config_pending = FALSE; + nm_clear_nmp_object (&priv->default_route6); + nm_clear_nmp_object (&priv->default_routegw6); g_clear_object (&priv->dad6_ip6_config); dhcp6_cleanup (self, cleanup_type, FALSE); linklocal6_cleanup (self); @@ -9887,7 +9721,6 @@ nm_device_set_ip4_config (NMDevice *self, NMIP4Config *old_config = NULL; gboolean has_changes = FALSE; gboolean success = TRUE; - gboolean def_route_changed; int ip_ifindex = 0; g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); @@ -9940,7 +9773,6 @@ nm_device_set_ip4_config (NMDevice *self, g_clear_object (&priv->dev_ip4_config); } - def_route_changed = nm_default_route_manager_ip4_update_default_route (nm_netns_get_default_route_manager (priv->netns), self); concheck_periodic_update (self); if (!nm_device_sys_iface_state_is_external_or_assume (self)) @@ -9974,9 +9806,6 @@ nm_device_set_ip4_config (NMDevice *self, } nm_device_queue_recheck_assume (self); - } else if (def_route_changed) { - _LOGD (LOGD_IP4, "ip4-config: default route changed"); - g_signal_emit (self, signals[IP4_CONFIG_CHANGED], 0, priv->ip4_config, priv->ip4_config); } return success; @@ -10059,7 +9888,6 @@ nm_device_set_ip6_config (NMDevice *self, NMIP6Config *old_config = NULL; gboolean has_changes = FALSE; gboolean success = TRUE; - gboolean def_route_changed; int ip_ifindex = 0; g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); @@ -10110,8 +9938,6 @@ nm_device_set_ip6_config (NMDevice *self, nm_exported_object_get_path (NM_EXPORTED_OBJECT (old_config))); } - def_route_changed = nm_default_route_manager_ip6_update_default_route (nm_netns_get_default_route_manager (priv->netns), self); - if (has_changes) { NMSettingsConnection *settings_connection; @@ -10141,9 +9967,6 @@ nm_device_set_ip6_config (NMDevice *self, if (priv->ndisc) ndisc_set_router_config (priv->ndisc, self); - } else if (def_route_changed) { - _LOGD (LOGD_IP6, "ip6-config: default route changed"); - g_signal_emit (self, signals[IP6_CONFIG_CHANGED], 0, priv->ip6_config, priv->ip6_config); } return success; @@ -10798,6 +10621,12 @@ update_ip4_config (NMDevice *self, gboolean initial) nm_ip4_config_intersect (priv->wwan_ip4_config, priv->ext_ip4_config); for (iter = priv->vpn4_configs; iter; iter = iter->next) nm_ip4_config_intersect (iter->data, priv->ext_ip4_config); + if ( priv->default_route4 + && !nm_ip4_config_nmpobj_lookup (priv->ext_ip4_config, priv->default_route4)) + nm_clear_nmp_object (&priv->default_route4); + if ( priv->default_routegw4 + && !nm_ip4_config_nmpobj_lookup (priv->ext_ip4_config, priv->default_routegw4)) + nm_clear_nmp_object (&priv->default_routegw4); /* Remove parts from ext_ip4_config to only contain the information that * was configured externally -- we already have the same configuration from @@ -10810,6 +10639,10 @@ update_ip4_config (NMDevice *self, gboolean initial) nm_ip4_config_subtract (priv->ext_ip4_config, priv->wwan_ip4_config); for (iter = priv->vpn4_configs; iter; iter = iter->next) nm_ip4_config_subtract (priv->ext_ip4_config, iter->data); + if (priv->default_route4) + nm_ip4_config_nmpobj_remove (priv->ext_ip4_config, priv->default_route4); + if (priv->default_routegw4) + nm_ip4_config_nmpobj_remove (priv->ext_ip4_config, priv->default_routegw4); ip4_config_merge_and_apply (self, NULL, FALSE); } @@ -10870,6 +10703,12 @@ update_ip6_config (NMDevice *self, gboolean initial) nm_ip6_config_intersect (priv->wwan_ip6_config, priv->ext_ip6_config); for (iter = priv->vpn6_configs; iter; iter = iter->next) nm_ip6_config_intersect (iter->data, priv->ext_ip6_config); + if ( priv->default_route6 + && !nm_ip6_config_nmpobj_lookup (priv->ext_ip6_config, priv->default_route6)) + nm_clear_nmp_object (&priv->default_route6); + if ( priv->default_routegw6 + && !nm_ip6_config_nmpobj_lookup (priv->ext_ip6_config, priv->default_routegw6)) + nm_clear_nmp_object (&priv->default_routegw6); /* Remove parts from ext_ip6_config to only contain the information that * was configured externally -- we already have the same configuration from @@ -10884,6 +10723,10 @@ update_ip6_config (NMDevice *self, gboolean initial) nm_ip6_config_subtract (priv->ext_ip6_config, priv->wwan_ip6_config); for (iter = priv->vpn6_configs; iter; iter = iter->next) nm_ip6_config_subtract (priv->ext_ip6_config, iter->data); + if (priv->default_route6) + nm_ip6_config_nmpobj_remove (priv->ext_ip6_config, priv->default_route6); + if (priv->default_routegw6) + nm_ip6_config_nmpobj_remove (priv->ext_ip6_config, priv->default_routegw6); ip6_config_merge_and_apply (self, FALSE); } @@ -12118,16 +11961,6 @@ _cleanup_generic_post (NMDevice *self, CleanupType cleanup_type) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - if (cleanup_type == CLEANUP_TYPE_DECONFIGURE) { - _update_default_route (self, AF_INET, FALSE, FALSE); - _update_default_route (self, AF_INET6, FALSE, FALSE); - } else { - _update_default_route (self, AF_INET, priv->default_route.v4_has, TRUE); - _update_default_route (self, AF_INET6, priv->default_route.v6_has, TRUE); - } - _update_default_route (self, AF_INET, FALSE, TRUE); - _update_default_route (self, AF_INET6, FALSE, TRUE); - priv->v4_commit_first_time = TRUE; priv->v6_commit_first_time = TRUE; @@ -12138,6 +11971,10 @@ _cleanup_generic_post (NMDevice *self, CleanupType cleanup_type) */ nm_device_set_ip4_config (self, NULL, 0, TRUE); nm_device_set_ip6_config (self, NULL, TRUE); + nm_clear_nmp_object (&priv->default_route4); + nm_clear_nmp_object (&priv->default_route6); + nm_clear_nmp_object (&priv->default_routegw4); + nm_clear_nmp_object (&priv->default_routegw6); g_clear_object (&priv->proxy_config); g_clear_object (&priv->con_ip4_config); g_clear_object (&priv->dev_ip4_config); @@ -13819,9 +13656,6 @@ nm_device_init (NMDevice *self) priv->ip6_saved_properties = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_free); priv->sys_iface_state = NM_DEVICE_SYS_IFACE_STATE_EXTERNAL; - priv->default_route.v4_is_assumed = TRUE; - priv->default_route.v6_is_assumed = TRUE; - priv->v4_commit_first_time = TRUE; priv->v6_commit_first_time = TRUE; } diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h index f46a64ca29..811fc258ec 100644 --- a/src/devices/nm-device.h +++ b/src/devices/nm-device.h @@ -710,8 +710,8 @@ gboolean nm_device_owns_iface (NMDevice *device, const char *iface); NMConnection *nm_device_new_default_connection (NMDevice *self); -const NMPlatformIP4Route *nm_device_get_ip4_default_route (NMDevice *self, gboolean *out_is_assumed); -const NMPlatformIP6Route *nm_device_get_ip6_default_route (NMDevice *self, gboolean *out_is_assumed); +const NMPObject *nm_device_get_best_default_route (NMDevice *self, + int addr_family); void nm_device_spawn_iface_helper (NMDevice *self); diff --git a/src/nm-default-route-manager.c b/src/nm-default-route-manager.c deleted file mode 100644 index dc8f83b4a4..0000000000 --- a/src/nm-default-route-manager.c +++ /dev/null @@ -1,1561 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager -- Network link manager - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright (C) 2014 Red Hat, Inc. - */ - - -#include "nm-default.h" - -#include "nm-default-route-manager.h" - -#include - -#include "devices/nm-device.h" -#include "vpn/nm-vpn-connection.h" -#include "platform/nm-platform.h" -#include "platform/nm-platform-utils.h" -#include "platform/nmp-object.h" -#include "nm-manager.h" -#include "nm-ip4-config.h" -#include "nm-ip6-config.h" -#include "nm-act-request.h" - -/*****************************************************************************/ - -NM_GOBJECT_PROPERTIES_DEFINE_BASE ( - PROP_LOG_WITH_PTR, - PROP_PLATFORM, -); - -typedef struct { - GPtrArray *entries_ip4; - GPtrArray *entries_ip6; - - NMPlatform *platform; - - struct { - guint guard; - guint backoff_wait_time_ms; - guint idle_handle; - gboolean has_v4_changes; - gboolean has_v6_changes; - } resync; - - /* During disposing, we unref the sources of all entries. This happens usually - * during shutdown, which might call the final deletion of the object. That - * again might cause calls back into NMDefaultRouteManager, which finds dangling - * pointers. - * Guard every publicly accessible function to return early if the instance - * is already disposing. */ - bool disposed; - - bool log_with_ptr; -} NMDefaultRouteManagerPrivate; - -struct _NMDefaultRouteManager { - GObject parent; - NMDefaultRouteManagerPrivate _priv; -}; - -struct _NMDefaultRouteManagerClass { - GObjectClass parent; -}; - -G_DEFINE_TYPE (NMDefaultRouteManager, nm_default_route_manager, G_TYPE_OBJECT) - -#define NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMDefaultRouteManager, NM_IS_DEFAULT_ROUTE_MANAGER) - -/*****************************************************************************/ - -#define _NMLOG_PREFIX_NAME "default-route" -#undef _NMLOG_ENABLED -#define _NMLOG_ENABLED(level, addr_family) \ - ({ \ - const int __addr_family = (addr_family); \ - const NMLogLevel __level = (level); \ - const NMLogDomain __domain = __addr_family == AF_INET ? LOGD_IP4 : (__addr_family == AF_INET6 ? LOGD_IP6 : LOGD_IP); \ - \ - nm_logging_enabled (__level, __domain); \ - }) -#define _NMLOG(level, addr_family, ...) \ - G_STMT_START { \ - const int __addr_family = (addr_family); \ - const NMLogLevel __level = (level); \ - const NMLogDomain __domain = __addr_family == AF_INET ? LOGD_IP4 : (__addr_family == AF_INET6 ? LOGD_IP6 : LOGD_IP); \ - \ - if (nm_logging_enabled (__level, __domain)) { \ - char __prefix_buf[100]; \ - \ - _nm_log (__level, __domain, 0, NULL, NULL, \ - "%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ - NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self)->log_with_ptr \ - ? nm_sprintf_buf (__prefix_buf, "%s%c[%p]", \ - _NMLOG2_PREFIX_NAME, \ - __addr_family == AF_INET ? '4' : (__addr_family == AF_INET6 ? '6' : '-'), \ - self) \ - : _NMLOG2_PREFIX_NAME \ - _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ - } \ - } G_STMT_END - -#define _NMLOG2_PREFIX_NAME _NMLOG_PREFIX_NAME -#undef _NMLOG2_ENABLED -#define _NMLOG2_ENABLED _NMLOG_ENABLED -#define _NMLOG2(level, vtable, entry_idx, entry, ...) \ - G_STMT_START { \ - const int __addr_family = (vtable)->vt->addr_family; \ - const NMLogLevel __level = (level); \ - const NMLogDomain __domain = __addr_family == AF_INET ? LOGD_IP4 : (__addr_family == AF_INET6 ? LOGD_IP6 : LOGD_IP); \ - \ - if (nm_logging_enabled (__level, __domain)) { \ - char __prefix_buf[100]; \ - guint __entry_idx = (entry_idx); \ - const Entry *const __entry = (entry); \ - \ - _nm_log (__level, __domain, 0, NULL, NULL, \ - "%s: entry[%u/%s:%p:%s:%chas:%csync]: "_NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ - NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self)->log_with_ptr \ - ? nm_sprintf_buf (__prefix_buf, "%s%c[%p]", \ - _NMLOG2_PREFIX_NAME, \ - __addr_family == AF_INET ? '4' : (__addr_family == AF_INET6 ? '6' : '-'), \ - self) \ - : _NMLOG2_PREFIX_NAME, \ - __entry_idx, \ - NM_IS_DEVICE (__entry->source.pointer) ? "dev" : "vpn", \ - __entry->source.pointer, \ - NM_IS_DEVICE (__entry->source.pointer) ? nm_device_get_iface (__entry->source.device) : nm_active_connection_get_settings_connection_id (NM_ACTIVE_CONNECTION (__entry->source.vpn)), \ - (__entry->never_default ? '-' : '+'), \ - (__entry->synced ? '+' : '-') \ - _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ - } \ - } G_STMT_END - -/*****************************************************************************/ - -static void _resync_idle_cancel (NMDefaultRouteManager *self); - -/*****************************************************************************/ - -typedef struct { - union { - void *pointer; - GObject *object; - NMDevice *device; - NMVpnConnection *vpn; - } source; - NMPlatformIPXRoute route; - - /* Whether the route is synced to platform and has a default route. - * - * ( synced && !never_default): the interface gets a default route that - * is enforced and managed by NMDefaultRouteManager. - * - * (!synced && !never_default): the interface has this route, but it is assumed. - * Assumed interfaces are those that have no tracked entry or that only have - * (!synced && !never_default) entries. NMDefaultRouteManager will not touch - * default routes on these interfaces. - * This combination makes only sense for device sources. - * They are tracked so that assumed devices can also be the best device. - * - * ( synced && never_default): entries of this kind are a placeholder - * to indicate that the ifindex is managed but has no default-route. - * Missing entries also indicate that a certain ifindex has no default-route. - * The difference is that missing entries are considered assumed while on - * (synced && never_default) entries the absence of the default route - * is enforced. NMDefaultRouteManager will actively remove any default - * route on such ifindexes. - * Also, for VPN sources in addition we track them so that a never-default - * VPN connection can be chosen by get_best_config() to receive the DNS configuration. - * - * (!synced && never_default): this combination makes no sense. - */ - gboolean synced; - gboolean never_default; - - guint32 effective_metric; -} Entry; - -typedef struct { - const NMPlatformVTableRoute *vt; - GPtrArray *(*get_entries) (NMDefaultRouteManagerPrivate *priv); -} VTableIP; - -static const VTableIP vtable_ip4, vtable_ip6; - -static gboolean -_vt_routes_has_entry (const VTableIP *vtable, const GPtrArray *routes, const Entry *entry) -{ - guint i; - NMPlatformIPXRoute route = entry->route; - - if (!routes) - return FALSE; - - route.rx.metric = entry->effective_metric; - - if (vtable->vt->is_ip4) { - for (i = 0; i < routes->len; i++) { - const NMPlatformIP4Route *r = NMP_OBJECT_CAST_IP4_ROUTE (routes->pdata[i]); - - route.rx.rt_source = r->rt_source; - if (nm_platform_ip4_route_cmp_full (r, &route.r4) == 0) - return TRUE; - } - } else { - for (i = 0; i < routes->len; i++) { - const NMPlatformIP6Route *r = NMP_OBJECT_CAST_IP6_ROUTE (routes->pdata[i]); - - route.rx.rt_source = r->rt_source; - if (nm_platform_ip6_route_cmp_full (r, &route.r6) == 0) - return TRUE; - } - } - return FALSE; -} - -static void -_entry_free (Entry *entry) -{ - if (entry) { - g_object_unref (entry->source.object); - g_slice_free (Entry, entry); - } -} - -static Entry * -_entry_find_by_source (GPtrArray *entries, gpointer source, guint *out_idx) -{ - guint i; - - for (i = 0; i < entries->len; i++) { - Entry *e = g_ptr_array_index (entries, i); - - if (e->source.pointer == source) { - if (out_idx) - *out_idx = i; - return e; - } - } - - if (out_idx) - *out_idx = G_MAXUINT; - return NULL; -} - -static gboolean -_platform_route_sync_add (const VTableIP *vtable, NMDefaultRouteManager *self, guint32 metric) -{ - NMDefaultRouteManagerPrivate *priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self); - GPtrArray *entries = vtable->get_entries (priv); - char buf1[sizeof (_nm_utils_to_string_buffer)]; - char buf2[sizeof (_nm_utils_to_string_buffer)]; - guint i; - Entry *entry_unsynced = NULL; - Entry *entry = NULL; - gboolean success; - - /* Find the entries for the given metric. - * The effective metric for synced entries is chosen in a way that it - * is unique (except for G_MAXUINT32, where a clash is not solvable). */ - for (i = 0; i < entries->len; i++) { - Entry *e = g_ptr_array_index (entries, i); - - if (e->never_default) - continue; - - if (e->effective_metric != metric) - continue; - - if (e->synced) { - g_assert (!entry || metric == G_MAXUINT32); - if (!entry) - entry = e; - } else - entry_unsynced = e; - } - - /* We don't expect to have an unsynced *and* a synced entry for the same metric. - * Unless, (a) their metric is G_MAXUINT32, in which case we could not find an unused effective metric, - * or (b) if we have an unsynced and a synced entry for the same ifindex. - * The latter case happens for example when activating an openvpn connection (synced) and - * assuming the corresponding tun0 interface (unsynced). */ - g_assert (!entry || !entry_unsynced || (entry->route.rx.ifindex == entry_unsynced->route.rx.ifindex) || metric == G_MAXUINT32); - - /* we only add the route, if we have an (to be synced) entry for it. */ - if (!entry) - return FALSE; - - if (vtable->vt->is_ip4) { - NMPObject obj; - NMPlatformIP4Route *rt; - const NMPObject *plobj; - - nmp_object_stackinit (&obj, NMP_OBJECT_TYPE_IP4_ROUTE, (const NMPlatformObject *) &entry->route.r4); - rt = NMP_OBJECT_CAST_IP4_ROUTE (&obj); - rt->network = 0; - rt->plen = 0; - rt->metric = entry->effective_metric; - - nm_platform_ip_route_normalize (AF_INET, (NMPlatformIPRoute *) rt); - plobj = nm_dedup_multi_entry_get_obj (nm_platform_lookup_entry (priv->platform, - NMP_CACHE_ID_TYPE_OBJECT_TYPE, - &obj)); - if ( plobj - && nm_platform_ip4_route_cmp (rt, - NMP_OBJECT_CAST_IP4_ROUTE (plobj), - NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) == 0) { - _LOGt (AF_INET, "already exists: %s", - nm_platform_ip4_route_to_string (rt, NULL, 0)); - return FALSE; - } - - if (plobj) { - _LOGt (AF_INET, "update platform route: %s; with route: %s", - nm_platform_ip4_route_to_string (NMP_OBJECT_CAST_IP4_ROUTE (plobj), buf1, sizeof (buf1)), - nm_platform_ip4_route_to_string (rt, buf2, sizeof (buf2))); - } - - success = (nm_platform_ip4_route_add (priv->platform, NMP_NLM_FLAG_REPLACE, rt) == NM_PLATFORM_ERROR_SUCCESS); - } else { - NMPObject obj; - NMPlatformIP6Route *rt; - const NMPObject *plobj; - - nmp_object_stackinit (&obj, NMP_OBJECT_TYPE_IP6_ROUTE, (const NMPlatformObject *) &entry->route.r6); - rt = NMP_OBJECT_CAST_IP6_ROUTE (&obj); - rt->network = in6addr_any; - rt->plen = 0; - rt->metric = entry->effective_metric; - - nm_platform_ip_route_normalize (AF_INET6, (NMPlatformIPRoute *) rt); - plobj = nm_dedup_multi_entry_get_obj (nm_platform_lookup_entry (priv->platform, - NMP_CACHE_ID_TYPE_OBJECT_TYPE, - &obj)); - if ( plobj - && nm_platform_ip6_route_cmp (rt, - NMP_OBJECT_CAST_IP6_ROUTE (plobj), - NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) == 0) { - _LOGt (AF_INET, "already exists: %s", - nm_platform_ip6_route_to_string (rt, NULL, 0)); - return FALSE; - } - - if (plobj) { - _LOGt (AF_INET, "update platform route: %s; with route: %s", - nm_platform_ip6_route_to_string (NMP_OBJECT_CAST_IP6_ROUTE (plobj), buf1, sizeof (buf1)), - nm_platform_ip6_route_to_string (rt, buf2, sizeof (buf2))); - } - - success = (nm_platform_ip6_route_add (priv->platform, NMP_NLM_FLAG_REPLACE, rt) == NM_PLATFORM_ERROR_SUCCESS); - } - - if (!success) { - _LOGW (vtable->vt->addr_family, "failed to add default route %s with effective metric %u", - vtable->vt->route_to_string (&entry->route, NULL, 0), (guint) entry->effective_metric); - } - return TRUE; -} - -static gboolean -_platform_route_sync_flush (const VTableIP *vtable, NMDefaultRouteManager *self, int ifindex_to_flush) -{ - NMDefaultRouteManagerPrivate *priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self); - GPtrArray *entries = vtable->get_entries (priv); - gs_unref_ptrarray GPtrArray *routes = NULL; - guint i, j; - gboolean changed = FALSE; - - /* prune all other default routes from this device. */ - - routes = nm_platform_lookup_route_default_clone (priv->platform, - vtable->vt->obj_type, - nm_platform_lookup_predicate_routes_main_skip_rtprot_kernel, - NULL); - if (!routes) - return FALSE; - - for (i = 0; i < routes->len; i++) { - const NMPlatformIPRoute *route; - gboolean has_ifindex_synced = FALSE; - Entry *entry = NULL; - - route = NMP_OBJECT_CAST_IP_ROUTE (routes->pdata[i]); - - /* look at all entries and see if the route for this ifindex pair is - * a known entry. */ - for (j = 0; j < entries->len; j++) { - Entry *e = g_ptr_array_index (entries, j); - - if ( e->route.rx.ifindex == route->ifindex - && e->synced) { - has_ifindex_synced = TRUE; - if ( !e->never_default - && e->effective_metric == route->metric) - entry = e; - } - } - - /* we only delete the route if we don't have a matching entry, - * and there is at least one entry that references this ifindex - * (indicating that the ifindex is managed by us -- not assumed). - * - * Otherwise, don't delete the route because it's configured - * externally (and will be assumed -- or already is assumed). - */ - if ( !entry - && (has_ifindex_synced || ifindex_to_flush == route->ifindex)) { - nm_platform_ip_route_delete (priv->platform, NMP_OBJECT_UP_CAST (route)); - changed = TRUE; - } - } - return changed; -} - -static int -_sort_entries_cmp (gconstpointer a, gconstpointer b, gpointer user_data) -{ - guint32 m_a, m_b; - const Entry *e_a = *((const Entry **) a); - const Entry *e_b = *((const Entry **) b); - - /* when comparing routes, we consider the (original) metric. */ - m_a = e_a->route.rx.metric; - m_b = e_b->route.rx.metric; - - /* we normalize route.metric already in _ipx_update_default_route(). - * so we can just compare the metrics numerically */ - - if (m_a != m_b) - return (m_a < m_b) ? -1 : 1; - - /* If the metrics are equal, we prefer the one that is !never_default */ - if (!!e_a->never_default != !!e_b->never_default) - return e_a->never_default ? 1 : -1; - - /* If the metrics are equal, we prefer the one that is assumed (!synced). - * Entries that we sync, can be modified so that only the best - * entry has a (deterministically) lowest metric. - * With assumed devices we cannot increase/change the metric. - * For example: two devices, both metric 0. One is assumed the other is - * synced. - * If we would choose the synced entry as best, we cannot - * increase the metric of the assumed one and we would have non-determinism. - * If we instead prefer the assumed device, we can increase the metric - * of the synced device and the assumed device is (deterministically) - * prefered. - * If both devices are assumed, we also have non-determinism, but also - * we don't reorder either. - */ - if (!!e_a->synced != !!e_b->synced) - return e_a->synced ? 1 : -1; - - /* otherwise, do not reorder */ - return 0; -} - -static GHashTable * -_get_assumed_interface_metrics (const VTableIP *vtable, NMDefaultRouteManager *self, const GPtrArray *routes) -{ - NMDefaultRouteManagerPrivate *priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self); - GPtrArray *entries; - guint i, j; - GHashTable *result; - - /* create a list of all metrics that are currently assigned on an interface - * that is *not* already covered by one of our synced entries. - * IOW, returns the metrics that are in use by assumed interfaces - * that we want to preserve. */ - - entries = vtable->get_entries (priv); - - result = g_hash_table_new (NULL, NULL); - - if (routes) { - for (i = 0; i < routes->len; i++) { - gboolean ifindex_has_synced_entry = FALSE; - const NMPlatformIPRoute *route; - - route = NMP_OBJECT_CAST_IP_ROUTE (routes->pdata[i]); - - for (j = 0; j < entries->len; j++) { - Entry *e = g_ptr_array_index (entries, j); - - if ( e->synced - && e->route.rx.ifindex == route->ifindex) { - ifindex_has_synced_entry = TRUE; - break; - } - } - - if (!ifindex_has_synced_entry) - g_hash_table_add (result, GUINT_TO_POINTER (vtable->vt->metric_normalize (route->metric))); - } - } - - /* also add all non-synced metrics from our entries list. We might have there some metrics that - * we track as non-synced but that are no longer part of platform routes. Anyway, for now - * we still want to treat them as assumed. */ - for (i = 0; i < entries->len; i++) { - gboolean ifindex_has_synced_entry = FALSE; - Entry *e_i = g_ptr_array_index (entries, i); - - if (e_i->synced) - continue; - - for (j = 0; j < entries->len; j++) { - Entry *e_j = g_ptr_array_index (entries, j); - - if ( j != i - && (e_j->synced && e_j->route.rx.ifindex == e_i->route.rx.ifindex)) { - ifindex_has_synced_entry = TRUE; - break; - } - } - - if (!ifindex_has_synced_entry) - g_hash_table_add (result, GUINT_TO_POINTER (vtable->vt->metric_normalize (e_i->route.rx.metric))); - } - - return result; -} - -static gboolean -_resync_all (const VTableIP *vtable, NMDefaultRouteManager *self, const Entry *changed_entry, const Entry *old_entry, gboolean external_change) -{ - NMDefaultRouteManagerPrivate *priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self); - Entry *entry; - guint i, j; - gint64 last_metric = -1; - guint32 expected_metric; - GPtrArray *entries; - GArray *changed_metrics = g_array_new (FALSE, FALSE, sizeof (guint32)); - GHashTable *assumed_metrics; - gs_unref_ptrarray GPtrArray *routes = NULL; - gboolean changed = FALSE; - int ifindex_to_flush = 0; - - g_assert (priv->resync.guard == 0); - priv->resync.guard++; - - if (!external_change) { - if (vtable->vt->is_ip4) - priv->resync.has_v4_changes = FALSE; - else - priv->resync.has_v6_changes = FALSE; - if (!priv->resync.has_v4_changes && !priv->resync.has_v6_changes) - _resync_idle_cancel (self); - } - - entries = vtable->get_entries (priv); - - routes = nm_platform_lookup_route_default_clone (priv->platform, - vtable->vt->obj_type, - nm_platform_lookup_predicate_routes_main_skip_rtprot_kernel, - NULL); - - assumed_metrics = _get_assumed_interface_metrics (vtable, self, routes); - - if (old_entry && old_entry->synced && !old_entry->never_default) { - /* The old version obviously changed. */ - g_array_append_val (changed_metrics, old_entry->effective_metric); - } - - /* first iterate over all entries and adjust the effective metrics. */ - for (i = 0; i < entries->len; i++) { - entry = g_ptr_array_index (entries, i); - - if (entry->never_default) - continue; - - if (!entry->synced) { - gboolean has_synced_entry = FALSE; - - /* A non synced entry is completely ignored, if we have - * a synced entry for the same if index. - * Otherwise the metric of the entry is still remembered as - * last_metric to avoid reusing it. */ - for (j = 0; j < entries->len; j++) { - const Entry *e = g_ptr_array_index (entries, j); - - if ( e->synced - && e->route.rx.ifindex == entry->route.rx.ifindex) { - has_synced_entry = TRUE; - break; - } - } - if (!has_synced_entry) - last_metric = MAX (last_metric, (gint64) entry->effective_metric); - continue; - } - - expected_metric = entry->route.rx.metric; - if ((gint64) expected_metric <= last_metric) - expected_metric = last_metric == G_MAXUINT32 ? G_MAXUINT32 : last_metric + 1; - - while ( expected_metric < G_MAXUINT32 - && g_hash_table_contains (assumed_metrics, GUINT_TO_POINTER (expected_metric))) { - gboolean has_metric_for_ifindex = FALSE; - - /* Check if there are assumed devices that have default routes with this metric. - * If there are any, we have to pick another effective_metric. */ - - /* However, if there is a matching route (ifindex+metric) for our current entry, we are done. */ - if (routes) { - for (j = 0; j < routes->len; j++) { - const NMPlatformIPRoute *r = NMP_OBJECT_CAST_IP_ROUTE (routes->pdata[i]); - - if ( r->metric == expected_metric - && r->ifindex == entry->route.rx.ifindex) { - has_metric_for_ifindex = TRUE; - break; - } - } - } - if (has_metric_for_ifindex) - break; - expected_metric++; - } - - if (changed_entry == entry) { - /* for the changed entry, the previous metric was either old_entry->effective_metric, - * or none. Hence, we only have to remember what is going to change. */ - g_array_append_val (changed_metrics, expected_metric); - if (!old_entry) { - _LOG2D (vtable, i, entry, "sync:add %s (%u)", - vtable->vt->route_to_string (&entry->route, NULL, 0), (guint) expected_metric); - } else if (old_entry != changed_entry) { - _LOG2D (vtable, i, entry, "sync:update %s (%u -> %u)", - vtable->vt->route_to_string (&entry->route, NULL, 0), (guint) old_entry->effective_metric, - (guint) expected_metric); - } else { - _LOG2D (vtable, i, entry, "sync:resync %s (%u)", - vtable->vt->route_to_string (&entry->route, NULL, 0), (guint) expected_metric); - } - } else if (entry->effective_metric != expected_metric) { - g_array_append_val (changed_metrics, entry->effective_metric); - g_array_append_val (changed_metrics, expected_metric); - _LOG2D (vtable, i, entry, "sync:metric %s (%u -> %u)", - vtable->vt->route_to_string (&entry->route, NULL, 0), (guint) entry->effective_metric, - (guint) expected_metric); - } else { - if (!_vt_routes_has_entry (vtable, routes, entry)) { - g_array_append_val (changed_metrics, entry->effective_metric); - _LOG2D (vtable, i, entry, "sync:re-add %s (%u -> %u)", - vtable->vt->route_to_string (&entry->route, NULL, 0), (guint) entry->effective_metric, - (guint) entry->effective_metric); - } - } - - if (entry->effective_metric != expected_metric) { - entry->effective_metric = expected_metric; - changed = TRUE; - } - last_metric = expected_metric; - } - - g_array_sort_with_data (changed_metrics, nm_cmp_uint32_p_with_data, NULL); - last_metric = -1; - for (j = 0; j < changed_metrics->len; j++) { - expected_metric = g_array_index (changed_metrics, guint32, j); - - if (last_metric == (gint64) expected_metric) { - /* skip duplicates. */ - continue; - } - changed |= _platform_route_sync_add (vtable, self, expected_metric); - last_metric = expected_metric; - } - - if ( old_entry - && !changed_entry - && old_entry->synced - && !old_entry->never_default) { - /* If we entriely remove an entry that was synced before, we must make - * sure to flush routes for this ifindex too. Otherwise they linger - * around as "assumed" routes */ - ifindex_to_flush = old_entry->route.rx.ifindex; - } - - changed |= _platform_route_sync_flush (vtable, self, ifindex_to_flush); - - g_array_free (changed_metrics, TRUE); - g_hash_table_unref (assumed_metrics); - - priv->resync.guard--; - return changed; -} - -static gboolean -_entry_at_idx_update (const VTableIP *vtable, NMDefaultRouteManager *self, guint entry_idx, const Entry *old_entry) -{ - NMDefaultRouteManagerPrivate *priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self); - Entry *entry; - GPtrArray *entries; - - entries = vtable->get_entries (priv); - g_assert (entry_idx < entries->len); - - entry = g_ptr_array_index (entries, entry_idx); - - g_assert ( !old_entry - || (entry->source.pointer == old_entry->source.pointer && entry->route.rx.ifindex == old_entry->route.rx.ifindex)); - - if (!entry->synced && !entry->never_default) - entry->effective_metric = entry->route.rx.metric; - - _LOG2D (vtable, entry_idx, entry, "%s %s (%"G_GUINT32_FORMAT")", - old_entry - ? (entry != old_entry - ? "record:update" - : "record:resync") - : "record:add ", - vtable->vt->route_to_string (&entry->route, NULL, 0), - entry->effective_metric); - - g_ptr_array_sort_with_data (entries, _sort_entries_cmp, NULL); - - return _resync_all (vtable, self, entry, old_entry, FALSE); -} - -static gboolean -_entry_at_idx_remove (const VTableIP *vtable, NMDefaultRouteManager *self, guint entry_idx) -{ - NMDefaultRouteManagerPrivate *priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self); - Entry *entry; - GPtrArray *entries; - gboolean ret; - - entries = vtable->get_entries (priv); - - g_assert (entry_idx < entries->len); - - entry = g_ptr_array_index (entries, entry_idx); - - _LOG2D (vtable, entry_idx, entry, "record:remove %s (%u)", - vtable->vt->route_to_string (&entry->route, NULL, 0), (guint) entry->effective_metric); - - /* Remove the entry from the list (but don't free it yet) */ - g_ptr_array_index (entries, entry_idx) = NULL; - g_ptr_array_remove_index (entries, entry_idx); - - ret = _resync_all (vtable, self, NULL, entry, FALSE); - _entry_free (entry); - - return ret; -} - -/*****************************************************************************/ - -static gboolean -_ipx_update_default_route (const VTableIP *vtable, - NMDefaultRouteManager *self, - gpointer source) -{ - NMDefaultRouteManagerPrivate *priv; - Entry *entry; - guint entry_idx; - const NMPlatformIPRoute *default_route = NULL; - NMPlatformIPXRoute rt; - int ip_ifindex; - GPtrArray *entries; - NMDevice *device = NULL; - NMVpnConnection *vpn = NULL; - gboolean never_default = FALSE; - gboolean synced = FALSE, ret; - - g_return_val_if_fail (NM_IS_DEFAULT_ROUTE_MANAGER (self), FALSE); - - priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self); - if (priv->disposed) - return FALSE; - - if (NM_IS_DEVICE (source)) - device = source; - else if (NM_IS_VPN_CONNECTION (source)) - vpn = source; - else - g_return_val_if_reached (FALSE); - - if (device) - ip_ifindex = nm_device_get_ip_ifindex (device); - else - ip_ifindex = nm_vpn_connection_get_ip_ifindex (vpn, TRUE); - - entries = vtable->get_entries (priv); - entry = _entry_find_by_source (entries, source, &entry_idx); - - if ( entry - && entry->route.rx.ifindex != ip_ifindex) { - /* Strange... the ifindex changed... Remove the device and start again. */ - _LOG2D (vtable, entry_idx, entry, "ifindex changed: %d -> %d", - entry->route.rx.ifindex, ip_ifindex); - - g_object_freeze_notify (G_OBJECT (self)); - _entry_at_idx_remove (vtable, self, entry_idx); - g_assert (!_entry_find_by_source (entries, source, NULL)); - ret = _ipx_update_default_route (vtable, self, source); - g_object_thaw_notify (G_OBJECT (self)); - return ret; - } - - /* get the @default_route from the device. */ - if (ip_ifindex > 0) { - if (device) { - gboolean is_assumed = FALSE; - - if (vtable->vt->is_ip4) - default_route = (const NMPlatformIPRoute *) nm_device_get_ip4_default_route (device, &is_assumed); - else - default_route = (const NMPlatformIPRoute *) nm_device_get_ip6_default_route (device, &is_assumed); - if (!default_route && !is_assumed) { - /* the device has no default route, but it is not assumed. That means, NMDefaultRouteManager - * enforces that the device has no default route. - * - * Hence we have to keep track of this entry, otherwise a missing entry tells us - * that the interface is assumed and NM would not remove the default routes on - * the device. */ - memset (&rt, 0, sizeof (rt)); - rt.rx.ifindex = ip_ifindex; - rt.rx.rt_source = NM_IP_CONFIG_SOURCE_UNKNOWN; - rt.rx.metric = G_MAXUINT32; - default_route = &rt.rx; - - never_default = TRUE; - } - synced = !is_assumed; - } else { - NMConnection *connection = nm_active_connection_get_applied_connection ((NMActiveConnection *) vpn); - - if ( connection - && nm_vpn_connection_get_vpn_state (vpn) == NM_VPN_CONNECTION_STATE_ACTIVATED) { - - memset (&rt, 0, sizeof (rt)); - if (vtable->vt->is_ip4) { - NMIP4Config *vpn_config; - - vpn_config = nm_vpn_connection_get_ip4_config (vpn); - if (vpn_config) { - never_default = nm_ip4_config_get_never_default (vpn_config); - rt.r4.ifindex = ip_ifindex; - rt.r4.rt_source = NM_IP_CONFIG_SOURCE_VPN; - rt.r4.gateway = nm_ip4_config_get_gateway (vpn_config); - rt.r4.metric = nm_vpn_connection_get_ip4_route_metric (vpn); - rt.r4.mss = nm_ip4_config_get_mss (vpn_config); - default_route = &rt.rx; - } - } else { - NMIP6Config *vpn_config; - - vpn_config = nm_vpn_connection_get_ip6_config (vpn); - if (vpn_config) { - const struct in6_addr *int_gw = nm_ip6_config_get_gateway (vpn_config); - - never_default = nm_ip6_config_get_never_default (vpn_config); - rt.r6.ifindex = ip_ifindex; - rt.r6.rt_source = NM_IP_CONFIG_SOURCE_VPN; - rt.r6.gateway = int_gw ? *int_gw : in6addr_any; - rt.r6.metric = nm_vpn_connection_get_ip6_route_metric (vpn); - rt.r6.mss = nm_ip6_config_get_mss (vpn_config); - default_route = &rt.rx; - } - } - } - if (nm_vpn_connection_get_ip_ifindex (vpn, FALSE) > 0) - synced = TRUE; - else { - /* a VPN connection without tunnel device cannot have a non-synced, missing default route. - * Either it has a default route (which is synced), or it has no entry. */ - synced = default_route && !never_default; - } - } - } - - g_assert (!default_route || default_route->plen == 0); - - if (!synced && never_default) { - /* having a non-synced, never-default entry is non-sensical. Unset - * @default_route so that we don't add such an entry below. */ - default_route = NULL; - } - - if (!entry && !default_route) { - /* nothing to do */ - return FALSE; - } else if (!entry) { - /* add */ - entry = g_slice_new0 (Entry); - entry->source.object = g_object_ref (source); - - if (vtable->vt->is_ip4) - entry->route.r4 = *((const NMPlatformIP4Route *) default_route); - else - entry->route.r6 = *((const NMPlatformIP6Route *) default_route); - - /* only use normalized metrics */ - entry->route.rx.metric = vtable->vt->metric_normalize (entry->route.rx.metric); - entry->route.rx.ifindex = ip_ifindex; - entry->never_default = never_default; - entry->effective_metric = entry->route.rx.metric; - entry->synced = synced; - - g_ptr_array_add (entries, entry); - return _entry_at_idx_update (vtable, self, entries->len - 1, NULL); - } else if (default_route) { - /* update */ - Entry old_entry, new_entry; - - new_entry = *entry; - if (vtable->vt->is_ip4) - new_entry.route.r4 = *((const NMPlatformIP4Route *) default_route); - else - new_entry.route.r6 = *((const NMPlatformIP6Route *) default_route); - /* only use normalized metrics */ - new_entry.route.rx.metric = vtable->vt->metric_normalize (new_entry.route.rx.metric); - new_entry.route.rx.ifindex = ip_ifindex; - new_entry.never_default = never_default; - new_entry.synced = synced; - - if (memcmp (entry, &new_entry, sizeof (new_entry)) == 0) { - if (!synced) { - /* the internal book-keeping doesn't change, so don't do a full - * sync of the configured routes. */ - return FALSE; - } - return _entry_at_idx_update (vtable, self, entry_idx, entry); - } else { - old_entry = *entry; - *entry = new_entry; - return _entry_at_idx_update (vtable, self, entry_idx, &old_entry); - } - } else { - /* delete */ - return _entry_at_idx_remove (vtable, self, entry_idx); - } -} - -gboolean -nm_default_route_manager_ip4_update_default_route (NMDefaultRouteManager *self, - gpointer source) -{ - return _ipx_update_default_route (&vtable_ip4, self, source); -} - -gboolean -nm_default_route_manager_ip6_update_default_route (NMDefaultRouteManager *self, - gpointer source) -{ - return _ipx_update_default_route (&vtable_ip6, self, source); -} - -/*****************************************************************************/ - -static NMDevice * -_ipx_get_best_device (const VTableIP *vtable, NMDefaultRouteManager *self, const GSList *devices) -{ - NMDefaultRouteManagerPrivate *priv; - GPtrArray *entries; - guint i; - - g_return_val_if_fail (NM_IS_DEFAULT_ROUTE_MANAGER (self), NULL); - - if (!devices) - return NULL; - - priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self); - if (priv->disposed) - return NULL; - entries = vtable->get_entries (priv); - - for (i = 0; i < entries->len; i++) { - Entry *entry = g_ptr_array_index (entries, i); - NMDeviceState state; - - if (!NM_IS_DEVICE (entry->source.pointer)) - continue; - - if (entry->never_default) - continue; - - state = nm_device_get_state (entry->source.device); - if ( state <= NM_DEVICE_STATE_DISCONNECTED - || state >= NM_DEVICE_STATE_DEACTIVATING) { - /* FIXME: we also track unmanaged devices with assumed default routes. - * Skip them, they are (currently) no candidates for best-device. - * - * Later we also want to properly assume connections for unmanaged devices. - * - * Also, we don't want to have DEACTIVATING devices returned as best_device(). */ - continue; - } - - if (g_slist_find ((GSList *) devices, entry->source.device)) { - g_return_val_if_fail (nm_device_get_act_request (entry->source.pointer), entry->source.pointer); - return entry->source.pointer; - } - } - return NULL; -} - -/** _ipx_get_best_activating_device: - * @vtable: the virtual table - * @self: #NMDefaultRouteManager - * @devices: list of devices to be searched. Only devices from this list will be considered - * @fully_activated: if #TRUE, only search for devices that are fully activated. Otherwise, - * search if there is a best device going to be activated. In the latter case, this will - * return NULL if the best device is already activated. - * @preferred_device: if not-NULL, this device is preferred if there are more devices with - * the same priority. - **/ -static NMDevice * -_ipx_get_best_activating_device (const VTableIP *vtable, NMDefaultRouteManager *self, const GSList *devices, NMDevice *preferred_device) -{ - NMDefaultRouteManagerPrivate *priv; - const GSList *iter; - NMDevice *best_device = NULL; - guint32 best_prio = G_MAXUINT32; - NMDevice *best_activated_device; - - g_return_val_if_fail (NM_IS_DEFAULT_ROUTE_MANAGER (self), NULL); - - priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self); - if (priv->disposed) - return NULL; - - best_activated_device = _ipx_get_best_device (vtable, self, devices); - - for (iter = devices; iter; iter = g_slist_next (iter)) { - NMDevice *device = NM_DEVICE (iter->data); - guint32 prio; - Entry *entry; - - entry = _entry_find_by_source (vtable->get_entries (priv), device, NULL); - - if (entry) { - /* of all the device that have an entry, we already know that best_activated_device - * is the best. entry cannot be better. */ - if (entry->source.device != best_activated_device) - continue; - prio = entry->effective_metric; - } else { - NMDeviceState state = nm_device_get_state (device); - - if ( state <= NM_DEVICE_STATE_DISCONNECTED - || state >= NM_DEVICE_STATE_DEACTIVATING) - continue; - - if (!nm_utils_connection_has_default_route (nm_device_get_applied_connection (device), vtable->vt->addr_family, NULL)) - continue; - - prio = nm_device_get_ip4_route_metric (device); - } - prio = vtable->vt->metric_normalize (prio); - - if ( !best_device - || prio < best_prio - || (prio == best_prio && preferred_device == device)) { - best_device = device; - best_prio = prio; - } - } - - /* There's only a best activating device if the best device - * among all activating and already-activated devices is a - * still-activating one. - */ - if (best_device && nm_device_get_state (best_device) >= NM_DEVICE_STATE_SECONDARIES) - return NULL; - return best_device; -} - -NMDevice * -nm_default_route_manager_ip4_get_best_device (NMDefaultRouteManager *self, const GSList *devices, gboolean fully_activated, NMDevice *preferred_device) -{ - if (fully_activated) - return _ipx_get_best_device (&vtable_ip4, self, devices); - else - return _ipx_get_best_activating_device (&vtable_ip4, self, devices, preferred_device); -} - -NMDevice * -nm_default_route_manager_ip6_get_best_device (NMDefaultRouteManager *self, const GSList *devices, gboolean fully_activated, NMDevice *preferred_device) -{ - if (fully_activated) - return _ipx_get_best_device (&vtable_ip6, self, devices); - else - return _ipx_get_best_activating_device (&vtable_ip6, self, devices, preferred_device); -} - -/*****************************************************************************/ - -static gpointer -_ipx_get_best_config (const VTableIP *vtable, - NMDefaultRouteManager *self, - gboolean ignore_never_default, - const char **out_ip_iface, - NMActiveConnection **out_ac, - NMDevice **out_device, - NMVpnConnection **out_vpn) -{ - NMDefaultRouteManagerPrivate *priv; - GPtrArray *entries; - guint i; - gpointer config_result = NULL; - - g_return_val_if_fail (NM_IS_DEFAULT_ROUTE_MANAGER (self), NULL); - - if (out_ip_iface) - *out_ip_iface = NULL; - if (out_ac) - *out_ac = NULL; - if (out_device) - *out_device = NULL; - if (out_vpn) - *out_vpn = NULL; - - priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self); - if (priv->disposed) - return NULL; - - g_return_val_if_fail (NM_IS_DEFAULT_ROUTE_MANAGER (self), NULL); - - priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self); - entries = vtable->get_entries (priv); - - for (i = 0; i < entries->len; i++) { - Entry *entry = g_ptr_array_index (entries, i); - - if (!NM_IS_DEVICE (entry->source.pointer)) { - NMVpnConnection *vpn = NM_VPN_CONNECTION (entry->source.vpn); - - if (entry->never_default && !ignore_never_default) - continue; - - if (vtable->vt->is_ip4) - config_result = nm_vpn_connection_get_ip4_config (vpn); - else - config_result = nm_vpn_connection_get_ip6_config (vpn); - g_assert (config_result); - - if (out_vpn) - *out_vpn = vpn; - if (out_ac) - *out_ac = NM_ACTIVE_CONNECTION (vpn); - if (out_ip_iface) - *out_ip_iface = nm_vpn_connection_get_ip_iface (vpn, TRUE); - } else { - NMDevice *device = entry->source.device; - NMActRequest *req; - NMDeviceState state; - - if (entry->never_default) - continue; - - state = nm_device_get_state (device); - if ( state <= NM_DEVICE_STATE_DISCONNECTED - || state >= NM_DEVICE_STATE_DEACTIVATING) { - /* FIXME: the device has a default route, but we ignore it due to - * unexpected state. That happens for example for unmanaged devices. - * - * In the future, we want unmanaged devices also assume a connection - * if they are activated externally. - * - * Also, we don't want to have DEACTIVATING devices returned as best_config(). */ - continue; - } - - if (vtable->vt->is_ip4) - config_result = nm_device_get_ip4_config (device); - else - config_result = nm_device_get_ip6_config (device); - g_assert (config_result); - req = nm_device_get_act_request (device); - g_assert (req); - - if (out_device) - *out_device = device; - if (out_ac) - *out_ac = NM_ACTIVE_CONNECTION (req); - if (out_ip_iface) - *out_ip_iface = nm_device_get_ip_iface (device); - } - break; - } - - return config_result; -} - -NMIP4Config * -nm_default_route_manager_ip4_get_best_config (NMDefaultRouteManager *self, - gboolean ignore_never_default, - const char **out_ip_iface, - NMActiveConnection **out_ac, - NMDevice **out_device, - NMVpnConnection **out_vpn) -{ - return _ipx_get_best_config (&vtable_ip4, - self, - ignore_never_default, - out_ip_iface, - out_ac, - out_device, - out_vpn); -} - -NMIP6Config * -nm_default_route_manager_ip6_get_best_config (NMDefaultRouteManager *self, - gboolean ignore_never_default, - const char **out_ip_iface, - NMActiveConnection **out_ac, - NMDevice **out_device, - NMVpnConnection **out_vpn) -{ - return _ipx_get_best_config (&vtable_ip6, - self, - ignore_never_default, - out_ip_iface, - out_ac, - out_device, - out_vpn); -} - -/*****************************************************************************/ - -static GPtrArray * -_v4_get_entries (NMDefaultRouteManagerPrivate *priv) -{ - return priv->entries_ip4; -} - -static GPtrArray * -_v6_get_entries (NMDefaultRouteManagerPrivate *priv) -{ - return priv->entries_ip6; -} - -static const VTableIP vtable_ip4 = { - .vt = &nm_platform_vtable_route_v4, - .get_entries = _v4_get_entries, -}; - -static const VTableIP vtable_ip6 = { - .vt = &nm_platform_vtable_route_v6, - .get_entries = _v6_get_entries, -}; - -/*****************************************************************************/ - -static gboolean -_resync_now (NMDefaultRouteManager *self) -{ - gboolean has_v4_changes, has_v6_changes; - gboolean changed = FALSE; - - NMDefaultRouteManagerPrivate *priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self); - - has_v4_changes = priv->resync.has_v4_changes; - has_v6_changes = priv->resync.has_v6_changes; - - _LOGD (0, "resync: sync now (%u) (IPv4 changes: %s, IPv6 changes: %s)", priv->resync.idle_handle, - has_v4_changes ? "yes" : "no", has_v6_changes ? "yes" : "no"); - - priv->resync.has_v4_changes = FALSE; - priv->resync.has_v6_changes = FALSE; - nm_clear_g_source (&priv->resync.idle_handle); - priv->resync.backoff_wait_time_ms = - priv->resync.backoff_wait_time_ms == 0 - ? 100 - : priv->resync.backoff_wait_time_ms * 2; - - if (has_v4_changes) - changed |= _resync_all (&vtable_ip4, self, NULL, NULL, TRUE); - - if (has_v6_changes) - changed |= _resync_all (&vtable_ip6, self, NULL, NULL, TRUE); - - if (!changed) { - /* Nothing changed: reset the backoff wait time */ - _resync_idle_cancel (self); - } - - return changed; -} - -/** - * nm_default_route_manager_resync: - * @self: the #NMDefaultRouteManager instance - * @af_family: the address family to resync, can be - * AF_INET, AF_INET6 or AF_UNSPEC to sync both. - * - * #NMDefaultRouteManager keeps an internal list of configured - * routes. Usually, it configures routes in the system only - * - when that internal list changes due to - * nm_default_route_manager_ip4_update_default_route() or - * nm_default_route_manager_ip6_update_default_route(). - * - when platform notifies about changes, via _resync_idle_now(). - * This forces a resync to update the internal bookkeeping - * with what is currently configured in the system, but also - * reconfigure the system with all non-assumed default routes. - * - * Returns: %TRUE if anything changed during resync. - */ -gboolean -nm_default_route_manager_resync (NMDefaultRouteManager *self, - int af_family) -{ - NMDefaultRouteManagerPrivate *priv; - - g_return_val_if_fail (NM_IS_DEFAULT_ROUTE_MANAGER (self), FALSE); - g_return_val_if_fail (NM_IN_SET (af_family, AF_INET, AF_INET6, AF_UNSPEC), FALSE); - - priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self); - - if (priv->disposed) - return FALSE; - - switch (af_family) { - case AF_INET: - priv->resync.has_v4_changes = TRUE; - break; - case AF_INET6: - priv->resync.has_v6_changes = TRUE; - break; - default: - priv->resync.has_v4_changes = TRUE; - priv->resync.has_v6_changes = TRUE; - break; - } - - return _resync_now (self); -} - -static gboolean -_resync_idle_now (NMDefaultRouteManager *self) -{ - NMDefaultRouteManagerPrivate *priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self); - - priv->resync.idle_handle = 0; - _resync_now (self); - return G_SOURCE_REMOVE; -} - -static void -_resync_idle_cancel (NMDefaultRouteManager *self) -{ - NMDefaultRouteManagerPrivate *priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self); - - if (priv->resync.idle_handle) { - _LOGD (0, "resync: cancelled (%u)", priv->resync.idle_handle); - g_source_remove (priv->resync.idle_handle); - priv->resync.idle_handle = 0; - } - priv->resync.backoff_wait_time_ms = 0; - priv->resync.has_v4_changes = FALSE; - priv->resync.has_v6_changes = FALSE; -} - -static void -_resync_idle_reschedule (NMDefaultRouteManager *self) -{ - NMDefaultRouteManagerPrivate *priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self); - - /* since we react on external changes and re-add/remove default routes for - * the interfaces we manage, there could be the erroneous situation where two applications - * fight over a certain default route. - * Avoid this, by increasingly wait longer to touch the system (backoff wait time). */ - - if (priv->resync.backoff_wait_time_ms == 0) { - /* for scheduling idle, always reschedule (to process all other events first) */ - if (priv->resync.idle_handle) - g_source_remove (priv->resync.idle_handle); - else - _LOGD (0, "resync: schedule on idle"); - /* Schedule this at low priority so that on an external change to platform - * a NMDevice has a chance to picks up the changes first. */ - priv->resync.idle_handle = g_idle_add_full (G_PRIORITY_LOW, (GSourceFunc) _resync_idle_now, self, NULL); - } else if (!priv->resync.idle_handle) { - priv->resync.idle_handle = g_timeout_add (priv->resync.backoff_wait_time_ms, (GSourceFunc) _resync_idle_now, self); - _LOGD (0, "resync: schedule in %u.%03u seconds (%u)", priv->resync.backoff_wait_time_ms/1000, - priv->resync.backoff_wait_time_ms%1000, priv->resync.idle_handle); - } -} - -static void -_platform_changed_cb (NMPlatform *platform, - int obj_type_i, - int ifindex, - gpointer platform_object, - int change_type_i, - NMDefaultRouteManager *self) -{ - NMDefaultRouteManagerPrivate *priv; - const NMPObjectType obj_type = obj_type_i; - const VTableIP *vtable; - - switch (obj_type) { - case NMP_OBJECT_TYPE_IP4_ADDRESS: - vtable = &vtable_ip4; - break; - case NMP_OBJECT_TYPE_IP6_ADDRESS: - vtable = &vtable_ip6; - break; - case NMP_OBJECT_TYPE_IP4_ROUTE: - if (!NM_PLATFORM_IP_ROUTE_IS_DEFAULT (platform_object)) - return; - vtable = &vtable_ip4; - break; - case NMP_OBJECT_TYPE_IP6_ROUTE: - if (!NM_PLATFORM_IP_ROUTE_IS_DEFAULT (platform_object)) - return; - vtable = &vtable_ip6; - break; - default: - g_return_if_reached (); - } - - priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self); - - if (priv->resync.guard) { - /* callbacks while executing _resync_all() are ignored. */ - return; - } - - if (vtable->vt->is_ip4) - priv->resync.has_v4_changes = TRUE; - else - priv->resync.has_v6_changes = TRUE; - - _resync_idle_reschedule (self); -} - -/*****************************************************************************/ - -static void -set_property (GObject *object, guint prop_id, - const GValue *value, GParamSpec *pspec) -{ - NMDefaultRouteManager *self = NM_DEFAULT_ROUTE_MANAGER (object); - NMDefaultRouteManagerPrivate *priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self); - - switch (prop_id) { - case PROP_LOG_WITH_PTR: - /* construct-only */ - priv->log_with_ptr = g_value_get_boolean (value); - break; - case PROP_PLATFORM: - /* construct-only */ - priv->platform = g_value_get_object (value) ? : NM_PLATFORM_GET; - if (!priv->platform) - g_return_if_reached (); - g_object_ref (priv->platform); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -/*****************************************************************************/ - -static void -nm_default_route_manager_init (NMDefaultRouteManager *self) -{ -} - -static void -constructed (GObject *object) -{ - NMDefaultRouteManager *self = NM_DEFAULT_ROUTE_MANAGER (object); - NMDefaultRouteManagerPrivate *priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self); - - priv->entries_ip4 = g_ptr_array_new_full (0, (GDestroyNotify) _entry_free); - priv->entries_ip6 = g_ptr_array_new_full (0, (GDestroyNotify) _entry_free); - - g_signal_connect (priv->platform, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, G_CALLBACK (_platform_changed_cb), self); - g_signal_connect (priv->platform, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, G_CALLBACK (_platform_changed_cb), self); - g_signal_connect (priv->platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, G_CALLBACK (_platform_changed_cb), self); - g_signal_connect (priv->platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, G_CALLBACK (_platform_changed_cb), self); -} - -NMDefaultRouteManager * -nm_default_route_manager_new (gboolean log_with_ptr, NMPlatform *platform) -{ - return g_object_new (NM_TYPE_DEFAULT_ROUTE_MANAGER, - NM_DEFAULT_ROUTE_MANAGER_LOG_WITH_PTR, log_with_ptr, - NM_DEFAULT_ROUTE_MANAGER_PLATFORM, platform, - NULL); -} - -static void -dispose (GObject *object) -{ - NMDefaultRouteManager *self = NM_DEFAULT_ROUTE_MANAGER (object); - NMDefaultRouteManagerPrivate *priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self); - - priv->disposed = TRUE; - - if (priv->platform) { - g_signal_handlers_disconnect_by_func (priv->platform, G_CALLBACK (_platform_changed_cb), self); - g_clear_object (&priv->platform); - } - - _resync_idle_cancel (self); - - /* g_ptr_array_free() invokes the free function for all entries without actually - * removing them and having dangling pointers in the process. _entry_free() - * will unref the source, which might cause the destruction of the object, which - * might trigger calling into @self again. This is guarded by priv->dispose. - * If you remove priv->dispose, you must refactor the lines below to remove enties - * one-by-one. - */ - if (priv->entries_ip4) { - g_ptr_array_free (priv->entries_ip4, TRUE); - priv->entries_ip4 = NULL; - } - if (priv->entries_ip6) { - g_ptr_array_free (priv->entries_ip6, TRUE); - priv->entries_ip6 = NULL; - } - - G_OBJECT_CLASS (nm_default_route_manager_parent_class)->dispose (object); -} - -static void -nm_default_route_manager_class_init (NMDefaultRouteManagerClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->constructed = constructed; - object_class->dispose = dispose; - object_class->set_property = set_property; - - obj_properties[PROP_LOG_WITH_PTR] = - g_param_spec_boolean (NM_DEFAULT_ROUTE_MANAGER_LOG_WITH_PTR, "", "", - TRUE, - G_PARAM_WRITABLE | - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS); - - obj_properties[PROP_PLATFORM] = - g_param_spec_object (NM_DEFAULT_ROUTE_MANAGER_PLATFORM, "", "", - NM_TYPE_PLATFORM, - G_PARAM_WRITABLE | - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS); - - g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); -} diff --git a/src/nm-default-route-manager.h b/src/nm-default-route-manager.h deleted file mode 100644 index d6ad719b0b..0000000000 --- a/src/nm-default-route-manager.h +++ /dev/null @@ -1,64 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager -- Network link manager - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright (C) 2014 Red Hat, Inc. - */ - -#ifndef __NETWORKMANAGER_DEFAULT_ROUTE_MANAGER_H__ -#define __NETWORKMANAGER_DEFAULT_ROUTE_MANAGER_H__ - -#include "nm-connection.h" - -#define NM_TYPE_DEFAULT_ROUTE_MANAGER (nm_default_route_manager_get_type ()) -#define NM_DEFAULT_ROUTE_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEFAULT_ROUTE_MANAGER, NMDefaultRouteManager)) -#define NM_DEFAULT_ROUTE_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEFAULT_ROUTE_MANAGER, NMDefaultRouteManagerClass)) -#define NM_IS_DEFAULT_ROUTE_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEFAULT_ROUTE_MANAGER)) -#define NM_IS_DEFAULT_ROUTE_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEFAULT_ROUTE_MANAGER)) -#define NM_DEFAULT_ROUTE_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEFAULT_ROUTE_MANAGER, NMDefaultRouteManagerClass)) - -#define NM_DEFAULT_ROUTE_MANAGER_LOG_WITH_PTR "log-with-ptr" -#define NM_DEFAULT_ROUTE_MANAGER_PLATFORM "platform" - -typedef struct _NMDefaultRouteManagerClass NMDefaultRouteManagerClass; - -GType nm_default_route_manager_get_type (void); - -NMDefaultRouteManager *nm_default_route_manager_new (gboolean log_with_ptr, NMPlatform *platform); - -gboolean nm_default_route_manager_ip4_update_default_route (NMDefaultRouteManager *manager, gpointer source); -gboolean nm_default_route_manager_ip6_update_default_route (NMDefaultRouteManager *manager, gpointer source); - -NMDevice *nm_default_route_manager_ip4_get_best_device (NMDefaultRouteManager *manager, const GSList *devices, gboolean fully_activated, NMDevice *preferred_device); -NMDevice *nm_default_route_manager_ip6_get_best_device (NMDefaultRouteManager *manager, const GSList *devices, gboolean fully_activated, NMDevice *preferred_device); - -NMIP4Config *nm_default_route_manager_ip4_get_best_config (NMDefaultRouteManager *manager, - gboolean ignore_never_default, - const char **out_ip_iface, - NMActiveConnection **out_ac, - NMDevice **out_device, - NMVpnConnection **out_vpn); -NMIP6Config *nm_default_route_manager_ip6_get_best_config (NMDefaultRouteManager *manager, - gboolean ignore_never_default, - const char **out_ip_iface, - NMActiveConnection **out_ac, - NMDevice **out_device, - NMVpnConnection **out_vpn); - -gboolean nm_default_route_manager_resync (NMDefaultRouteManager *self, - int af_family); - -#endif /* NM_DEFAULT_ROUTE_MANAGER_H */ diff --git a/src/nm-ip4-config.c b/src/nm-ip4-config.c index cf8435414a..59583fa080 100644 --- a/src/nm-ip4-config.c +++ b/src/nm-ip4-config.c @@ -779,8 +779,6 @@ nm_ip4_config_capture (NMDedupMultiIndex *multi_idx, NMPlatform *platform, int i continue; if (route->rt_source == NM_IP_CONFIG_SOURCE_RTPROT_KERNEL) continue; - if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route)) - continue; _add_route (self, plobj, NULL, NULL); } diff --git a/src/nm-ip6-config.c b/src/nm-ip6-config.c index 0aad49d54d..3c8bf709fb 100644 --- a/src/nm-ip6-config.c +++ b/src/nm-ip6-config.c @@ -503,8 +503,6 @@ nm_ip6_config_capture (NMDedupMultiIndex *multi_idx, NMPlatform *platform, int i continue; if (route->rt_source == NM_IP_CONFIG_SOURCE_RTPROT_KERNEL) continue; - if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route)) - continue; _add_route (self, plobj, NULL, NULL); } diff --git a/src/nm-netns.c b/src/nm-netns.c index 8069cfccc3..96ab2b3550 100644 --- a/src/nm-netns.c +++ b/src/nm-netns.c @@ -26,7 +26,6 @@ #include "platform/nm-platform.h" #include "platform/nmp-netns.h" -#include "nm-default-route-manager.h" #include "nm-core-internal.h" #include "NetworkManagerUtils.h" @@ -39,7 +38,6 @@ NM_GOBJECT_PROPERTIES_DEFINE_BASE ( typedef struct { NMPlatform *platform; NMPNetns *platform_netns; - NMDefaultRouteManager *default_route_manager; bool log_with_ptr; } NMNetnsPrivate; @@ -80,12 +78,6 @@ nm_netns_get_multi_idx (NMNetns *self) return nm_platform_get_multi_idx (NM_NETNS_GET_PRIVATE (self)->platform); } -NMDefaultRouteManager * -nm_netns_get_default_route_manager (NMNetns *self) -{ - return NM_NETNS_GET_PRIVATE (self)->default_route_manager; -} - /*****************************************************************************/ static void @@ -129,7 +121,6 @@ constructed (GObject *object) log_with_ptr = nm_platform_get_log_with_ptr (priv->platform); priv->platform_netns = nm_platform_netns_get (priv->platform); - priv->default_route_manager = nm_default_route_manager_new (log_with_ptr, priv->platform); G_OBJECT_CLASS (nm_netns_parent_class)->constructed (object); } @@ -148,7 +139,6 @@ dispose (GObject *object) NMNetns *self = NM_NETNS (object); NMNetnsPrivate *priv = NM_NETNS_GET_PRIVATE (self); - g_clear_object (&priv->default_route_manager); g_clear_object (&priv->platform); G_OBJECT_CLASS (nm_netns_parent_class)->dispose (object); diff --git a/src/nm-netns.h b/src/nm-netns.h index bc71880400..ae343ccebf 100644 --- a/src/nm-netns.h +++ b/src/nm-netns.h @@ -39,7 +39,6 @@ NMNetns *nm_netns_new (NMPlatform *platform); NMPlatform *nm_netns_get_platform (NMNetns *self); NMPNetns *nm_netns_get_platform_netns (NMNetns *self); -NMDefaultRouteManager *nm_netns_get_default_route_manager (NMNetns *self); struct _NMDedupMultiIndex *nm_netns_get_multi_idx (NMNetns *self); diff --git a/src/nm-policy.c b/src/nm-policy.c index f3c915db9d..8d392b2235 100644 --- a/src/nm-policy.c +++ b/src/nm-policy.c @@ -31,7 +31,6 @@ #include "NetworkManagerUtils.h" #include "nm-act-request.h" #include "devices/nm-device.h" -#include "nm-default-route-manager.h" #include "nm-setting-ip4-config.h" #include "nm-setting-connection.h" #include "platform/nm-platform.h" @@ -375,25 +374,81 @@ device_ip6_subnet_needed (NMDevice *device, /*****************************************************************************/ static NMDevice * -get_best_ip4_device (NMPolicy *self, gboolean fully_activated) +get_best_ip_device (NMPolicy *self, + int addr_family, + gboolean fully_activated) { NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self); + const GSList *iter; + NMDevice *best_device; + NMDevice *prev_device; + guint32 best_metric = G_MAXUINT32; + gboolean best_is_fully_activated = FALSE; - return nm_default_route_manager_ip4_get_best_device (nm_netns_get_default_route_manager (priv->netns), - nm_manager_get_devices (priv->manager), - fully_activated, - priv->default_device4); -} + nm_assert (NM_IN_SET (addr_family, AF_INET, AF_INET6)); -static NMDevice * -get_best_ip6_device (NMPolicy *self, gboolean fully_activated) -{ - NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self); + /* we prefer the current device in case of identical metric. + * Hence, try that one first.*/ + best_device = NULL; + prev_device = addr_family == AF_INET + ? (fully_activated ? priv->default_device4 : priv->activating_device4) + : (fully_activated ? priv->default_device6 : priv->activating_device6); - return nm_default_route_manager_ip6_get_best_device (nm_netns_get_default_route_manager (priv->netns), - nm_manager_get_devices (priv->manager), - fully_activated, - priv->default_device6); + for (iter = nm_manager_get_devices (priv->manager); iter; iter = iter->next) { + NMDevice *device = NM_DEVICE (iter->data); + NMDeviceState state; + const NMPObject *r; + NMConnection *connection; + guint32 metric; + gboolean is_fully_activated; + + state = nm_device_get_state (device); + if ( state <= NM_DEVICE_STATE_DISCONNECTED + || state >= NM_DEVICE_STATE_DEACTIVATING) + continue; + + if (nm_device_sys_iface_state_is_external (device)) + continue; + + r = nm_device_get_best_default_route (device, addr_family); + if (r) { + /* XXX: the best route might have rt_source NM_IP_CONFIG_SOURCE_VPN, + * which means it was injected by a VPN, not added by device. + * + * In this case, is it really the best device? Why do we even need the best + * device?? */ + metric = nm_utils_ip_route_metric_normalize (addr_family, + NMP_OBJECT_CAST_IP_ROUTE (r)->metric); + is_fully_activated = TRUE; + } else if ( !fully_activated + && (connection = nm_device_get_applied_connection (device)) + && nm_utils_connection_has_default_route (connection, addr_family, NULL)) { + metric = nm_utils_ip_route_metric_normalize (addr_family, + nm_device_get_ip_route_metric (device, addr_family)); + is_fully_activated = FALSE; + } else + continue; + + if ( !best_device + || (!best_is_fully_activated && is_fully_activated) + || ( metric < best_metric + || (metric == best_metric && device == prev_device))) { + best_device = device; + best_metric = metric; + best_is_fully_activated = is_fully_activated; + } + } + + if ( !fully_activated + && best_device + && best_is_fully_activated) { + /* There's only a best activating device if the best device + * among all activating and already-activated devices is a + * still-activating one. */ + return NULL; + } + + return best_device; } static gboolean @@ -782,20 +837,83 @@ update_default_ac (NMPolicy *self, set_active_func (best, TRUE); } -static NMIP4Config * -get_best_ip4_config (NMPolicy *self, - gboolean ignore_never_default, - const char **out_ip_iface, - NMActiveConnection **out_ac, - NMDevice **out_device, - NMVpnConnection **out_vpn) +static gpointer +get_best_ip_config (NMPolicy *self, + int addr_family, + const char **out_ip_iface, + NMActiveConnection **out_ac, + NMDevice **out_device, + NMVpnConnection **out_vpn) { - return nm_default_route_manager_ip4_get_best_config (nm_netns_get_default_route_manager (NM_POLICY_GET_PRIVATE (self)->netns), - ignore_never_default, - out_ip_iface, - out_ac, - out_device, - out_vpn); + NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self); + NMDevice *device; + gpointer conf; + const GSList *iter; + + nm_assert (NM_IN_SET (addr_family, AF_INET, AF_INET6)); + + for (iter = nm_manager_get_active_connections (priv->manager); iter; iter = iter->next) { + NMActiveConnection *active = NM_ACTIVE_CONNECTION (iter->data); + NMVpnConnection *candidate; + NMVpnConnectionState vpn_state; + + if (!NM_IS_VPN_CONNECTION (active)) + continue; + + candidate = NM_VPN_CONNECTION (active); + + vpn_state = nm_vpn_connection_get_vpn_state (candidate); + if (vpn_state != NM_VPN_CONNECTION_STATE_ACTIVATED) + continue; + + if (addr_family == AF_INET) + conf = nm_vpn_connection_get_ip4_config (candidate); + else + conf = nm_vpn_connection_get_ip6_config (candidate); + if (!conf) + continue; + + if (addr_family == AF_INET) { + if (!nm_ip4_config_best_default_route_get (conf)) + continue; + } else { + if (!nm_ip6_config_best_default_route_get (conf)) + continue; + } + + /* FIXME: in case of multiple VPN candidates, choose the one with the + * best metric. */ + NM_SET_OUT (out_device, NULL); + NM_SET_OUT (out_vpn, candidate); + NM_SET_OUT (out_ac, active); + NM_SET_OUT (out_ip_iface, nm_vpn_connection_get_ip_iface (candidate, TRUE)); + return conf; + } + + device = get_best_ip_device (self, addr_family, TRUE); + if (device) { + NMActRequest *req; + + if (addr_family == AF_INET) + conf = nm_device_get_ip4_config (device); + else + conf = nm_device_get_ip6_config (device); + req = nm_device_get_act_request (device); + + if (conf && req) { + NM_SET_OUT (out_device, device); + NM_SET_OUT (out_vpn, NULL); + NM_SET_OUT (out_ac, NM_ACTIVE_CONNECTION (req)); + NM_SET_OUT (out_ip_iface, nm_device_get_ip_iface (device)); + return conf; + } + } + + NM_SET_OUT (out_device, NULL); + NM_SET_OUT (out_vpn, NULL); + NM_SET_OUT (out_ac, NULL); + NM_SET_OUT (out_ip_iface, NULL); + return NULL; } static void @@ -806,7 +924,7 @@ update_ip4_dns (NMPolicy *self, NMDnsManager *dns_mgr) NMVpnConnection *vpn = NULL; NMDnsIPConfigType dns_type = NM_DNS_IP_CONFIG_TYPE_BEST_DEVICE; - ip4_config = get_best_ip4_config (self, TRUE, &ip_iface, NULL, NULL, &vpn); + ip4_config = get_best_ip_config (self, AF_INET, &ip_iface, NULL, NULL, &vpn); if (ip4_config) { if (vpn) dns_type = NM_DNS_IP_CONFIG_TYPE_VPN; @@ -830,7 +948,7 @@ update_ip4_routing (NMPolicy *self, gboolean force_update) /* Note that we might have an IPv4 VPN tunneled over an IPv6-only device, * so we can get (vpn != NULL && best == NULL). */ - if (!get_best_ip4_config (self, FALSE, &ip_iface, &best_ac, &best, &vpn)) { + if (!get_best_ip_config (self, AF_INET, &ip_iface, &best_ac, &best, &vpn)) { if (nm_clear_g_object (&priv->default_device4)) { _LOGt (LOGD_DNS, "set-default-device-4: %p", NULL); _notify (self, PROP_DEFAULT_IP4_DEVICE); @@ -873,22 +991,6 @@ update_ip4_routing (NMPolicy *self, gboolean force_update) _notify (self, PROP_DEFAULT_IP4_DEVICE); } -static NMIP6Config * -get_best_ip6_config (NMPolicy *self, - gboolean ignore_never_default, - const char **out_ip_iface, - NMActiveConnection **out_ac, - NMDevice **out_device, - NMVpnConnection **out_vpn) -{ - return nm_default_route_manager_ip6_get_best_config (nm_netns_get_default_route_manager (NM_POLICY_GET_PRIVATE (self)->netns), - ignore_never_default, - out_ip_iface, - out_ac, - out_device, - out_vpn); -} - static void update_ip6_dns_delegation (NMPolicy *self) { @@ -912,7 +1014,7 @@ update_ip6_dns (NMPolicy *self, NMDnsManager *dns_mgr) NMVpnConnection *vpn = NULL; NMDnsIPConfigType dns_type = NM_DNS_IP_CONFIG_TYPE_BEST_DEVICE; - ip6_config = get_best_ip6_config (self, TRUE, &ip_iface, NULL, NULL, &vpn); + ip6_config = get_best_ip_config (self, AF_INET6, &ip_iface, NULL, NULL, &vpn); if (ip6_config) { if (vpn) dns_type = NM_DNS_IP_CONFIG_TYPE_VPN; @@ -954,7 +1056,7 @@ update_ip6_routing (NMPolicy *self, gboolean force_update) /* Note that we might have an IPv6 VPN tunneled over an IPv4-only device, * so we can get (vpn != NULL && best == NULL). */ - if (!get_best_ip6_config (self, FALSE, &ip_iface, &best_ac, &best, &vpn)) { + if (!get_best_ip_config (self, AF_INET6, &ip_iface, &best_ac, &best, &vpn)) { if (nm_clear_g_object (&priv->default_device6)) { _LOGt (LOGD_DNS, "set-default-device-6: %p", NULL); _notify (self, PROP_DEFAULT_IP6_DEVICE); @@ -1024,8 +1126,8 @@ check_activating_devices (NMPolicy *self) NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self); NMDevice *best4, *best6 = NULL; - best4 = get_best_ip4_device (self, FALSE); - best6 = get_best_ip6_device (self, FALSE); + best4 = get_best_ip_device (self, AF_INET, FALSE); + best6 = get_best_ip_device (self, AF_INET6, FALSE); g_object_freeze_notify (G_OBJECT (self)); diff --git a/src/nm-types.h b/src/nm-types.h index 64d718b1ac..bd5e249baa 100644 --- a/src/nm-types.h +++ b/src/nm-types.h @@ -38,7 +38,6 @@ typedef struct _NMConfigData NMConfigData; typedef struct _NMArpingManager NMArpingManager; typedef struct _NMConnectionProvider NMConnectionProvider; typedef struct _NMConnectivity NMConnectivity; -typedef struct _NMDefaultRouteManager NMDefaultRouteManager; typedef struct _NMDevice NMDevice; typedef struct _NMDhcp4Config NMDhcp4Config; typedef struct _NMDhcp6Config NMDhcp6Config; diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index 0d40e0773d..6a301765f6 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -3697,11 +3697,6 @@ nm_platform_ip_route_sync (NMPlatform *self, for (i = 0; i < plat_routes->len; i++) { plat_o = plat_routes->pdata[i]; - if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (NMP_OBJECT_CAST_IP_ROUTE (plat_o))) { - /* don't delete default routes. */ - continue; - } - if ( !routes_idx || !g_hash_table_lookup (routes_idx, plat_o)) { if (!nm_platform_ip_route_delete (self, plat_o)) { diff --git a/src/vpn/nm-vpn-connection.c b/src/vpn/nm-vpn-connection.c index a22a55e0ad..9e4189feb7 100644 --- a/src/vpn/nm-vpn-connection.c +++ b/src/vpn/nm-vpn-connection.c @@ -44,7 +44,6 @@ #include "settings/nm-agent-manager.h" #include "nm-core-internal.h" #include "nm-pacrunner-manager.h" -#include "nm-default-route-manager.h" #include "nm-firewall-manager.h" #include "nm-config.h" #include "nm-vpn-plugin-info.h" @@ -498,9 +497,6 @@ _set_vpn_state (NMVpnConnection *self, dispatcher_cleanup (self); - nm_default_route_manager_ip4_update_default_route (nm_netns_get_default_route_manager (priv->netns), self); - nm_default_route_manager_ip6_update_default_route (nm_netns_get_default_route_manager (priv->netns), self); - /* The connection gets destroyed by the VPN manager when it enters the * disconnected/failed state, but we need to keep it around for a bit * to send out signals and handle the dispatcher. So ref it. @@ -1167,9 +1163,6 @@ nm_vpn_connection_apply_config (NMVpnConnection *self) nm_platform_link_set_mtu (nm_netns_get_platform (priv->netns), priv->ip_ifindex, priv->mtu); } - nm_default_route_manager_ip4_update_default_route (nm_netns_get_default_route_manager (priv->netns), self); - nm_default_route_manager_ip6_update_default_route (nm_netns_get_default_route_manager (priv->netns), self); - _LOGI ("VPN connection: (IP Config Get) complete"); if (priv->vpn_state < STATE_PRE_UP) _set_vpn_state (self, STATE_PRE_UP, NM_ACTIVE_CONNECTION_STATE_REASON_NONE, FALSE); @@ -1596,6 +1589,18 @@ nm_vpn_connection_ip4_config_get (NMVpnConnection *self, GVariant *dict) nm_connection_get_setting_ip4_config (_get_applied_connection (self)), route_metric); + if (!nm_ip4_config_get_never_default (config)) { + const NMPlatformIP4Route r = { + .ifindex = ip_ifindex, + .rt_source = NM_IP_CONFIG_SOURCE_VPN, + .gateway = nm_ip4_config_get_gateway (config), + .metric = route_metric, + .mss = nm_ip4_config_get_mss (config), + }; + + nm_ip4_config_add_route (config, &r, NULL); + } + if (priv->ip4_config) { nm_ip4_config_replace (priv->ip4_config, config, NULL); g_object_unref (config); @@ -1758,6 +1763,18 @@ next: nm_connection_get_setting_ip6_config (_get_applied_connection (self)), route_metric); + if (!nm_ip6_config_get_never_default (config)) { + const NMPlatformIP6Route r = { + .ifindex = ip_ifindex, + .rt_source = NM_IP_CONFIG_SOURCE_VPN, + .gateway = *(nm_ip6_config_get_gateway (config) ?: &in6addr_any), + .metric = route_metric, + .mss = nm_ip6_config_get_mss (config), + }; + + nm_ip6_config_add_route (config, &r, NULL); + } + if (priv->ip6_config) { nm_ip6_config_replace (priv->ip6_config, config, NULL); g_object_unref (config);