diff options
author | Thomas Haller <thaller@redhat.com> | 2015-04-08 14:51:56 +0200 |
---|---|---|
committer | Thomas Haller <thaller@redhat.com> | 2015-04-08 14:51:56 +0200 |
commit | 3c240a6d0b0b1ab6576ded7b3bb8c261b32d3d19 (patch) | |
tree | 552d222544820e4e86e697acf564f1a8beacce4d | |
parent | aabc6fc57b252604b52648b819dd937aabba7805 (diff) | |
parent | bd0c44c1cf1883399d0d1de710a22382b3a38dce (diff) | |
download | NetworkManager-3c240a6d0b0b1ab6576ded7b3bb8c261b32d3d19.tar.gz |
route-manager: merge branch 'th/route-manager-bgo740064'
https://bugzilla.gnome.org/show_bug.cgi?id=740064
24 files changed, 1009 insertions, 392 deletions
diff --git a/include/nm-test-utils.h b/include/nm-test-utils.h index ddea03745d..87d6c72cae 100644 --- a/include/nm-test-utils.h +++ b/include/nm-test-utils.h @@ -169,7 +169,7 @@ nmtst_free (void) } inline static void -__nmtst_init (int *argc, char ***argv, gboolean assert_logging, const char *log_level, const char *log_domains) +__nmtst_init (int *argc, char ***argv, gboolean assert_logging, const char *log_level, const char *log_domains, gboolean *out_set_logging) { const char *nmtst_debug; gboolean is_debug = FALSE; @@ -178,6 +178,11 @@ __nmtst_init (int *argc, char ***argv, gboolean assert_logging, const char *log_ GArray *debug_messages = g_array_new (TRUE, FALSE, sizeof (char *)); int i; gboolean no_expect_message = FALSE; + gboolean _out_set_logging; + + if (!out_set_logging) + out_set_logging = &_out_set_logging; + *out_set_logging = FALSE; g_assert (!nmtst_initialized ()); @@ -275,6 +280,7 @@ __nmtst_init (int *argc, char ***argv, gboolean assert_logging, const char *log_ gboolean success = TRUE; #ifdef __NETWORKMANAGER_LOGGING_H__ success = nm_logging_setup (log_level, log_domains, NULL, NULL); + *out_set_logging = TRUE; #endif g_assert (success); } else if (__nmtst_internal.no_expect_message) { @@ -291,6 +297,7 @@ __nmtst_init (int *argc, char ***argv, gboolean assert_logging, const char *log_ gboolean success; success = nm_logging_setup (log_level, log_domains, NULL, NULL); + *out_set_logging = TRUE; g_assert (success); } #endif @@ -306,7 +313,7 @@ __nmtst_init (int *argc, char ***argv, gboolean assert_logging, const char *log_ } if ((!__nmtst_internal.assert_logging || (__nmtst_internal.assert_logging && __nmtst_internal.no_expect_message)) && - (is_debug || (log_level && !g_ascii_strcasecmp (log_level, "DEBUG"))) && + (is_debug || (c_log_level && (!g_ascii_strcasecmp (c_log_level, "DEBUG") || !g_ascii_strcasecmp (c_log_level, "TRACE")))) && !g_getenv ("G_MESSAGES_DEBUG")) { /* if we are @is_debug or @log_level=="DEBUG" and @@ -336,18 +343,27 @@ __nmtst_init (int *argc, char ***argv, gboolean assert_logging, const char *log_ inline static void nmtst_init_with_logging (int *argc, char ***argv, const char *log_level, const char *log_domains) { - __nmtst_init (argc, argv, FALSE, log_level, log_domains); + __nmtst_init (argc, argv, FALSE, log_level, log_domains, NULL); } inline static void -nmtst_init_assert_logging (int *argc, char ***argv) +nmtst_init_assert_logging (int *argc, char ***argv, const char *log_level, const char *log_domains) { - __nmtst_init (argc, argv, TRUE, NULL, NULL); + gboolean set_logging; + + __nmtst_init (argc, argv, TRUE, NULL, NULL, &set_logging); + + if (!set_logging) { + gboolean success; + + success = nm_logging_setup (log_level, log_domains, NULL, NULL); + g_assert (success); + } } #else inline static void nmtst_init (int *argc, char ***argv, gboolean assert_logging) { - __nmtst_init (argc, argv, assert_logging, NULL, NULL); + __nmtst_init (argc, argv, assert_logging, NULL, NULL, NULL); } #endif @@ -394,7 +410,7 @@ nmtst_get_rand (void) gint64 i; i = g_ascii_strtoll (str, &s, 0); - g_assert (s[0] == '\0' && i >= 0 && i < G_MAXINT32); + g_assert (s[0] == '\0' && i >= 0 && i < G_MAXUINT32); seed = i; __nmtst_internal.rand = g_rand_new_with_seed (seed); @@ -406,7 +422,7 @@ nmtst_get_rand (void) } __nmtst_internal.rand_seed = seed; - __NMTST_LOG (g_message, ">> initialize nmtst_get_rand() with seed=%u", seed); + __NMTST_LOG (g_message, ">> initialize nmtst_get_rand() with NMTST_SEED_RAND=%u", seed); } return __nmtst_internal.rand; } @@ -670,14 +686,28 @@ nmtst_platform_ip6_route_full (const char *network, guint plen, const char *gate return route; } +inline static int +_nmtst_platform_ip4_routes_equal_sort (gconstpointer a, gconstpointer b, gpointer user_data) +{ + return nm_platform_ip4_route_cmp ((const NMPlatformIP4Route *) a, (const NMPlatformIP4Route *) b); +} + inline static void -nmtst_platform_ip4_routes_equal (const NMPlatformIP4Route *a, const NMPlatformIP4Route *b, gsize len) +nmtst_platform_ip4_routes_equal (const NMPlatformIP4Route *a, const NMPlatformIP4Route *b, gsize len, gboolean ignore_order) { gsize i; + gs_free const NMPlatformIP4Route *c_a = NULL, *c_b = NULL; g_assert (a); g_assert (b); + if (ignore_order) { + a = c_a = g_memdup (a, sizeof (NMPlatformIP4Route) * len); + b = c_b = g_memdup (b, sizeof (NMPlatformIP4Route) * len); + g_qsort_with_data (c_a, len, sizeof (NMPlatformIP4Route), _nmtst_platform_ip4_routes_equal_sort, NULL); + g_qsort_with_data (c_b, len, sizeof (NMPlatformIP4Route), _nmtst_platform_ip4_routes_equal_sort, NULL); + } + for (i = 0; i < len; i++) { if (nm_platform_ip4_route_cmp (&a[i], &b[i]) != 0) { g_error ("Error comparing IPv4 route[%lu]: %s vs %s", (long unsigned) i, @@ -691,14 +721,28 @@ nmtst_platform_ip4_routes_equal (const NMPlatformIP4Route *a, const NMPlatformIP } } +inline static int +_nmtst_platform_ip6_routes_equal_sort (gconstpointer a, gconstpointer b, gpointer user_data) +{ + return nm_platform_ip6_route_cmp ((const NMPlatformIP6Route *) a, (const NMPlatformIP6Route *) b); +} + inline static void -nmtst_platform_ip6_routes_equal (const NMPlatformIP6Route *a, const NMPlatformIP6Route *b, gsize len) +nmtst_platform_ip6_routes_equal (const NMPlatformIP6Route *a, const NMPlatformIP6Route *b, gsize len, gboolean ignore_order) { gsize i; + gs_free const NMPlatformIP6Route *c_a = NULL, *c_b = NULL; g_assert (a); g_assert (b); + if (ignore_order) { + a = c_a = g_memdup (a, sizeof (NMPlatformIP6Route) * len); + b = c_b = g_memdup (b, sizeof (NMPlatformIP6Route) * len); + g_qsort_with_data (c_a, len, sizeof (NMPlatformIP6Route), _nmtst_platform_ip6_routes_equal_sort, NULL); + g_qsort_with_data (c_b, len, sizeof (NMPlatformIP6Route), _nmtst_platform_ip6_routes_equal_sort, NULL); + } + for (i = 0; i < len; i++) { if (nm_platform_ip6_route_cmp (&a[i], &b[i]) != 0) { g_error ("Error comparing IPv6 route[%lu]: %s vs %s", (long unsigned) i, diff --git a/src/NetworkManagerUtils.c b/src/NetworkManagerUtils.c index ba2319af5f..2a88c946a4 100644 --- a/src/NetworkManagerUtils.c +++ b/src/NetworkManagerUtils.c @@ -150,6 +150,80 @@ nm_utils_ip6_address_clear_host_address (struct in6_addr *dst, const struct in6_ return dst; } +void +nm_utils_array_remove_at_indexes (GArray *array, const guint *indexes_to_delete, gsize len) +{ + gsize elt_size; + guint index_to_delete; + guint i_src; + guint mm_src, mm_dst, mm_len; + gsize i_itd; + guint res_length; + + g_return_if_fail (array); + if (!len) + return; + g_return_if_fail (indexes_to_delete); + + elt_size = g_array_get_element_size (array); + + i_itd = 0; + index_to_delete = indexes_to_delete[0]; + if (index_to_delete >= array->len) + g_return_if_reached (); + + res_length = array->len - 1; + + mm_dst = index_to_delete; + mm_src = index_to_delete; + mm_len = 0; + + for (i_src = index_to_delete; i_src < array->len; i_src++) { + if (i_src < index_to_delete) + mm_len++; + else { + /* we require indexes_to_delete to contain non-repeated, ascending + * indexes. Otherwise we would need to presort the indexes. */ + while (TRUE) { + guint dd; + + if (i_itd + 1 >= len) { + index_to_delete = G_MAXUINT; + break; + } + + dd = indexes_to_delete[++i_itd]; + if (dd > index_to_delete) { + if (dd >= array->len) + g_warn_if_reached (); + else { + g_assert (res_length > 0); + res_length--; + } + index_to_delete = dd; + break; + } + g_warn_if_reached (); + } + + if (mm_len) { + memmove (&array->data[mm_dst * elt_size], + &array->data[mm_src * elt_size], + mm_len * elt_size); + mm_dst += mm_len; + mm_src += mm_len + 1; + mm_len = 0; + } else + mm_src++; + } + } + if (mm_len) { + memmove (&array->data[mm_dst * elt_size], + &array->data[mm_src * elt_size], + mm_len * elt_size); + } + g_array_set_size (array, res_length); +} int nm_spawn_process (const char *args, GError **error) diff --git a/src/NetworkManagerUtils.h b/src/NetworkManagerUtils.h index 5633d4b2f7..99de3b66b9 100644 --- a/src/NetworkManagerUtils.h +++ b/src/NetworkManagerUtils.h @@ -183,6 +183,8 @@ void nm_utils_ipv6_interface_identfier_get_from_addr (NMUtilsIPv6IfaceId *iid, GVariant *nm_utils_connection_hash_to_dict (GHashTable *hash); GHashTable *nm_utils_connection_dict_to_hash (GVariant *dict); +void nm_utils_array_remove_at_indexes (GArray *array, const guint *indexes_to_delete, gsize len); + void nm_utils_setpgid (gpointer unused); #endif /* __NETWORKMANAGER_UTILS_H__ */ diff --git a/src/dhcp-manager/tests/test-dhcp-utils.c b/src/dhcp-manager/tests/test-dhcp-utils.c index 961bd8a2ed..286c1d7476 100644 --- a/src/dhcp-manager/tests/test-dhcp-utils.c +++ b/src/dhcp-manager/tests/test-dhcp-utils.c @@ -694,8 +694,7 @@ NMTST_DEFINE (); int main (int argc, char **argv) { - nmtst_init_assert_logging (&argc, &argv); - nm_logging_setup ("WARN", "DEFAULT", NULL, NULL); + nmtst_init_assert_logging (&argc, &argv, "WARN", "DEFAULT"); g_test_add_func ("/dhcp/generic-options", test_generic_options); g_test_add_func ("/dhcp/wins-options", test_wins_options); diff --git a/src/nm-default-route-manager.c b/src/nm-default-route-manager.c index 27b5d783a9..65fb584c5a 100644 --- a/src/nm-default-route-manager.c +++ b/src/nm-default-route-manager.c @@ -64,10 +64,11 @@ NM_DEFINE_SINGLETON_GETTER (NMDefaultRouteManager, nm_default_route_manager_get, #define _LOG(level, addr_family, ...) \ G_STMT_START { \ - int __addr_family = (addr_family); \ - guint64 __domain = __addr_family == AF_INET ? LOGD_IP4 : LOGD_IP6; \ + 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))) { \ + if (nm_logging_enabled (__level, __domain)) { \ char __ch = __addr_family == AF_INET ? '4' : (__addr_family == AF_INET6 ? '6' : '-'); \ char __prefix[30] = "default-route"; \ \ @@ -75,7 +76,7 @@ NM_DEFINE_SINGLETON_GETTER (NMDefaultRouteManager, nm_default_route_manager_get, g_snprintf (__prefix, sizeof (__prefix), "default-route%c[%p]", __ch, (self)); \ else \ __prefix[STRLEN ("default-route")] = __ch; \ - nm_log ((level), (__domain), \ + nm_log (__level, __domain, \ "%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ __prefix _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ } \ @@ -141,22 +142,16 @@ typedef struct { } Entry; typedef struct { - int addr_family; + const NMPlatformVTableRoute *vt; GPtrArray *(*get_entries) (NMDefaultRouteManagerPrivate *priv); - const char *(*platform_route_to_string) (const NMPlatformIPRoute *route); - GArray *(*platform_route_get_all) (int ifindex, NMPlatformGetRouteMode mode); - gboolean (*platform_route_delete_default) (int ifindex, guint32 metric); - guint32 (*route_metric_normalize) (guint32 metric); } VTableIP; static const VTableIP vtable_ip4, vtable_ip6; -#define VTABLE_IS_IP4 (vtable->addr_family == AF_INET) - static NMPlatformIPRoute * _vt_route_index (const VTableIP *vtable, GArray *routes, guint index) { - if (VTABLE_IS_IP4) + if (vtable->vt->is_ip4) return (NMPlatformIPRoute *) &g_array_index (routes, NMPlatformIP4Route, index); else return (NMPlatformIPRoute *) &g_array_index (routes, NMPlatformIP6Route, index); @@ -170,7 +165,7 @@ _vt_routes_has_entry (const VTableIP *vtable, GArray *routes, const Entry *entry route.rx.metric = entry->effective_metric; - if (VTABLE_IS_IP4) { + if (vtable->vt->is_ip4) { for (i = 0; i < routes->len; i++) { NMPlatformIP4Route *r = &g_array_index (routes, NMPlatformIP4Route, i); @@ -260,7 +255,7 @@ _platform_route_sync_add (const VTableIP *vtable, NMDefaultRouteManager *self, g if (!entry) return FALSE; - if (VTABLE_IS_IP4) { + if (vtable->vt->is_ip4) { success = nm_platform_ip4_route_add (entry->route.rx.ifindex, entry->route.rx.source, 0, @@ -279,8 +274,8 @@ _platform_route_sync_add (const VTableIP *vtable, NMDefaultRouteManager *self, g entry->route.rx.mss); } if (!success) { - _LOGW (vtable->addr_family, "failed to add default route %s with effective metric %u", - vtable->platform_route_to_string (&entry->route.rx), (guint) entry->effective_metric); + _LOGW (vtable->vt->addr_family, "failed to add default route %s with effective metric %u", + vtable->vt->route_to_string (&entry->route), (guint) entry->effective_metric); } return TRUE; } @@ -295,7 +290,7 @@ _platform_route_sync_flush (const VTableIP *vtable, NMDefaultRouteManager *self, gboolean changed = FALSE; /* prune all other default routes from this device. */ - routes = vtable->platform_route_get_all (0, NM_PLATFORM_GET_ROUTE_MODE_ONLY_DEFAULT); + routes = vtable->vt->route_get_all (0, NM_PLATFORM_GET_ROUTE_MODE_ONLY_DEFAULT); for (i = 0; i < routes->len; i++) { const NMPlatformIPRoute *route; @@ -327,7 +322,7 @@ _platform_route_sync_flush (const VTableIP *vtable, NMDefaultRouteManager *self, */ if ( !entry && (has_ifindex_synced || ifindex_to_flush == route->ifindex)) { - vtable->platform_route_delete_default (route->ifindex, route->metric); + vtable->vt->route_delete_default (route->ifindex, route->metric); changed = TRUE; } } @@ -411,7 +406,7 @@ _get_assumed_interface_metrics (const VTableIP *vtable, NMDefaultRouteManager *s } if (!ifindex_has_synced_entry) - g_hash_table_add (result, GUINT_TO_POINTER (vtable->route_metric_normalize (route->metric))); + g_hash_table_add (result, GUINT_TO_POINTER (vtable->vt->metric_normalize (route->metric))); } return result; @@ -447,7 +442,7 @@ _resync_all (const VTableIP *vtable, NMDefaultRouteManager *self, const Entry *c priv->resync.guard++; if (!external_change) { - if (VTABLE_IS_IP4) + if (vtable->vt->is_ip4) priv->resync.has_v4_changes = FALSE; else priv->resync.has_v6_changes = FALSE; @@ -457,7 +452,7 @@ _resync_all (const VTableIP *vtable, NMDefaultRouteManager *self, const Entry *c entries = vtable->get_entries (priv); - routes = vtable->platform_route_get_all (0, NM_PLATFORM_GET_ROUTE_MODE_ONLY_DEFAULT); + routes = vtable->vt->route_get_all (0, NM_PLATFORM_GET_ROUTE_MODE_ONLY_DEFAULT); assumed_metrics = _get_assumed_interface_metrics (vtable, self, routes); @@ -527,24 +522,24 @@ _resync_all (const VTableIP *vtable, NMDefaultRouteManager *self, const Entry *c * or none. Hence, we only have to remember what is going to change. */ g_array_append_val (changed_metrics, expected_metric); if (old_entry) { - _LOGD (vtable->addr_family, LOG_ENTRY_FMT": update %s (%u -> %u)", LOG_ENTRY_ARGS (i, entry), - vtable->platform_route_to_string (&entry->route.rx), (guint) old_entry->effective_metric, + _LOGD (vtable->vt->addr_family, LOG_ENTRY_FMT": update %s (%u -> %u)", LOG_ENTRY_ARGS (i, entry), + vtable->vt->route_to_string (&entry->route), (guint) old_entry->effective_metric, (guint) expected_metric); } else { - _LOGD (vtable->addr_family, LOG_ENTRY_FMT": add %s (%u)", LOG_ENTRY_ARGS (i, entry), - vtable->platform_route_to_string (&entry->route.rx), (guint) expected_metric); + _LOGD (vtable->vt->addr_family, LOG_ENTRY_FMT": add %s (%u)", LOG_ENTRY_ARGS (i, entry), + vtable->vt->route_to_string (&entry->route), (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); - _LOGD (vtable->addr_family, LOG_ENTRY_FMT": resync metric %s (%u -> %u)", LOG_ENTRY_ARGS (i, entry), - vtable->platform_route_to_string (&entry->route.rx), (guint) entry->effective_metric, + _LOGD (vtable->vt->addr_family, LOG_ENTRY_FMT": resync metric %s (%u -> %u)", LOG_ENTRY_ARGS (i, entry), + vtable->vt->route_to_string (&entry->route), (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); - _LOGD (vtable->addr_family, LOG_ENTRY_FMT": readd route %s (%u -> %u)", LOG_ENTRY_ARGS (i, entry), - vtable->platform_route_to_string (&entry->route.rx), (guint) entry->effective_metric, + _LOGD (vtable->vt->addr_family, LOG_ENTRY_FMT": readd route %s (%u -> %u)", LOG_ENTRY_ARGS (i, entry), + vtable->vt->route_to_string (&entry->route), (guint) entry->effective_metric, (guint) entry->effective_metric); } } @@ -608,10 +603,10 @@ _entry_at_idx_update (const VTableIP *vtable, NMDefaultRouteManager *self, guint if (!entry->synced && !entry->never_default) entry->effective_metric = entry->route.rx.metric; - _LOGD (vtable->addr_family, LOG_ENTRY_FMT": %s %s", + _LOGD (vtable->vt->addr_family, LOG_ENTRY_FMT": %s %s", LOG_ENTRY_ARGS (entry_idx, entry), old_entry ? "update" : "add", - vtable->platform_route_to_string (&entry->route.rx)); + vtable->vt->route_to_string (&entry->route)); g_ptr_array_sort_with_data (entries, _sort_entries_cmp, NULL); @@ -631,8 +626,8 @@ _entry_at_idx_remove (const VTableIP *vtable, NMDefaultRouteManager *self, guint entry = g_ptr_array_index (entries, entry_idx); - _LOGD (vtable->addr_family, LOG_ENTRY_FMT": remove %s (%u)", LOG_ENTRY_ARGS (entry_idx, entry), - vtable->platform_route_to_string (&entry->route.rx), (guint) entry->effective_metric); + _LOGD (vtable->vt->addr_family, LOG_ENTRY_FMT": remove %s (%u)", LOG_ENTRY_ARGS (entry_idx, entry), + vtable->vt->route_to_string (&entry->route), (guint) entry->effective_metric); /* Remove the entry from the list (but don't free it yet) */ g_ptr_array_index (entries, entry_idx) = NULL; @@ -692,7 +687,7 @@ _ipx_update_default_route (const VTableIP *vtable, NMDefaultRouteManager *self, if ( entry && entry->route.rx.ifindex != ip_ifindex) { /* Strange... the ifindex changed... Remove the device and start again. */ - _LOGD (vtable->addr_family, "ifindex of "LOG_ENTRY_FMT" changed: %d -> %d", + _LOGD (vtable->vt->addr_family, "ifindex of "LOG_ENTRY_FMT" changed: %d -> %d", LOG_ENTRY_ARGS (entry_idx, entry), entry->route.rx.ifindex, ip_ifindex); @@ -709,7 +704,7 @@ _ipx_update_default_route (const VTableIP *vtable, NMDefaultRouteManager *self, if (device) { gboolean is_assumed; - if (VTABLE_IS_IP4) + 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); @@ -737,7 +732,7 @@ _ipx_update_default_route (const VTableIP *vtable, NMDefaultRouteManager *self, && nm_vpn_connection_get_vpn_state (vpn) == NM_VPN_CONNECTION_STATE_ACTIVATED) { memset (&rt, 0, sizeof (rt)); - if (VTABLE_IS_IP4) { + if (vtable->vt->is_ip4) { NMIP4Config *vpn_config; vpn_config = nm_vpn_connection_get_ip4_config (vpn); @@ -779,13 +774,13 @@ _ipx_update_default_route (const VTableIP *vtable, NMDefaultRouteManager *self, entry = g_slice_new0 (Entry); entry->source.object = g_object_ref (source); - if (VTABLE_IS_IP4) + 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->route_metric_normalize (entry->route.rx.metric); + 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; @@ -798,12 +793,12 @@ _ipx_update_default_route (const VTableIP *vtable, NMDefaultRouteManager *self, Entry old_entry, new_entry; new_entry = *entry; - if (VTABLE_IS_IP4) + 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->route_metric_normalize (new_entry.route.rx.metric); + 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; @@ -843,14 +838,14 @@ _ipx_connection_has_default_route (const VTableIP *vtable, NMDefaultRouteManager g_return_val_if_fail (NM_IS_DEFAULT_ROUTE_MANAGER (self), FALSE); g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE); - if (VTABLE_IS_IP4) + if (vtable->vt->is_ip4) s_ip = nm_connection_get_setting_ip4_config (connection); else s_ip = nm_connection_get_setting_ip6_config (connection); if (!s_ip || nm_setting_ip_config_get_never_default (s_ip)) return FALSE; - if (VTABLE_IS_IP4) { + if (vtable->vt->is_ip4) { method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG); if ( !method || !strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED) @@ -980,7 +975,7 @@ _ipx_get_best_activating_device (const VTableIP *vtable, NMDefaultRouteManager * prio = nm_device_get_ip4_route_metric (device); } - prio = vtable->route_metric_normalize (prio); + prio = vtable->vt->metric_normalize (prio); if ( !best_device || prio < best_prio @@ -1062,7 +1057,7 @@ _ipx_get_best_config (const VTableIP *vtable, if (entry->never_default && !ignore_never_default) continue; - if (VTABLE_IS_IP4) + if (vtable->vt->is_ip4) config_result = nm_vpn_connection_get_ip4_config (vpn); else config_result = nm_vpn_connection_get_ip6_config (vpn); @@ -1095,7 +1090,7 @@ _ipx_get_best_config (const VTableIP *vtable, continue; } - if (VTABLE_IS_IP4) + if (vtable->vt->is_ip4) config_result = nm_device_get_ip4_config (device); else config_result = nm_device_get_ip6_config (device); @@ -1164,40 +1159,14 @@ _v6_get_entries (NMDefaultRouteManagerPrivate *priv) return priv->entries_ip6; } -static gboolean -_v4_platform_route_delete_default (int ifindex, guint32 metric) -{ - return nm_platform_ip4_route_delete (ifindex, 0, 0, metric); -} - -static gboolean -_v6_platform_route_delete_default (int ifindex, guint32 metric) -{ - return nm_platform_ip6_route_delete (ifindex, in6addr_any, 0, metric); -} - -static guint32 -_v4_route_metric_normalize (guint32 metric) -{ - return metric; -} - static const VTableIP vtable_ip4 = { - .addr_family = AF_INET, + .vt = &nm_platform_vtable_route_v4, .get_entries = _v4_get_entries, - .platform_route_to_string = (const char *(*)(const NMPlatformIPRoute *)) nm_platform_ip4_route_to_string, - .platform_route_get_all = nm_platform_ip4_route_get_all, - .platform_route_delete_default = _v4_platform_route_delete_default, - .route_metric_normalize = _v4_route_metric_normalize, }; static const VTableIP vtable_ip6 = { - .addr_family = AF_INET6, + .vt = &nm_platform_vtable_route_v6, .get_entries = _v6_get_entries, - .platform_route_to_string = (const char *(*)(const NMPlatformIPRoute *)) nm_platform_ip6_route_to_string, - .platform_route_get_all = nm_platform_ip6_route_get_all, - .platform_route_delete_default = _v6_platform_route_delete_default, - .route_metric_normalize = nm_utils_ip6_route_metric_normalize, }; /***********************************************************************************/ @@ -1296,7 +1265,7 @@ _platform_ipx_route_changed_cb (const VTableIP *vtable, return; } - if (VTABLE_IS_IP4) + if (vtable->vt->is_ip4) priv->resync.has_v4_changes = TRUE; else priv->resync.has_v6_changes = TRUE; diff --git a/src/nm-route-manager.c b/src/nm-route-manager.c index 94f0cbbf3f..219bbbb346 100644 --- a/src/nm-route-manager.c +++ b/src/nm-route-manager.c @@ -25,197 +25,583 @@ #include "nm-route-manager.h" #include "nm-platform.h" #include "nm-logging.h" - +#include "gsystem-local-alloc.h" #include "NetworkManagerUtils.h" +//#define DEBUG_ROUTE_MANAGER + +typedef struct { + guint len; + NMPlatformIPXRoute *entries[1]; +} RouteIndex; + +typedef struct { + GArray *entries; + RouteIndex *index; +} RouteEntries; + typedef struct { - GArray *ip4_routes; - GArray *ip6_routes; + RouteEntries ip4_routes; + RouteEntries ip6_routes; } NMRouteManagerPrivate; #define NM_ROUTE_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_ROUTE_MANAGER, NMRouteManagerPrivate)) -G_DEFINE_TYPE (NMRouteManager, nm_route_manager, G_TYPE_OBJECT) +G_DEFINE_TYPE (NMRouteManager, nm_route_manager, G_TYPE_OBJECT); -static const NMPlatformIP4Route * -array_get_ip4_route (const GArray *routes, int ifindex, const NMPlatformIP4Route *route) +NM_DEFINE_SINGLETON_GETTER (NMRouteManager, nm_route_manager_get, NM_TYPE_ROUTE_MANAGER); + +/*********************************************************************************************/ + +typedef struct { + const NMPlatformVTableRoute *vt; + + /* a compare function for two routes that considers only the fields network/plen,metric. */ + int (*route_id_cmp) (const NMPlatformIPXRoute *r1, const NMPlatformIPXRoute *r2); +} VTableIP; + +static const VTableIP vtable_v4, vtable_v6; + +#define VTABLE_ROUTE_INDEX(vtable, garray, idx) ((NMPlatformIPXRoute *) &((garray)->data[(idx) * (vtable)->vt->sizeof_route])) + +#define VTABLE_IS_DEVICE_ROUTE(vtable, route) ((vtable)->vt->is_ip4 \ + ? ((route)->r4.gateway == 0) \ + : IN6_IS_ADDR_UNSPECIFIED (&(route)->r6.gateway) ) + +#define CMP_AND_RETURN_INT(a, b) \ + G_STMT_START { \ + typeof(a) _a = (a), _b = (b); \ + \ + if (_a < _b) \ + return -1; \ + if (_a > _b) \ + return 1; \ + } G_STMT_END + +/*********************************************************************************************/ + +#define _LOG_PREFIX_NAME "route-mgr" + +#define _LOG(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 __ch = __addr_family == AF_INET ? '4' : (__addr_family == AF_INET6 ? '6' : '-'); \ + char __prefix[30] = _LOG_PREFIX_NAME; \ + \ + if ((self) != singleton_instance) \ + g_snprintf (__prefix, sizeof (__prefix), "%s%c[%p]", _LOG_PREFIX_NAME, __ch, (self)); \ + else \ + __prefix[STRLEN (_LOG_PREFIX_NAME)] = __ch; \ + nm_log ((level), (__domain), \ + "%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ + __prefix _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ + } \ + } G_STMT_END +#define _LOG_LEVEL_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); \ + }) + +#ifdef DEBUG_ROUTE_MANAGER +#define _LOGT_ENABLED(addr_family) _LOG_LEVEL_ENABLED (LOGL_TRACE, addr_family) +#define _LOGT(addr_family, ...) _LOG (LOGL_TRACE, addr_family, __VA_ARGS__) +#else +#define _LOGT_ENABLED(addr_family) FALSE +#define _LOGT(addr_family, ...) G_STMT_START { (void) 0; } G_STMT_END +#endif + +#define _LOGD(addr_family, ...) _LOG (LOGL_DEBUG, addr_family, __VA_ARGS__) +#define _LOGI(addr_family, ...) _LOG (LOGL_INFO , addr_family, __VA_ARGS__) +#define _LOGW(addr_family, ...) _LOG (LOGL_WARN , addr_family, __VA_ARGS__) +#define _LOGE(addr_family, ...) _LOG (LOGL_ERR , addr_family, __VA_ARGS__) + +/*********************************************************************************************/ + +#if defined (DEBUG_ROUTE_MANAGER) && !defined (G_DISABLE_ASSERT) +inline static void +ASSERT_route_index_valid (const VTableIP *vtable, const GArray *entries, const RouteIndex *index, gboolean unique_ifindexes) { - guint len = routes ? routes->len : 0; - guint i; + guint i, j; + int c; + const NMPlatformIPXRoute *r1, *r2; + gs_unref_hashtable GHashTable *ptrs = g_hash_table_new (NULL, NULL); + const NMPlatformIPXRoute *r_first = NULL, *r_last = NULL; + + g_assert (index); + + if (entries) + g_assert_cmpint (entries->len, ==, index->len); + else + g_assert (index->len == 0); + + if (index->len > 0) { + r_first = VTABLE_ROUTE_INDEX (vtable, entries, 0); + r_last = VTABLE_ROUTE_INDEX (vtable, entries, index->len - 1); + } - for (i = 0; i < len; i++) { - NMPlatformIP4Route *c = &g_array_index (routes, NMPlatformIP4Route, i); + /* assert that the @index is valid for the @entries. */ - if (ifindex) { - /* Looking for a specific route. */ - if ( c->ifindex != ifindex - || route->mss != c->mss - || route->gateway != c->gateway) - continue; - } + g_assert (!index->entries[index->len]); + for (i = 0; i < index->len; i++) { + r1 = index->entries[i]; + + g_assert (r1); + g_assert (r1 >= r_first); + g_assert (r1 <= r_last); + g_assert_cmpint ((((char *) r1) - ((char *) entries->data)) % vtable->vt->sizeof_route, ==, 0); + + g_assert (!g_hash_table_contains (ptrs, (gpointer) r1)); + g_hash_table_add (ptrs, (gpointer) r1); - if (route->network == c->network && - route->plen == c->plen && - route->metric == c->metric) - return c; + for (j = i; j > 0; ) { + r2 = index->entries[--j]; + + c = vtable->route_id_cmp (r1, r2); + g_assert (c >= 0); + if (c != 0) + break; + if (unique_ifindexes) + g_assert_cmpint (r1->rx.ifindex, !=, r2->rx.ifindex); + } } +} +#else +#define ASSERT_route_index_valid(vtable, entries, index, unique_ifindexes) G_STMT_START { (void) 0; } G_STMT_END +#endif - return NULL; +/*********************************************************************************************/ + +static int +_v4_route_id_cmp (const NMPlatformIP4Route *r1, const NMPlatformIP4Route *r2) +{ + CMP_AND_RETURN_INT (r1->plen, r2->plen); + CMP_AND_RETURN_INT (r1->metric, r2->metric); + CMP_AND_RETURN_INT (nm_utils_ip4_address_clear_host_address (r1->network, r1->plen), + nm_utils_ip4_address_clear_host_address (r2->network, r2->plen)); + return 0; } -static const NMPlatformIP6Route * -array_get_ip6_route (const GArray *routes, int ifindex, const NMPlatformIP6Route *route) +static int +_v6_route_id_cmp (const NMPlatformIP6Route *r1, const NMPlatformIP6Route *r2) { - guint len = routes ? routes->len : 0; + struct in6_addr n1, n2; + + CMP_AND_RETURN_INT (r1->plen, r2->plen); + CMP_AND_RETURN_INT (nm_utils_ip6_route_metric_normalize (r1->metric), + nm_utils_ip6_route_metric_normalize (r2->metric)); + + nm_utils_ip6_address_clear_host_address (&n1, &r1->network, r1->plen); + nm_utils_ip6_address_clear_host_address (&n2, &r2->network, r2->plen); + return memcmp (&n1, &n2, sizeof (n1)); +} + +/*********************************************************************************************/ + +static int +_route_index_create_sort (const NMPlatformIPXRoute **p1, const NMPlatformIPXRoute ** p2, const VTableIP *vtable) +{ + return vtable->route_id_cmp (*p1, *p2); +} + +static RouteIndex * +_route_index_create (const VTableIP *vtable, const GArray *routes) +{ + RouteIndex *index; guint i; + guint len = routes ? routes->len : 0; - for (i = 0; i < len; i++) { - NMPlatformIP6Route *c = &g_array_index (routes, NMPlatformIP6Route, i); - int route_metric = nm_utils_ip6_route_metric_normalize (route->metric); - int c_metric = nm_utils_ip6_route_metric_normalize (c->metric); + index = g_malloc (sizeof (RouteIndex) + len * sizeof (NMPlatformIPXRoute *)); - if (ifindex) { - /* Looking for a specific route. */ - if ( c->ifindex != ifindex - || route->mss != c->mss - || !IN6_ARE_ADDR_EQUAL (&route->gateway, &c->gateway)) - continue; + index->len = len; + for (i = 0; i < len; i++) + index->entries[i] = VTABLE_ROUTE_INDEX (vtable, routes, i); + index->entries[i] = NULL; + + /* this is a stable sort, which is very important at this point. */ + g_qsort_with_data (index->entries, + len, + sizeof (NMPlatformIPXRoute *), + (GCompareDataFunc) _route_index_create_sort, + (gpointer) vtable); + return index; +} + +static guint +_route_index_reverse_idx (const VTableIP *vtable, const RouteIndex *index, guint idx_idx, const GArray *routes) +{ + const NMPlatformIPXRoute *r, *r0; + gssize offset; + + /* reverse the @idx_idx that points into @index, to the corresponding index into the unsorted @routes array. */ + + r = index->entries[idx_idx]; + r0 = VTABLE_ROUTE_INDEX (vtable, routes, 0); + + if (vtable->vt->is_ip4) + offset = &r->r4 - &r0->r4; + else + offset = &r->r6 - &r0->r6; + g_assert (offset >= 0 && offset < index->len); + g_assert (VTABLE_ROUTE_INDEX (vtable, routes, offset) == r); + return offset; +} + +/*********************************************************************************************/ + +static gboolean +_route_equals_ignoring_ifindex (const VTableIP *vtable, const NMPlatformIPXRoute *r1, const NMPlatformIPXRoute *r2) +{ + NMPlatformIPXRoute r2_backup; + + if (r1->rx.ifindex != r2->rx.ifindex) { + memcpy (&r2_backup, r2, vtable->vt->sizeof_route); + r2_backup.rx.ifindex = r1->rx.ifindex; + r2 = &r2_backup; + } + return vtable->vt->route_cmp (r1, r2) == 0; +} + +static NMPlatformIPXRoute * +_get_next_ipx_route (const RouteIndex *index, gboolean start_at_zero, guint *cur_idx, int ifindex) +{ + guint i; + + if (start_at_zero) + i = 0; + else + i = *cur_idx + 1; + /* Find the next route with matching @ifindex. */ + for (; i < index->len; i++) { + if (index->entries[i]->rx.ifindex == ifindex) { + *cur_idx = i; + return index->entries[i]; } + } + *cur_idx = index->len; + return NULL; +} + +static const NMPlatformIPXRoute * +_get_next_known_route (const VTableIP *vtable, const RouteIndex *index, gboolean start_at_zero, guint *cur_idx) +{ + guint i = 0; + const NMPlatformIPXRoute *cur = NULL; + + if (!start_at_zero) { + i = *cur_idx; + cur = index->entries[i]; + i++; + } + /* For @known_routes we expect that all routes have the same @ifindex. This is not enforced however, + * the ifindex value of these routes is ignored. */ + for (; i < index->len; i++) { + const NMPlatformIPXRoute *r = index->entries[i]; + + /* skip over default routes. */ + if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (r)) + continue; + + /* @known_routes should not, but could contain duplicate routes. Skip over them. */ + if (cur && vtable->route_id_cmp (cur, r) == 0) + continue; - if (IN6_ARE_ADDR_EQUAL (&route->network, &c->network) && - route->plen == c->plen && - route_metric == c_metric) - return c; + *cur_idx = i; + return r; } + *cur_idx = index->len; + return NULL; +} +static const NMPlatformIPXRoute * +_get_next_plat_route (const RouteIndex *index, gboolean start_at_zero, guint *cur_idx) +{ + if (start_at_zero) + *cur_idx = 0; + else + ++*cur_idx; + + /* get next route from the platform index. */ + if (*cur_idx < index->len) + return index->entries[*cur_idx]; + *cur_idx = index->len; return NULL; } +static int +_sort_indexes_cmp (guint *a, guint *b) +{ + CMP_AND_RETURN_INT (*a, *b); + g_return_val_if_reached (0); +} -/** - * nm_route_manager_ip4_route_sync: - * @ifindex: Interface index - * @known_routes: List of routes - * - * A convenience function to synchronize routes for a specific interface - * with the least possible disturbance. It simply removes routes that are - * not listed and adds routes that are. - * Default routes are ignored (both in @known_routes and those already - * configured on the device). - * - * Returns: %TRUE on success. - */ -gboolean -nm_route_manager_ip4_route_sync (NMRouteManager *self, int ifindex, const GArray *known_routes) +/*********************************************************************************************/ + +static gboolean +_vx_route_sync (const VTableIP *vtable, NMRouteManager *self, int ifindex, const GArray *known_routes) { NMRouteManagerPrivate *priv = NM_ROUTE_MANAGER_GET_PRIVATE (self); - GArray *plat_routes, *routes = priv->ip4_routes; - NMPlatformIP4Route route; - const NMPlatformIP4Route *known_route; - const NMPlatformIP4Route *existing; - gboolean success; - int i, i_type; - - /* Learn about routes that platform knows but we don't. */ - plat_routes = nm_platform_ip4_route_get_all (0, NM_PLATFORM_GET_ROUTE_MODE_NO_DEFAULT); - for (i = 0; i < plat_routes->len; i++) { - existing = &g_array_index (plat_routes, NMPlatformIP4Route, i); - if (!array_get_ip4_route (routes, existing->ifindex, existing)) - g_array_append_val (routes, *existing); + GArray *plat_routes; + RouteEntries *ipx_routes; + RouteIndex *plat_routes_idx, *known_routes_idx; + gboolean success = TRUE; + guint i, i_type; + GArray *to_delete_indexes = NULL, *to_restore_routes = NULL; + GPtrArray *to_add_routes = NULL; + guint i_known_routes, i_plat_routes, i_ipx_routes; + const NMPlatformIPXRoute *cur_known_route, *cur_plat_route; + NMPlatformIPXRoute *cur_ipx_route; + + ipx_routes = vtable->vt->is_ip4 ? &priv->ip4_routes : &priv->ip6_routes; + plat_routes = vtable->vt->route_get_all (ifindex, NM_PLATFORM_GET_ROUTE_MODE_NO_DEFAULT); + plat_routes_idx = _route_index_create (vtable, plat_routes); + known_routes_idx = _route_index_create (vtable, known_routes); + + ASSERT_route_index_valid (vtable, plat_routes, plat_routes_idx, TRUE); + ASSERT_route_index_valid (vtable, known_routes, known_routes_idx, FALSE); + + _LOGD (vtable->vt->addr_family, "%3d: sync %u IPv%c routes", ifindex, known_routes_idx->len, vtable->vt->is_ip4 ? '4' : '6'); + if (_LOGT_ENABLED (vtable->vt->addr_family)) { + for (i = 0; i < known_routes_idx->len; i++) { + _LOGT (vtable->vt->addr_family, "%3d: sync new addr #%u: %s", + ifindex, i, vtable->vt->route_to_string (VTABLE_ROUTE_INDEX (vtable, known_routes, i))); + } + for (i = 0; i < ipx_routes->index->len; i++) + _LOGT (vtable->vt->addr_family, "%3d: STATE: has #%u - %s", ifindex, i, vtable->vt->route_to_string (ipx_routes->index->entries[i])); } - /* Delete unknown routes */ - for (i = 0; i < routes->len;) { - route = g_array_index (routes, NMPlatformIP4Route, i); - - if (route.ifindex == ifindex) { - /* Our route. Keep it? */ - if (array_get_ip4_route (known_routes, route.ifindex, &route)) { - i++; - continue; + /*************************************************************************** + * Check which routes are in @known_routes, and update @ipx_routes. + * + * This first part only updates @ipx_routes to find out what routes must + * be added/deleted. + **************************************************************************/ + + /* iterate over @ipx_routes and @known_routes */ + cur_ipx_route = _get_next_ipx_route (ipx_routes->index, TRUE, &i_ipx_routes, ifindex); + cur_known_route = _get_next_known_route (vtable, known_routes_idx, TRUE, &i_known_routes); + while (cur_ipx_route || cur_known_route) { + int route_id_cmp_result = -1; + + while ( cur_ipx_route + && ( !cur_known_route + || ((route_id_cmp_result = vtable->route_id_cmp (cur_ipx_route, cur_known_route)) < 0))) { + /* we have @cur_ipx_route, which is less then @cur_known_route. Hence, + * the route does no longer exist in @known_routes */ + if (!to_delete_indexes) + to_delete_indexes = g_array_new (FALSE, FALSE, sizeof (guint)); + g_array_append_val (to_delete_indexes, i_ipx_routes); + + /* later we will delete @cur_ipx_route. See if @cur_ipx_route was shadowing another route, that + * we must restore. */ + if (i_ipx_routes + 1 < ipx_routes->index->len) { + const NMPlatformIPXRoute *next_route = ipx_routes->index->entries[i_ipx_routes + 1]; + + if (vtable->route_id_cmp (cur_ipx_route, next_route) == 0) { + if (!to_restore_routes) + to_restore_routes = g_array_new (FALSE, FALSE, vtable->vt->sizeof_route); + g_array_append_vals (to_restore_routes, next_route, 1); + g_assert (next_route->rx.ifindex != ifindex); + } } - g_array_remove_index (routes, i); - } else { - i++; + /* find the next @cur_ipx_route with matching ifindex. */ + cur_ipx_route = _get_next_ipx_route (ipx_routes->index, FALSE, &i_ipx_routes, ifindex); + } + if ( cur_ipx_route + && cur_known_route + && route_id_cmp_result == 0) { + if (!_route_equals_ignoring_ifindex (vtable, cur_ipx_route, cur_known_route)) { + /* The routes match. Update the entry in place. As this is an exact match of primary + * fields, this only updates possibly modified fields such as @gateway or @mss. + * Modifiying @cur_ipx_route this way does not invalidate @ipx_routes->index. */ + memcpy (cur_ipx_route, cur_known_route, vtable->vt->sizeof_route); + cur_ipx_route->rx.ifindex = ifindex; + cur_ipx_route->rx.metric = vtable->vt->metric_normalize (cur_ipx_route->rx.metric); + _LOGT (vtable->vt->addr_family, "%3d: STATE: update #%u - %s", ifindex, i_ipx_routes, vtable->vt->route_to_string (cur_ipx_route)); + } + } else if (cur_known_route) { + g_assert (!cur_ipx_route || route_id_cmp_result > 0); + /* @cur_known_route is new. We cannot immediately add @cur_known_route to @ipx_routes, because + * it would invalidate @ipx_routes->index. Instead remember to add it later. */ + if (!to_add_routes) + to_add_routes = g_ptr_array_new (); + g_ptr_array_add (to_add_routes, (gpointer) cur_known_route); } - existing = array_get_ip4_route (routes, 0, &route); - if ( existing - && !array_get_ip4_route (plat_routes, existing->ifindex, existing)) { - /* The route that should already exist is not there. - * Try to add it. */ - nm_platform_ip4_route_add (existing->ifindex, - existing->source, - existing->network, - existing->plen, - existing->gateway, - 0, - existing->metric, - existing->mss); - - /* It's now hopefully in platform. Take a note so that we - * don't attempt to add it again. */ - g_array_append_val (plat_routes, *existing); + if (cur_ipx_route && (!cur_known_route || route_id_cmp_result == 0)) + cur_ipx_route = _get_next_ipx_route (ipx_routes->index, FALSE, &i_ipx_routes, ifindex); + if (cur_known_route) + cur_known_route = _get_next_known_route (vtable, known_routes_idx, FALSE, &i_known_routes); + } + + /* Update @ipx_routes with the just learned changes. */ + if (to_delete_indexes || to_add_routes) { + if (to_delete_indexes) { + for (i = 0; i < to_delete_indexes->len; i++) { + guint idx = g_array_index (to_delete_indexes, guint, i); + + _LOGT (vtable->vt->addr_family, "%3d: STATE: delete #%u - %s", ifindex, idx, vtable->vt->route_to_string (ipx_routes->index->entries[idx])); + g_array_index (to_delete_indexes, guint, i) = _route_index_reverse_idx (vtable, ipx_routes->index, idx, ipx_routes->entries); + } + g_array_sort (to_delete_indexes, (GCompareFunc) _sort_indexes_cmp); + nm_utils_array_remove_at_indexes (ipx_routes->entries, &g_array_index (to_delete_indexes, guint, 0), to_delete_indexes->len); + g_array_unref (to_delete_indexes); } + if (to_add_routes) { + for (i = 0; i < to_add_routes->len; i++) { + NMPlatformIPXRoute *ipx_route; + + g_array_append_vals (ipx_routes->entries, g_ptr_array_index (to_add_routes, i), 1); - if (route.ifindex == ifindex) { - /* Clean up. */ - nm_platform_ip4_route_delete (route.ifindex, - route.network, - route.plen, - route.metric); + ipx_route = VTABLE_ROUTE_INDEX (vtable, ipx_routes->entries, ipx_routes->entries->len - 1); + ipx_route->rx.ifindex = ifindex; + ipx_route->rx.metric = vtable->vt->metric_normalize (ipx_route->rx.metric); + + _LOGT (vtable->vt->addr_family, "%3d: STATE: added #%u - %s", ifindex, ipx_routes->entries->len - 1, vtable->vt->route_to_string (ipx_route)); + } + g_ptr_array_unref (to_add_routes); } + g_free (ipx_routes->index); + ipx_routes->index = _route_index_create (vtable, ipx_routes->entries); + ASSERT_route_index_valid (vtable, ipx_routes->entries, ipx_routes->index, TRUE); } - g_array_unref (plat_routes); + /*************************************************************************** + * Delete routes in platform, that no longer exist in @ipx_routes + ***************************************************************************/ - if (!known_routes) - return TRUE; + /* iterate over @plat_routes and @ipx_routes */ + cur_plat_route = _get_next_plat_route (plat_routes_idx, TRUE, &i_plat_routes); + cur_ipx_route = _get_next_ipx_route (ipx_routes->index, TRUE, &i_ipx_routes, ifindex); + while (cur_plat_route) { + int route_id_cmp_result = 0; - /* Add missing routes */ - for (i_type = 0, success = TRUE; i_type < 2 && success; i_type++) { - for (i = 0; i < known_routes->len && success; i++) { - known_route = &g_array_index (known_routes, NMPlatformIP4Route, i); + g_assert (cur_plat_route->rx.ifindex == ifindex); - g_assert (known_route->ifindex); + _LOGT (vtable->vt->addr_family, "%3d: platform rt #%u - %s", ifindex, i_ipx_routes, vtable->vt->route_to_string (cur_plat_route)); - if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (known_route)) - continue; + /* skip over @cur_ipx_route that are ordered before @cur_plat_route */ + while ( cur_ipx_route + && ((route_id_cmp_result = vtable->route_id_cmp (cur_ipx_route, cur_plat_route)) < 0)) { + cur_ipx_route = _get_next_ipx_route (ipx_routes->index, FALSE, &i_ipx_routes, ifindex); + } + + /* if @cur_ipx_route is not equal to @plat_route, the route must be deleted. */ + if (!(cur_ipx_route && route_id_cmp_result == 0)) + vtable->vt->route_delete (ifindex, cur_plat_route); + + cur_plat_route = _get_next_plat_route (plat_routes_idx, FALSE, &i_plat_routes); + } + + /*************************************************************************** + * Restore shadowed routes. These routes are on an other @ifindex, but were + * shadowed before. Unshadow them now. + **************************************************************************/ + + if (to_restore_routes) { + for (i_type = 0; i_type < 2; i_type++) { + for (i = 0; i < to_restore_routes->len; i++) { + const NMPlatformIPXRoute *rest_route = VTABLE_ROUTE_INDEX (vtable, to_restore_routes, i); + + if ( (i_type == 0 && !VTABLE_IS_DEVICE_ROUTE (vtable, rest_route)) + || (i_type == 1 && VTABLE_IS_DEVICE_ROUTE (vtable, rest_route))) { + /* Make two runs over the list of @to_restore_routes. On the first, only add + * device routes, on the second the others (gateway routes). */ + continue; + } + vtable->vt->route_add (0, rest_route, 0); + } + } + g_array_unref (to_restore_routes); + } - if ( (i_type == 0 && known_route->gateway != 0) - || (i_type == 1 && known_route->gateway == 0)) { - /* Make two runs over the list of routes. On the first, only add + /*************************************************************************** + * Sync @ipx_routes for @ifindex to platform + **************************************************************************/ + + for (i_type = 0; i_type < 2; i_type++) { + /* iterate (twice) over @ipx_routes and @plat_routes */ + cur_plat_route = _get_next_plat_route (plat_routes_idx, TRUE, &i_plat_routes); + cur_ipx_route = _get_next_ipx_route (ipx_routes->index, TRUE, &i_ipx_routes, ifindex); + /* Iterate here over @ipx_routes instead of @known_routes. That is done because + * we need to know whether a route is shadowed by another route, and that + * requires to look at @ipx_routes. */ + for (; cur_ipx_route; cur_ipx_route = _get_next_ipx_route (ipx_routes->index, FALSE, &i_ipx_routes, ifindex)) { + int route_id_cmp_result = -1; + + if ( (i_type == 0 && !VTABLE_IS_DEVICE_ROUTE (vtable, cur_ipx_route)) + || (i_type == 1 && VTABLE_IS_DEVICE_ROUTE (vtable, cur_ipx_route))) { + /* Make two runs over the list of @ipx_routes. On the first, only add * device routes, on the second the others (gateway routes). */ continue; } - /* Ignore routes that already exist */ - if (!array_get_ip4_route (routes, 0, known_route)) { - success = nm_platform_ip4_route_add (known_route->ifindex, - known_route->source, - known_route->network, - known_route->plen, - known_route->gateway, - 0, - known_route->metric, - known_route->mss); - if (!success && known_route->source < NM_IP_CONFIG_SOURCE_USER) { - nm_log_dbg (LOGD_CORE, "ignore error adding IPv4 route to kernel: %s", - nm_platform_ip4_route_to_string (known_route)); - success = TRUE; - } + if ( i_ipx_routes > 0 + && vtable->route_id_cmp (cur_ipx_route, ipx_routes->index->entries[i_ipx_routes - 1]) == 0) { + /* @cur_ipx_route is shadewed by another route. */ + continue; } - if (!array_get_ip4_route (routes, known_route->ifindex, known_route)) - g_array_append_val (routes, *known_route); + /* skip over @plat_routes that are ordered before our @cur_ipx_route. */ + while ( cur_plat_route + && (route_id_cmp_result = vtable->route_id_cmp (cur_plat_route, cur_ipx_route)) < 0) + cur_plat_route = _get_next_plat_route (plat_routes_idx, FALSE, &i_plat_routes); + + /* only add the route if we don't have an identical route in @plat_routes, + * i.e. if @cur_plat_route is different from @cur_ipx_route. */ + if ( !cur_plat_route + || route_id_cmp_result != 0 + || !_route_equals_ignoring_ifindex (vtable, cur_plat_route, cur_ipx_route)) { + gboolean s; + + s = vtable->vt->route_add (ifindex, cur_ipx_route, 0); + if (!s && cur_ipx_route->rx.source < NM_IP_CONFIG_SOURCE_USER) { + _LOGD (vtable->vt->addr_family, "ignore error adding IPv%c route to kernel: %s", + vtable->vt->is_ip4 ? '4' : '6', + vtable->vt->route_to_string (cur_ipx_route)); + /* Remember that there was a failure, but for now continue trying to sync the + * remaining routes. */ + success = FALSE; + } + } } } + g_free (known_routes_idx); + g_free (plat_routes_idx); + g_array_unref (plat_routes); + return success; } /** + * nm_route_manager_ip4_route_sync: + * @ifindex: Interface index + * @known_routes: List of routes + * + * A convenience function to synchronize routes for a specific interface + * with the least possible disturbance. It simply removes routes that are + * not listed and adds routes that are. + * Default routes are ignored (both in @known_routes and those already + * configured on the device). + * + * Returns: %TRUE on success. + */ +gboolean +nm_route_manager_ip4_route_sync (NMRouteManager *self, int ifindex, const GArray *known_routes) +{ + return _vx_route_sync (&vtable_v4, self, ifindex, known_routes); +} + +/** * nm_route_manager_ip6_route_sync: * @ifindex: Interface index * @known_routes: List of routes @@ -231,108 +617,7 @@ nm_route_manager_ip4_route_sync (NMRouteManager *self, int ifindex, const GArray gboolean nm_route_manager_ip6_route_sync (NMRouteManager *self, int ifindex, const GArray *known_routes) { - NMRouteManagerPrivate *priv = NM_ROUTE_MANAGER_GET_PRIVATE (self); - GArray *plat_routes, *routes = priv->ip6_routes; - NMPlatformIP6Route route; - const NMPlatformIP6Route *known_route; - const NMPlatformIP6Route *existing; - gboolean success; - int i, i_type; - - /* Learn about routes that platform knows but we don't. */ - plat_routes = nm_platform_ip6_route_get_all (0, NM_PLATFORM_GET_ROUTE_MODE_NO_DEFAULT); - for (i = 0; i < plat_routes->len; i++) { - existing = &g_array_index (plat_routes, NMPlatformIP6Route, i); - if (!array_get_ip6_route (routes, existing->ifindex, existing)) - g_array_append_val (routes, *existing); - } - - for (i = 0; i < routes->len;) { - route = g_array_index (routes, NMPlatformIP6Route, i); - - if (route.ifindex == ifindex) { - /* Our route. Keep it? */ - if (array_get_ip6_route (known_routes, route.ifindex, &route)) { - i++; - continue; - } - - g_array_remove_index (routes, i); - } else { - i++; - } - - existing = array_get_ip6_route (routes, 0, &route); - if ( existing - && !array_get_ip6_route (plat_routes, existing->ifindex, existing)) { - /* The route that should already exist is not there. - * Try to add it. */ - nm_platform_ip6_route_add (existing->ifindex, - existing->source, - existing->network, - existing->plen, - existing->gateway, - existing->metric, - existing->mss); - - /* It's now hopefully in platform. Take a note so that we - * don't attempt to add it again. */ - g_array_append_val (plat_routes, *existing); - } - - if (route.ifindex == ifindex) { - /* Clean up. */ - nm_platform_ip6_route_delete (route.ifindex, - route.network, - route.plen, - route.metric); - } - } - - g_array_unref (plat_routes); - - if (!known_routes) - return TRUE; - - /* Add missing routes */ - for (i_type = 0, success = TRUE; i_type < 2 && success; i_type++) { - for (i = 0; i < known_routes->len && success; i++) { - known_route = &g_array_index (known_routes, NMPlatformIP6Route, i); - - g_assert (known_route->ifindex); - - if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (known_route)) - continue; - - if ( (i_type == 0 && !IN6_IS_ADDR_UNSPECIFIED (&known_route->gateway)) - || (i_type == 1 && IN6_IS_ADDR_UNSPECIFIED (&known_route->gateway))) { - /* Make two runs over the list of routes. On the first, only add - * device routes, on the second the others (gateway routes). */ - continue; - } - - /* Ignore routes that already exist */ - if (!array_get_ip6_route (routes, 0, known_route)) { - success = nm_platform_ip6_route_add (known_route->ifindex, - known_route->source, - known_route->network, - known_route->plen, - known_route->gateway, - known_route->metric, - known_route->mss); - if (!success && known_route->source < NM_IP_CONFIG_SOURCE_USER) { - nm_log_dbg (LOGD_CORE, "ignore error adding IPv6 route to kernel: %s", - nm_platform_ip6_route_to_string (known_route)); - success = TRUE; - } - } - - if (!array_get_ip6_route (routes, known_route->ifindex, known_route)) - g_array_append_val (routes, *known_route); - } - } - - return success; + return _vx_route_sync (&vtable_v6, self, ifindex, known_routes); } gboolean @@ -342,15 +627,29 @@ nm_route_manager_route_flush (NMRouteManager *self, int ifindex) && nm_route_manager_ip6_route_sync (self, ifindex, NULL); } -NM_DEFINE_SINGLETON_GETTER (NMRouteManager, nm_route_manager_get, NM_TYPE_ROUTE_MANAGER); +/*********************************************************************************************/ + +static const VTableIP vtable_v4 = { + .vt = &nm_platform_vtable_route_v4, + .route_id_cmp = (int (*) (const NMPlatformIPXRoute *, const NMPlatformIPXRoute *)) _v4_route_id_cmp, +}; + +static const VTableIP vtable_v6 = { + .vt = &nm_platform_vtable_route_v6, + .route_id_cmp = (int (*) (const NMPlatformIPXRoute *, const NMPlatformIPXRoute *)) _v6_route_id_cmp, +}; + +/*********************************************************************************************/ static void nm_route_manager_init (NMRouteManager *self) { NMRouteManagerPrivate *priv = NM_ROUTE_MANAGER_GET_PRIVATE (self); - priv->ip4_routes = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP4Route)); - priv->ip6_routes = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP6Route)); + priv->ip4_routes.entries = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP4Route)); + priv->ip6_routes.entries = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP6Route)); + priv->ip4_routes.index = _route_index_create (&vtable_v4, priv->ip4_routes.entries); + priv->ip6_routes.index = _route_index_create (&vtable_v6, priv->ip6_routes.entries); } static void @@ -358,8 +657,10 @@ finalize (GObject *object) { NMRouteManagerPrivate *priv = NM_ROUTE_MANAGER_GET_PRIVATE (object); - g_array_free (priv->ip4_routes, TRUE); - g_array_free (priv->ip6_routes, TRUE); + g_array_free (priv->ip4_routes.entries, TRUE); + g_array_free (priv->ip6_routes.entries, TRUE); + g_free (priv->ip4_routes.index); + g_free (priv->ip6_routes.index); G_OBJECT_CLASS (nm_route_manager_parent_class)->finalize (object); } diff --git a/src/platform/nm-fake-platform.c b/src/platform/nm-fake-platform.c index 8288fb9fc4..09e1d37cc7 100644 --- a/src/platform/nm-fake-platform.c +++ b/src/platform/nm-fake-platform.c @@ -1152,7 +1152,7 @@ ip4_route_add (NMPlatform *platform, int ifindex, NMIPConfigSource source, route.source = NM_IP_CONFIG_SOURCE_KERNEL; route.ifindex = ifindex; route.source = source; - route.network = network; + route.network = nm_utils_ip4_address_clear_host_address (network, plen); route.plen = plen; route.gateway = gateway; route.metric = metric; @@ -1169,8 +1169,8 @@ ip4_route_add (NMPlatform *platform, int ifindex, NMIPConfigSource source, break; } if (i == priv->ip4_routes->len) { - g_warning ("Fake platform: error adding %s: Network Unreachable", - nm_platform_ip4_route_to_string (&route)); + nm_log_warn (LOGD_PLATFORM, "Fake platform: error adding %s: Network Unreachable", + nm_platform_ip4_route_to_string (&route)); return FALSE; } } @@ -1217,7 +1217,7 @@ ip6_route_add (NMPlatform *platform, int ifindex, NMIPConfigSource source, route.source = NM_IP_CONFIG_SOURCE_KERNEL; route.ifindex = ifindex; route.source = source; - route.network = network; + nm_utils_ip6_address_clear_host_address (&route.network, &network, plen); route.plen = plen; route.gateway = gateway; route.metric = metric; @@ -1236,8 +1236,8 @@ ip6_route_add (NMPlatform *platform, int ifindex, NMIPConfigSource source, break; } if (i == priv->ip6_routes->len) { - g_warning ("Fake platform: error adding %s: Network Unreachable", - nm_platform_ip6_route_to_string (&route)); + nm_log_warn (LOGD_PLATFORM, "Fake platform: error adding %s: Network Unreachable", + nm_platform_ip6_route_to_string (&route)); return FALSE; } } diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index 39c38f8343..969ff3cd31 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -1737,10 +1737,6 @@ add_object (NMPlatform *platform, struct nl_object *obj) auto_nl_object struct nl_object *object = obj; NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); int nle; - struct nl_dump_params dp = { - .dp_type = NL_DUMP_DETAILS, - .dp_fd = stderr, - }; g_return_val_if_fail (object, FALSE); @@ -1756,7 +1752,18 @@ add_object (NMPlatform *platform, struct nl_object *obj) break; default: error ("Netlink error adding %s: %s", to_string_object (platform, object), nl_geterror (nle)); - nl_object_dump (object, &dp); + if (nm_logging_enabled (LOGL_DEBUG, LOGD_PLATFORM)) { + char buf[256]; + struct nl_dump_params dp = { + .dp_type = NL_DUMP_DETAILS, + .dp_buf = buf, + .dp_buflen = sizeof (buf), + }; + + nl_object_dump (object, &dp); + buf[sizeof (buf) - 1] = '\0'; + debug ("netlink object:\n%s", buf); + } return FALSE; } diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index e4e5dbaa6a..6397061b00 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -2637,6 +2637,97 @@ log_ip6_route (NMPlatform *p, int ifindex, NMPlatformIP6Route *route, NMPlatform /******************************************************************/ +static gboolean +_vtr_v4_route_add (int ifindex, const NMPlatformIPXRoute *route, guint32 v4_pref_src) +{ + return nm_platform_ip4_route_add (ifindex > 0 ? ifindex : route->rx.ifindex, + route->rx.source, + route->r4.network, + route->rx.plen, + route->r4.gateway, + v4_pref_src, + route->rx.metric, + route->rx.mss); +} + +static gboolean +_vtr_v6_route_add (int ifindex, const NMPlatformIPXRoute *route, guint32 v4_pref_src) +{ + return nm_platform_ip6_route_add (ifindex > 0 ? ifindex : route->rx.ifindex, + route->rx.source, + route->r6.network, + route->rx.plen, + route->r6.gateway, + route->rx.metric, + route->rx.mss); +} + +static gboolean +_vtr_v4_route_delete (int ifindex, const NMPlatformIPXRoute *route) +{ + return nm_platform_ip4_route_delete (ifindex > 0 ? ifindex : route->rx.ifindex, + route->r4.network, + route->rx.plen, + route->rx.metric); +} + +static gboolean +_vtr_v6_route_delete (int ifindex, const NMPlatformIPXRoute *route) +{ + return nm_platform_ip6_route_delete (ifindex > 0 ? ifindex : route->rx.ifindex, + route->r6.network, + route->rx.plen, + route->rx.metric); +} + +static guint32 +_vtr_v4_metric_normalize (guint32 metric) +{ + return metric; +} + +static gboolean +_vtr_v4_route_delete_default (int ifindex, guint32 metric) +{ + return nm_platform_ip4_route_delete (ifindex, 0, 0, metric); +} + +static gboolean +_vtr_v6_route_delete_default (int ifindex, guint32 metric) +{ + return nm_platform_ip6_route_delete (ifindex, in6addr_any, 0, metric); +} + +/******************************************************************/ + +const NMPlatformVTableRoute nm_platform_vtable_route_v4 = { + .is_ip4 = TRUE, + .addr_family = AF_INET, + .sizeof_route = sizeof (NMPlatformIP4Route), + .route_cmp = (int (*) (const NMPlatformIPXRoute *a, const NMPlatformIPXRoute *b)) nm_platform_ip4_route_cmp, + .route_to_string = (const char *(*) (const NMPlatformIPXRoute *route)) nm_platform_ip4_route_to_string, + .route_get_all = nm_platform_ip4_route_get_all, + .route_add = _vtr_v4_route_add, + .route_delete = _vtr_v4_route_delete, + .route_delete_default = _vtr_v4_route_delete_default, + .metric_normalize = _vtr_v4_metric_normalize, +}; + +const NMPlatformVTableRoute nm_platform_vtable_route_v6 = { + .is_ip4 = FALSE, + .addr_family = AF_INET6, + .sizeof_route = sizeof (NMPlatformIP6Route), + .route_cmp = (int (*) (const NMPlatformIPXRoute *a, const NMPlatformIPXRoute *b)) nm_platform_ip6_route_cmp, + .route_to_string = (const char *(*) (const NMPlatformIPXRoute *route)) nm_platform_ip6_route_to_string, + .route_get_all = nm_platform_ip6_route_get_all, + .route_add = _vtr_v6_route_add, + .route_delete = _vtr_v6_route_delete, + .route_delete_default = _vtr_v6_route_delete_default, + .metric_normalize = nm_utils_ip6_route_metric_normalize, +}; + +/******************************************************************/ + static void nm_platform_init (NMPlatform *object) { diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index b90343d79f..920d9cec8c 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -247,6 +247,23 @@ typedef union { typedef struct { + gboolean is_ip4; + int addr_family; + gsize sizeof_route; + int (*route_cmp) (const NMPlatformIPXRoute *a, const NMPlatformIPXRoute *b); + const char *(*route_to_string) (const NMPlatformIPXRoute *route); + GArray *(*route_get_all) (int ifindex, NMPlatformGetRouteMode mode); + gboolean (*route_add) (int ifindex, const NMPlatformIPXRoute *route, guint32 v4_pref_src); + gboolean (*route_delete) (int ifindex, const NMPlatformIPXRoute *route); + gboolean (*route_delete_default) (int ifindex, guint32 metric); + guint32 (*metric_normalize) (guint32 metric); +} NMPlatformVTableRoute; + +extern const NMPlatformVTableRoute nm_platform_vtable_route_v4; +extern const NMPlatformVTableRoute nm_platform_vtable_route_v6; + + +typedef struct { int peer; } NMPlatformVethProperties; diff --git a/src/platform/tests/test-address.c b/src/platform/tests/test-address.c index 902b33ee59..21cd427fed 100644 --- a/src/platform/tests/test-address.c +++ b/src/platform/tests/test-address.c @@ -247,6 +247,12 @@ test_ip6_address_external (void) } void +init_tests (int *argc, char ***argv) +{ + nmtst_init_with_logging (argc, argv, NULL, "ALL"); +} + +void setup_tests (void) { SignalData *link_added = add_signal_ifname (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_ADDED, link_callback, DEVICE_NAME); diff --git a/src/platform/tests/test-cleanup.c b/src/platform/tests/test-cleanup.c index 5d789d1bdf..796ca874e5 100644 --- a/src/platform/tests/test-cleanup.c +++ b/src/platform/tests/test-cleanup.c @@ -87,6 +87,12 @@ test_cleanup_internal (void) } void +init_tests (int *argc, char ***argv) +{ + nmtst_init_with_logging (argc, argv, NULL, "ALL"); +} + +void setup_tests (void) { nm_platform_link_delete (nm_platform_link_get_ifindex (DEVICE_NAME)); diff --git a/src/platform/tests/test-common.c b/src/platform/tests/test-common.c index d8e8775f82..1980282dc8 100644 --- a/src/platform/tests/test-common.c +++ b/src/platform/tests/test-common.c @@ -255,7 +255,7 @@ main (int argc, char **argv) int result; const char *program = *argv; - nmtst_init_with_logging (&argc, &argv, NULL, "ALL"); + init_tests (&argc, &argv); NM_PRAGMA_WARNING_DISABLE("-Wtautological-compare") if (SETUP == nm_linux_platform_setup && getuid() != 0) { @@ -263,10 +263,10 @@ main (int argc, char **argv) nmtst_reexec_sudo (); #ifdef REQUIRE_ROOT_TESTS - g_message ("Fail test: requires root privileges (%s)", program); + g_print ("Fail test: requires root privileges (%s)\n", program); return EXIT_FAILURE; #else - g_message ("Skipping test: requires root privileges (%s)", program); + g_print ("Skipping test: requires root privileges (%s)\n", program); return 77; #endif } diff --git a/src/platform/tests/test-common.h b/src/platform/tests/test-common.h index 17c4029d8c..367833d111 100644 --- a/src/platform/tests/test-common.h +++ b/src/platform/tests/test-common.h @@ -9,6 +9,8 @@ #include "nm-fake-platform.h" #include "nm-linux-platform.h" +#include "nm-test-utils.h" + #define DEVICE_NAME "nm-test-device" #define debug(...) nm_log_dbg (LOGD_PLATFORM, __VA_ARGS__) @@ -43,5 +45,6 @@ void link_callback (NMPlatform *platform, int ifindex, NMPlatformLink *received, void run_command (const char *format, ...); +void init_tests (int *argc, char ***argv); void setup_tests (void); diff --git a/src/platform/tests/test-link.c b/src/platform/tests/test-link.c index 0643b052cd..323ebc921c 100644 --- a/src/platform/tests/test-link.c +++ b/src/platform/tests/test-link.c @@ -540,6 +540,12 @@ test_external (void) } void +init_tests (int *argc, char ***argv) +{ + nmtst_init_with_logging (argc, argv, NULL, "ALL"); +} + +void setup_tests (void) { nm_platform_link_delete (nm_platform_link_get_ifindex (DEVICE_NAME)); diff --git a/src/platform/tests/test-route.c b/src/platform/tests/test-route.c index b6e99ab9fb..af50db6754 100644 --- a/src/platform/tests/test-route.c +++ b/src/platform/tests/test-route.c @@ -197,7 +197,7 @@ test_ip4_route (void) rts[2].mss = mss; g_assert_cmpint (routes->len, ==, 3); g_assert (!memcmp (routes->data, rts, sizeof (rts))); - nmtst_platform_ip4_routes_equal ((NMPlatformIP4Route *) routes->data, rts, routes->len); + nmtst_platform_ip4_routes_equal ((NMPlatformIP4Route *) routes->data, rts, routes->len, TRUE); g_array_unref (routes); /* Remove route */ @@ -293,7 +293,7 @@ test_ip6_route (void) rts[2].mss = mss; g_assert_cmpint (routes->len, ==, 3); g_assert (!memcmp (routes->data, rts, sizeof (rts))); - nmtst_platform_ip6_routes_equal ((NMPlatformIP6Route *) routes->data, rts, routes->len); + nmtst_platform_ip6_routes_equal ((NMPlatformIP6Route *) routes->data, rts, routes->len, TRUE); g_array_unref (routes); /* Remove route */ @@ -312,6 +312,12 @@ test_ip6_route (void) } void +init_tests (int *argc, char ***argv) +{ + nmtst_init_with_logging (argc, argv, NULL, "ALL"); +} + +void setup_tests (void) { SignalData *link_added = add_signal_ifname (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_ADDED, link_callback, DEVICE_NAME); diff --git a/src/settings/plugins/ibft/tests/test-ibft.c b/src/settings/plugins/ibft/tests/test-ibft.c index d5da9ec022..980a9ceed1 100644 --- a/src/settings/plugins/ibft/tests/test-ibft.c +++ b/src/settings/plugins/ibft/tests/test-ibft.c @@ -276,7 +276,7 @@ NMTST_DEFINE (); int main (int argc, char **argv) { - nmtst_init_assert_logging (&argc, &argv); + nmtst_init_assert_logging (&argc, &argv, "INFO", "DEFAULT"); g_test_add_func (TPATH "ibft/dhcp", test_read_ibft_dhcp); g_test_add_func (TPATH "ibft/static", test_read_ibft_static); diff --git a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh-utils.c b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh-utils.c index 8494c63825..13f1126e48 100644 --- a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh-utils.c +++ b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh-utils.c @@ -121,7 +121,7 @@ int main (int argc, char **argv) { char *base; - nmtst_init_assert_logging (&argc, &argv); + nmtst_init_assert_logging (&argc, &argv, "INFO", "DEFAULT"); /* The tests */ test_get_ifcfg_name ("get-ifcfg-name-bad", "/foo/bar/adfasdfadf", FALSE, NULL); diff --git a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c index e5e4f3d477..24b2f398e3 100644 --- a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c +++ b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c @@ -12443,7 +12443,7 @@ NMTST_DEFINE (); int main (int argc, char **argv) { - nmtst_init_assert_logging (&argc, &argv); + nmtst_init_assert_logging (&argc, &argv, "INFO", "DEFAULT"); g_test_add_func (TPATH "svUnescape", test_svUnescape); g_test_add_func (TPATH "vlan-trailing-spaces", test_read_vlan_trailing_spaces); diff --git a/src/settings/plugins/ifnet/tests/test_all.c b/src/settings/plugins/ifnet/tests/test_all.c index 9c3991287d..76ed0c7d62 100644 --- a/src/settings/plugins/ifnet/tests/test_all.c +++ b/src/settings/plugins/ifnet/tests/test_all.c @@ -448,8 +448,7 @@ main (int argc, char **argv) nm_linux_platform_setup (); - nmtst_init_assert_logging (&argc, &argv); - nm_logging_setup ("WARN", "DEFAULT", NULL, NULL); + nmtst_init_assert_logging (&argc, &argv, "WARN", "DEFAULT"); f = g_build_filename (argv[1], "net", NULL); ifnet_init (f); diff --git a/src/settings/plugins/keyfile/tests/test-keyfile.c b/src/settings/plugins/keyfile/tests/test-keyfile.c index a86bfed6c2..ab9081621e 100644 --- a/src/settings/plugins/keyfile/tests/test-keyfile.c +++ b/src/settings/plugins/keyfile/tests/test-keyfile.c @@ -3632,7 +3632,7 @@ NMTST_DEFINE (); int main (int argc, char **argv) { - nmtst_init_assert_logging (&argc, &argv); + nmtst_init_assert_logging (&argc, &argv, "INFO", "DEFAULT"); /* The tests */ g_test_add_func ("/keyfile/test_read_valid_wired_connection ", test_read_valid_wired_connection); diff --git a/src/tests/config/test-config.c b/src/tests/config/test-config.c index b03cfc112a..f4d72d333f 100644 --- a/src/tests/config/test-config.c +++ b/src/tests/config/test-config.c @@ -292,7 +292,7 @@ NMTST_DEFINE (); int main (int argc, char **argv) { - nmtst_init_assert_logging (&argc, &argv); + nmtst_init_assert_logging (&argc, &argv, "INFO", "DEFAULT"); /* Initialize the DBus manager singleton explicitly, because it is accessed by * the class initializer of NMDevice (used by the NMTestDevice stub). diff --git a/src/tests/test-general-with-expect.c b/src/tests/test-general-with-expect.c index f5010dae4a..274cbc854e 100644 --- a/src/tests/test-general-with-expect.c +++ b/src/tests/test-general-with-expect.c @@ -330,6 +330,104 @@ test_nm_utils_kill_child (void) g_test_assert_expected_messages (); } +/*******************************************/ + +static void +_remove_at_indexes_init_random_idx (GArray *idx, guint array_len, guint idx_len) +{ + GRand *rand = nmtst_get_rand (); + gs_free char *mask = NULL; + guint i, max_test_idx; + + g_assert (idx); + g_assert (array_len > 0); + g_assert (idx_len >= 1 && idx_len <= array_len); + + mask = g_new0 (char, array_len); + + max_test_idx = array_len - 1; + for (i = 0; i < idx_len; i++) { + guint itest; + + /* find a index itest that is not yet taken */ + if (max_test_idx == 0) + itest = 0; + else + itest = g_rand_int_range (rand, 0, max_test_idx); + while (itest < array_len && mask[itest]) + itest++; + g_assert (itest <= max_test_idx); + g_assert (!mask[itest]); + + mask[itest] = TRUE; + if (itest == max_test_idx) { + g_assert (max_test_idx > 0 || i == idx_len - 1); + + if (max_test_idx == 0) + g_assert_cmpint (i, ==, idx_len - 1); + else { + max_test_idx--; + while (max_test_idx > 0 && mask[max_test_idx]) + max_test_idx--; + if (mask[max_test_idx]) + g_assert_cmpint (i, ==, idx_len - 1); + } + } + } + + g_array_set_size (idx, 0); + for (i = 0; i < array_len; i++) { + if (mask[i]) + g_array_append_val (idx, i); + } + g_assert_cmpint (idx->len, ==, idx_len); +} + +static void +test_nm_utils_array_remove_at_indexes () +{ + gs_unref_array GArray *idx = NULL, *array = NULL; + gs_unref_hashtable GHashTable *unique = NULL; + guint i_len, i_idx_len, i_rnd, i; + + idx = g_array_new (FALSE, FALSE, sizeof (guint)); + array = g_array_new (FALSE, FALSE, sizeof (gssize)); + unique = g_hash_table_new (NULL, NULL); + for (i_len = 1; i_len < 20; i_len++) { + for (i_idx_len = 1; i_idx_len <= i_len; i_idx_len++) { + for (i_rnd = 0; i_rnd < 20; i_rnd++) { + + _remove_at_indexes_init_random_idx (idx, i_len, i_idx_len); + g_array_set_size (array, i_len); + for (i = 0; i < i_len; i++) + g_array_index (array, gssize, i) = i; + + nm_utils_array_remove_at_indexes (array, &g_array_index (idx, guint, 0), i_idx_len); + + g_hash_table_remove_all (unique); + /* ensure that all the indexes are still unique */ + for (i = 0; i < array->len; i++) + g_hash_table_add (unique, GUINT_TO_POINTER (g_array_index (array, gssize, i))); + g_assert_cmpint (g_hash_table_size (unique), ==, array->len); + + for (i = 0; i < idx->len; i++) + g_hash_table_add (unique, GUINT_TO_POINTER (g_array_index (idx, guint, i))); + g_assert_cmpint (g_hash_table_size (unique), ==, i_len); + + /* ensure proper sort order in array */ + for (i = 0; i < array->len; i++) { + gssize i1 = g_array_index (array, gssize, i); + + g_assert (i1 >= 0 && i1 < i_len); + if (i > 0) { + gsize i0 = g_array_index (array, gssize, i - 1); + g_assert_cmpint (i0, <, i1); + } + } + } + } + } +} /*******************************************/ @@ -338,11 +436,10 @@ NMTST_DEFINE (); int main (int argc, char **argv) { - nmtst_init_assert_logging (&argc, &argv); - - nm_logging_setup ("DEBUG", "DEFAULT", NULL, NULL); + nmtst_init_assert_logging (&argc, &argv, "DEBUG", "DEFAULT"); g_test_add_func ("/general/nm_utils_kill_child", test_nm_utils_kill_child); + g_test_add_func ("/general/nm_utils_array_remove_at_indexes", test_nm_utils_array_remove_at_indexes); return g_test_run (); } diff --git a/src/tests/test-route-manager.c b/src/tests/test-route-manager.c index b93dbb18f5..8332a8624c 100644 --- a/src/tests/test-route-manager.c +++ b/src/tests/test-route-manager.c @@ -36,7 +36,7 @@ typedef struct { } test_fixture; static void -setup_dev0_ip4 (int ifindex) +setup_dev0_ip4 (int ifindex, guint mss_of_first_route, guint32 metric_of_second_route) { GArray *routes = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP4Route)); NMPlatformIP4Route route; @@ -49,13 +49,15 @@ setup_dev0_ip4 (int ifindex) route.plen = 24; route.gateway = INADDR_ANY; route.metric = 20; + route.mss = mss_of_first_route; g_array_append_val (routes, route); route.source = NM_IP_CONFIG_SOURCE_USER; inet_pton (AF_INET, "7.0.0.0", &route.network); route.plen = 8; inet_pton (AF_INET, "6.6.6.1", &route.gateway); - route.metric = 21; + route.metric = metric_of_second_route; + route.mss = 0; g_array_append_val (routes, route); nm_route_manager_ip4_route_sync (nm_route_manager_get (), ifindex, routes); @@ -162,7 +164,7 @@ test_ip4 (test_fixture *fixture, gconstpointer user_data) .ifindex = fixture->ifindex0, .gateway = INADDR_ANY, .metric = 20, - .mss = 0, + .mss = 1000, }, { .source = NM_IP_CONFIG_SOURCE_USER, @@ -170,7 +172,7 @@ test_ip4 (test_fixture *fixture, gconstpointer user_data) .plen = 8, .ifindex = fixture->ifindex0, .gateway = nmtst_inet4_from_string ("6.6.6.1"), - .metric = 21, + .metric = 21021, .mss = 0, }, { @@ -233,19 +235,10 @@ test_ip4 (test_fixture *fixture, gconstpointer user_data) .metric = 20, .mss = 0, }, - { - .source = NM_IP_CONFIG_SOURCE_USER, - .network = nmtst_inet4_from_string ("8.0.0.0"), - .plen = 8, - .ifindex = fixture->ifindex1, - .gateway = nmtst_inet4_from_string ("6.6.6.2"), - .metric = 22, - .mss = 0, - }, }; - setup_dev0_ip4 (fixture->ifindex0); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING, "*error adding*"); + setup_dev0_ip4 (fixture->ifindex0, 1000, 21021); + g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING, "*error adding 8.0.0.0/8 via 6.6.6.2 dev nm-test-device1 *"); setup_dev1_ip4 (fixture->ifindex1); g_test_assert_expected_messages (); @@ -254,28 +247,29 @@ test_ip4 (test_fixture *fixture, gconstpointer user_data) * 8.0.0.0/8 could not be added. */ routes = ip4_routes (fixture); g_assert_cmpint (routes->len, ==, G_N_ELEMENTS (state1)); - nmtst_platform_ip4_routes_equal ((NMPlatformIP4Route *) routes->data, state1, routes->len); + nmtst_platform_ip4_routes_equal ((NMPlatformIP4Route *) routes->data, state1, routes->len, TRUE); g_array_free (routes, TRUE); + g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING, "*error adding 8.0.0.0/8 via 6.6.6.2 dev nm-test-device1 *"); setup_dev1_ip4 (fixture->ifindex1); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING, "*error adding*"); - setup_dev0_ip4 (fixture->ifindex0); g_test_assert_expected_messages (); + setup_dev0_ip4 (fixture->ifindex0, 0, 21); + /* Ensure nothing changed. */ routes = ip4_routes (fixture); g_assert_cmpint (routes->len, ==, G_N_ELEMENTS (state1)); - nmtst_platform_ip4_routes_equal ((NMPlatformIP4Route *) routes->data, state1, routes->len); + state1[0].mss = 0; + state1[1].metric = 21; + nmtst_platform_ip4_routes_equal ((NMPlatformIP4Route *) routes->data, state1, routes->len, TRUE); g_array_free (routes, TRUE); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING, "*error adding*"); update_dev0_ip4 (fixture->ifindex0); - g_test_assert_expected_messages (); /* 7.0.0.0/8 on dev0 was updated for gateway removal*/ routes = ip4_routes (fixture); g_assert_cmpint (routes->len, ==, G_N_ELEMENTS (state2)); - nmtst_platform_ip4_routes_equal ((NMPlatformIP4Route *) routes->data, state2, routes->len); + nmtst_platform_ip4_routes_equal ((NMPlatformIP4Route *) routes->data, state2, routes->len, TRUE); g_array_free (routes, TRUE); nm_route_manager_route_flush (nm_route_manager_get (), fixture->ifindex0); @@ -286,7 +280,7 @@ test_ip4 (test_fixture *fixture, gconstpointer user_data) * No dev0 routes left. */ routes = ip4_routes (fixture); g_assert_cmpint (routes->len, ==, G_N_ELEMENTS (state3)); - nmtst_platform_ip4_routes_equal ((NMPlatformIP4Route *) routes->data, state3, routes->len); + nmtst_platform_ip4_routes_equal ((NMPlatformIP4Route *) routes->data, state3, routes->len, TRUE); g_array_free (routes, TRUE); nm_route_manager_route_flush (nm_route_manager_get (), fixture->ifindex1); @@ -570,19 +564,10 @@ test_ip6 (test_fixture *fixture, gconstpointer user_data) .metric = 1024, .mss = 0, }, - { - .source = NM_IP_CONFIG_SOURCE_USER, - .network = *nmtst_inet6_from_string ("2001:db8:d34d::"), - .plen = 64, - .ifindex = fixture->ifindex1, - .gateway = *nmtst_inet6_from_string ("2001:db8:8086::2"), - .metric = 20, - .mss = 0, - }, }; setup_dev0_ip6 (fixture->ifindex0); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING, "*error adding*"); + g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING, "*error adding 2001:db8:d34d::/64 via 2001:db8:8086::2 dev nm-test-device1 *"); setup_dev1_ip6 (fixture->ifindex1); g_test_assert_expected_messages (); @@ -592,28 +577,27 @@ test_ip6 (test_fixture *fixture, gconstpointer user_data) * 2001:db8:abad:c0de::/64 routes did not clash */ routes = ip6_routes (fixture); g_assert_cmpint (routes->len, ==, G_N_ELEMENTS (state1)); - nmtst_platform_ip6_routes_equal ((NMPlatformIP6Route *) routes->data, state1, routes->len); + nmtst_platform_ip6_routes_equal ((NMPlatformIP6Route *) routes->data, state1, routes->len, TRUE); g_array_free (routes, TRUE); + + g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING, "*error adding 2001:db8:d34d::/64 via 2001:db8:8086::2 dev nm-test-device1 *"); setup_dev1_ip6 (fixture->ifindex1); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING, "*error adding*"); - setup_dev0_ip6 (fixture->ifindex0); g_test_assert_expected_messages (); + setup_dev0_ip6 (fixture->ifindex0); /* Ensure nothing changed. */ routes = ip6_routes (fixture); g_assert_cmpint (routes->len, ==, G_N_ELEMENTS (state1)); - nmtst_platform_ip6_routes_equal ((NMPlatformIP6Route *) routes->data, state1, routes->len); + nmtst_platform_ip6_routes_equal ((NMPlatformIP6Route *) routes->data, state1, routes->len, TRUE); g_array_free (routes, TRUE); - g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING, "*error adding*"); update_dev0_ip6 (fixture->ifindex0); - g_test_assert_expected_messages (); /* 2001:db8:abad:c0de::/64 on dev0 was updated for gateway removal*/ routes = ip6_routes (fixture); g_assert_cmpint (routes->len, ==, G_N_ELEMENTS (state2)); - nmtst_platform_ip6_routes_equal ((NMPlatformIP6Route *) routes->data, state2, routes->len); + nmtst_platform_ip6_routes_equal ((NMPlatformIP6Route *) routes->data, state2, routes->len, TRUE); g_array_free (routes, TRUE); nm_route_manager_route_flush (nm_route_manager_get (), fixture->ifindex0); @@ -625,7 +609,7 @@ test_ip6 (test_fixture *fixture, gconstpointer user_data) * No dev0 routes left. */ routes = ip6_routes (fixture); g_assert_cmpint (routes->len, ==, G_N_ELEMENTS (state3)); - nmtst_platform_ip6_routes_equal ((NMPlatformIP6Route *) routes->data, state3, routes->len); + nmtst_platform_ip6_routes_equal ((NMPlatformIP6Route *) routes->data, state3, routes->len, TRUE); g_array_free (routes, TRUE); nm_route_manager_route_flush (nm_route_manager_get (), fixture->ifindex1); @@ -674,6 +658,12 @@ fixture_teardown (test_fixture *fixture, gconstpointer user_data) } void +init_tests (int *argc, char ***argv) +{ + nmtst_init_assert_logging (argc, argv, "WARN", "ALL"); +} + +void setup_tests (void) { g_test_add ("/route-manager/ip4", test_fixture, NULL, fixture_setup, test_ip4, fixture_teardown); |