summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Haller <thaller@redhat.com>2017-08-14 14:18:53 +0200
committerThomas Haller <thaller@redhat.com>2017-08-24 10:48:03 +0200
commitf0de7d347f3663c6706b05d462adb2f144f78ab3 (patch)
tree04f25465d1e8fac7c41869851a0f6553f4fcf716
parent974ff6299633bf739a73a240797cd46ebcd0e043 (diff)
downloadNetworkManager-f0de7d347f3663c6706b05d462adb2f144f78ab3.tar.gz
platform: add non-exclusive routes and drop route-manager
Previously, we would add exclusive routes via netlink message flags NLM_F_CREATE | NLM_F_REPLACE for RTM_NEWROUTE. Similar to `ip route replace`. Using that form of RTM_NEWROUTE message, we could only add a certain route with a certain network/plen,metric triple once. That was already hugely inconvenient, because - when configuring routes, multiple (managed) interfaces may get conflicting routes (multihoming). Only one of the routes can be actually configured using `ip route replace`, so we need to track routes that are currently shadowed. - when configuring routes, we might replace externally configured routes on unmanaged interfaces. We should not interfere with such routes. That was worked around by having NMRouteManager (and NMDefaultRouteManager). NMRouteManager would keep a list of the routes which NetworkManager would like to configure, even if momentarily being unable to do so due to conflicting routes. This worked mostly well but was complicated. It involved bumping metrics to avoid conflicts for device routes, as we might require them for gateway routes. Drop that now. Instead, use the corresponding of `ip route append` to configure routes. This allows NetworkManager to confiure (almost) all routes that we care. Especially, it can configure all routes on a managed interface, without replacing/interfering with routes on other interfaces. Hence, NMRouteManager becomes obsolete. It practice it is a bit more complicated because: - when adding an IPv4 address, kernel will automatically create a device route for the subnet. We should avoid that by using the IFA_F_NOPREFIXROUTE flag for IPv4 addresses (still to-do). But as kernel may not support that flag for IPv4 addresses yet (and we don't require such a kernel yet), we still need functionality similar to nm_route_manager_ip4_route_register_device_route_purge_list(). This functionality is now handled via nm_platform_ip4_dev_route_blacklist_set(). - trying to configure an IPv6 route with a source address will be rejected by kernel as long as the address is tentative (see related bug rh#1457196). Preferably, NMDevice would keep the list of routes which should be configured, while kernel would have the list of what actually is configured. There is a feed-back loop where both affect each other (for example, when externally deleting a route, NMDevice must forget about it too). Previously, NMRouteManager would have the task of remembering all routes which we currently want to configure, but cannot due to conflicting routes. We get rid of that, because now we configure non-exclusive routes. We however still will need to remember IPv6 routes with a source address, that currently cannot be configured yet. Hence, we will need to keep track of routes that currently cannot be configured, but later may be. That is still not done yet, as NMRouteManager didn't handle this correctly either.
-rw-r--r--Makefile.am27
-rw-r--r--shared/nm-utils/nm-dedup-multi.c8
-rw-r--r--shared/nm-utils/nm-dedup-multi.h4
-rw-r--r--src/devices/nm-device.c216
-rw-r--r--src/devices/wwan/nm-modem.c10
-rw-r--r--src/nm-iface-helper.c15
-rw-r--r--src/nm-ip4-config.c181
-rw-r--r--src/nm-ip4-config.h9
-rw-r--r--src/nm-ip6-config.c44
-rw-r--r--src/nm-ip6-config.h5
-rw-r--r--src/nm-netns.c10
-rw-r--r--src/nm-netns.h1
-rw-r--r--src/nm-route-manager.c1380
-rw-r--r--src/nm-route-manager.h49
-rw-r--r--src/nm-types.h1
-rw-r--r--src/platform/nm-platform.c466
-rw-r--r--src/platform/nm-platform.h21
-rw-r--r--src/tests/test-route-manager.c974
-rw-r--r--src/vpn/nm-vpn-connection.c19
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;
}