diff --git a/src/nm-default-route-manager.c b/src/nm-default-route-manager.c index 5944654cff..9e7bc1e3b8 100644 --- a/src/nm-default-route-manager.c +++ b/src/nm-default-route-manager.c @@ -124,7 +124,7 @@ NM_DEFINE_SINGLETON_GETTER (NMDefaultRouteManager, nm_default_route_manager_get, const Entry *const __entry = (entry); \ \ _nm_log (__level, __domain, 0, \ - "%s: entry[%u/%s:%p:%s:%c:%csync]: "_NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ + "%s: entry[%u/%s:%p:%s:%chas:%csync]: "_NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ self != singleton_instance \ ? nm_sprintf_buf (__prefix_buf, "%s%c[%p]", \ _NMLOG2_PREFIX_NAME, \ @@ -135,7 +135,7 @@ NM_DEFINE_SINGLETON_GETTER (NMDefaultRouteManager, nm_default_route_manager_get, 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 ? '0' : '1'), \ + (__entry->never_default ? '-' : '+'), \ (__entry->synced ? '+' : '-') \ _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ } \ @@ -483,17 +483,6 @@ _get_assumed_interface_metrics (const VTableIP *vtable, NMDefaultRouteManager *s return result; } -static int -_sort_metrics_ascending_fcn (gconstpointer a, gconstpointer b) -{ - guint32 m_a = *((guint32 *) a); - guint32 m_b = *((guint32 *) b); - - if (m_a < m_b) - return -1; - return m_a == m_b ? 0 : 1; -} - static gboolean _resync_all (const VTableIP *vtable, NMDefaultRouteManager *self, const Entry *changed_entry, const Entry *old_entry, gboolean external_change) { @@ -536,8 +525,6 @@ _resync_all (const VTableIP *vtable, NMDefaultRouteManager *self, const Entry *c for (i = 0; i < entries->len; i++) { entry = g_ptr_array_index (entries, i); - g_assert (entry != old_entry); - if (entry->never_default) continue; @@ -592,12 +579,15 @@ _resync_all (const VTableIP *vtable, NMDefaultRouteManager *self, const Entry *c /* 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) { + 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:add %s (%u)", + _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) { @@ -624,7 +614,7 @@ _resync_all (const VTableIP *vtable, NMDefaultRouteManager *self, const Entry *c g_array_free (routes, TRUE); - g_array_sort (changed_metrics, _sort_metrics_ascending_fcn); + 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); @@ -675,7 +665,11 @@ _entry_at_idx_update (const VTableIP *vtable, NMDefaultRouteManager *self, guint entry->effective_metric = entry->route.rx.metric; _LOG2D (vtable, entry_idx, entry, "%s %s (%"G_GUINT32_FORMAT")", - old_entry ? "record:update" : "record:add ", + old_entry + ? (entry != old_entry + ? "record:update" + : "record:resync") + : "record:add ", vtable->vt->route_to_string (&entry->route, NULL, 0), entry->effective_metric); @@ -712,7 +706,9 @@ _entry_at_idx_remove (const VTableIP *vtable, NMDefaultRouteManager *self, guint /*****************************************************************************/ static void -_ipx_update_default_route (const VTableIP *vtable, NMDefaultRouteManager *self, gpointer source) +_ipx_update_default_route (const VTableIP *vtable, + NMDefaultRouteManager *self, + gpointer source) { NMDefaultRouteManagerPrivate *priv; Entry *entry; @@ -784,9 +780,8 @@ _ipx_update_default_route (const VTableIP *vtable, NMDefaultRouteManager *self, default_route = &rt.rx; never_default = TRUE; - synced = TRUE; - } else - synced = default_route && !is_assumed; + } + synced = !is_assumed; } else { NMConnection *connection = nm_active_connection_get_applied_connection ((NMActiveConnection *) vpn); @@ -878,12 +873,18 @@ _ipx_update_default_route (const VTableIP *vtable, NMDefaultRouteManager *self, new_entry.never_default = never_default; new_entry.synced = synced; - if (memcmp (entry, &new_entry, sizeof (new_entry)) == 0) - return; - - old_entry = *entry; - *entry = new_entry; - _entry_at_idx_update (vtable, self, entry_idx, &old_entry); + 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; + } + _entry_at_idx_update (vtable, self, entry_idx, entry); + } else { + old_entry = *entry; + *entry = new_entry; + _entry_at_idx_update (vtable, self, entry_idx, &old_entry); + } } else { /* delete */ _entry_at_idx_remove (vtable, self, entry_idx); @@ -891,13 +892,15 @@ _ipx_update_default_route (const VTableIP *vtable, NMDefaultRouteManager *self, } void -nm_default_route_manager_ip4_update_default_route (NMDefaultRouteManager *self, gpointer source) +nm_default_route_manager_ip4_update_default_route (NMDefaultRouteManager *self, + gpointer source) { _ipx_update_default_route (&vtable_ip4, self, source); } void -nm_default_route_manager_ip6_update_default_route (NMDefaultRouteManager *self, gpointer source) +nm_default_route_manager_ip6_update_default_route (NMDefaultRouteManager *self, + gpointer source) { _ipx_update_default_route (&vtable_ip6, self, source); } @@ -1259,7 +1262,7 @@ static const VTableIP vtable_ip6 = { /*****************************************************************************/ static gboolean -_resync_idle_now (NMDefaultRouteManager *self) +_resync_now (NMDefaultRouteManager *self) { gboolean has_v4_changes, has_v6_changes; gboolean changed = FALSE; @@ -1274,7 +1277,7 @@ _resync_idle_now (NMDefaultRouteManager *self) priv->resync.has_v4_changes = FALSE; priv->resync.has_v6_changes = FALSE; - priv->resync.idle_handle = 0; + nm_clear_g_source (&priv->resync.idle_handle); priv->resync.backoff_wait_time_ms = priv->resync.backoff_wait_time_ms == 0 ? 100 @@ -1291,6 +1294,64 @@ _resync_idle_now (NMDefaultRouteManager *self) _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; } @@ -1336,15 +1397,36 @@ _resync_idle_reschedule (NMDefaultRouteManager *self) } static void -_platform_ipx_route_changed_cb (const VTableIP *vtable, - NMDefaultRouteManager *self, - const NMPlatformIPRoute *route) +_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; - if (route && !NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route)) { - /* we only care about address changes or changes of default route. */ - return; + 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); @@ -1362,34 +1444,6 @@ _platform_ipx_route_changed_cb (const VTableIP *vtable, _resync_idle_reschedule (self); } -static void -_platform_changed_cb (NMPlatform *platform, - int obj_type_i, - int ifindex, - gpointer platform_object, - int change_type_i, - NMDefaultRouteManager *self) -{ - const NMPObjectType obj_type = obj_type_i; - - switch (obj_type) { - case NMP_OBJECT_TYPE_IP4_ADDRESS: - _platform_ipx_route_changed_cb (&vtable_ip4, self, NULL); - break; - case NMP_OBJECT_TYPE_IP6_ADDRESS: - _platform_ipx_route_changed_cb (&vtable_ip6, self, NULL); - break; - case NMP_OBJECT_TYPE_IP4_ROUTE: - _platform_ipx_route_changed_cb (&vtable_ip4, self, (const NMPlatformIPRoute *) platform_object); - break; - case NMP_OBJECT_TYPE_IP6_ROUTE: - _platform_ipx_route_changed_cb (&vtable_ip6, self, (const NMPlatformIPRoute *) platform_object); - break; - default: - g_return_if_reached (); - } -} - /*****************************************************************************/ static void diff --git a/src/nm-default-route-manager.h b/src/nm-default-route-manager.h index bd5e873295..cecc9501a9 100644 --- a/src/nm-default-route-manager.h +++ b/src/nm-default-route-manager.h @@ -61,4 +61,7 @@ NMIP6Config *nm_default_route_manager_ip6_get_best_config (NMDefaultRouteManager NMDevice **out_device, NMVpnConnection **out_vpn); +gboolean nm_default_route_manager_resync (NMDefaultRouteManager *self, + int af_family); + #endif /* NM_DEFAULT_ROUTE_MANAGER_H */