diff options
-rw-r--r-- | Makefile.am | 27 | ||||
-rw-r--r-- | shared/nm-utils/nm-dedup-multi.c | 8 | ||||
-rw-r--r-- | shared/nm-utils/nm-dedup-multi.h | 4 | ||||
-rw-r--r-- | src/devices/nm-device.c | 216 | ||||
-rw-r--r-- | src/devices/wwan/nm-modem.c | 10 | ||||
-rw-r--r-- | src/nm-iface-helper.c | 15 | ||||
-rw-r--r-- | src/nm-ip4-config.c | 181 | ||||
-rw-r--r-- | src/nm-ip4-config.h | 9 | ||||
-rw-r--r-- | src/nm-ip6-config.c | 44 | ||||
-rw-r--r-- | src/nm-ip6-config.h | 5 | ||||
-rw-r--r-- | src/nm-netns.c | 10 | ||||
-rw-r--r-- | src/nm-netns.h | 1 | ||||
-rw-r--r-- | src/nm-route-manager.c | 1380 | ||||
-rw-r--r-- | src/nm-route-manager.h | 49 | ||||
-rw-r--r-- | src/nm-types.h | 1 | ||||
-rw-r--r-- | src/platform/nm-platform.c | 466 | ||||
-rw-r--r-- | src/platform/nm-platform.h | 21 | ||||
-rw-r--r-- | src/tests/test-route-manager.c | 974 | ||||
-rw-r--r-- | src/vpn/nm-vpn-connection.c | 19 |
19 files changed, 770 insertions, 2670 deletions
diff --git a/Makefile.am b/Makefile.am index 92ad10c36c..e8a6a8aeea 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1323,9 +1323,6 @@ src_libNetworkManagerBase_la_SOURCES = \ src/nm-ip6-config.c \ src/nm-ip6-config.h \ \ - src/nm-route-manager.c \ - src/nm-route-manager.h \ - \ src/dhcp/nm-dhcp-client.c \ src/dhcp/nm-dhcp-client.h \ src/dhcp/nm-dhcp-client-logging.h \ @@ -2961,8 +2958,6 @@ check_programs += \ src/tests/test-general-with-expect \ src/tests/test-ip4-config \ src/tests/test-ip6-config \ - src/tests/test-route-manager-linux \ - src/tests/test-route-manager-fake \ src/tests/test-dcb \ src/tests/test-systemd \ src/tests/test-resolvconf-capture \ @@ -3010,28 +3005,6 @@ $(src_tests_test_general_with_expect_OBJECTS): $(libnm_core_lib_h_pub_mkenums) $(src_tests_test_wired_defname_OBJECTS): $(libnm_core_lib_h_pub_mkenums) $(src_tests_test_utils_OBJECTS): $(libnm_core_lib_h_pub_mkenums) -src_tests_test_route_manager_ldflags = \ - $(CODE_COVERAGE_LDFLAGS) - -src_tests_test_route_manager_ldadd = \ - src/libNetworkManagerTest.la \ - $(GLIB_LIBS) \ - $(LIBUDEV_LIBS) \ - $(LIBNL_LIBS) - -src_tests_test_route_manager_fake_SOURCES = src/tests/test-route-manager.c -src_tests_test_route_manager_fake_CPPFLAGS = $(src_tests_cppflags_fake) -src_tests_test_route_manager_fake_LDFLAGS = $(src_tests_test_route_manager_ldflags) -src_tests_test_route_manager_fake_LDADD = $(src_tests_test_route_manager_ldadd) - -src_tests_test_route_manager_linux_SOURCES = src/tests/test-route-manager.c -src_tests_test_route_manager_linux_CPPFLAGS = $(src_tests_cppflags_linux) -src_tests_test_route_manager_linux_LDFLAGS = $(src_tests_test_route_manager_ldflags) -src_tests_test_route_manager_linux_LDADD = $(src_tests_test_route_manager_ldadd) - -$(src_tests_test_route_manager_fake_OBJECTS): $(libnm_core_lib_h_pub_mkenums) -$(src_tests_test_route_manager_linux_OBJECTS): $(libnm_core_lib_h_pub_mkenums) - src_tests_test_systemd_CPPFLAGS = $(src_libsystemd_nm_la_cppflags) src_tests_test_systemd_LDADD = \ src/libsystemd-nm.la \ diff --git a/shared/nm-utils/nm-dedup-multi.c b/shared/nm-utils/nm-dedup-multi.c index 383e40fd72..51a866abca 100644 --- a/shared/nm-utils/nm-dedup-multi.c +++ b/shared/nm-utils/nm-dedup-multi.c @@ -77,7 +77,7 @@ nm_dedup_multi_idx_type_init (NMDedupMultiIdxType *idx_type, /*****************************************************************************/ static NMDedupMultiEntry * -_entry_lookup_obj (NMDedupMultiIndex *self, +_entry_lookup_obj (const NMDedupMultiIndex *self, const NMDedupMultiIdxType *idx_type, const NMDedupMultiObj *obj) { @@ -92,7 +92,7 @@ _entry_lookup_obj (NMDedupMultiIndex *self, } static NMDedupMultiHeadEntry * -_entry_lookup_head (NMDedupMultiIndex *self, +_entry_lookup_head (const NMDedupMultiIndex *self, const NMDedupMultiIdxType *idx_type, const NMDedupMultiObj *obj) { @@ -682,7 +682,7 @@ nm_dedup_multi_index_remove_idx (NMDedupMultiIndex *self, * Returns: the cache entry or %NULL if the entry wasn't found. */ const NMDedupMultiEntry * -nm_dedup_multi_index_lookup_obj (NMDedupMultiIndex *self, +nm_dedup_multi_index_lookup_obj (const NMDedupMultiIndex *self, const NMDedupMultiIdxType *idx_type, /*const NMDedupMultiObj * */ gconstpointer obj) { @@ -708,7 +708,7 @@ nm_dedup_multi_index_lookup_obj (NMDedupMultiIndex *self, * Returns: the cache entry or %NULL if the entry wasn't found. */ const NMDedupMultiHeadEntry * -nm_dedup_multi_index_lookup_head (NMDedupMultiIndex *self, +nm_dedup_multi_index_lookup_head (const NMDedupMultiIndex *self, const NMDedupMultiIdxType *idx_type, /*const NMDedupMultiObj * */ gconstpointer obj) { diff --git a/shared/nm-utils/nm-dedup-multi.h b/shared/nm-utils/nm-dedup-multi.h index c99d6d11e1..ff505b696f 100644 --- a/shared/nm-utils/nm-dedup-multi.h +++ b/shared/nm-utils/nm-dedup-multi.h @@ -278,11 +278,11 @@ gboolean nm_dedup_multi_index_add (NMDedupMultiIndex *self, const NMDedupMultiEntry **out_entry, /* const NMDedupMultiObj ** */ gpointer out_obj_old); -const NMDedupMultiEntry *nm_dedup_multi_index_lookup_obj (NMDedupMultiIndex *self, +const NMDedupMultiEntry *nm_dedup_multi_index_lookup_obj (const NMDedupMultiIndex *self, const NMDedupMultiIdxType *idx_type, /*const NMDedupMultiObj * */ gconstpointer obj); -const NMDedupMultiHeadEntry *nm_dedup_multi_index_lookup_head (NMDedupMultiIndex *self, +const NMDedupMultiHeadEntry *nm_dedup_multi_index_lookup_head (const NMDedupMultiIndex *self, const NMDedupMultiIdxType *idx_type, /*const NMDedupMultiObj * */ gconstpointer obj); diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 29b20f790a..740f270fa2 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -66,7 +66,6 @@ #include "dns/nm-dns-manager.h" #include "nm-core-internal.h" #include "nm-default-route-manager.h" -#include "nm-route-manager.h" #include "systemd/nm-sd.h" #include "nm-lldp-listener.h" #include "nm-audit-manager.h" @@ -491,16 +490,14 @@ static void nm_device_set_proxy_config (NMDevice *self, const char *pac_url); static gboolean nm_device_set_ip4_config (NMDevice *self, NMIP4Config *config, guint32 default_route_metric, - gboolean commit, - gboolean routes_full_sync); + gboolean commit); static gboolean ip4_config_merge_and_apply (NMDevice *self, NMIP4Config *config, gboolean commit); static gboolean nm_device_set_ip6_config (NMDevice *self, NMIP6Config *config, - gboolean commit, - gboolean routes_full_sync); + gboolean commit); static gboolean ip6_config_merge_and_apply (NMDevice *self, gboolean commit); @@ -2767,6 +2764,99 @@ link_changed_cb (NMPlatform *platform, } } +/*****************************************************************************/ + +typedef struct { + in_addr_t network; + guint8 plen; +} IP4RPFilterData; + +static guint +_v4_has_shadowed_routes_detect_hash (const IP4RPFilterData *d) +{ + guint h = 0; + + h = NM_HASH_COMBINE (h, d->network); + h = NM_HASH_COMBINE (h, d->plen); + return h; +} + +static gboolean +_v4_has_shadowed_routes_detect_equal (const IP4RPFilterData *d1, const IP4RPFilterData *d2) +{ + return d1->network == d2->network && d1->plen == d2->plen; +} + +static gboolean +_v4_has_shadowed_routes_detect (NMDevice *self) +{ + NMPlatform *platform; + int ifindex; + NMPLookup lookup; + const NMDedupMultiHeadEntry *head_entry; + NMDedupMultiIter iter; + const NMPObject *o; + guint data_len; + gs_unref_hashtable GHashTable *data_hash = NULL; + gs_free IP4RPFilterData *data_arr = NULL; + + ifindex = nm_device_get_ip_ifindex (self); + if (ifindex <= 0) + return FALSE; + + platform = nm_device_get_platform (self); + + head_entry = nm_platform_lookup (platform, + nmp_lookup_init_addrroute (&lookup, + NMP_OBJECT_TYPE_IP4_ROUTE, + ifindex)); + if (!head_entry) + return FALSE; + + /* first, create a lookup index @data_hash for all network/plen pairs. */ + data_len = 0; + data_arr = g_new (IP4RPFilterData, head_entry->len); + data_hash = g_hash_table_new ((GHashFunc) _v4_has_shadowed_routes_detect_hash, + (GEqualFunc) _v4_has_shadowed_routes_detect_equal); + + nmp_cache_iter_for_each (&iter, head_entry, &o) { + const NMPlatformIP4Route *r = NMP_OBJECT_CAST_IP4_ROUTE (o); + IP4RPFilterData *d; + + nm_assert (r->ifindex == ifindex); + + if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (r)) + continue; + + d = &data_arr[data_len++]; + d->network = nm_utils_ip4_address_clear_host_address (r->network, r->plen); + d->plen = r->plen; + g_hash_table_add (data_hash, d); + } + + /* then, search if there is any route on another interface with the same + * network/plen destination. If yes, we consider this a multihoming + * setup. */ + head_entry = nm_platform_lookup (platform, + nmp_lookup_init_obj_type (&lookup, + NMP_OBJECT_TYPE_IP4_ROUTE)); + nmp_cache_iter_for_each (&iter, head_entry, &o) { + const NMPlatformIP4Route *r = NMP_OBJECT_CAST_IP4_ROUTE (o); + IP4RPFilterData d; + + if ( r->ifindex == ifindex + || NM_PLATFORM_IP_ROUTE_IS_DEFAULT (r)) + continue; + + d.network = nm_utils_ip4_address_clear_host_address (r->network, r->plen); + d.plen = r->plen; + if (g_hash_table_contains (data_hash, &d)) + return TRUE; + } + + return FALSE; +} + static void ip4_rp_filter_update (NMDevice *self) { @@ -2793,20 +2883,6 @@ ip4_rp_filter_update (NMDevice *self) } static void -ip4_routes_changed_changed_cb (NMRouteManager *route_manager, NMDevice *self) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - int ifindex = nm_device_get_ip_ifindex (self); - - if (nm_device_sys_iface_state_is_external_or_assume (self)) - return; - - priv->v4_has_shadowed_routes = nm_route_manager_ip4_routes_shadowed (route_manager, - ifindex); - ip4_rp_filter_update (self); -} - -static void link_changed (NMDevice *self, const NMPlatformLink *pllink) { /* stub implementation of virtual function to allow subclasses to chain up. */ @@ -3846,8 +3922,8 @@ nm_device_removed (NMDevice *self, gboolean unconfigure_ip_config) _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, FALSE); - nm_device_set_ip6_config (self, NULL, FALSE, FALSE); + nm_device_set_ip4_config (self, NULL, 0, FALSE); + nm_device_set_ip6_config (self, NULL, FALSE); } static gboolean @@ -5582,7 +5658,6 @@ ip4_config_merge_and_apply (NMDevice *self, const guint32 default_route_metric = nm_device_get_ip4_route_metric (self); guint32 gateway; gboolean connection_has_default_route, connection_is_never_default; - gboolean routes_full_sync; gboolean ignore_auto_routes = FALSE; gboolean ignore_auto_dns = FALSE; gboolean auto_method = FALSE; @@ -5749,11 +5824,7 @@ END_ADD_DEFAULT_ROUTE: NM_DEVICE_GET_CLASS (self)->ip4_config_pre_commit (self, composite); } - routes_full_sync = commit - && priv->v4_commit_first_time - && !nm_device_sys_iface_state_is_external_or_assume (self); - - success = nm_device_set_ip4_config (self, composite, default_route_metric, commit, routes_full_sync); + success = nm_device_set_ip4_config (self, composite, default_route_metric, commit); g_object_unref (composite); if (commit) @@ -6308,7 +6379,6 @@ ip6_config_merge_and_apply (NMDevice *self, gboolean has_direct_route; const struct in6_addr *gateway; gboolean connection_has_default_route, connection_is_never_default; - gboolean routes_full_sync; gboolean ignore_auto_routes = FALSE; gboolean ignore_auto_dns = FALSE; gboolean auto_method = FALSE; @@ -6497,11 +6567,7 @@ END_ADD_DEFAULT_ROUTE: } } - routes_full_sync = commit - && priv->v6_commit_first_time - && !nm_device_sys_iface_state_is_external_or_assume (self); - - success = nm_device_set_ip6_config (self, composite, commit, routes_full_sync); + success = nm_device_set_ip6_config (self, composite, commit); g_object_unref (composite); if (commit) priv->v6_commit_first_time = FALSE; @@ -9853,46 +9919,35 @@ static gboolean nm_device_set_ip4_config (NMDevice *self, NMIP4Config *new_config, guint32 default_route_metric, - gboolean commit, - gboolean routes_full_sync) + gboolean commit) { NMDevicePrivate *priv; NMIP4Config *old_config = NULL; gboolean has_changes = FALSE; gboolean success = TRUE; gboolean def_route_changed; - int ip_ifindex, config_ifindex; + int ip_ifindex = 0; g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); - _LOGD (LOGD_IP4, "ip4-config: update (commit=%d, routes-full-sync=%d, new-config=%p)", - commit, routes_full_sync, new_config); + _LOGD (LOGD_IP4, "ip4-config: update (commit=%d, new-config=%p)", + commit, new_config); - priv = NM_DEVICE_GET_PRIVATE (self); - ip_ifindex = nm_device_get_ip_ifindex (self); + nm_assert ( !new_config + || ( new_config + && ((ip_ifindex = nm_device_get_ip_ifindex (self)) > 0) + && ip_ifindex == nm_ip4_config_get_ifindex (new_config))); - if (new_config) { - config_ifindex = nm_ip4_config_get_ifindex (new_config); - if (config_ifindex > 0) - g_return_val_if_fail (ip_ifindex == config_ifindex, FALSE); - } + priv = NM_DEVICE_GET_PRIVATE (self); old_config = priv->ip4_config; /* Always commit to nm-platform to update lifetimes */ if (commit && new_config) { - gboolean assumed = nm_device_sys_iface_state_is_external_or_assume (self); - _commit_mtu (self, new_config); - /* For assumed devices we must not touch the kernel-routes, such as the device-route. - * FIXME: this is wrong in case where "assumed" means "take-over-seamlessly". In this - * case, we should manage the device route, for example on new DHCP lease. */ success = nm_ip4_config_commit (new_config, nm_device_get_platform (self), - nm_netns_get_route_manager (priv->netns), - ip_ifindex, - routes_full_sync, - assumed ? (gint64) -1 : (gint64) default_route_metric); + default_route_metric); } if (new_config) { @@ -10031,29 +10086,26 @@ nm_device_set_wwan_ip4_config (NMDevice *self, NMIP4Config *config) static gboolean nm_device_set_ip6_config (NMDevice *self, NMIP6Config *new_config, - gboolean commit, - gboolean routes_full_sync) + gboolean commit) { NMDevicePrivate *priv; NMIP6Config *old_config = NULL; gboolean has_changes = FALSE; gboolean success = TRUE; gboolean def_route_changed; - int ip_ifindex, config_ifindex; + int ip_ifindex = 0; g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); - _LOGD (LOGD_IP6, "ip6-config: update (commit=%d, routes-full-sync=%d, new-config=%p)", - commit, routes_full_sync, new_config); + _LOGD (LOGD_IP6, "ip6-config: update (commit=%d, new-config=%p)", + commit, new_config); - priv = NM_DEVICE_GET_PRIVATE (self); - ip_ifindex = nm_device_get_ip_ifindex (self); + nm_assert ( !new_config + || ( new_config + && ((ip_ifindex = nm_device_get_ip_ifindex (self)) > 0) + && ip_ifindex == nm_ip6_config_get_ifindex (new_config))); - if (new_config) { - config_ifindex = nm_ip6_config_get_ifindex (new_config); - if (config_ifindex > 0) - g_return_val_if_fail (ip_ifindex == config_ifindex, FALSE); - } + priv = NM_DEVICE_GET_PRIVATE (self); old_config = priv->ip6_config; @@ -10061,10 +10113,7 @@ nm_device_set_ip6_config (NMDevice *self, if (commit && new_config) { _commit_mtu (self, priv->ip4_config); success = nm_ip6_config_commit (new_config, - nm_device_get_platform (self), - nm_netns_get_route_manager (priv->netns), - ip_ifindex, - routes_full_sync); + nm_device_get_platform (self)); } if (new_config) { @@ -10900,6 +10949,11 @@ queued_ip4_config_change (gpointer user_data) set_unmanaged_external_down (self, TRUE); + if (!nm_device_sys_iface_state_is_external_or_assume (self)) { + priv->v4_has_shadowed_routes = _v4_has_shadowed_routes_detect (self);; + ip4_rp_filter_update (self); + } + return FALSE; } @@ -12105,8 +12159,8 @@ _cleanup_generic_post (NMDevice *self, CleanupType cleanup_type) /* Clean up IP configs; this does not actually deconfigure the * interface; the caller must flush routes and addresses explicitly. */ - nm_device_set_ip4_config (self, NULL, 0, TRUE, TRUE); - nm_device_set_ip6_config (self, NULL, TRUE, TRUE); + nm_device_set_ip4_config (self, NULL, 0, TRUE); + nm_device_set_ip6_config (self, NULL, TRUE); g_clear_object (&priv->proxy_config); g_clear_object (&priv->con_ip4_config); g_clear_object (&priv->dev_ip4_config); @@ -12194,18 +12248,24 @@ nm_device_cleanup (NMDevice *self, NMDeviceStateReason reason, CleanupType clean if (NM_DEVICE_GET_CLASS (self)->deactivate) NM_DEVICE_GET_CLASS (self)->deactivate (self); + ifindex = nm_device_get_ip_ifindex (self); + if (cleanup_type == CLEANUP_TYPE_DECONFIGURE) { /* master: release slaves */ nm_device_master_release_slaves (self); /* Take out any entries in the routing table and any IP address the device had. */ - ifindex = nm_device_get_ip_ifindex (self); if (ifindex > 0) { - nm_route_manager_route_flush (nm_netns_get_route_manager (priv->netns), ifindex); - nm_platform_ip_address_flush (nm_device_get_platform (self), AF_UNSPEC, ifindex); + NMPlatform *platform = nm_device_get_platform (self); + + nm_platform_ip_route_flush (platform, AF_UNSPEC, ifindex); + nm_platform_ip_address_flush (platform, AF_UNSPEC, ifindex); } } + if (ifindex > 0) + nm_platform_ip4_dev_route_blacklist_set (nm_device_get_platform (self), ifindex, NULL); + /* slave: mark no longer enslaved */ if ( priv->master && nm_platform_link_get_master (nm_device_get_platform (self), priv->ifindex) <= 0) @@ -13851,9 +13911,6 @@ constructed (GObject *object) g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, G_CALLBACK (device_ipx_changed), self); g_signal_connect (platform, NM_PLATFORM_SIGNAL_LINK_CHANGED, G_CALLBACK (link_changed_cb), self); - g_signal_connect (nm_netns_get_route_manager (priv->netns), NM_ROUTE_MANAGER_IP4_ROUTES_CHANGED, - G_CALLBACK (ip4_routes_changed_changed_cb), self); - priv->settings = g_object_ref (NM_SETTINGS_GET); g_assert (priv->settings); @@ -13894,9 +13951,6 @@ dispose (GObject *object) g_signal_handlers_disconnect_by_func (platform, G_CALLBACK (device_ipx_changed), self); g_signal_handlers_disconnect_by_func (platform, G_CALLBACK (link_changed_cb), self); - g_signal_handlers_disconnect_by_func (nm_netns_get_route_manager (priv->netns), - G_CALLBACK (ip4_routes_changed_changed_cb), self); - g_slist_free_full (priv->arping.dad_list, (GDestroyNotify) nm_arping_manager_destroy); priv->arping.dad_list = NULL; diff --git a/src/devices/wwan/nm-modem.c b/src/devices/wwan/nm-modem.c index b6bcc25fbb..6e2ad7f2e9 100644 --- a/src/devices/wwan/nm-modem.c +++ b/src/devices/wwan/nm-modem.c @@ -32,7 +32,6 @@ #include "nm-setting-connection.h" #include "NetworkManagerUtils.h" #include "devices/nm-device-private.h" -#include "nm-route-manager.h" #include "nm-netns.h" #include "nm-act-request.h" #include "nm-ip4-config.h" @@ -1066,10 +1065,11 @@ deactivate_cleanup (NMModem *self, NMDevice *device) priv->ip6_method == NM_MODEM_IP_METHOD_AUTO) { ifindex = nm_device_get_ip_ifindex (device); if (ifindex > 0) { - nm_route_manager_route_flush (nm_netns_get_route_manager (nm_device_get_netns (device)), - ifindex); - nm_platform_ip_address_flush (nm_device_get_platform (device), AF_UNSPEC, ifindex); - nm_platform_link_set_down (nm_device_get_platform (device), ifindex); + NMPlatform *platform = nm_device_get_platform (device); + + nm_platform_ip_route_flush (platform, AF_UNSPEC, ifindex); + nm_platform_ip_address_flush (platform, AF_UNSPEC, ifindex); + nm_platform_link_set_down (platform, ifindex); } } } diff --git a/src/nm-iface-helper.c b/src/nm-iface-helper.c index 3b60e0d64f..c695d591f6 100644 --- a/src/nm-iface-helper.c +++ b/src/nm-iface-helper.c @@ -42,7 +42,6 @@ #include "nm-utils.h" #include "nm-setting-ip6-config.h" #include "systemd/nm-sd.h" -#include "nm-route-manager.h" #if !defined(NM_DIST_VERSION) # define NM_DIST_VERSION VERSION @@ -98,12 +97,6 @@ static struct { /*****************************************************************************/ -NMRouteManager *route_manager_get (void); - -NM_DEFINE_SINGLETON_GETTER (NMRouteManager, route_manager_get, NM_TYPE_ROUTE_MANAGER); - -/*****************************************************************************/ - static void dhcp4_state_changed (NMDhcpClient *client, NMDhcpState state, @@ -122,13 +115,17 @@ dhcp4_state_changed (NMDhcpClient *client, switch (state) { case NM_DHCP_STATE_BOUND: g_assert (ip4_config); + g_assert (nm_ip4_config_get_ifindex (ip4_config) == gl.ifindex); + existing = nm_ip4_config_capture (nm_platform_get_multi_idx (NM_PLATFORM_GET), NM_PLATFORM_GET, gl.ifindex, FALSE); if (last_config) nm_ip4_config_subtract (existing, last_config); nm_ip4_config_merge (existing, ip4_config, NM_IP_CONFIG_MERGE_DEFAULT); - if (!nm_ip4_config_commit (existing, NM_PLATFORM_GET, route_manager_get (), gl.ifindex, TRUE, global_opt.priority_v4)) + if (!nm_ip4_config_commit (existing, + NM_PLATFORM_GET, + global_opt.priority_v4)) _LOGW (LOGD_DHCP4, "failed to apply DHCPv4 config"); if (last_config) @@ -257,7 +254,7 @@ ndisc_config_changed (NMNDisc *ndisc, const NMNDiscData *rdata, guint changed_in } nm_ip6_config_merge (existing, ndisc_config, NM_IP_CONFIG_MERGE_DEFAULT); - if (!nm_ip6_config_commit (existing, NM_PLATFORM_GET, route_manager_get (), gl.ifindex, TRUE)) + if (!nm_ip6_config_commit (existing, NM_PLATFORM_GET)) _LOGW (LOGD_IP6, "failed to apply IPv6 config"); } diff --git a/src/nm-ip4-config.c b/src/nm-ip4-config.c index e94662107d..606bb342fc 100644 --- a/src/nm-ip4-config.c +++ b/src/nm-ip4-config.c @@ -33,7 +33,6 @@ #include "platform/nm-platform.h" #include "platform/nm-platform-utils.h" #include "NetworkManagerUtils.h" -#include "nm-route-manager.h" #include "nm-core-internal.h" #include "introspection/org.freedesktop.NetworkManager.IP4Config.h" @@ -284,6 +283,48 @@ append_force_and_out: return FALSE; } +/** + * _nm_ip_config_lookup_ip_route: + * @multi_idx: + * @idx_type: + * @needle: + * @cmp_type: after lookup, filter the result by comparing with @cmp_type. Only + * return the result, if it compares equal to @needle according to this @cmp_type. + * Note that the index uses %NM_PLATFORM_IP_ROUTE_CMP_TYPE_DST type, so passing + * that compare-type means not to filter any further. + * + * Returns: the found entry or %NULL. + */ +const NMDedupMultiEntry * +_nm_ip_config_lookup_ip_route (const NMDedupMultiIndex *multi_idx, + const NMIPConfigDedupMultiIdxType *idx_type, + const NMPObject *needle, + NMPlatformIPRouteCmpType cmp_type) +{ + const NMDedupMultiEntry *entry; + + nm_assert (multi_idx); + nm_assert (idx_type); + nm_assert (NM_IN_SET (idx_type->obj_type, NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)); + nm_assert (NMP_OBJECT_GET_TYPE (needle) == idx_type->obj_type); + + entry = nm_dedup_multi_index_lookup_obj (multi_idx, + &idx_type->parent, + needle); + if (!entry) + return NULL; + + if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_DST) + nm_assert (nm_platform_ip4_route_cmp (NMP_OBJECT_CAST_IP4_ROUTE (entry->obj), NMP_OBJECT_CAST_IP4_ROUTE (needle), cmp_type) == 0); + else { + if (nm_platform_ip4_route_cmp (NMP_OBJECT_CAST_IP4_ROUTE (entry->obj), + NMP_OBJECT_CAST_IP4_ROUTE (needle), + cmp_type) != 0) + return NULL; + } + return entry; +} + /*****************************************************************************/ NM_GOBJECT_PROPERTIES_DEFINE (NMIP4Config, @@ -352,6 +393,9 @@ G_DEFINE_TYPE (NMIP4Config, nm_ip4_config, NM_TYPE_EXPORTED_OBJECT) static void _add_address (NMIP4Config *self, const NMPObject *obj_new, const NMPlatformIP4Address *new); static void _add_route (NMIP4Config *self, const NMPObject *obj_new, const NMPlatformIP4Route *new); +static const NMDedupMultiEntry *_lookup_route (const NMIP4Config *self, + const NMPObject *needle, + NMPlatformIPRouteCmpType cmp_type); /*****************************************************************************/ @@ -669,37 +713,41 @@ nm_ip4_config_capture (NMDedupMultiIndex *multi_idx, NMPlatform *platform, int i } gboolean -nm_ip4_config_commit (const NMIP4Config *self, NMPlatform *platform, NMRouteManager *route_manager, int ifindex, gboolean routes_full_sync, gint64 default_route_metric) +nm_ip4_config_commit (const NMIP4Config *self, + NMPlatform *platform, + guint32 default_route_metric) { + const NMIP4ConfigPrivate *priv; gs_unref_ptrarray GPtrArray *addresses = NULL; - const NMDedupMultiHeadEntry *head_entry; + gs_unref_ptrarray GPtrArray *routes = NULL; + gs_unref_ptrarray GPtrArray *ip4_dev_route_blacklist = NULL; + int ifindex; guint i; - gs_unref_array GArray *routes = NULL; - gs_unref_array GArray *device_route_purge_list = NULL; - const CList *iter; + gboolean success = TRUE; + + g_return_val_if_fail (NM_IS_IP4_CONFIG (self), FALSE); + + priv = NM_IP4_CONFIG_GET_PRIVATE (self); + ifindex = nm_ip4_config_get_ifindex (self); g_return_val_if_fail (ifindex > 0, FALSE); - g_return_val_if_fail (self != NULL, FALSE); addresses = nm_dedup_multi_objs_to_ptr_array_head (nm_ip4_config_lookup_addresses (self), NULL, NULL); - nm_platform_ip4_address_sync (platform, ifindex, addresses); - - /* Routes */ - head_entry = nm_ip4_config_lookup_routes (self); - - routes = g_array_sized_new (FALSE, FALSE, sizeof (NMPlatformIP4Route), head_entry ? head_entry->len : 0); + routes = nm_dedup_multi_objs_to_ptr_array_head (nm_ip4_config_lookup_routes (self), + NULL, NULL); - if ( default_route_metric >= 0 - && addresses) { + if (addresses) { /* For IPv6, we explicitly add the device-routes (onlink) to NMIP6Config. * As we don't do that for IPv4, add it here shortly before syncing - * the routes. For NMRouteManager these routes are very much important. */ + * the routes. */ for (i = 0; i < addresses->len; i++) { const NMPObject *o = addresses->pdata[i]; const NMPlatformIP4Address *addr; - NMPlatformIP4Route route = { 0 }; + nm_auto_nmpobj NMPObject *r = NULL; + NMPlatformIP4Route *route; + in_addr_t network; if (!o) continue; @@ -710,48 +758,77 @@ nm_ip4_config_commit (const NMIP4Config *self, NMPlatform *platform, NMRouteMana nm_assert (addr->plen <= 32); - route.ifindex = ifindex; - route.rt_source = NM_IP_CONFIG_SOURCE_KERNEL; - /* The destination network depends on the peer-address. */ - route.network = nm_utils_ip4_address_clear_host_address (addr->peer_address, addr->plen); + network = nm_utils_ip4_address_clear_host_address (addr->peer_address, addr->plen); - if (_ipv4_is_zeronet (route.network)) { + if (_ipv4_is_zeronet (network)) { /* Kernel doesn't add device-routes for destinations that * start with 0.x.y.z. Skip them. */ continue; } - route.plen = addr->plen; - route.pref_src = addr->address; - route.metric = default_route_metric; - - g_array_append_val (routes, route); + r = nmp_object_new (NMP_OBJECT_TYPE_IP4_ROUTE, NULL); + route = NMP_OBJECT_CAST_IP4_ROUTE (r); + + route->ifindex = ifindex; + route->rt_source = NM_IP_CONFIG_SOURCE_KERNEL; + route->network = network; + route->plen = addr->plen; + route->pref_src = addr->address; + route->metric = default_route_metric; + route->scope_inv = nm_platform_route_scope_inv (NM_RT_SCOPE_LINK); + + nm_platform_ip_route_normalize (AF_INET, (NMPlatformIPRoute *) route); + + if (_lookup_route (self, + r, + NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID)) { + /* we already track this route. Don't add it again. */ + } else { + if (!routes) + routes = g_ptr_array_new_with_free_func ((GDestroyNotify) nmp_object_unref); + g_ptr_array_add (routes, (gpointer) nmp_object_ref (r)); + } if (default_route_metric != NM_PLATFORM_ROUTE_METRIC_IP4_DEVICE_ROUTE) { - if (!device_route_purge_list) - device_route_purge_list = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP4Route)); - route.metric = NM_PLATFORM_ROUTE_METRIC_IP4_DEVICE_ROUTE; - g_array_append_val (device_route_purge_list, route); + nm_auto_nmpobj NMPObject *r_dev = NULL; + + r_dev = nmp_object_clone (r, FALSE); + route = NMP_OBJECT_CAST_IP4_ROUTE (r_dev); + route->metric = NM_PLATFORM_ROUTE_METRIC_IP4_DEVICE_ROUTE; + + nm_platform_ip_route_normalize (AF_INET, (NMPlatformIPRoute *) route); + + if (_lookup_route (self, + r_dev, + NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID)) { + /* we track such a route explicitly. Don't blacklist it. */ + } else { + if (!ip4_dev_route_blacklist) + ip4_dev_route_blacklist = g_ptr_array_new_with_free_func ((GDestroyNotify) nmp_object_unref); + + g_ptr_array_add (ip4_dev_route_blacklist, + g_steal_pointer (&r_dev)); + } } } } - if (head_entry) { - c_list_for_each (iter, &head_entry->lst_entries_head) { - g_array_append_vals (routes, - NMP_OBJECT_CAST_IP4_ROUTE (c_list_entry (iter, NMDedupMultiEntry, lst_entries)->obj), - 1); - } - } + nm_platform_ip4_address_sync (platform, ifindex, addresses); - nm_route_manager_ip4_route_register_device_route_purge_list (route_manager, device_route_purge_list); + if (!nm_platform_ip_route_sync (platform, + AF_INET, + ifindex, + routes, + nm_platform_lookup_predicate_routes_skip_rtprot_kernel, + NULL)) + success = FALSE; - if (!nm_route_manager_ip4_route_sync (route_manager, ifindex, routes, - default_route_metric < 0, routes_full_sync)) - return FALSE; + nm_platform_ip4_dev_route_blacklist_set (platform, + ifindex, + ip4_dev_route_blacklist); - return TRUE; + return success; } static void @@ -1996,6 +2073,24 @@ nm_ip4_config_address_exists (const NMIP4Config *self, /*****************************************************************************/ +static const NMDedupMultiEntry * +_lookup_route (const NMIP4Config *self, + const NMPObject *needle, + NMPlatformIPRouteCmpType cmp_type) +{ + const NMIP4ConfigPrivate *priv; + + nm_assert (NM_IS_IP4_CONFIG (self)); + nm_assert (NMP_OBJECT_GET_TYPE (needle) == NMP_OBJECT_TYPE_IP4_ROUTE); + + priv = NM_IP4_CONFIG_GET_PRIVATE (self); + + return _nm_ip_config_lookup_ip_route (priv->multi_idx, + &priv->idx_ip4_routes_, + needle, + cmp_type); +} + void nm_ip4_config_reset_routes (NMIP4Config *self) { diff --git a/src/nm-ip4-config.h b/src/nm-ip4-config.h index 594dcc5bf2..ab8fa51d4d 100644 --- a/src/nm-ip4-config.h +++ b/src/nm-ip4-config.h @@ -96,6 +96,11 @@ gboolean _nm_ip_config_add_obj (NMDedupMultiIndex *multi_idx, gboolean merge, gboolean append_force); +const NMDedupMultiEntry *_nm_ip_config_lookup_ip_route (const NMDedupMultiIndex *multi_idx, + const NMIPConfigDedupMultiIdxType *idx_type, + const NMPObject *needle, + NMPlatformIPRouteCmpType cmp_type); + /*****************************************************************************/ #define NM_TYPE_IP4_CONFIG (nm_ip4_config_get_type ()) @@ -137,7 +142,9 @@ int nm_ip4_config_get_ifindex (const NMIP4Config *self); NMDedupMultiIndex *nm_ip4_config_get_multi_idx (const NMIP4Config *self); NMIP4Config *nm_ip4_config_capture (NMDedupMultiIndex *multi_idx, NMPlatform *platform, int ifindex, gboolean capture_resolv_conf); -gboolean nm_ip4_config_commit (const NMIP4Config *self, NMPlatform *platform, NMRouteManager *route_manager, int ifindex, gboolean routes_full_sync, gint64 default_route_metric); +gboolean nm_ip4_config_commit (const NMIP4Config *self, + NMPlatform *platform, + guint32 default_route_metric); void nm_ip4_config_merge_setting (NMIP4Config *self, NMSettingIPConfig *setting, guint32 default_route_metric); NMSetting *nm_ip4_config_create_setting (const NMIP4Config *self); diff --git a/src/nm-ip6-config.c b/src/nm-ip6-config.c index 9fc4c3e2ff..3d42fb7b8a 100644 --- a/src/nm-ip6-config.c +++ b/src/nm-ip6-config.c @@ -32,7 +32,6 @@ #include "platform/nmp-object.h" #include "platform/nm-platform.h" #include "platform/nm-platform-utils.h" -#include "nm-route-manager.h" #include "nm-core-internal.h" #include "NetworkManagerUtils.h" #include "nm-ip4-config.h" @@ -516,42 +515,33 @@ nm_ip6_config_capture (NMDedupMultiIndex *multi_idx, NMPlatform *platform, int i gboolean nm_ip6_config_commit (const NMIP6Config *self, - NMPlatform *platform, - NMRouteManager *route_manager, - int ifindex, - gboolean routes_full_sync) + NMPlatform *platform) { gs_unref_ptrarray GPtrArray *addresses = NULL; - const NMDedupMultiHeadEntry *head_entry; - gs_unref_array GArray *routes = NULL; - const CList *iter; + gs_unref_ptrarray GPtrArray *routes = NULL; + int ifindex; + gboolean success = TRUE; + + g_return_val_if_fail (NM_IS_IP6_CONFIG (self), FALSE); + ifindex = nm_ip6_config_get_ifindex (self); g_return_val_if_fail (ifindex > 0, FALSE); - g_return_val_if_fail (self != NULL, FALSE); - /* Addresses */ addresses = nm_dedup_multi_objs_to_ptr_array_head (nm_ip6_config_lookup_addresses (self), NULL, NULL); - + routes = nm_dedup_multi_objs_to_ptr_array_head (nm_ip6_config_lookup_routes (self), + NULL, NULL); nm_platform_ip6_address_sync (platform, ifindex, addresses, TRUE); - /* Routes */ - head_entry = nm_ip6_config_lookup_routes (self); - - routes = g_array_sized_new (FALSE, FALSE, sizeof (NMPlatformIP6Route), head_entry ? head_entry->len : 0); - - if (head_entry) { - c_list_for_each (iter, &head_entry->lst_entries_head) { - g_array_append_vals (routes, - NMP_OBJECT_CAST_IP6_ROUTE (c_list_entry (iter, NMDedupMultiEntry, lst_entries)->obj), - 1); - } - } - - if (!nm_route_manager_ip6_route_sync (route_manager, ifindex, routes, TRUE, routes_full_sync)) - return FALSE; + if (!nm_platform_ip_route_sync (platform, + AF_INET6, + ifindex, + routes, + nm_platform_lookup_predicate_routes_skip_rtprot_kernel, + NULL)) + success = FALSE; - return TRUE; + return success; } static void diff --git a/src/nm-ip6-config.h b/src/nm-ip6-config.h index f296224fab..6e7b112110 100644 --- a/src/nm-ip6-config.h +++ b/src/nm-ip6-config.h @@ -112,10 +112,7 @@ struct _NMDedupMultiIndex *nm_ip6_config_get_multi_idx (const NMIP6Config *self) NMIP6Config *nm_ip6_config_capture (struct _NMDedupMultiIndex *multi_idx, NMPlatform *platform, int ifindex, gboolean capture_resolv_conf, NMSettingIP6ConfigPrivacy use_temporary); gboolean nm_ip6_config_commit (const NMIP6Config *self, - NMPlatform *platform, - NMRouteManager *route_manager, - int ifindex, - gboolean routes_full_sync); + NMPlatform *platform); void nm_ip6_config_merge_setting (NMIP6Config *self, NMSettingIPConfig *setting, guint32 default_route_metric); NMSetting *nm_ip6_config_create_setting (const NMIP6Config *self); diff --git a/src/nm-netns.c b/src/nm-netns.c index f5e6b0014d..8069cfccc3 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-route-manager.h" #include "nm-default-route-manager.h" #include "nm-core-internal.h" #include "NetworkManagerUtils.h" @@ -40,7 +39,6 @@ NM_GOBJECT_PROPERTIES_DEFINE_BASE ( typedef struct { NMPlatform *platform; NMPNetns *platform_netns; - NMRouteManager *route_manager; NMDefaultRouteManager *default_route_manager; bool log_with_ptr; } NMNetnsPrivate; @@ -88,12 +86,6 @@ nm_netns_get_default_route_manager (NMNetns *self) return NM_NETNS_GET_PRIVATE (self)->default_route_manager; } -NMRouteManager * -nm_netns_get_route_manager (NMNetns *self) -{ - return NM_NETNS_GET_PRIVATE (self)->route_manager; -} - /*****************************************************************************/ static void @@ -137,7 +129,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->route_manager = nm_route_manager_new (log_with_ptr, 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); @@ -157,7 +148,6 @@ dispose (GObject *object) NMNetns *self = NM_NETNS (object); NMNetnsPrivate *priv = NM_NETNS_GET_PRIVATE (self); - g_clear_object (&priv->route_manager); g_clear_object (&priv->default_route_manager); g_clear_object (&priv->platform); diff --git a/src/nm-netns.h b/src/nm-netns.h index ebe9d1f2a8..bc71880400 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); -NMRouteManager *nm_netns_get_route_manager (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-route-manager.c b/src/nm-route-manager.c deleted file mode 100644 index 12583e5c06..0000000000 --- a/src/nm-route-manager.c +++ /dev/null @@ -1,1380 +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) 2015 Red Hat, Inc. - */ - -#include "nm-default.h" - -#include "nm-route-manager.h" - -#include <string.h> - -#include "platform/nm-platform.h" -#include "platform/nmp-object.h" -#include "nm-core-internal.h" -#include "NetworkManagerUtils.h" - -/* if within half a second after adding an IP address a matching device-route shows - * up, we delete it. */ -#define IP4_DEVICE_ROUTES_WAIT_TIME_NS (NM_UTILS_NS_PER_SECOND / 2) - -#define IP4_DEVICE_ROUTES_GC_INTERVAL_MSEC ((IP4_DEVICE_ROUTES_WAIT_TIME_NS / 1000000) * 3) - -/*****************************************************************************/ - -typedef struct { - guint len; - NMPlatformIPXRoute *entries[1]; -} RouteIndex; - -typedef struct { - GArray *entries; - RouteIndex *index; - - /* list of effective metrics. The indexes of the array correspond to @index, not @entries. */ - GArray *effective_metrics; - - /* this array contains the effective metrics but using the reversed index that corresponds - * to @entries, instead of @index. */ - GArray *effective_metrics_reverse; -} RouteEntries; - -typedef struct { - NMRouteManager *self; - gint64 scheduled_at_ns; - guint idle_id; - const NMPObject *obj; - const NMPObject *obj_cached; -} IP4DeviceRoutePurgeEntry; - -/*****************************************************************************/ - -enum { - IP4_ROUTES_CHANGED, - LAST_SIGNAL, -}; -static guint signals[LAST_SIGNAL] = { 0 }; - -NM_GOBJECT_PROPERTIES_DEFINE_BASE ( - PROP_LOG_WITH_PTR, - PROP_PLATFORM, -); - -typedef struct { - NMPlatform *platform; - - RouteEntries ip4_routes; - RouteEntries ip6_routes; - struct { - GHashTable *entries; - guint gc_id; - } ip4_device_routes; - - bool log_with_ptr; -} NMRouteManagerPrivate; - -struct _NMRouteManager { - GObject parent; - NMRouteManagerPrivate _priv; -}; - -struct _NMRouteManagerClass { - GObjectClass parent; -}; - -G_DEFINE_TYPE (NMRouteManager, nm_route_manager, G_TYPE_OBJECT); - -#define NM_ROUTE_MANAGER_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMRouteManager, NM_IS_ROUTE_MANAGER) - -/*****************************************************************************/ - -typedef struct { - const NMPlatformVTableRoute *vt; - - /* a compare function for two routes that considers only the destination fields network/plen. - * It is a looser comparisong then @route_id_cmp(), that means that if @route_dest_cmp() - * returns non-zero, also @route_id_cmp() returns the same value. It also means, that - * sorting by @route_id_cmp() implicitly sorts by @route_dest_cmp() as well. */ - int (*route_dest_cmp) (const NMPlatformIPXRoute *r1, const NMPlatformIPXRoute *r2); - - /* 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 _NMLOG_PREFIX_NAME "route-mgr" -#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 __ch = __addr_family == AF_INET ? '4' : (__addr_family == AF_INET6 ? '6' : '-'); \ - char __prefix[30] = _NMLOG_PREFIX_NAME; \ - \ - if (NM_ROUTE_MANAGER_GET_PRIVATE (self)->log_with_ptr) \ - g_snprintf (__prefix, sizeof (__prefix), "%s%c[%p]", _NMLOG_PREFIX_NAME, __ch, (self)); \ - else \ - __prefix[NM_STRLEN (_NMLOG_PREFIX_NAME)] = __ch; \ - _nm_log ((level), (__domain), 0, NULL, NULL, \ - "%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ - __prefix _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ - } \ - } G_STMT_END - -/*****************************************************************************/ - -static gboolean _ip4_device_routes_cancel (NMRouteManager *self); - -/*****************************************************************************/ - -#if NM_MORE_ASSERTS && !defined (G_DISABLE_ASSERT) -static inline void -ASSERT_route_index_valid (const VTableIP *vtable, const GArray *entries, const RouteIndex *index, gboolean unique_ifindexes) -{ - 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); - } - - /* assert that the @index is valid for the @entries. */ - - 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); - - 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 - -/*****************************************************************************/ - -static int -_v4_route_dest_cmp (const NMPlatformIP4Route *r1, const NMPlatformIP4Route *r2) -{ - CMP_AND_RETURN_INT (r1->plen, r2->plen); - 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 int -_v6_route_dest_cmp (const NMPlatformIP6Route *r1, const NMPlatformIP6Route *r2) -{ - struct in6_addr n1, n2; - - CMP_AND_RETURN_INT (r1->plen, r2->plen); - - 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 -_v4_route_id_cmp (const NMPlatformIP4Route *r1, const NMPlatformIP4Route *r2) -{ - CMP_AND_RETURN_INT (r1->plen, r2->plen); - 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)); - CMP_AND_RETURN_INT (r1->metric, r2->metric); - return 0; -} - -static int -_v6_route_id_cmp (const NMPlatformIP6Route *r1, const NMPlatformIP6Route *r2) -{ - struct in6_addr n1, n2; - int c; - - CMP_AND_RETURN_INT (r1->plen, r2->plen); - - nm_utils_ip6_address_clear_host_address (&n1, &r1->network, r1->plen); - nm_utils_ip6_address_clear_host_address (&n2, &r2->network, r2->plen); - c = memcmp (&n1, &n2, sizeof (n1)); - if (c != 0) - return c; - - CMP_AND_RETURN_INT (nm_utils_ip6_route_metric_normalize (r1->metric), - nm_utils_ip6_route_metric_normalize (r2->metric)); - return 0; -} - -/*****************************************************************************/ - -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; - - index = g_malloc (sizeof (RouteIndex) + len * sizeof (NMPlatformIPXRoute *)); - - 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 RouteIndex * -_route_index_create_from_platform (const VTableIP *vtable, - NMPlatform *platform, - int ifindex, - gboolean ignore_kernel_routes, - GPtrArray **out_storage) -{ - RouteIndex *index; - guint i, j, len; - GPtrArray *storage; - - nm_assert (out_storage && !*out_storage); - - storage = nm_platform_lookup_addrroute_clone (platform, - vtable->vt->obj_type, - ifindex, - ignore_kernel_routes - ? nm_platform_lookup_predicate_routes_skip_rtprot_kernel - : NULL, - NULL); - if (!storage) - return _route_index_create (vtable, NULL); - - len = storage->len; - index = g_malloc (sizeof (RouteIndex) + len * sizeof (NMPlatformIPXRoute *)); - - j = 0; - for (i = 0; i < len; i++) { - const NMPlatformIPXRoute *ipx_route = NMP_OBJECT_CAST_IPX_ROUTE (storage->pdata[i]); - - if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (ipx_route)) - continue; - - /* we cast away the const-ness of the NMPObjects. The caller must - * ensure not to modify the object via index->entries. */ - index->entries[j++] = (NMPlatformIPXRoute *) ipx_route; - } - index->entries[j] = NULL; - index->len = j; - - /* this is a stable sort, which is very important at this point. */ - g_qsort_with_data (index->entries, - index->len, - sizeof (NMPlatformIPXRoute *), - (GCompareDataFunc) _route_index_create_sort, - (gpointer) vtable); - *out_storage = storage; - return index; -} - -static int -_vx_route_id_cmp_full (const NMPlatformIPXRoute *r1, const NMPlatformIPXRoute *r2, const VTableIP *vtable) -{ - return vtable->route_id_cmp (r1, r2); -} - -static gssize -_route_index_find (const VTableIP *vtable, const RouteIndex *index, const NMPlatformIPXRoute *needle) -{ - gssize idx, idx2; - - idx = _nm_utils_ptrarray_find_binary_search ((gconstpointer *) index->entries, index->len, needle, (GCompareDataFunc) _vx_route_id_cmp_full, (gpointer) vtable); - if (idx < 0) - return idx; - - /* we only know that the route at index @idx has matching destination. Also find the one with the right - * ifindex by searching the neighbours */ - - idx2 = idx; - do { - if (index->entries[idx2]->rx.ifindex == needle->rx.ifindex) - return idx2; - } while ( idx2 > 0 - && vtable->route_id_cmp (index->entries[--idx2], needle) != 0); - - for (idx++; idx < index->len; idx++ ){ - if (vtable->route_id_cmp (index->entries[idx], needle) != 0) - break; - if (index->entries[idx]->rx.ifindex == needle->rx.ifindex) - return idx; - } - - return ~idx; -} - -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, gint64 r2_metric) -{ - NMPlatformIPXRoute r2_backup; - - if ( r1->rx.ifindex != r2->rx.ifindex - || (r2_metric >= 0 && ((guint32) r2_metric) != r2->rx.metric)) { - memcpy (&r2_backup, r2, vtable->vt->sizeof_route); - r2_backup.rx.ifindex = r1->rx.ifindex; - if (r2_metric >= 0) - r2_backup.rx.metric = (guint32) r2_metric; - r2 = &r2_backup; - } - return vtable->vt->route_cmp (r1, r2, NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) == 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; - - *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) { - nm_assert (NMP_OBJECT_UP_CAST (index->entries[*cur_idx])); - 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); -} - -/*****************************************************************************/ - -static gboolean -_vx_route_sync (const VTableIP *vtable, NMRouteManager *self, int ifindex, const GArray *known_routes, gboolean ignore_kernel_routes, gboolean full_sync) -{ - NMRouteManagerPrivate *priv = NM_ROUTE_MANAGER_GET_PRIVATE (self); - gs_unref_ptrarray GPtrArray *plat_routes = NULL; - RouteEntries *ipx_routes; - RouteIndex *plat_routes_idx, *known_routes_idx; - gboolean success = TRUE; - guint i, i_type; - GArray *to_delete_indexes = 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; - gint64 *p_effective_metric = NULL; - gboolean ipx_routes_changed = FALSE; - gint64 *effective_metrics = NULL; - - nm_platform_process_events (priv->platform); - - ipx_routes = vtable->vt->is_ip4 ? &priv->ip4_routes : &priv->ip6_routes; - - /* the objects referenced by play_routes_idx are shared from the platform cache. They - * must not be modified. */ - plat_routes_idx = _route_index_create_from_platform (vtable, priv->platform, ifindex, ignore_kernel_routes, &plat_routes); - - known_routes_idx = _route_index_create (vtable, known_routes); - - effective_metrics = &g_array_index (ipx_routes->effective_metrics, gint64, 0); - - 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 route #%u: %s", - ifindex, i, vtable->vt->route_to_string (VTABLE_ROUTE_INDEX (vtable, known_routes, i), NULL, 0)); - } - for (i = 0; i < ipx_routes->index->len; i++) - _LOGt (vtable->vt->addr_family, "%3d: STATE: has #%u - %s (%lld)", - ifindex, i, - vtable->vt->route_to_string (ipx_routes->index->entries[i], NULL, 0), - (long long) g_array_index (ipx_routes->effective_metrics, gint64, i)); - } - - /*************************************************************************** - * 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); - - /* 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, -1)) { - /* 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); - nm_utils_ipx_address_clear_host_address (vtable->vt->addr_family, cur_ipx_route->rx.network_ptr, - cur_ipx_route->rx.network_ptr, cur_ipx_route->rx.plen); - ipx_routes_changed = TRUE; - _LOGt (vtable->vt->addr_family, "%3d: STATE: update #%u - %s", ifindex, i_ipx_routes, - vtable->vt->route_to_string (cur_ipx_route, NULL, 0)); - } - } 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); - } - - 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); - } - - if (!full_sync && to_delete_indexes) { - /*************************************************************************** - * Delete routes in platform, that we are about to remove from @ipx_routes - * - * When doing a non-full_sync, we delete routes from platform that were previously - * known by route-manager, and are now deleted. - ***************************************************************************/ - - /* iterate over @to_delete_indexes and @plat_routes. - * @to_delete_indexes contains the indexes (relative to ipx_routes->index) of items - * we are about to delete. */ - cur_plat_route = _get_next_plat_route (plat_routes_idx, TRUE, &i_plat_routes); - for (i = 0; i < to_delete_indexes->len; i++) { - int route_dest_cmp_result = 0; - i_ipx_routes = g_array_index (to_delete_indexes, guint, i); - cur_ipx_route = ipx_routes->index->entries[i_ipx_routes]; - p_effective_metric = &effective_metrics[i_ipx_routes]; - - nm_assert (cur_ipx_route->rx.ifindex == ifindex); - - if (*p_effective_metric == -1) - continue; - - /* skip over @plat_routes that are ordered before our @cur_ipx_route. */ - while ( cur_plat_route - && (route_dest_cmp_result = vtable->route_dest_cmp (cur_plat_route, cur_ipx_route)) <= 0) { - if ( route_dest_cmp_result == 0 - && cur_plat_route->rx.metric >= *p_effective_metric) - break; - cur_plat_route = _get_next_plat_route (plat_routes_idx, FALSE, &i_plat_routes); - } - - if (!cur_plat_route) { - /* no more platform routes. Break the loop. */ - break; - } - - if ( route_dest_cmp_result == 0 - && cur_plat_route->rx.metric == *p_effective_metric) { - /* we are about to delete cur_ipx_route and we have a matching route - * in platform. Delete it. */ - _LOGt (vtable->vt->addr_family, "%3d: platform rt-rm #%u - %s", ifindex, i_plat_routes, - vtable->vt->route_to_string (cur_plat_route, NULL, 0)); - nm_assert (ifindex == cur_plat_route->rx.ifindex); - nm_platform_ip_route_delete (priv->platform, NMP_OBJECT_UP_CAST (cur_plat_route)); - } - } - } - - /* 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], NULL, 0)); - 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); - nm_utils_array_remove_at_indexes (ipx_routes->effective_metrics_reverse, &g_array_index (to_delete_indexes, guint, 0), to_delete_indexes->len); - g_array_unref (to_delete_indexes); - } - if (to_add_routes) { - guint j = ipx_routes->effective_metrics_reverse->len; - - g_array_set_size (ipx_routes->effective_metrics_reverse, j + to_add_routes->len); - - 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); - - 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); - nm_utils_ipx_address_clear_host_address (vtable->vt->addr_family, ipx_route->rx.network_ptr, - ipx_route->rx.network_ptr, ipx_route->rx.plen); - - g_array_index (ipx_routes->effective_metrics_reverse, gint64, j++) = -1; - - _LOGt (vtable->vt->addr_family, "%3d: STATE: added #%u - %s", ifindex, ipx_routes->entries->len - 1, - vtable->vt->route_to_string (ipx_route, NULL, 0)); - } - g_ptr_array_unref (to_add_routes); - } - g_free (ipx_routes->index); - ipx_routes->index = _route_index_create (vtable, ipx_routes->entries); - ipx_routes_changed = TRUE; - ASSERT_route_index_valid (vtable, ipx_routes->entries, ipx_routes->index, TRUE); - } - - if (ipx_routes_changed) { - /*************************************************************************** - * Rebuild the list of effective metrics. In case of conflicting routes, - * we configure device routes with a bumped metric. We do this, because non-direct - * routes might require this direct route to reach the gateway (e.g. the default - * route). - * - * We determine the effective metrics only based on our internal list @ipx_routes - * and don't consider @plat_routes. That means, we might bump the metric of a route - * and thereby cause a conflict with an existing route on an unmanaged device (which - * causes the route on the unmanaged device to be replaced). - * Still, that is not much different then from messing with unmanaged routes when - * the effective and the intended metrics equal. The rules is: NM will leave routes - * on unmanaged devices alone, unless they conflict with what NM wants to configure. - ***************************************************************************/ - - g_array_set_size (ipx_routes->effective_metrics, ipx_routes->entries->len); - effective_metrics = &g_array_index (ipx_routes->effective_metrics, gint64, 0); - - /* Completely regenerate the list of effective metrics by walking through - * ipx_routes->index and determining the effective metric. */ - - for (i_ipx_routes = 0; i_ipx_routes < ipx_routes->index->len; i_ipx_routes++) { - gint64 *p_effective_metric_before; - gboolean is_shadowed; - guint i_ipx_routes_before; - - cur_ipx_route = ipx_routes->index->entries[i_ipx_routes]; - p_effective_metric = &effective_metrics[i_ipx_routes]; - - is_shadowed = i_ipx_routes > 0 - && vtable->route_dest_cmp (cur_ipx_route, ipx_routes->index->entries[i_ipx_routes - 1]) == 0; - - if (!is_shadowed) { - /* the route is not shadowed, the effective metric is just as specified. */ - *p_effective_metric = cur_ipx_route->rx.metric; - goto next; - } - if (!VTABLE_IS_DEVICE_ROUTE (vtable, cur_ipx_route)) { - /* The route is not a device route. We want to add redundant device routes, because - * we might need the direct routes to the gateway. For non-direct routes, there is not much - * reason to do the metric increment. */ - *p_effective_metric = -1; - goto next; - } - - /* The current route might be shadowed by several other routes. Find the one with the highest metric, - * i.e. the one with an effecive metric set and in the index before the current index. */ - i_ipx_routes_before = i_ipx_routes; - while (TRUE) { - nm_assert (i_ipx_routes_before > 0); - - i_ipx_routes_before--; - - p_effective_metric_before = &effective_metrics[i_ipx_routes_before]; - - if (*p_effective_metric_before == -1) { - /* this route is also shadowed, continue search. */ - continue; - } - - if (*p_effective_metric_before < cur_ipx_route->rx.metric) { - /* the previous route has a lower metric. There is no conflict, - * just use the original metric. */ - *p_effective_metric = cur_ipx_route->rx.metric; - } else if (*p_effective_metric_before == G_MAXUINT32) { - /* we cannot bump the metric. Don't configure this route. */ - *p_effective_metric = -1; - } else { - /* bump the metric by one. */ - *p_effective_metric = *p_effective_metric_before + 1; - } - break; - } -next: - _LOGt (vtable->vt->addr_family, "%3d: new metric #%u - %s (%lld)", - ifindex, i_ipx_routes, - vtable->vt->route_to_string (cur_ipx_route, NULL, 0), - (long long) *p_effective_metric); - } - } - - if (full_sync) { - /*************************************************************************** - * Delete all routes in platform, that no longer exist in @ipx_routes - * - * Different from the delete action above, we delete every unknown route on - * the interface. - ***************************************************************************/ - - /* 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); - if (cur_ipx_route) - p_effective_metric = &effective_metrics[i_ipx_routes]; - while (cur_plat_route) { - int route_dest_cmp_result = 0; - - nm_assert (cur_plat_route->rx.ifindex == ifindex); - - _LOGt (vtable->vt->addr_family, "%3d: platform rt #%u - %s", ifindex, i_plat_routes, vtable->vt->route_to_string (cur_plat_route, NULL, 0)); - - /* skip over @cur_ipx_route that are ordered before @cur_plat_route */ - while ( cur_ipx_route - && ((route_dest_cmp_result = vtable->route_dest_cmp (cur_ipx_route, cur_plat_route)) <= 0)) { - if ( route_dest_cmp_result == 0 - && *p_effective_metric != -1 - && *p_effective_metric >= cur_plat_route->rx.metric) { - break; - } - cur_ipx_route = _get_next_ipx_route (ipx_routes->index, FALSE, &i_ipx_routes, ifindex); - if (cur_ipx_route) - p_effective_metric = &effective_metrics[i_ipx_routes]; - } - - /* if @cur_ipx_route is not equal to @plat_route, the route must be deleted. */ - if ( !cur_ipx_route - || route_dest_cmp_result != 0 - || *p_effective_metric != cur_plat_route->rx.metric) { - nm_assert (ifindex == cur_plat_route->rx.ifindex); - nm_platform_ip_route_delete (priv->platform, NMP_OBJECT_UP_CAST (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 then what - * we are syncing now. But the current changes make it necessary to add those - * routes. - * - * Only add some routes that might be necessary. We don't delete any routes - * on other ifindexes here. I.e. we don't do a full sync, but only ~add~ routes - * that were shadowed previously, but should be now present with a different - * metric. - **************************************************************************/ - - if (ipx_routes_changed) { - GArray *gateway_routes = NULL; - - /* @effective_metrics_reverse contains the list of assigned metrics from the last - * sync. Walk through it and see what changes there are (and possibly restore a - * shadowed route). - * Thereby also update @effective_metrics_reverse to be up-to-date again. */ - for (i_ipx_routes = 0; i_ipx_routes < ipx_routes->entries->len; i_ipx_routes++) { - guint i_ipx_routes_reverse; - gint64 *p_effective_metric_reversed; - - p_effective_metric = &effective_metrics[i_ipx_routes]; - - i_ipx_routes_reverse = _route_index_reverse_idx (vtable, ipx_routes->index, i_ipx_routes, ipx_routes->entries); - p_effective_metric_reversed = &g_array_index (ipx_routes->effective_metrics_reverse, gint64, i_ipx_routes_reverse); - - if (*p_effective_metric_reversed == *p_effective_metric) { - /* The entry is up to date. No change, continue with the next one. */ - continue; - } - *p_effective_metric_reversed = *p_effective_metric; - - if (*p_effective_metric == -1) { - /* the entry is shadowed. Nothing to do. */ - continue; - } - - cur_ipx_route = ipx_routes->index->entries[i_ipx_routes]; - if (cur_ipx_route->rx.ifindex == ifindex) { - /* @cur_ipx_route is on the current @ifindex. No need to special handling them - * because we are about to do a full sync of the ifindex. */ - continue; - } - - /* the effective metric from previous sync changed. While @cur_ipx_route is not on the - * ifindex we are about to sync, we still must add this route. Possibly it was shadowed - * before, and now we want to restore it. - * - * Note that we don't do a full sync on the other ifindex. Especially, we don't delete - * or add any further routes then this. That means there might be some stale routes - * (with a higher metric!). They will only be removed on the next sync of that other - * ifindex. */ - - if (!VTABLE_IS_DEVICE_ROUTE (vtable, cur_ipx_route)) { - /* the route to restore has a gateway. We can only restore the route - * when we also have a direct route to the gateway. There can be cases - * where the direct route is shadowed too, and we cannot restore the gateway - * route. - * - * Restore first the direct-routes, and gateway-routes afterwards. - * This can avoid some cases where we would fail to add the - * gateway route. */ - if (!gateway_routes) - gateway_routes = g_array_new (FALSE, FALSE, sizeof (guint)); - g_array_append_val (gateway_routes, i_ipx_routes); - } else - vtable->vt->route_add (priv->platform, NMP_NLM_FLAG_REPLACE, - cur_ipx_route, 0, *p_effective_metric); - } - - if (gateway_routes) { - for (i = 0; i < gateway_routes->len; i++) { - i_ipx_routes = g_array_index (gateway_routes, guint, i); - vtable->vt->route_add (priv->platform, NMP_NLM_FLAG_REPLACE, - ipx_routes->index->entries[i_ipx_routes], - 0, effective_metrics[i_ipx_routes]); - } - g_array_unref (gateway_routes); - } - } - - /*************************************************************************** - * 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_dest_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; - } - - p_effective_metric = &effective_metrics[i_ipx_routes]; - - if (*p_effective_metric == -1) { - /* @cur_ipx_route is shadewed by another route. */ - continue; - } - - /* skip over @plat_routes that are ordered before our @cur_ipx_route. */ - while ( cur_plat_route - && (route_dest_cmp_result = vtable->route_dest_cmp (cur_plat_route, cur_ipx_route)) <= 0) { - if ( route_dest_cmp_result == 0 - && cur_plat_route->rx.metric >= *p_effective_metric) - break; - 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_dest_cmp_result != 0 - || !_route_equals_ignoring_ifindex (vtable, cur_plat_route, cur_ipx_route, *p_effective_metric)) { - - if (!vtable->vt->route_add (priv->platform, NMP_NLM_FLAG_REPLACE, - cur_ipx_route, ifindex, *p_effective_metric)) { - if (cur_ipx_route->rx.rt_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, NULL, 0)); - } else { - /* Remember that there was a failure, but for now continue trying - * to sync the remaining routes. */ - success = FALSE; - } - } - } - } - } - - if (vtable->vt->is_ip4 && ipx_routes_changed) - g_signal_emit (self, signals[IP4_ROUTES_CHANGED], 0); - - g_free (known_routes_idx); - g_free (plat_routes_idx); - - return success; -} - -/** - * nm_route_manager_ip4_route_sync: - * @ifindex: Interface index - * @known_routes: List of routes - * @ignore_kernel_routes: if %TRUE, ignore kernel routes. - * @full_sync: whether to do a full sync and delete routes - * that are configured on the interface but not currently - * tracked by route-manager. - * - * 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, gboolean ignore_kernel_routes, gboolean full_sync) -{ - return _vx_route_sync (&vtable_v4, self, ifindex, known_routes, ignore_kernel_routes, full_sync); -} - -/** - * nm_route_manager_ip6_route_sync: - * @ifindex: Interface index - * @known_routes: List of routes - * @ignore_kernel_routes: if %TRUE, ignore kernel routes. - * @full_sync: whether to do a full sync and delete routes - * that are configured on the interface but not currently - * tracked by route-manager. - * - * 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_ip6_route_sync (NMRouteManager *self, int ifindex, const GArray *known_routes, gboolean ignore_kernel_routes, gboolean full_sync) -{ - return _vx_route_sync (&vtable_v6, self, ifindex, known_routes, ignore_kernel_routes, full_sync); -} - -gboolean -nm_route_manager_route_flush (NMRouteManager *self, int ifindex) -{ - bool success = TRUE; - - success &= (bool) nm_route_manager_ip4_route_sync (self, ifindex, NULL, FALSE, TRUE); - success &= (bool) nm_route_manager_ip6_route_sync (self, ifindex, NULL, FALSE, TRUE); - return success; -} - -/** - * nm_route_manager_ip4_routes_shadowed: - * @ifindex: Interface index - * - * Returns: %TRUE if some other link has a route to the same destination - * with a lower metric. - */ -gboolean -nm_route_manager_ip4_routes_shadowed (NMRouteManager *self, int ifindex) -{ - NMRouteManagerPrivate *priv = NM_ROUTE_MANAGER_GET_PRIVATE (self); - RouteIndex *index = priv->ip4_routes.index; - const NMPlatformIP4Route *route; - guint i; - - for (i = 1; i < index->len; i++) { - route = (const NMPlatformIP4Route *) index->entries[i]; - - if (route->ifindex != ifindex) - continue; - if (_v4_route_dest_cmp (route, (const NMPlatformIP4Route *) index->entries[i - 1]) == 0) - return TRUE; - } - - return FALSE; -} - -/*****************************************************************************/ - -static gboolean -_ip4_device_routes_entry_expired (const IP4DeviceRoutePurgeEntry *entry, gint64 now) -{ - return entry->scheduled_at_ns + IP4_DEVICE_ROUTES_WAIT_TIME_NS < now; -} - -static IP4DeviceRoutePurgeEntry * -_ip4_device_routes_purge_entry_create (NMRouteManager *self, const NMPlatformIP4Route *route, gint64 now_ns) -{ - IP4DeviceRoutePurgeEntry *entry; - - entry = g_slice_new (IP4DeviceRoutePurgeEntry); - - entry->self = self; - entry->scheduled_at_ns = now_ns; - entry->idle_id = 0; - entry->obj = nmp_object_new (NMP_OBJECT_TYPE_IP4_ROUTE, (NMPlatformObject *) route); - entry->obj_cached = NULL; - return entry; -} - -static void -_ip4_device_routes_purge_entry_free (IP4DeviceRoutePurgeEntry *entry) -{ - nmp_object_unref (entry->obj); - nmp_object_unref (entry->obj_cached); - nm_clear_g_source (&entry->idle_id); - g_slice_free (IP4DeviceRoutePurgeEntry, entry); -} - -static gboolean -_ip4_device_routes_idle_cb (IP4DeviceRoutePurgeEntry *entry) -{ - NMRouteManager *self; - NMRouteManagerPrivate *priv; - - nm_clear_g_source (&entry->idle_id); - - self = entry->self; - priv = NM_ROUTE_MANAGER_GET_PRIVATE (self); - if (_route_index_find (&vtable_v4, priv->ip4_routes.index, &entry->obj->ipx_route) >= 0) { - /* we have an identical route in our list. Don't delete it. */ - return G_SOURCE_REMOVE; - } - - _LOGt (vtable_v4.vt->addr_family, "device-route: delete %s", nmp_object_to_string (entry->obj_cached, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); - - nm_platform_ip_route_delete (priv->platform, entry->obj_cached); - - g_hash_table_remove (priv->ip4_device_routes.entries, entry->obj); - _ip4_device_routes_cancel (self); - return G_SOURCE_REMOVE; -} - -static void -_ip4_device_routes_ip4_route_changed (NMPlatform *platform, - int obj_type_i, - int ifindex, - const NMPlatformIP4Route *route, - int change_type_i, - NMRouteManager *self) -{ - const NMPlatformSignalChangeType change_type = change_type_i; - NMRouteManagerPrivate *priv; - const NMPObject *obj; - IP4DeviceRoutePurgeEntry *entry; - - if ( route->rt_source != NM_IP_CONFIG_SOURCE_RTPROT_KERNEL - || route->metric != 0) { - /* we don't have an automatically created device route at hand. Bail out early. */ - return; - } - - priv = NM_ROUTE_MANAGER_GET_PRIVATE (self); - - obj = NMP_OBJECT_UP_CAST (route); - - entry = g_hash_table_lookup (priv->ip4_device_routes.entries, obj); - if (!entry) - return; - - if (_ip4_device_routes_entry_expired (entry, nm_utils_get_monotonic_timestamp_ns ())) { - _LOGt (vtable_v4.vt->addr_family, "device-route: cleanup-ch %s", nmp_object_to_string (entry->obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); - g_hash_table_remove (priv->ip4_device_routes.entries, entry->obj); - _ip4_device_routes_cancel (self); - return; - } - - entry->obj_cached = nmp_object_unref (entry->obj_cached); - - if (change_type == NM_PLATFORM_SIGNAL_REMOVED) { - if (nm_clear_g_source (&entry->idle_id)) - _LOGt (vtable_v4.vt->addr_family, "device-route: unschedule %s", nmp_object_to_string (entry->obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); - return; - } - - entry->obj_cached = nmp_object_ref (obj); - if (entry->idle_id == 0) { - _LOGt (vtable_v4.vt->addr_family, "device-route: schedule %s", nmp_object_to_string (entry->obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); - entry->idle_id = g_idle_add ((GSourceFunc) _ip4_device_routes_idle_cb, entry); - } -} - -static gboolean -_ip4_device_routes_cancel (NMRouteManager *self) -{ - NMRouteManagerPrivate *priv = NM_ROUTE_MANAGER_GET_PRIVATE (self); - - if (priv->ip4_device_routes.gc_id) { - if (g_hash_table_size (priv->ip4_device_routes.entries) > 0) - return G_SOURCE_CONTINUE; - _LOGt (vtable_v4.vt->addr_family, "device-route: cancel"); - if (priv->platform) - g_signal_handlers_disconnect_by_func (priv->platform, G_CALLBACK (_ip4_device_routes_ip4_route_changed), self); - nm_clear_g_source (&priv->ip4_device_routes.gc_id); - } - return G_SOURCE_REMOVE; -} - -static gboolean -_ip4_device_routes_gc (NMRouteManager *self) -{ - NMRouteManagerPrivate *priv; - GHashTableIter iter; - IP4DeviceRoutePurgeEntry *entry; - gint64 now = nm_utils_get_monotonic_timestamp_ns (); - - priv = NM_ROUTE_MANAGER_GET_PRIVATE (self); - - g_hash_table_iter_init (&iter, priv->ip4_device_routes.entries); - while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &entry)) { - if (_ip4_device_routes_entry_expired (entry, now)) { - _LOGt (vtable_v4.vt->addr_family, "device-route: cleanup-gc %s", nmp_object_to_string (entry->obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); - g_hash_table_iter_remove (&iter); - } - } - - return _ip4_device_routes_cancel (self); -} - -/** - * nm_route_manager_ip4_route_register_device_route_purge_list: - * - * When adding an IPv4 address, kernel will automatically add a device route with - * metric zero. We don't want that route and want to delete it. However, the route - * by kernel immediately, but some time after. That means during nm_route_manager_ip4_route_sync() - * such a route doesn't exist yet. We must remember that we expect such a route to appear later - * and to remove it. */ -void -nm_route_manager_ip4_route_register_device_route_purge_list (NMRouteManager *self, GArray *device_route_purge_list) -{ - NMRouteManagerPrivate *priv; - guint i; - gint64 now_ns; - - if (!device_route_purge_list || device_route_purge_list->len == 0) - return; - - priv = NM_ROUTE_MANAGER_GET_PRIVATE (self); - - now_ns = nm_utils_get_monotonic_timestamp_ns (); - for (i = 0; i < device_route_purge_list->len; i++) { - IP4DeviceRoutePurgeEntry *entry; - - entry = _ip4_device_routes_purge_entry_create (self, &g_array_index (device_route_purge_list, NMPlatformIP4Route, i), now_ns); - _LOGt (vtable_v4.vt->addr_family, "device-route: watch (%s) %s", - g_hash_table_contains (priv->ip4_device_routes.entries, entry->obj) - ? "update" : "new", - nmp_object_to_string (entry->obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); - g_hash_table_replace (priv->ip4_device_routes.entries, - (NMPObject *) nmp_object_ref (entry->obj), - entry); - } - if (priv->ip4_device_routes.gc_id == 0) { - g_signal_connect (priv->platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, G_CALLBACK (_ip4_device_routes_ip4_route_changed), self); - priv->ip4_device_routes.gc_id = g_timeout_add (IP4_DEVICE_ROUTES_GC_INTERVAL_MSEC, (GSourceFunc) _ip4_device_routes_gc, self); - } -} - -/*****************************************************************************/ - -static const VTableIP vtable_v4 = { - .vt = &nm_platform_vtable_route_v4, - .route_dest_cmp = (int (*) (const NMPlatformIPXRoute *, const NMPlatformIPXRoute *)) _v4_route_dest_cmp, - .route_id_cmp = (int (*) (const NMPlatformIPXRoute *, const NMPlatformIPXRoute *)) _v4_route_id_cmp, -}; - -static const VTableIP vtable_v6 = { - .vt = &nm_platform_vtable_route_v6, - .route_dest_cmp = (int (*) (const NMPlatformIPXRoute *, const NMPlatformIPXRoute *)) _v6_route_dest_cmp, - .route_id_cmp = (int (*) (const NMPlatformIPXRoute *, const NMPlatformIPXRoute *)) _v6_route_id_cmp, -}; - -/*****************************************************************************/ - -static void -set_property (GObject *object, guint prop_id, - const GValue *value, GParamSpec *pspec) -{ - NMRouteManager *self = NM_ROUTE_MANAGER (object); - NMRouteManagerPrivate *priv = NM_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_route_manager_init (NMRouteManager *self) -{ - NMRouteManagerPrivate *priv = NM_ROUTE_MANAGER_GET_PRIVATE (self); - - 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.effective_metrics = g_array_new (FALSE, FALSE, sizeof (gint64)); - priv->ip6_routes.effective_metrics = g_array_new (FALSE, FALSE, sizeof (gint64)); - priv->ip4_routes.effective_metrics_reverse = g_array_new (FALSE, FALSE, sizeof (gint64)); - priv->ip6_routes.effective_metrics_reverse = g_array_new (FALSE, FALSE, sizeof (gint64)); - 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); - priv->ip4_device_routes.entries = g_hash_table_new_full ((GHashFunc) nmp_object_id_hash, - (GEqualFunc) nmp_object_id_equal, - (GDestroyNotify) nmp_object_unref, - (GDestroyNotify) _ip4_device_routes_purge_entry_free); -} - -NMRouteManager * -nm_route_manager_new (gboolean log_with_ptr, NMPlatform *platform) -{ - return g_object_new (NM_TYPE_ROUTE_MANAGER, - NM_ROUTE_MANAGER_LOG_WITH_PTR, log_with_ptr, - NM_ROUTE_MANAGER_PLATFORM, platform, - NULL); -} - -static void -dispose (GObject *object) -{ - NMRouteManager *self = NM_ROUTE_MANAGER (object); - NMRouteManagerPrivate *priv = NM_ROUTE_MANAGER_GET_PRIVATE (self); - - g_hash_table_remove_all (priv->ip4_device_routes.entries); - _ip4_device_routes_cancel (self); - - G_OBJECT_CLASS (nm_route_manager_parent_class)->dispose (object); -} - -static void -finalize (GObject *object) -{ - NMRouteManagerPrivate *priv = NM_ROUTE_MANAGER_GET_PRIVATE ((NMRouteManager *) object); - - g_array_free (priv->ip4_routes.entries, TRUE); - g_array_free (priv->ip6_routes.entries, TRUE); - g_array_free (priv->ip4_routes.effective_metrics, TRUE); - g_array_free (priv->ip6_routes.effective_metrics, TRUE); - g_array_free (priv->ip4_routes.effective_metrics_reverse, TRUE); - g_array_free (priv->ip6_routes.effective_metrics_reverse, TRUE); - g_free (priv->ip4_routes.index); - g_free (priv->ip6_routes.index); - - g_hash_table_unref (priv->ip4_device_routes.entries); - - g_clear_object (&priv->platform); - - G_OBJECT_CLASS (nm_route_manager_parent_class)->finalize (object); -} - -static void -nm_route_manager_class_init (NMRouteManagerClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->set_property = set_property; - object_class->dispose = dispose; - object_class->finalize = finalize; - - obj_properties[PROP_LOG_WITH_PTR] = - g_param_spec_boolean (NM_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_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); - - signals[IP4_ROUTES_CHANGED] = - g_signal_new (NM_ROUTE_MANAGER_IP4_ROUTES_CHANGED, - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - 0, NULL, NULL, NULL, - G_TYPE_NONE, 0); -} diff --git a/src/nm-route-manager.h b/src/nm-route-manager.h deleted file mode 100644 index bdf79a09ab..0000000000 --- a/src/nm-route-manager.h +++ /dev/null @@ -1,49 +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) 2015 Red Hat, Inc. - */ - -#ifndef __NM_ROUTE_MANAGER_H__ -#define __NM_ROUTE_MANAGER_H__ - -#define NM_TYPE_ROUTE_MANAGER (nm_route_manager_get_type ()) -#define NM_ROUTE_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_ROUTE_MANAGER, NMRouteManager)) -#define NM_ROUTE_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_ROUTE_MANAGER, NMRouteManagerClass)) -#define NM_IS_ROUTE_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_ROUTE_MANAGER)) -#define NM_IS_ROUTE_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_ROUTE_MANAGER)) -#define NM_ROUTE_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_ROUTE_MANAGER, NMRouteManagerClass)) - -#define NM_ROUTE_MANAGER_LOG_WITH_PTR "log-with-ptr" -#define NM_ROUTE_MANAGER_PLATFORM "platform" - -#define NM_ROUTE_MANAGER_IP4_ROUTES_CHANGED "ip4-routes-changed" - -typedef struct _NMRouteManagerClass NMRouteManagerClass; - -GType nm_route_manager_get_type (void); - -gboolean nm_route_manager_ip4_route_sync (NMRouteManager *self, int ifindex, const GArray *known_routes, gboolean ignore_kernel_routes, gboolean full_sync); -gboolean nm_route_manager_ip6_route_sync (NMRouteManager *self, int ifindex, const GArray *known_routes, gboolean ignore_kernel_routes, gboolean full_sync); -gboolean nm_route_manager_route_flush (NMRouteManager *self, int ifindex); - -gboolean nm_route_manager_ip4_routes_shadowed (NMRouteManager *self, int ifindex); -void nm_route_manager_ip4_route_register_device_route_purge_list (NMRouteManager *self, GArray *device_route_purge_list); - -NMRouteManager *nm_route_manager_new (gboolean log_with_ptr, NMPlatform *platform); - -#endif /* __NM_ROUTE_MANAGER_H__ */ diff --git a/src/nm-types.h b/src/nm-types.h index 136cc450a8..64d718b1ac 100644 --- a/src/nm-types.h +++ b/src/nm-types.h @@ -50,7 +50,6 @@ typedef struct _NMNetns NMNetns; typedef struct _NMPolicy NMPolicy; typedef struct _NMRfkillManager NMRfkillManager; typedef struct _NMPacrunnerManager NMPacrunnerManager; -typedef struct _NMRouteManager NMRouteManager; typedef struct _NMSessionMonitor NMSessionMonitor; typedef struct _NMSleepMonitor NMSleepMonitor; typedef struct _NMLldpListener NMLldpListener; diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index e906ba3bc6..3b471e2075 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -91,6 +91,9 @@ enum { typedef struct _NMPlatformPrivate { bool use_udev:1; bool log_with_ptr:1; + guint ip4_dev_route_blacklist_check_id; + guint ip4_dev_route_blacklist_gc_timeout_id; + GHashTable *ip4_dev_route_blacklist_hash; NMDedupMultiIndex *multi_idx; NMPCache *cache; } NMPlatformPrivate; @@ -101,6 +104,10 @@ G_DEFINE_TYPE (NMPlatform, nm_platform, G_TYPE_OBJECT) /*****************************************************************************/ +static void _ip4_dev_route_blacklist_schedule (NMPlatform *self); + +/*****************************************************************************/ + gboolean nm_platform_get_use_udev (NMPlatform *self) { @@ -3482,6 +3489,153 @@ nm_platform_ip_address_flush (NMPlatform *self, /*****************************************************************************/ +/** + * nm_platform_ip_route_sync: + * @self: the #NMPlatform instance. + * @addr_family: AF_INET or AF_INET6. + * @ifindex: the @ifindex for which the routes are to be added. + * @routes: (allow-none): a list of routes to configure. Must contain + * NMPObject instances of routes, according to @addr_family. + * @kernel_delete_predicate: (allow-none): if not %NULL, previously + * existing routes already configured will only be deleted if the + * predicate returns TRUE. This allows to preserve/ignore some + * routes. For example by passing @nm_platform_lookup_predicate_routes_skip_rtprot_kernel, + * routes with "proto kernel" will be left untouched. + * @kernel_delete_userdata: user data for @kernel_delete_predicate. + * + * Returns: %TRUE on success. + */ +gboolean +nm_platform_ip_route_sync (NMPlatform *self, + int addr_family, + int ifindex, + GPtrArray *routes, + NMPObjectPredicateFunc kernel_delete_predicate, + gpointer kernel_delete_userdata) +{ + const NMPlatformVTableRoute *vt; + gs_unref_ptrarray GPtrArray *plat_routes = NULL; + gs_unref_hashtable GHashTable *routes_idx = NULL; + const NMPObject *plat_o; + const NMPObject *conf_o; + const NMDedupMultiEntry *plat_entry; + guint i; + int i_type; + + nm_assert (NM_IS_PLATFORM (self)); + nm_assert (NM_IN_SET (addr_family, AF_INET, AF_INET6)); + nm_assert (ifindex > 0); + + vt = addr_family == AF_INET + ? &nm_platform_vtable_route_v4 + : &nm_platform_vtable_route_v6; + + plat_routes = nm_platform_lookup_addrroute_clone (self, + vt->obj_type, + ifindex, + kernel_delete_predicate, + kernel_delete_userdata); + /* first delete routes which are in platform (@plat_routes), but not to configure (@routes/@routes_idx). */ + if (plat_routes) { + + /* create a lookup index. */ + if (routes && routes->len > 0) { + routes_idx = g_hash_table_new ((GHashFunc) nmp_object_id_hash, + (GEqualFunc) nmp_object_id_equal); + for (i = 0; i < routes->len; i++) { + conf_o = routes->pdata[i]; + if (!nm_g_hash_table_insert (routes_idx, (gpointer) conf_o, (gpointer) conf_o)) { + /* we ignore duplicate @routes. */ + } + } + } + + 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; + } + + conf_o = routes_idx ? g_hash_table_lookup (routes_idx, plat_o) : NULL; + if ( !conf_o + || vt->route_cmp (NMP_OBJECT_CAST_IPX_ROUTE (conf_o), + NMP_OBJECT_CAST_IPX_ROUTE (plat_o), + NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) == 0) { + /* the route in platform is identical to the one we want to add. + * Keep it. */ + continue; + } + + if (!nm_platform_ip_route_delete (self, plat_o)) { + /* ignore error... */ + } + } + } + + if (!routes) + return TRUE; + + for (i_type = 0; i_type < 2; i_type++) { + for (i = 0; i < routes->len; i++) { + conf_o = routes->pdata[i]; + +#define VTABLE_IS_DEVICE_ROUTE(vt, o) (vt->is_ip4 \ + ? (NMP_OBJECT_CAST_IP4_ROUTE (o)->gateway == 0) \ + : IN6_IS_ADDR_UNSPECIFIED (&NMP_OBJECT_CAST_IP6_ROUTE (o)->gateway) ) + + if ( (i_type == 0 && !VTABLE_IS_DEVICE_ROUTE (vt, conf_o)) + || (i_type == 1 && VTABLE_IS_DEVICE_ROUTE (vt, conf_o))) { + /* we add routes in two runs over @i_type. + * + * First device routes, then gateway routes. */ + continue; + } + + plat_entry = nm_platform_lookup_entry (self, + NMP_CACHE_ID_TYPE_OBJECT_TYPE, + conf_o); + if (plat_entry) { + /* we alreay have a route with the same ID in the platform cache. + * Skip adding it again. It should identical already, otherwise we would + * have deleted it in the previous step. */ + continue; + } + + if (!nm_platform_ip_route_add (self, + NMP_NLM_FLAG_APPEND, + conf_o)) { + /* ignore error adding route. */ + } + } + } + + return TRUE; +} + +gboolean +nm_platform_ip_route_flush (NMPlatform *self, + int addr_family, + int ifindex) +{ + gboolean success = TRUE; + + _CHECK_SELF (self, klass, FALSE); + + nm_assert (NM_IN_SET (addr_family, AF_UNSPEC, + AF_INET, + AF_INET6)); + + if (NM_IN_SET (addr_family, AF_UNSPEC, AF_INET)) + success &= nm_platform_ip_route_sync (self, AF_INET, ifindex, NULL, NULL, NULL); + if (NM_IN_SET (addr_family, AF_UNSPEC, AF_INET6)) + success &= nm_platform_ip_route_sync (self, AF_INET6, ifindex, NULL, NULL, NULL); + return success; +} + +/*****************************************************************************/ + static guint8 _ip_route_scope_inv_get_normalized (const NMPlatformIP4Route *route) { @@ -3616,6 +3770,274 @@ nm_platform_ip_route_delete (NMPlatform *self, /*****************************************************************************/ +#define IP4_DEV_ROUTE_BLACKLIST_TIMEOUT_MS ((int) 1500) +#define IP4_DEV_ROUTE_BLACKLIST_GC_TIMEOUT_S ((int) (((IP4_DEV_ROUTE_BLACKLIST_TIMEOUT_MS + 999) * 3) / 1000)) + +static gint64 +_ip4_dev_route_blacklist_timeout_ms_get (gint64 timeout_ms) +{ + return timeout_ms >> 1; +} + +static gint64 +_ip4_dev_route_blacklist_timeout_ms_marked (gint64 timeout_ms) +{ + return !!(timeout_ms & ((gint64) 1)); +} + +static gboolean +_ip4_dev_route_blacklist_check_cb (gpointer user_data) +{ + NMPlatform *self = user_data; + NMPlatformPrivate *priv = NM_PLATFORM_GET_PRIVATE (self); + GHashTableIter iter; + const NMPObject *p_obj; + gint64 *p_timeout_ms; + gint64 now_ms; + + priv->ip4_dev_route_blacklist_check_id = 0; + +again: + if (!priv->ip4_dev_route_blacklist_hash) + goto out; + + now_ms = nm_utils_get_monotonic_timestamp_ms (); + + g_hash_table_iter_init (&iter, priv->ip4_dev_route_blacklist_hash); + while (g_hash_table_iter_next (&iter, (gpointer *) &p_obj, (gpointer *) &p_timeout_ms)) { + if (!_ip4_dev_route_blacklist_timeout_ms_marked (*p_timeout_ms)) + continue; + + /* unmark because we checked it. */ + *p_timeout_ms = *p_timeout_ms & ~((gint64) 1); + + if (now_ms > _ip4_dev_route_blacklist_timeout_ms_get (*p_timeout_ms)) + continue; + + if (!nm_platform_lookup_entry (self, + NMP_CACHE_ID_TYPE_OBJECT_TYPE, + p_obj)) + continue; + + _LOGT ("ip4-dev-route: delete %s", + nmp_object_to_string (p_obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + nm_platform_ip_route_delete (self, p_obj); + goto again; + } + +out: + return G_SOURCE_REMOVE; +} + +static void +_ip4_dev_route_blacklist_check_schedule (NMPlatform *self) +{ + NMPlatformPrivate *priv = NM_PLATFORM_GET_PRIVATE (self); + + if (!priv->ip4_dev_route_blacklist_check_id) { + priv->ip4_dev_route_blacklist_check_id = g_idle_add_full (G_PRIORITY_HIGH, + _ip4_dev_route_blacklist_check_cb, + self, + NULL); + } +} + +static void +_ip4_dev_route_blacklist_notify_route (NMPlatform *self, + const NMPObject *obj) +{ + NMPlatformPrivate *priv; + const NMPObject *p_obj; + gint64 *p_timeout_ms; + gint64 now_ms; + + nm_assert (NM_IS_PLATFORM (self)); + nm_assert (NMP_OBJECT_GET_TYPE (obj) == NMP_OBJECT_TYPE_IP4_ROUTE); + + priv = NM_PLATFORM_GET_PRIVATE (self); + + nm_assert (priv->ip4_dev_route_blacklist_gc_timeout_id); + + if (!g_hash_table_lookup_extended (priv->ip4_dev_route_blacklist_hash, + obj, + (gpointer *) &p_obj, + (gpointer *) &p_timeout_ms)) + return; + + now_ms = nm_utils_get_monotonic_timestamp_ms (); + if (now_ms > _ip4_dev_route_blacklist_timeout_ms_get (*p_timeout_ms)) { + /* already expired. Wait for gc. */ + return; + } + + if (_ip4_dev_route_blacklist_timeout_ms_marked (*p_timeout_ms)) { + nm_assert (priv->ip4_dev_route_blacklist_check_id); + return; + } + + /* We cannot delete it right away because we are in the process of receiving netlink messages. + * It may be possible to do so, but complicated and error prone. + * + * Instead, we mark the entry and schedule an idle action (with high priority). */ + *p_timeout_ms = (*p_timeout_ms) | ((gint64) 1); + _ip4_dev_route_blacklist_check_schedule (self); +} + +static gboolean +_ip4_dev_route_blacklist_gc_timeout_handle (gpointer user_data) +{ + NMPlatform *self = user_data; + NMPlatformPrivate *priv = NM_PLATFORM_GET_PRIVATE (self); + GHashTableIter iter; + const NMPObject *p_obj; + gint64 *p_timeout_ms; + gint64 now_ms; + + nm_assert (priv->ip4_dev_route_blacklist_gc_timeout_id); + + now_ms = nm_utils_get_monotonic_timestamp_ms (); + + g_hash_table_iter_init (&iter, priv->ip4_dev_route_blacklist_hash); + while (g_hash_table_iter_next (&iter, (gpointer *) &p_obj, (gpointer *) &p_timeout_ms)) { + if (now_ms > _ip4_dev_route_blacklist_timeout_ms_get (*p_timeout_ms)) { + _LOGT ("ip4-dev-route: cleanup %s", + nmp_object_to_string (p_obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + g_hash_table_iter_remove (&iter); + } + } + + _ip4_dev_route_blacklist_schedule (self); + return G_SOURCE_CONTINUE; +} + +static void +_ip4_dev_route_blacklist_schedule (NMPlatform *self) +{ + NMPlatformPrivate *priv = NM_PLATFORM_GET_PRIVATE (self); + + if ( !priv->ip4_dev_route_blacklist_hash + || g_hash_table_size (priv->ip4_dev_route_blacklist_hash) == 0) { + g_clear_pointer (&priv->ip4_dev_route_blacklist_hash, g_hash_table_unref); + nm_clear_g_source (&priv->ip4_dev_route_blacklist_gc_timeout_id); + } else { + if (!priv->ip4_dev_route_blacklist_gc_timeout_id) { + /* this timeout is only to garbage collect the expired entries from priv->ip4_dev_route_blacklist_hash. + * It can run infrequently, and it doesn't hurt if expired entries linger around a bit + * longer then necessary. */ + priv->ip4_dev_route_blacklist_gc_timeout_id = g_timeout_add_seconds (IP4_DEV_ROUTE_BLACKLIST_GC_TIMEOUT_S, + _ip4_dev_route_blacklist_gc_timeout_handle, + self); + } + } +} + +/** + * nm_platform_ip4_dev_route_blacklist_set: + * @self: + * @ifindex: + * @ip4_dev_route_blacklist: + * + * When adding an IP address, kernel automatically adds a device route. + * This can be suppressed via the IFA_F_NOPREFIXROUTE address flag. For IPv6 + * addresses, we require kernel support for IFA_F_NOPREFIXROUTE and always + * add the device route manually. + * + * For IPv4, this flag is rather new and we don't rely on it yet. We want to use + * it (but currently still don't). So, for IPv4, kernel possibly adds a device + * route, however it has a wrong metric of zero. We add our own device route (with + * proper metric), but need to delete the route that kernel adds. + * + * The problem is, that kernel does not immidiately add the route, when adding + * the address. It only shows up some time later. So, we register here a list + * of blacklisted routes, and when they show up within a time out, we assume it's + * the kernel generated one, and we delete it. + * + * Eventually, we want to get rid of this and use IFA_F_NOPREFIXROUTE for IPv4 + * routes as well. + */ +void +nm_platform_ip4_dev_route_blacklist_set (NMPlatform *self, + int ifindex, + GPtrArray *ip4_dev_route_blacklist) +{ + NMPlatformPrivate *priv; + GHashTableIter iter; + const NMPObject *p_obj; + guint i; + gint64 timeout_ms; + gint64 timeout_ms_val; + gint64 *p_timeout_ms; + gboolean needs_check = FALSE; + + nm_assert (NM_IS_PLATFORM (self)); + nm_assert (ifindex > 0); + + priv = NM_PLATFORM_GET_PRIVATE (self); + + /* first, expire all for current ifindex... */ + if (priv->ip4_dev_route_blacklist_hash) { + g_hash_table_iter_init (&iter, priv->ip4_dev_route_blacklist_hash); + while (g_hash_table_iter_next (&iter, (gpointer *) &p_obj, (gpointer *) &p_timeout_ms)) { + if (NMP_OBJECT_CAST_IP4_ROUTE (p_obj)->ifindex == ifindex) { + /* we could g_hash_table_iter_remove(&iter) the current entry. + * Instead, just expire it and let _ip4_dev_route_blacklist_gc_timeout_handle() + * handle it. + * + * The assumption is, that ip4_dev_route_blacklist contains the very same entry + * again, with a new timeout. So, we can un-expire it below. */ + *p_timeout_ms = 0; + } + } + } + + if ( ip4_dev_route_blacklist + && ip4_dev_route_blacklist->len > 0) { + + if (!priv->ip4_dev_route_blacklist_hash) { + priv->ip4_dev_route_blacklist_hash = g_hash_table_new_full ((GHashFunc) nmp_object_id_hash, + (GEqualFunc) nmp_object_id_equal, + (GDestroyNotify) nmp_object_unref, + nm_g_slice_free_fcn_gint64); + } + + timeout_ms = nm_utils_get_monotonic_timestamp_ms () + IP4_DEV_ROUTE_BLACKLIST_TIMEOUT_MS; + timeout_ms_val = (timeout_ms << 1) | ((gint64) 1); + for (i = 0; i < ip4_dev_route_blacklist->len; i++) { + const NMPObject *o; + + needs_check = TRUE; + o = ip4_dev_route_blacklist->pdata[i]; + if (g_hash_table_lookup_extended (priv->ip4_dev_route_blacklist_hash, + o, + (gpointer *) &p_obj, + (gpointer *) &p_timeout_ms)) { + if (nmp_object_equal (p_obj, o)) { + /* un-expire and reuse the entry. */ + _LOGT ("ip4-dev-route: register %s (update)", + nmp_object_to_string (p_obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + *p_timeout_ms = timeout_ms_val; + continue; + } + } + + _LOGT ("ip4-dev-route: register %s", + nmp_object_to_string (o, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + p_timeout_ms = g_slice_new (gint64); + *p_timeout_ms = timeout_ms_val; + g_hash_table_replace (priv->ip4_dev_route_blacklist_hash, + (gpointer) nmp_object_ref (o), + p_timeout_ms); + } + } + + _ip4_dev_route_blacklist_schedule (self); + + if (needs_check) + _ip4_dev_route_blacklist_check_schedule (self); +} + +/*****************************************************************************/ + const char * nm_platform_vlan_qos_mapping_to_string (const char *name, const NMVlanQosMapping *map, @@ -5235,6 +5657,11 @@ nm_platform_cache_update_emit_signal (NMPlatform *self, klass = NMP_OBJECT_GET_CLASS (o); + if ( klass->obj_type == NMP_OBJECT_TYPE_IP4_ROUTE + && NM_PLATFORM_GET_PRIVATE (self)->ip4_dev_route_blacklist_gc_timeout_id + && NM_IN_SET (cache_op, NMP_CACHE_OPS_ADDED, NMP_CACHE_OPS_UPDATED)) + _ip4_dev_route_blacklist_notify_route (self, o); + _LOGt ("emit signal %s %s: %s", klass->signal_type, nm_platform_signal_change_type_to_string ((NMPlatformSignalChangeType) cache_op), @@ -5284,40 +5711,6 @@ nm_platform_netns_push (NMPlatform *self, NMPNetns **netns) /*****************************************************************************/ -static gboolean -_vtr_v4_route_add (NMPlatform *self, - NMPNlmFlags flags, - const NMPlatformIPXRoute *route, - int ifindex, - gint64 metric) -{ - NMPlatformIP4Route rt = route->r4; - - if (ifindex > 0) - rt.ifindex = ifindex; - if (metric >= 0) - rt.metric = metric; - - return nm_platform_ip4_route_add (self, flags, &rt); -} - -static gboolean -_vtr_v6_route_add (NMPlatform *self, - NMPNlmFlags flags, - const NMPlatformIPXRoute *route, - int ifindex, - gint64 metric) -{ - NMPlatformIP6Route rt = route->r6; - - if (ifindex > 0) - rt.ifindex = ifindex; - if (metric >= 0) - rt.metric = metric; - - return nm_platform_ip6_route_add (self, flags, &rt); -} - static guint32 _vtr_v4_metric_normalize (guint32 metric) { @@ -5333,7 +5726,6 @@ const NMPlatformVTableRoute nm_platform_vtable_route_v4 = { .sizeof_route = sizeof (NMPlatformIP4Route), .route_cmp = (int (*) (const NMPlatformIPXRoute *a, const NMPlatformIPXRoute *b, NMPlatformIPRouteCmpType cmp_type)) nm_platform_ip4_route_cmp, .route_to_string = (const char *(*) (const NMPlatformIPXRoute *route, char *buf, gsize len)) nm_platform_ip4_route_to_string, - .route_add = _vtr_v4_route_add, .metric_normalize = _vtr_v4_metric_normalize, }; @@ -5344,7 +5736,6 @@ const NMPlatformVTableRoute nm_platform_vtable_route_v6 = { .sizeof_route = sizeof (NMPlatformIP6Route), .route_cmp = (int (*) (const NMPlatformIPXRoute *a, const NMPlatformIPXRoute *b, NMPlatformIPRouteCmpType cmp_type)) nm_platform_ip6_route_cmp, .route_to_string = (const char *(*) (const NMPlatformIPXRoute *route, char *buf, gsize len)) nm_platform_ip6_route_to_string, - .route_add = _vtr_v6_route_add, .metric_normalize = nm_utils_ip6_route_metric_normalize, }; @@ -5416,6 +5807,9 @@ finalize (GObject *object) NMPlatform *self = NM_PLATFORM (object); NMPlatformPrivate *priv = NM_PLATFORM_GET_PRIVATE (self); + nm_clear_g_source (&priv->ip4_dev_route_blacklist_check_id); + nm_clear_g_source (&priv->ip4_dev_route_blacklist_gc_timeout_id); + g_clear_pointer (&priv->ip4_dev_route_blacklist_hash, g_hash_table_unref); g_clear_object (&self->_netns); nm_dedup_multi_index_unref (priv->multi_idx); nmp_cache_free (priv->cache); diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index 315fcd5c5e..112d894fed 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -63,6 +63,8 @@ typedef gboolean (*NMPObjectPredicateFunc) (const NMPObject *obj, #define IFA_F_NOPREFIXROUTE 0x200 #endif +#define NM_RT_SCOPE_LINK 253 /* RT_SCOPE_LINK */ + /* Define of the IN6_ADDR_GEN_MODE_* values to workaround old kernel headers * that don't define it. */ #define NM_IN6_ADDR_GEN_MODE_UNKNOWN 255 /* no corresponding value. */ @@ -521,11 +523,6 @@ typedef struct { gsize sizeof_route; int (*route_cmp) (const NMPlatformIPXRoute *a, const NMPlatformIPXRoute *b, NMPlatformIPRouteCmpType cmp_type); const char *(*route_to_string) (const NMPlatformIPXRoute *route, char *buf, gsize len); - gboolean (*route_add) (NMPlatform *self, - NMPNlmFlags flags, - const NMPlatformIPXRoute *route, - int ifindex, - gint64 metric); guint32 (*metric_normalize) (guint32 metric); } NMPlatformVTableRoute; @@ -1128,6 +1125,16 @@ gboolean nm_platform_ip6_route_add (NMPlatform *self, NMPNlmFlags flags, const N gboolean nm_platform_ip_route_delete (NMPlatform *self, const NMPObject *route); +gboolean nm_platform_ip_route_sync (NMPlatform *self, + int addr_family, + int ifindex, + GPtrArray *routes, + NMPObjectPredicateFunc kernel_delete_predicate, + gpointer kernel_delete_userdata); +gboolean nm_platform_ip_route_flush (NMPlatform *self, + int addr_family, + int ifindex); + const char *nm_platform_link_to_string (const NMPlatformLink *link, char *buf, gsize len); const char *nm_platform_lnk_gre_to_string (const NMPlatformLnkGre *lnk, char *buf, gsize len); const char *nm_platform_lnk_infiniband_to_string (const NMPlatformLnkInfiniband *lnk, char *buf, gsize len); @@ -1218,6 +1225,10 @@ gboolean nm_platform_ethtool_set_wake_on_lan (NMPlatform *self, int ifindex, NMS gboolean nm_platform_ethtool_set_link_settings (NMPlatform *self, int ifindex, gboolean autoneg, guint32 speed, NMPlatformLinkDuplexType duplex); gboolean nm_platform_ethtool_get_link_settings (NMPlatform *self, int ifindex, gboolean *out_autoneg, guint32 *out_speed, NMPlatformLinkDuplexType *out_duplex); +void nm_platform_ip4_dev_route_blacklist_set (NMPlatform *self, + int ifindex, + GPtrArray *ip4_dev_route_blacklist); + struct _NMDedupMultiIndex *nm_platform_get_multi_idx (NMPlatform *self); #endif /* __NETWORKMANAGER_PLATFORM_H__ */ diff --git a/src/tests/test-route-manager.c b/src/tests/test-route-manager.c deleted file mode 100644 index 2778cfcc82..0000000000 --- a/src/tests/test-route-manager.c +++ /dev/null @@ -1,974 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* - * 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, 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) 2015 Red Hat, Inc. - * - */ - -#include "nm-default.h" - -#include <arpa/inet.h> -#include <linux/rtnetlink.h> - -#include "platform/nm-platform.h" -#include "platform/nm-platform-utils.h" -#include "nm-route-manager.h" - -#include "platform/tests/test-common.h" - -typedef struct { - int ifindex0, ifindex1; -} test_fixture; - -NMRouteManager *route_manager_get (void); - -NM_DEFINE_SINGLETON_GETTER (NMRouteManager, route_manager_get, NM_TYPE_ROUTE_MANAGER); - -/*****************************************************************************/ - -static void -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 = { 0 }; - - route.ifindex = ifindex; - route.mss = 0; - - route.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER); - inet_pton (AF_INET, "6.6.6.0", &route.network); - route.plen = 24; - route.gateway = INADDR_ANY; - route.metric = 20; - route.mss = mss_of_first_route; - g_array_append_val (routes, route); - - route.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (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 = metric_of_second_route; - route.mss = 0; - g_array_append_val (routes, route); - - nm_route_manager_ip4_route_sync (route_manager_get (), ifindex, routes, TRUE, TRUE); - g_array_free (routes, TRUE); -} - -static void -setup_dev1_ip4 (int ifindex) -{ - GArray *routes = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP4Route)); - NMPlatformIP4Route route = { 0 }; - - route.ifindex = ifindex; - route.mss = 0; - - /* Add some route outside of route manager. The route manager - * should get rid of it upon sync. */ - nmtstp_ip4_route_add (NM_PLATFORM_GET, - route.ifindex, - NM_IP_CONFIG_SOURCE_USER, - nmtst_inet4_from_string ("9.0.0.0"), - 8, - INADDR_ANY, - 0, - 10, - route.mss); - - route.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER); - inet_pton (AF_INET, "6.6.6.0", &route.network); - route.plen = 24; - route.gateway = INADDR_ANY; - route.metric = 20; - g_array_append_val (routes, route); - - route.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER); - inet_pton (AF_INET, "7.0.0.0", &route.network); - route.plen = 8; - route.gateway = INADDR_ANY; - route.metric = 22; - g_array_append_val (routes, route); - - route.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER); - inet_pton (AF_INET, "8.0.0.0", &route.network); - route.plen = 8; - inet_pton (AF_INET, "6.6.6.2", &route.gateway); - route.metric = 22; - g_array_append_val (routes, route); - - nm_route_manager_ip4_route_sync (route_manager_get (), ifindex, routes, TRUE, TRUE); - g_array_free (routes, TRUE); -} - -static void -update_dev0_ip4 (int ifindex) -{ - GArray *routes = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP4Route)); - NMPlatformIP4Route route = { 0 }; - - route.ifindex = ifindex; - route.mss = 0; - - route.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER); - inet_pton (AF_INET, "6.6.6.0", &route.network); - route.plen = 24; - route.gateway = INADDR_ANY; - route.metric = 20; - g_array_append_val (routes, route); - - route.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER); - inet_pton (AF_INET, "7.0.0.0", &route.network); - route.plen = 8; - route.gateway = INADDR_ANY; - route.metric = 21; - g_array_append_val (routes, route); - - nm_route_manager_ip4_route_sync (route_manager_get (), ifindex, routes, TRUE, TRUE); - g_array_free (routes, TRUE); -} - - -static GArray * -ip_routes (test_fixture *fixture, NMPObjectType obj_type) -{ - const NMPClass *klass; - GArray *routes; - const NMDedupMultiHeadEntry *pl_head_entry; - NMDedupMultiIter iter; - const NMPObject *plobj = NULL; - guint i; - - g_assert (NM_IN_SET (obj_type, NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)); - - klass = nmp_class_from_type (obj_type); - - routes = g_array_new (FALSE, FALSE, klass->sizeof_public); - - for (i = 0; i < 2; i++) { - int ifindex; - - if (i == 0) - ifindex = fixture->ifindex0; - else - ifindex = fixture->ifindex1; - - pl_head_entry = nm_platform_lookup_addrroute (NM_PLATFORM_GET, - obj_type, - ifindex); - nmp_cache_iter_for_each (&iter, pl_head_entry, &plobj) { - const NMPlatformIPRoute *r = NMP_OBJECT_CAST_IP_ROUTE (plobj); - - if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (r)) - continue; - if (r->rt_source == NM_IP_CONFIG_SOURCE_RTPROT_KERNEL) - continue; - g_assert (r->ifindex == ifindex); - g_assert (nmp_object_is_visible (plobj)); - g_array_append_vals (routes, r, 1); - } - } - - return routes; -} - -static void -test_ip4 (test_fixture *fixture, gconstpointer user_data) -{ - GArray *routes; - - NMPlatformIP4Route state1[] = { - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = nmtst_inet4_from_string ("6.6.6.0"), - .plen = 24, - .ifindex = fixture->ifindex0, - .gateway = INADDR_ANY, - .metric = 20, - .mss = 1000, - .scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK), - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = nmtst_inet4_from_string ("7.0.0.0"), - .plen = 8, - .ifindex = fixture->ifindex0, - .gateway = nmtst_inet4_from_string ("6.6.6.1"), - .metric = 21021, - .mss = 0, - .scope_inv = nm_platform_route_scope_inv (RT_SCOPE_UNIVERSE), - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = nmtst_inet4_from_string ("7.0.0.0"), - .plen = 8, - .ifindex = fixture->ifindex1, - .gateway = INADDR_ANY, - .metric = 22, - .mss = 0, - .scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK), - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = nmtst_inet4_from_string ("6.6.6.0"), - .plen = 24, - .ifindex = fixture->ifindex1, - .gateway = INADDR_ANY, - .metric = 21, - .mss = 0, - .scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK), - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (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, - .scope_inv = nm_platform_route_scope_inv (RT_SCOPE_UNIVERSE), - }, - }; - - NMPlatformIP4Route state2[] = { - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = nmtst_inet4_from_string ("6.6.6.0"), - .plen = 24, - .ifindex = fixture->ifindex0, - .gateway = INADDR_ANY, - .metric = 20, - .mss = 0, - .scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK), - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = nmtst_inet4_from_string ("7.0.0.0"), - .plen = 8, - .ifindex = fixture->ifindex0, - .gateway = INADDR_ANY, - .metric = 21, - .mss = 0, - .scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK), - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = nmtst_inet4_from_string ("7.0.0.0"), - .plen = 8, - .ifindex = fixture->ifindex1, - .gateway = INADDR_ANY, - .metric = 22, - .mss = 0, - .scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK), - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = nmtst_inet4_from_string ("6.6.6.0"), - .plen = 24, - .ifindex = fixture->ifindex1, - .gateway = INADDR_ANY, - .metric = 21, - .mss = 0, - .scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK), - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (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, - .scope_inv = nm_platform_route_scope_inv (RT_SCOPE_UNIVERSE), - }, - }; - - NMPlatformIP4Route state3[] = { - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = nmtst_inet4_from_string ("7.0.0.0"), - .plen = 8, - .ifindex = fixture->ifindex1, - .gateway = INADDR_ANY, - .metric = 22, - .mss = 0, - .scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK), - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = nmtst_inet4_from_string ("6.6.6.0"), - .plen = 24, - .ifindex = fixture->ifindex1, - .gateway = INADDR_ANY, - .metric = 20, - .mss = 0, - .scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK), - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (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, - .scope_inv = nm_platform_route_scope_inv (RT_SCOPE_UNIVERSE), - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = nmtst_inet4_from_string ("6.6.6.0"), - .plen = 24, - .ifindex = fixture->ifindex1, - .gateway = INADDR_ANY, - /* this is a ghost entry because we synced ifindex0 and restore the route - * with metric 20 (above). But we don't remove the metric 21. */ - .metric = 21, - .mss = 0, - .scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK), - }, - }; - - setup_dev0_ip4 (fixture->ifindex0, 1000, 21021); - setup_dev1_ip4 (fixture->ifindex1); - g_test_assert_expected_messages (); - - /* - 6.6.6.0/24 on dev0 won over 6.6.6.0/24 on dev1 - * - 6.6.6.0/24 on dev1 has metric bumped. - * - 7.0.0.0/8 route, metric 21021 added - * - 7.0.0.0/8 route, metric 22 added - * - 8.0.0.0/8 could be added. */ - routes = ip_routes (fixture, NMP_OBJECT_TYPE_IP4_ROUTE); - g_assert_cmpint (routes->len, ==, G_N_ELEMENTS (state1)); - nmtst_platform_ip4_routes_equal ((NMPlatformIP4Route *) routes->data, state1, routes->len, TRUE); - g_array_free (routes, TRUE); - - setup_dev1_ip4 (fixture->ifindex1); - g_test_assert_expected_messages (); - - setup_dev0_ip4 (fixture->ifindex0, 0, 21); - - /* Ensure nothing changed. */ - routes = ip_routes (fixture, NMP_OBJECT_TYPE_IP4_ROUTE); - g_assert_cmpint (routes->len, ==, G_N_ELEMENTS (state1)); - 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); - - update_dev0_ip4 (fixture->ifindex0); - - /* minor changes in the routes. Quite similar to state1. */ - routes = ip_routes (fixture, NMP_OBJECT_TYPE_IP4_ROUTE); - g_assert_cmpint (routes->len, ==, G_N_ELEMENTS (state2)); - nmtst_platform_ip4_routes_equal ((NMPlatformIP4Route *) routes->data, state2, routes->len, TRUE); - g_array_free (routes, TRUE); - - nm_route_manager_route_flush (route_manager_get (), fixture->ifindex0); - - /* 6.6.6.0/24 is now on dev1 - * 6.6.6.0/24 is also still on dev1 with bumped metric 21. - * 7.0.0.0/8 gone from dev0, still present on dev1 - * 8.0.0.0/8 is present on dev1 - * No dev0 routes left. */ - routes = ip_routes (fixture, NMP_OBJECT_TYPE_IP4_ROUTE); - g_assert_cmpint (routes->len, ==, G_N_ELEMENTS (state3)); - nmtst_platform_ip4_routes_equal ((NMPlatformIP4Route *) routes->data, state3, routes->len, TRUE); - g_array_free (routes, TRUE); - - nm_route_manager_route_flush (route_manager_get (), fixture->ifindex1); - - /* No routes left. */ - routes = ip_routes (fixture, NMP_OBJECT_TYPE_IP4_ROUTE); - g_assert_cmpint (routes->len, ==, 0); - g_array_free (routes, TRUE); -} - -static void -setup_dev0_ip6 (int ifindex) -{ - GArray *routes = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP6Route)); - NMPlatformIP6Route *route; - - /* Add an address so that a route to the gateway below gets added. */ - nm_platform_ip6_address_add (NM_PLATFORM_GET, - ifindex, - *nmtst_inet6_from_string ("2001:db8:8086::666"), - 64, - in6addr_any, - 3600, - 3600, - 0); - - route = nmtst_platform_ip6_route_full ("2001:db8:8086::", - 48, - NULL, - ifindex, - NM_IP_CONFIG_SOURCE_USER, - 20, - 0); - g_array_append_val (routes, *route); - - route = nmtst_platform_ip6_route_full ("2001:db8:1337::", - 48, - NULL, - ifindex, - NM_IP_CONFIG_SOURCE_USER, - 0, - 0); - g_array_append_val (routes, *route); - - route = nmtst_platform_ip6_route_full ("2001:db8:abad:c0de::", - 64, - "2001:db8:8086::1", - ifindex, - NM_IP_CONFIG_SOURCE_USER, - 21, - 0); - g_array_append_val (routes, *route); - - nm_route_manager_ip6_route_sync (route_manager_get (), ifindex, routes, TRUE, TRUE); - g_array_free (routes, TRUE); -} - -static void -setup_dev1_ip6 (int ifindex) -{ - GArray *routes = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP6Route)); - NMPlatformIP6Route *route; - - /* Add some route outside of route manager. The route manager - * should get rid of it upon sync. */ - nmtstp_ip6_route_add (NM_PLATFORM_GET, - ifindex, - NM_IP_CONFIG_SOURCE_USER, - *nmtst_inet6_from_string ("2001:db8:8088::"), - 48, - in6addr_any, - in6addr_any, - 10, - 0); - - route = nmtst_platform_ip6_route_full ("2001:db8:8086::", - 48, - NULL, - ifindex, - NM_IP_CONFIG_SOURCE_USER, - 20, - 0); - g_array_append_val (routes, *route); - - route = nmtst_platform_ip6_route_full ("2001:db8:1337::", - 48, - NULL, - ifindex, - NM_IP_CONFIG_SOURCE_USER, - 1024, - 0); - g_array_append_val (routes, *route); - - route = nmtst_platform_ip6_route_full ("2001:db8:d34d::", - 64, - "2001:db8:8086::2", - ifindex, - NM_IP_CONFIG_SOURCE_USER, - 20, - 0); - g_array_append_val (routes, *route); - - route = nmtst_platform_ip6_route_full ("2001:db8:abad:c0de::", - 64, - NULL, - ifindex, - NM_IP_CONFIG_SOURCE_USER, - 22, - 0); - g_array_append_val (routes, *route); - - nm_route_manager_ip6_route_sync (route_manager_get (), ifindex, routes, TRUE, TRUE); - g_array_free (routes, TRUE); -} - -static void -update_dev0_ip6 (int ifindex) -{ - GArray *routes = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP6Route)); - NMPlatformIP6Route *route; - - /* Add an address so that a route to the gateway below gets added. */ - nm_platform_ip6_address_add (NM_PLATFORM_GET, - ifindex, - *nmtst_inet6_from_string ("2001:db8:8086::2"), - 64, - in6addr_any, - 3600, - 3600, - 0); - - route = nmtst_platform_ip6_route_full ("2001:db8:8086::", - 48, - NULL, - ifindex, - NM_IP_CONFIG_SOURCE_USER, - 20, - 0); - g_array_append_val (routes, *route); - - route = nmtst_platform_ip6_route_full ("2001:db8:1337::", - 48, - NULL, - ifindex, - NM_IP_CONFIG_SOURCE_USER, - 0, - 0); - g_array_append_val (routes, *route); - - route = nmtst_platform_ip6_route_full ("2001:db8:abad:c0de::", - 64, - NULL, - ifindex, - NM_IP_CONFIG_SOURCE_USER, - 21, - 0); - g_array_append_val (routes, *route); - - nm_route_manager_ip6_route_sync (route_manager_get (), ifindex, routes, TRUE, TRUE); - g_array_free (routes, TRUE); -} - -static void -test_ip6 (test_fixture *fixture, gconstpointer user_data) -{ - GArray *routes; - int i; - - NMPlatformIP6Route state1[] = { - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = *nmtst_inet6_from_string ("2001:db8:8086::"), - .plen = 48, - .ifindex = fixture->ifindex0, - .gateway = in6addr_any, - .metric = 20, - .mss = 0, - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = *nmtst_inet6_from_string ("2001:db8:1337::"), - .plen = 48, - .ifindex = fixture->ifindex0, - .gateway = in6addr_any, - .metric = 1024, - .mss = 0, - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = *nmtst_inet6_from_string ("2001:db8:abad:c0de::"), - .plen = 64, - .ifindex = fixture->ifindex0, - .gateway = *nmtst_inet6_from_string ("2001:db8:8086::1"), - .metric = 21, - .mss = 0, - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = *nmtst_inet6_from_string ("2001:db8:abad:c0de::"), - .plen = 64, - .ifindex = fixture->ifindex1, - .gateway = in6addr_any, - .metric = 22, - .mss = 0, - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = *nmtst_inet6_from_string ("2001:db8:1337::"), - .plen = 48, - .ifindex = fixture->ifindex1, - .gateway = in6addr_any, - .metric = 1025, - .mss = 0, - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = *nmtst_inet6_from_string ("2001:db8:8086::"), - .plen = 48, - .ifindex = fixture->ifindex1, - .gateway = in6addr_any, - .metric = 21, - .mss = 0, - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (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, - }, - }; - - NMPlatformIP6Route state2[] = { - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = *nmtst_inet6_from_string ("2001:db8:8086::"), - .plen = 48, - .ifindex = fixture->ifindex0, - .gateway = in6addr_any, - .metric = 20, - .mss = 0, - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = *nmtst_inet6_from_string ("2001:db8:1337::"), - .plen = 48, - .ifindex = fixture->ifindex0, - .gateway = in6addr_any, - .metric = 1024, - .mss = 0, - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = *nmtst_inet6_from_string ("2001:db8:abad:c0de::"), - .plen = 64, - .ifindex = fixture->ifindex0, - .gateway = in6addr_any, - .metric = 21, - .mss = 0, - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = *nmtst_inet6_from_string ("2001:db8:abad:c0de::"), - .plen = 64, - .ifindex = fixture->ifindex1, - .gateway = in6addr_any, - .metric = 22, - .mss = 0, - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = *nmtst_inet6_from_string ("2001:db8:1337::"), - .plen = 48, - .ifindex = fixture->ifindex1, - .gateway = in6addr_any, - .metric = 1025, - .mss = 0, - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = *nmtst_inet6_from_string ("2001:db8:8086::"), - .plen = 48, - .ifindex = fixture->ifindex1, - .gateway = in6addr_any, - .metric = 21, - .mss = 0, - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (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, - }, - }; - - NMPlatformIP6Route state3[] = { - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = *nmtst_inet6_from_string ("2001:db8:abad:c0de::"), - .plen = 64, - .ifindex = fixture->ifindex1, - .gateway = in6addr_any, - .metric = 22, - .mss = 0, - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = *nmtst_inet6_from_string ("2001:db8:8086::"), - .plen = 48, - .ifindex = fixture->ifindex1, - .gateway = in6addr_any, - .metric = 20, - .mss = 0, - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = *nmtst_inet6_from_string ("2001:db8:1337::"), - .plen = 48, - .ifindex = fixture->ifindex1, - .gateway = in6addr_any, - .metric = 1024, - .mss = 0, - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = *nmtst_inet6_from_string ("2001:db8:1337::"), - .plen = 48, - .ifindex = fixture->ifindex1, - .gateway = in6addr_any, - .metric = 1025, - .mss = 0, - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = *nmtst_inet6_from_string ("2001:db8:8086::"), - .plen = 48, - .ifindex = fixture->ifindex1, - .gateway = in6addr_any, - .metric = 21, - .mss = 0, - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (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); - setup_dev1_ip6 (fixture->ifindex1); - g_test_assert_expected_messages (); - - /* 2001:db8:8086::/48 on dev0 won over 2001:db8:8086::/48 on dev1 - * 2001:db8:d34d::/64 on dev1 could not be added - * 2001:db8:1337::/48 on dev0 won over 2001:db8:1337::/48 on dev1 and has metric 1024 - * 2001:db8:abad:c0de::/64 routes did not clash */ - routes = ip_routes (fixture, NMP_OBJECT_TYPE_IP6_ROUTE); - g_assert_cmpint (routes->len, ==, G_N_ELEMENTS (state1)); - nmtst_platform_ip6_routes_equal ((NMPlatformIP6Route *) routes->data, state1, routes->len, TRUE); - g_array_free (routes, TRUE); - - - setup_dev1_ip6 (fixture->ifindex1); - g_test_assert_expected_messages (); - setup_dev0_ip6 (fixture->ifindex0); - - /* Ensure nothing changed. */ - routes = ip_routes (fixture, NMP_OBJECT_TYPE_IP6_ROUTE); - g_assert_cmpint (routes->len, ==, G_N_ELEMENTS (state1)); - nmtst_platform_ip6_routes_equal ((NMPlatformIP6Route *) routes->data, state1, routes->len, TRUE); - g_array_free (routes, TRUE); - - update_dev0_ip6 (fixture->ifindex0); - - /* 2001:db8:abad:c0de::/64 on dev0 was updated for gateway removal*/ - routes = ip_routes (fixture, NMP_OBJECT_TYPE_IP6_ROUTE); - if (routes->len != G_N_ELEMENTS (state2)) { - NMPlatformIP6Route rr; - - /* hm, seems kernel may wrongly treat IPv6 gateway for `ip route replace`. - * See rh#1480427. - * - * We would expect that `ip route replace` replaces an existing route. - * However, kernel may not do so, and instead prepend it. - * - * Work around that, by checking if such a route exists and accept - * it. */ - g_assert (nmtstp_is_root_test ()); - g_assert_cmpint (routes->len, ==, G_N_ELEMENTS (state2) + 1); - rr = ((NMPlatformIP6Route) { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = *nmtst_inet6_from_string ("2001:db8:abad:c0de::"), - .plen = 64, - .ifindex = fixture->ifindex0, - .gateway = *nmtst_inet6_from_string ("2001:db8:8086::1"), - .metric = 21, - .mss = 0, - }); - for (i = 0; i < routes->len; i++) { - if (nm_platform_ip6_route_cmp (&rr, - &g_array_index (routes, NMPlatformIP6Route, i), - NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL) != 0) - continue; - g_array_remove_index (routes, i); - break; - } - } - nmtst_platform_ip6_routes_equal ((NMPlatformIP6Route *) routes->data, state2, routes->len, TRUE); - g_array_free (routes, TRUE); - - nm_route_manager_route_flush (route_manager_get (), fixture->ifindex0); - - /* 2001:db8:abad:c0de::/64 on dev1 is still there, went away from dev0 - * 2001:db8:8086::/48 is now on dev1 - * 2001:db8:1337::/48 is now on dev1, metric of 1024 still applies - * 2001:db8:d34d::/64 is present now that 2001:db8:8086::/48 is on dev1 - * No dev0 routes left. */ - routes = ip_routes (fixture, NMP_OBJECT_TYPE_IP6_ROUTE); - g_assert_cmpint (routes->len, ==, G_N_ELEMENTS (state3)); - nmtst_platform_ip6_routes_equal ((NMPlatformIP6Route *) routes->data, state3, routes->len, TRUE); - g_array_free (routes, TRUE); - - nm_route_manager_route_flush (route_manager_get (), fixture->ifindex1); - - /* No routes left. */ - routes = ip_routes (fixture, NMP_OBJECT_TYPE_IP6_ROUTE); - g_assert_cmpint (routes->len, ==, 0); - g_array_free (routes, TRUE); -} - -/*****************************************************************************/ - -static void -_assert_route_check (const NMPlatformVTableRoute *vtable, gboolean has, const NMPlatformIPXRoute *route) -{ - const NMPlatformIPXRoute *r; - NMPlatformIPXRoute c; - - g_assert (route); - - if (vtable->is_ip4) - r = (const NMPlatformIPXRoute *) nmtstp_ip4_route_get (NM_PLATFORM_GET, route->rx.ifindex, route->r4.network, route->rx.plen, route->rx.metric, route->r4.tos); - else - r = (const NMPlatformIPXRoute *) nmtstp_ip6_route_get (NM_PLATFORM_GET, route->rx.ifindex, &route->r6.network, route->rx.plen, route->rx.metric, &route->r6.src, route->r6.src_plen); - - if (!has) { - g_assert (!r); - } else { - char buf[sizeof (_nm_utils_to_string_buffer)]; - - if (r) { - if (vtable->is_ip4) - c.r4 = route->r4; - else - c.r6 = route->r6; - c.rx.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (c.rx.rt_source); - } - if (!r || vtable->route_cmp (r, &c, NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL) != 0) { - g_error ("Invalid route. Expect %s, has %s", - vtable->route_to_string (&c, NULL, 0), - vtable->route_to_string (r, buf, sizeof (buf))); - } - } -} - -static void -test_ip4_full_sync (test_fixture *fixture, gconstpointer user_data) -{ - const NMPlatformVTableRoute *vtable = &nm_platform_vtable_route_v4; - gs_unref_array GArray *routes = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP4Route)); - NMPlatformIP4Route r01, r02, r03; - - nm_log_dbg (LOGD_CORE, "TEST start test_ip4_full_sync(): start"); - - r01 = *nmtst_platform_ip4_route_full ("12.3.4.0", 24, NULL, - fixture->ifindex0, NM_IP_CONFIG_SOURCE_USER, - 100, 0, RT_SCOPE_LINK, NULL); - r02 = *nmtst_platform_ip4_route_full ("13.4.5.6", 32, "12.3.4.1", - fixture->ifindex0, NM_IP_CONFIG_SOURCE_USER, - 100, 0, RT_SCOPE_UNIVERSE, NULL); - r03 = *nmtst_platform_ip4_route_full ("14.5.6.7", 32, "12.3.4.1", - fixture->ifindex0, NM_IP_CONFIG_SOURCE_USER, - 110, 0, RT_SCOPE_UNIVERSE, NULL); - g_array_set_size (routes, 2); - g_array_index (routes, NMPlatformIP4Route, 0) = r01; - g_array_index (routes, NMPlatformIP4Route, 1) = r02; - nm_route_manager_ip4_route_sync (route_manager_get (), fixture->ifindex0, routes, TRUE, TRUE); - - _assert_route_check (vtable, TRUE, (const NMPlatformIPXRoute *) &r01); - _assert_route_check (vtable, TRUE, (const NMPlatformIPXRoute *) &r02); - _assert_route_check (vtable, FALSE, (const NMPlatformIPXRoute *) &r03); - - vtable->route_add (NM_PLATFORM_GET, NMP_NLM_FLAG_REPLACE, (const NMPlatformIPXRoute *) &r03, 0, -1); - - _assert_route_check (vtable, TRUE, (const NMPlatformIPXRoute *) &r01); - _assert_route_check (vtable, TRUE, (const NMPlatformIPXRoute *) &r02); - _assert_route_check (vtable, TRUE, (const NMPlatformIPXRoute *) &r03); - - nm_route_manager_ip4_route_sync (route_manager_get (), fixture->ifindex0, routes, TRUE, FALSE); - - _assert_route_check (vtable, TRUE, (const NMPlatformIPXRoute *) &r01); - _assert_route_check (vtable, TRUE, (const NMPlatformIPXRoute *) &r02); - _assert_route_check (vtable, TRUE, (const NMPlatformIPXRoute *) &r03); - - g_array_set_size (routes, 1); - - nm_route_manager_ip4_route_sync (route_manager_get (), fixture->ifindex0, routes, TRUE, FALSE); - - _assert_route_check (vtable, TRUE, (const NMPlatformIPXRoute *) &r01); - _assert_route_check (vtable, FALSE, (const NMPlatformIPXRoute *) &r02); - _assert_route_check (vtable, TRUE, (const NMPlatformIPXRoute *) &r03); - - nm_route_manager_ip4_route_sync (route_manager_get (), fixture->ifindex0, routes, TRUE, TRUE); - - _assert_route_check (vtable, TRUE, (const NMPlatformIPXRoute *) &r01); - _assert_route_check (vtable, FALSE, (const NMPlatformIPXRoute *) &r02); - _assert_route_check (vtable, FALSE, (const NMPlatformIPXRoute *) &r03); - - nm_log_dbg (LOGD_CORE, "TEST test_ip4_full_sync(): done"); -} - -/*****************************************************************************/ - -static void -fixture_setup (test_fixture *fixture, gconstpointer user_data) -{ - SignalData *link_added; - - link_added = add_signal_ifname (NM_PLATFORM_SIGNAL_LINK_CHANGED, - NM_PLATFORM_SIGNAL_ADDED, - link_callback, - "nm-test-device0"); - nm_platform_link_delete (NM_PLATFORM_GET, nm_platform_link_get_ifindex (NM_PLATFORM_GET, "nm-test-device0")); - g_assert (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, "nm-test-device0")); - g_assert (nm_platform_link_dummy_add (NM_PLATFORM_GET, "nm-test-device0", NULL) == NM_PLATFORM_ERROR_SUCCESS); - accept_signal (link_added); - free_signal (link_added); - fixture->ifindex0 = nm_platform_link_get_ifindex (NM_PLATFORM_GET, "nm-test-device0"); - g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, fixture->ifindex0, NULL)); - - link_added = add_signal_ifname (NM_PLATFORM_SIGNAL_LINK_CHANGED, - NM_PLATFORM_SIGNAL_ADDED, - link_callback, - "nm-test-device1"); - nm_platform_link_delete (NM_PLATFORM_GET, nm_platform_link_get_ifindex (NM_PLATFORM_GET, "nm-test-device1")); - g_assert (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, "nm-test-device1")); - g_assert (nm_platform_link_dummy_add (NM_PLATFORM_GET, "nm-test-device1", NULL) == NM_PLATFORM_ERROR_SUCCESS); - accept_signal (link_added); - free_signal (link_added); - fixture->ifindex1 = nm_platform_link_get_ifindex (NM_PLATFORM_GET, "nm-test-device1"); - g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, fixture->ifindex1, NULL)); -} - -static void -fixture_teardown (test_fixture *fixture, gconstpointer user_data) -{ - nm_platform_link_delete (NM_PLATFORM_GET, fixture->ifindex0); - nm_platform_link_delete (NM_PLATFORM_GET, fixture->ifindex1); -} - -/*****************************************************************************/ - -NMTstpSetupFunc const _nmtstp_setup_platform_func = SETUP; - -void -_nmtstp_init_tests (int *argc, char ***argv) -{ - nmtst_init_assert_logging (argc, argv, "WARN", "ALL"); -} - -void -_nmtstp_setup_tests (void) -{ - g_test_add ("/route-manager/ip4", test_fixture, NULL, fixture_setup, test_ip4, fixture_teardown); - g_test_add ("/route-manager/ip6", test_fixture, NULL, fixture_setup, test_ip6, fixture_teardown); - g_test_add ("/route-manager/ip4-full-sync", test_fixture, NULL, fixture_setup, test_ip4_full_sync, fixture_teardown); -} diff --git a/src/vpn/nm-vpn-connection.c b/src/vpn/nm-vpn-connection.c index 912df081e4..562867545e 100644 --- a/src/vpn/nm-vpn-connection.c +++ b/src/vpn/nm-vpn-connection.c @@ -45,7 +45,6 @@ #include "nm-core-internal.h" #include "nm-pacrunner-manager.h" #include "nm-default-route-manager.h" -#include "nm-route-manager.h" #include "nm-firewall-manager.h" #include "nm-config.h" #include "nm-vpn-plugin-info.h" @@ -394,9 +393,11 @@ vpn_cleanup (NMVpnConnection *self, NMDevice *parent_dev) NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self); if (priv->ip_ifindex) { - nm_platform_link_set_down (nm_netns_get_platform (priv->netns), priv->ip_ifindex); - nm_route_manager_route_flush (nm_netns_get_route_manager (priv->netns), priv->ip_ifindex); - nm_platform_ip_address_flush (nm_netns_get_platform (priv->netns), AF_UNSPEC, priv->ip_ifindex); + NMPlatform *platform = nm_netns_get_platform (priv->netns); + + nm_platform_link_set_down (platform, priv->ip_ifindex); + nm_platform_ip_route_flush (platform, AF_UNSPEC, priv->ip_ifindex); + nm_platform_ip_address_flush (platform, AF_UNSPEC, priv->ip_ifindex); } remove_parent_device_config (self, parent_dev); @@ -1104,21 +1105,17 @@ nm_vpn_connection_apply_config (NMVpnConnection *self) nm_platform_link_set_up (nm_netns_get_platform (priv->netns), priv->ip_ifindex, NULL); if (priv->ip4_config) { + nm_assert (priv->ip_ifindex == nm_ip4_config_get_ifindex (priv->ip4_config)); if (!nm_ip4_config_commit (priv->ip4_config, nm_netns_get_platform (priv->netns), - nm_netns_get_route_manager (priv->netns), - priv->ip_ifindex, - TRUE, nm_vpn_connection_get_ip4_route_metric (self))) return FALSE; } if (priv->ip6_config) { + nm_assert (priv->ip_ifindex == nm_ip6_config_get_ifindex (priv->ip6_config)); if (!nm_ip6_config_commit (priv->ip6_config, - nm_netns_get_platform (priv->netns), - nm_netns_get_route_manager (priv->netns), - priv->ip_ifindex, - TRUE)) + nm_netns_get_platform (priv->netns))) return FALSE; } |