diff options
author | Thomas Haller <thaller@redhat.com> | 2017-08-30 11:46:42 +0200 |
---|---|---|
committer | Thomas Haller <thaller@redhat.com> | 2017-09-08 11:11:21 +0200 |
commit | 77ec302714795f905301d500b9aab6c88001f32e (patch) | |
tree | 3e55697d2e25f6b4db765178e11cc0805e4f6a3f | |
parent | 0918b4914d03ec14f71b3e56178c44a6ec648b34 (diff) | |
download | NetworkManager-77ec302714795f905301d500b9aab6c88001f32e.tar.gz |
core: rework handling of default-routes and drop NMDefaultRouteManagerth/platform-route-pt4
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.
-rw-r--r-- | Makefile.am | 2 | ||||
-rw-r--r-- | src/devices/nm-device.c | 378 | ||||
-rw-r--r-- | src/devices/nm-device.h | 4 | ||||
-rw-r--r-- | src/nm-default-route-manager.c | 1561 | ||||
-rw-r--r-- | src/nm-default-route-manager.h | 64 | ||||
-rw-r--r-- | src/nm-ip4-config.c | 2 | ||||
-rw-r--r-- | src/nm-ip6-config.c | 2 | ||||
-rw-r--r-- | src/nm-netns.c | 10 | ||||
-rw-r--r-- | src/nm-netns.h | 1 | ||||
-rw-r--r-- | src/nm-policy.c | 202 | ||||
-rw-r--r-- | src/nm-types.h | 1 | ||||
-rw-r--r-- | src/platform/nm-platform.c | 5 | ||||
-rw-r--r-- | src/vpn/nm-vpn-connection.c | 31 |
13 files changed, 284 insertions, 1979 deletions
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 <string.h> - -#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; + + nm_assert (NM_IN_SET (addr_family, AF_INET, AF_INET6)); + + /* 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); + + 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; - 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); -} + state = nm_device_get_state (device); + if ( state <= NM_DEVICE_STATE_DISCONNECTED + || state >= NM_DEVICE_STATE_DEACTIVATING) + continue; -static NMDevice * -get_best_ip6_device (NMPolicy *self, gboolean fully_activated) -{ - NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self); + 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; - 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); + 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); |