route-manager: manage IPv4 device-routes with NMRouteManager

When adding an IPv4 address, kernel will also add a device-route.
We don't want that route because it has the wrong metric. Instead,
we add our own route (with a different metric) and remove the
kernel-added one.

This could be avoided if kernel would support an IPv4 address flag
IFA_F_NOPREFIXROUTE like it does for IPv6 (see related bug rh#1221311).

One important thing is, that we want don't want to manage the
device-route on assumed devices. Note that this is correct behavior
if "assumed" means "do-not-touch".
If "assumed" means "seamlessly-takeover", then this is wrong.
Imagine we get a new DHCP address. In this case, we would not manage
the device-route on the assumed device. This cannot be fixed without
splitting unmanaged/assumed with related bug bgo 746440.
This is no regression as we would also not manage device-routes
for assumed devices previously.

We also don't want to remove the device-route if the user added
it externally. Note that here we behave wrongly too, because we
don't record externally added kernel routes in update_ip_config().
This still needs fixing.

Let IPv4 device-routes also be managed by NMRouteManager. NMRouteManager
has a list of all routes and can properly add, remove, and restore
the device route as needed.

One problem is, that the device-route does not get added immediately
with the address. It only appears some time later. This is solved
by NMRouteManager watching platform and if a matchin device-route shows up
within a short time after configuring  addresses, remove it.
If the route appears after the short timeout, assume they were added for
other reasons (e.g. by the user) and don't remove them.

https://bugzilla.gnome.org/show_bug.cgi?id=751264
https://bugzilla.redhat.com/show_bug.cgi?id=1211287
(cherry picked from commit 5f54a323d1)
This commit is contained in:
Thomas Haller 2015-06-22 18:21:53 +02:00
parent 4f161e4bea
commit 42ac15c86f
9 changed files with 314 additions and 124 deletions

View file

@ -6332,10 +6332,11 @@ nm_device_set_ip4_config (NMDevice *self,
nm_device_set_mtu (self, nm_ip4_config_get_mtu (new_config));
/* for assumed devices we set the device_route_metric to the default which will
* stop nm_platform_ip4_address_sync() to replace the device routes. */
/* 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, ip_ifindex,
assumed ? NM_PLATFORM_ROUTE_METRIC_IP4_DEVICE_ROUTE : default_route_metric);
assumed ? (gint64) -1 : (gint64) default_route_metric);
if (!success)
reason_local = NM_DEVICE_STATE_REASON_CONFIG_FAILED;
}
@ -7133,6 +7134,11 @@ update_ip4_config (NMDevice *self, gboolean initial)
capture_lease_config (self, priv->ext_ip4_config, &priv->dev_ip4_config, NULL, NULL);
}
/* FIXME: ext_ip4_config does not contain routes with source==RTPROT_KERNEL.
* Hence, we will wrongly remove device-routes with metric=0 if they were added by
* the user on purpose. This should be fixed by also tracking and exposing
* kernel routes. */
/* This function was called upon external changes. Remove the configuration
* (adresses,routes) that is no longer present externally from the interal
* config. This way, we don't readd addresses that were manually removed

View file

@ -34,6 +34,7 @@
#include "NetworkManagerUtils.h"
#include "nm-core-internal.h"
#include "nm-route-manager.h"
#include "gsystem-local-alloc.h"
G_DEFINE_TYPE (NMIP4Config, nm_ip4_config, G_TYPE_OBJECT)
@ -253,25 +254,59 @@ nm_ip4_config_capture (int ifindex, gboolean capture_resolv_conf)
}
gboolean
nm_ip4_config_commit (const NMIP4Config *config, int ifindex, guint32 default_route_metric)
nm_ip4_config_commit (const NMIP4Config *config, int ifindex, gint64 default_route_metric)
{
NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
int i;
gs_unref_ptrarray GPtrArray *added_addresses = NULL;
g_return_val_if_fail (ifindex > 0, FALSE);
g_return_val_if_fail (config != NULL, FALSE);
/* Addresses */
nm_platform_ip4_address_sync (NM_PLATFORM_GET, ifindex, priv->addresses, default_route_metric);
nm_platform_ip4_address_sync (NM_PLATFORM_GET, ifindex, priv->addresses,
default_route_metric >= 0 ? &added_addresses : NULL);
/* Routes */
{
int count = nm_ip4_config_get_num_routes (config);
GArray *routes = g_array_sized_new (FALSE, FALSE, sizeof (NMPlatformIP4Route), count);
const NMPlatformIP4Route *route;
gboolean success;
gs_unref_array GArray *device_route_purge_list = NULL;
if ( default_route_metric >= 0
&& added_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. */
for (i = 0; i < added_addresses->len; i++) {
const NMPlatformIP4Address *addr = added_addresses->pdata[i];
NMPlatformIP4Route route = { 0 };
if (addr->plen == 0)
continue;
route.ifindex = ifindex;
route.source = NM_IP_CONFIG_SOURCE_KERNEL;
route.network = nm_utils_ip4_address_clear_host_address (addr->address, addr->plen);
route.plen = addr->plen;
route.pref_src = addr->address;
route.metric = default_route_metric;
g_array_append_val (routes, route);
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);
}
}
}
for (i = 0; i < count; i++) {
const NMPlatformIP4Route *route;
route = nm_ip4_config_get_route (config, i);
/* Don't add the route if it's more specific than one of the subnets
@ -281,10 +316,14 @@ nm_ip4_config_commit (const NMIP4Config *config, int ifindex, guint32 default_ro
&& nm_ip4_config_destination_is_direct (config, route->network, route->plen))
continue;
/* duplicates in @routes are no problem as route-manager handles them
* gracefully (by ignoring them). */
g_array_append_vals (routes, route, 1);
}
success = nm_route_manager_ip4_route_sync (nm_route_manager_get (), ifindex, routes, TRUE);
nm_route_manager_ip4_route_register_device_route_purge_list (nm_route_manager_get (), device_route_purge_list);
success = nm_route_manager_ip4_route_sync (nm_route_manager_get (), ifindex, routes, default_route_metric < 0);
g_array_unref (routes);
if (!success)
return FALSE;

View file

@ -64,7 +64,7 @@ const char * nm_ip4_config_get_dbus_path (const NMIP4Config *config);
/* Integration with nm-platform and nm-setting */
NMIP4Config *nm_ip4_config_capture (int ifindex, gboolean capture_resolv_conf);
gboolean nm_ip4_config_commit (const NMIP4Config *config, int ifindex, guint32 default_route_metric);
gboolean nm_ip4_config_commit (const NMIP4Config *config, int ifindex, gint64 default_route_metric);
void nm_ip4_config_merge_setting (NMIP4Config *config, NMSettingIPConfig *setting, guint32 default_route_metric);
NMSetting *nm_ip4_config_create_setting (const NMIP4Config *config);

View file

@ -24,10 +24,18 @@
#include "nm-route-manager.h"
#include "nm-platform.h"
#include "nmp-object.h"
#include "nm-core-internal.h"
#include "nm-logging.h"
#include "gsystem-local-alloc.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_SEC (IP4_DEVICE_ROUTES_WAIT_TIME_NS * 2)
typedef struct {
guint len;
NMPlatformIPXRoute *entries[1];
@ -38,11 +46,22 @@ typedef struct {
RouteIndex *index;
} RouteEntries;
typedef struct {
NMRouteManager *self;
gint64 scheduled_at_ns;
guint idle_id;
NMPObject *obj;
} IP4DeviceRoutePurgeEntry;
typedef struct {
NMPlatform *platform;
RouteEntries ip4_routes;
RouteEntries ip6_routes;
struct {
GHashTable *entries;
guint gc_id;
} ip4_device_routes;
} NMRouteManagerPrivate;
#define NM_ROUTE_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_ROUTE_MANAGER, NMRouteManagerPrivate))
@ -125,6 +144,10 @@ static const VTableIP vtable_v4, vtable_v6;
/*********************************************************************************************/
static gboolean _ip4_device_routes_cancel (NMRouteManager *self);
/*********************************************************************************************/
#if defined (NM_MORE_ASSERTS) && !defined (G_DISABLE_ASSERT)
inline static void
ASSERT_route_index_valid (const VTableIP *vtable, const GArray *entries, const RouteIndex *index, gboolean unique_ifindexes)
@ -234,6 +257,41 @@ _route_index_create (const VTableIP *vtable, const GArray *routes)
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 ((gpointer *) index->entries, index->len, (gpointer) 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)
{
@ -638,6 +696,182 @@ nm_route_manager_route_flush (NMRouteManager *self, int ifindex)
/*********************************************************************************************/
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);
return entry;
}
static void
_ip4_device_routes_purge_entry_free (IP4DeviceRoutePurgeEntry *entry)
{
nmp_object_unref (entry->obj);
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, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0));
nm_platform_ip4_route_delete (priv->platform,
entry->obj->ip4_route.ifindex,
entry->obj->ip4_route.network,
entry->obj->ip4_route.plen,
entry->obj->ip4_route.metric);
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,
NMPObjectType obj_type,
int ifindex,
const NMPlatformIP4Route *route,
NMPlatformSignalChangeType change_type,
NMPlatformReason reason,
NMRouteManager *self)
{
NMRouteManagerPrivate *priv;
NMPObject obj_needle;
IP4DeviceRoutePurgeEntry *entry;
if (change_type == NM_PLATFORM_SIGNAL_REMOVED)
return;
if ( route->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);
entry = g_hash_table_lookup (priv->ip4_device_routes.entries,
nmp_object_stackinit (&obj_needle, NMP_OBJECT_TYPE_IP4_ROUTE, (NMPlatformObject *) route));
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;
}
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,
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_SEC, (GSourceFunc) _ip4_device_routes_gc, self);
}
}
/*********************************************************************************************/
static const VTableIP vtable_v4 = {
.vt = &nm_platform_vtable_route_v4,
.route_id_cmp = (int (*) (const NMPlatformIPXRoute *, const NMPlatformIPXRoute *)) _v4_route_id_cmp,
@ -661,13 +895,21 @@ nm_route_manager_init (NMRouteManager *self)
priv->ip6_routes.entries = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP6Route));
priv->ip4_routes.index = _route_index_create (&vtable_v4, priv->ip4_routes.entries);
priv->ip6_routes.index = _route_index_create (&vtable_v6, priv->ip6_routes.entries);
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);
}
static void
dispose (GObject *object)
{
NMRouteManager *self = NM_ROUTE_MANAGER (object);
NMRouteManagerPrivate *priv = NM_ROUTE_MANAGER_GET_PRIVATE (object);
g_hash_table_remove_all (priv->ip4_device_routes.entries);
_ip4_device_routes_cancel (self);
g_clear_object (&priv->platform);
G_OBJECT_CLASS (nm_route_manager_parent_class)->dispose (object);
@ -683,6 +925,8 @@ finalize (GObject *object)
g_free (priv->ip4_routes.index);
g_free (priv->ip6_routes.index);
g_hash_table_unref (priv->ip4_device_routes.entries);
G_OBJECT_CLASS (nm_route_manager_parent_class)->finalize (object);
}

View file

@ -46,6 +46,8 @@ gboolean nm_route_manager_ip4_route_sync (NMRouteManager *self, int ifindex, con
gboolean nm_route_manager_ip6_route_sync (NMRouteManager *self, int ifindex, const GArray *known_routes, gboolean ignore_kernel_routes);
gboolean nm_route_manager_route_flush (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_get (void);
#endif /* NM_ROUTE_MANAGER_H */

View file

@ -1025,12 +1025,6 @@ ip6_address_exists (NMPlatform *platform, int ifindex, struct in6_addr addr, int
return FALSE;
}
static gboolean
ip4_check_reinstall_device_route (NMPlatform *platform, int ifindex, const NMPlatformIP4Address *address, guint32 device_route_metric)
{
return FALSE;
}
/******************************************************************/
static GArray *
@ -1468,8 +1462,6 @@ nm_fake_platform_class_init (NMFakePlatformClass *klass)
platform_class->ip4_address_exists = ip4_address_exists;
platform_class->ip6_address_exists = ip6_address_exists;
platform_class->ip4_check_reinstall_device_route = ip4_check_reinstall_device_route;
platform_class->ip4_route_get_all = ip4_route_get_all;
platform_class->ip6_route_get_all = ip6_route_get_all;
platform_class->ip4_route_add = ip4_route_add;

View file

@ -4182,64 +4182,6 @@ ip6_address_exists (NMPlatform *platform, int ifindex, struct in6_addr addr, int
return nmp_object_is_visible (nmp_cache_lookup_obj (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, &obj_needle));
}
static gboolean
ip4_check_reinstall_device_route (NMPlatform *platform, int ifindex, const NMPlatformIP4Address *address, guint32 device_route_metric)
{
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
guint32 device_network;
NMPObject obj_needle;
const NMPlatformIP4Address *const *addresses;
const NMPlatformIP4Route *const *routes;
device_network = nm_utils_ip4_address_clear_host_address (address->address, address->plen);
/* in many cases we expect the route to already exist. So first do an exact lookup
* to save the O(n) access below. */
nmp_object_stackinit_id_ip4_route (&obj_needle, ifindex, device_network, address->plen, device_route_metric);
if (nmp_cache_lookup_obj (priv->cache, &obj_needle)) {
/* There is already a route with metric 0 or the metric we want to install
* for the same subnet. */
return FALSE;
}
if (obj_needle.ip4_route.metric != 0) {
obj_needle.ip4_route.metric = 0;
if (nmp_cache_lookup_obj (priv->cache, &obj_needle))
return FALSE;
}
/* also check whether we already have the same address configured on *any* device. */
addresses = cache_lookup_all_objects (NMPlatformIP4Address, platform, NMP_OBJECT_TYPE_IP4_ADDRESS, FALSE);
if (addresses) {
for (; *addresses; addresses++) {
const NMPlatformIP4Address *addr_candidate = *addresses;
if ( addr_candidate->plen == address->plen
&& addr_candidate->address == device_network) {
/* If we already have the same address installed on any interface,
* we back off. */
return FALSE;
}
}
}
routes = cache_lookup_all_objects (NMPlatformIP4Route, platform, NMP_OBJECT_TYPE_IP4_ROUTE, FALSE);
if (routes) {
for (; *routes; routes++) {
const NMPlatformIP4Route *route_candidate = *routes;
if ( route_candidate->network == device_network
&& route_candidate->plen == address->plen
&& (route_candidate->metric == 0 || route_candidate->metric == device_route_metric)) {
/* If we already have the same address installed on any interface,
* we back off. */
return FALSE;
}
}
}
return TRUE;
}
/******************************************************************/
static GArray *
@ -5053,8 +4995,6 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass)
platform_class->ip4_address_exists = ip4_address_exists;
platform_class->ip6_address_exists = ip6_address_exists;
platform_class->ip4_check_reinstall_device_route = ip4_check_reinstall_device_route;
platform_class->ip4_route_get_all = ip4_route_get_all;
platform_class->ip6_route_get_all = ip6_route_get_all;
platform_class->ip4_route_add = ip4_route_add;

View file

@ -1991,32 +1991,15 @@ array_contains_ip6_address (const GArray *addresses, const NMPlatformIP6Address
return FALSE;
}
gboolean
nm_platform_ip4_check_reinstall_device_route (NMPlatform *self, int ifindex, const NMPlatformIP4Address *address, guint32 device_route_metric)
{
_CHECK_SELF (self, klass, FALSE);
if ( ifindex <= 0
|| address->plen <= 0
|| address->plen >= 32)
return FALSE;
if (device_route_metric == NM_PLATFORM_ROUTE_METRIC_IP4_DEVICE_ROUTE) {
/* The automatically added route would be already our desired priority.
* Nothing to do. */
return FALSE;
}
return klass->ip4_check_reinstall_device_route (self, ifindex, address, device_route_metric);
}
/**
* nm_platform_ip4_address_sync:
* @self: platform instance
* @ifindex: Interface index
* @known_addresses: List of addresses
* @device_route_metric: the route metric for adding subnet routes (replaces
* the kernel added routes).
* @out_added_addresses: (out): (allow-none): if not %NULL, return a #GPtrArray
* with the addresses added. The pointers point into @known_addresses.
* It possibly does not contain all addresses from @known_address because
* some addresses might be expired.
*
* A convenience function to synchronize addresses for a specific interface
* with the least possible disturbance. It simply removes addresses that are
@ -2025,7 +2008,7 @@ nm_platform_ip4_check_reinstall_device_route (NMPlatform *self, int ifindex, con
* Returns: %TRUE on success.
*/
gboolean
nm_platform_ip4_address_sync (NMPlatform *self, int ifindex, const GArray *known_addresses, guint32 device_route_metric)
nm_platform_ip4_address_sync (NMPlatform *self, int ifindex, const GArray *known_addresses, GPtrArray **out_added_addresses)
{
GArray *addresses;
NMPlatformIP4Address *address;
@ -2044,6 +2027,9 @@ nm_platform_ip4_address_sync (NMPlatform *self, int ifindex, const GArray *known
}
g_array_free (addresses, TRUE);
if (out_added_addresses)
*out_added_addresses = NULL;
if (!known_addresses)
return TRUE;
@ -2051,33 +2037,18 @@ nm_platform_ip4_address_sync (NMPlatform *self, int ifindex, const GArray *known
for (i = 0; i < known_addresses->len; i++) {
const NMPlatformIP4Address *known_address = &g_array_index (known_addresses, NMPlatformIP4Address, i);
guint32 lifetime, preferred;
guint32 network;
gboolean reinstall_device_route = FALSE;
if (!nmp_utils_lifetime_get (known_address->timestamp, known_address->lifetime, known_address->preferred,
now, ADDRESS_LIFETIME_PADDING, &lifetime, &preferred))
continue;
if (nm_platform_ip4_check_reinstall_device_route (self, ifindex, known_address, device_route_metric))
reinstall_device_route = TRUE;
if (!nm_platform_ip4_address_add (self, ifindex, known_address->address, known_address->peer_address, known_address->plen, lifetime, preferred, known_address->label))
return FALSE;
if (reinstall_device_route) {
/* Kernel automatically adds a device route for us with metric 0. That is not what we want.
* Remove it, and re-add it.
*
* In face of having the same subnets on two different interfaces with the same metric,
* this is a problem. Surprisingly, kernel is able to add two routes for the same subnet/prefix,metric
* to different interfaces. We cannot. Adding one, would replace the other. This is avoided
* by the above nm_platform_ip4_check_reinstall_device_route() check.
*/
network = nm_utils_ip4_address_clear_host_address (known_address->address, known_address->plen);
(void) nm_platform_ip4_route_add (self, ifindex, NM_IP_CONFIG_SOURCE_KERNEL, network, known_address->plen,
0, known_address->address, device_route_metric, 0);
(void) nm_platform_ip4_route_delete (self, ifindex, network, known_address->plen,
NM_PLATFORM_ROUTE_METRIC_IP4_DEVICE_ROUTE);
if (out_added_addresses) {
if (!*out_added_addresses)
*out_added_addresses = g_ptr_array_new ();
g_ptr_array_add (*out_added_addresses, (gpointer) known_address);
}
}
@ -2145,8 +2116,8 @@ nm_platform_address_flush (NMPlatform *self, int ifindex)
{
_CHECK_SELF (self, klass, FALSE);
return nm_platform_ip4_address_sync (self, ifindex, NULL, 0)
&& nm_platform_ip6_address_sync (self, ifindex, NULL, FALSE);
return nm_platform_ip4_address_sync (self, ifindex, NULL, NULL)
&& nm_platform_ip6_address_sync (self, ifindex, NULL, FALSE);
}
/******************************************************************/

View file

@ -520,8 +520,6 @@ typedef struct {
gboolean (*ip4_address_exists) (NMPlatform *, int ifindex, in_addr_t address, int plen);
gboolean (*ip6_address_exists) (NMPlatform *, int ifindex, struct in6_addr address, int plen);
gboolean (*ip4_check_reinstall_device_route) (NMPlatform *, int ifindex, const NMPlatformIP4Address *address, guint32 device_route_metric);
GArray * (*ip4_route_get_all) (NMPlatform *, int ifindex, NMPlatformGetRouteFlags flags);
GArray * (*ip6_route_get_all) (NMPlatform *, int ifindex, NMPlatformGetRouteFlags flags);
gboolean (*ip4_route_add) (NMPlatform *, int ifindex, NMIPConfigSource source,
@ -706,12 +704,10 @@ gboolean nm_platform_ip4_address_delete (NMPlatform *self, int ifindex, in_addr_
gboolean nm_platform_ip6_address_delete (NMPlatform *self, int ifindex, struct in6_addr address, int plen);
gboolean nm_platform_ip4_address_exists (NMPlatform *self, int ifindex, in_addr_t address, int plen);
gboolean nm_platform_ip6_address_exists (NMPlatform *self, int ifindex, struct in6_addr address, int plen);
gboolean nm_platform_ip4_address_sync (NMPlatform *self, int ifindex, const GArray *known_addresses, guint32 device_route_metric);
gboolean nm_platform_ip4_address_sync (NMPlatform *self, int ifindex, const GArray *known_addresses, GPtrArray **out_added_addresses);
gboolean nm_platform_ip6_address_sync (NMPlatform *self, int ifindex, const GArray *known_addresses, gboolean keep_link_local);
gboolean nm_platform_address_flush (NMPlatform *self, int ifindex);
gboolean nm_platform_ip4_check_reinstall_device_route (NMPlatform *self, int ifindex, const NMPlatformIP4Address *address, guint32 device_route_metric);
GArray *nm_platform_ip4_route_get_all (NMPlatform *self, int ifindex, NMPlatformGetRouteFlags flags);
GArray *nm_platform_ip6_route_get_all (NMPlatform *self, int ifindex, NMPlatformGetRouteFlags flags);
gboolean nm_platform_ip4_route_add (NMPlatform *self, int ifindex, NMIPConfigSource source,