route-manager: remember routes that should be active

Kernel likes to remove a route in case an equivalent route is added to another
interface. Avoid this situation and only apply the new routes in case the ones
that would cause a conflict are removed.

https://bugzilla.redhat.com/show_bug.cgi?id=1164441
https://bugzilla.gnome.org/show_bug.cgi?id=740064
This commit is contained in:
Lubomir Rintel 2015-01-05 18:12:15 +01:00
parent 7c52d094ed
commit 4c3ba29b40

View file

@ -18,6 +18,7 @@
* Copyright (C) 2015 Red Hat, Inc.
*/
#include <string.h>
#include "config.h"
@ -27,12 +28,19 @@
#include "NetworkManagerUtils.h"
typedef struct {
GArray *ip4_routes;
GArray *ip6_routes;
} NMRouteManagerPrivate;
#define NM_ROUTE_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_ROUTE_MANAGER, NMRouteManagerPrivate))
G_DEFINE_TYPE (NMRouteManager, nm_route_manager, G_TYPE_OBJECT)
static NMRouteManager *_instance;
static gboolean
array_contains_ip4_route (const GArray *routes, const NMPlatformIP4Route *route)
static const NMPlatformIP4Route *
array_get_ip4_route (const GArray *routes, int ifindex, const NMPlatformIP4Route *route)
{
guint len = routes ? routes->len : 0;
guint i;
@ -40,18 +48,25 @@ array_contains_ip4_route (const GArray *routes, const NMPlatformIP4Route *route)
for (i = 0; i < len; i++) {
NMPlatformIP4Route *c = &g_array_index (routes, NMPlatformIP4Route, i);
if (ifindex) {
/* Looking for a specific route. */
if ( c->ifindex != ifindex
|| route->mss != c->mss
|| route->gateway != c->gateway)
continue;
}
if (route->network == c->network &&
route->plen == c->plen &&
route->gateway == c->gateway &&
route->metric == c->metric)
return TRUE;
return c;
}
return FALSE;
return NULL;
}
static gboolean
array_contains_ip6_route (const GArray *routes, const NMPlatformIP6Route *route)
static const NMPlatformIP6Route *
array_get_ip6_route (const GArray *routes, int ifindex, const NMPlatformIP6Route *route)
{
guint len = routes ? routes->len : 0;
guint i;
@ -61,14 +76,21 @@ array_contains_ip6_route (const GArray *routes, const NMPlatformIP6Route *route)
int route_metric = nm_utils_ip6_route_metric_normalize (route->metric);
int c_metric = nm_utils_ip6_route_metric_normalize (c->metric);
if (ifindex) {
/* Looking for a specific route. */
if ( c->ifindex != ifindex
|| route->mss != c->mss
|| !IN6_ARE_ADDR_EQUAL (&route->gateway, &c->gateway))
continue;
}
if (IN6_ARE_ADDR_EQUAL (&route->network, &c->network) &&
route->plen == c->plen &&
IN6_ARE_ADDR_EQUAL (&route->gateway, &c->gateway) &&
route_metric == c_metric)
return TRUE;
return c;
}
return FALSE;
return NULL;
}
@ -88,31 +110,76 @@ array_contains_ip6_route (const GArray *routes, const NMPlatformIP6Route *route)
gboolean
nm_route_manager_ip4_route_sync (NMRouteManager *self, int ifindex, const GArray *known_routes)
{
GArray *routes;
NMPlatformIP4Route *route;
NMRouteManagerPrivate *priv = NM_ROUTE_MANAGER_GET_PRIVATE (self);
GArray *plat_routes, *routes = priv->ip4_routes;
NMPlatformIP4Route route;
const NMPlatformIP4Route *known_route;
const NMPlatformIP4Route *existing;
gboolean success;
int i, i_type;
/* Learn about routes that platform knows but we don't. */
plat_routes = nm_platform_ip4_route_get_all (0, NM_PLATFORM_GET_ROUTE_MODE_NO_DEFAULT);
for (i = 0; i < plat_routes->len; i++) {
existing = &g_array_index (plat_routes, NMPlatformIP4Route, i);
if (!array_get_ip4_route (routes, existing->ifindex, existing))
g_array_append_val (routes, *existing);
}
/* Delete unknown routes */
routes = nm_platform_ip4_route_get_all (ifindex, NM_PLATFORM_GET_ROUTE_MODE_NO_DEFAULT);
for (i = 0; i < routes->len; i++) {
route = &g_array_index (routes, NMPlatformIP4Route, i);
for (i = 0; i < routes->len;) {
route = g_array_index (routes, NMPlatformIP4Route, i);
if (!array_contains_ip4_route (known_routes, route))
(void) nm_platform_ip4_route_delete (ifindex, route->network, route->plen, route->metric);
if (route.ifindex == ifindex) {
/* Our route. Keep it? */
if (array_get_ip4_route (known_routes, route.ifindex, &route)) {
i++;
continue;
}
g_array_remove_index (routes, i);
} else {
i++;
}
existing = array_get_ip4_route (routes, 0, &route);
if ( existing
&& !array_get_ip4_route (plat_routes, existing->ifindex, existing)) {
/* The route that should already exist is not there.
* Try to add it. */
nm_platform_ip4_route_add (existing->ifindex,
existing->source,
existing->network,
existing->plen,
existing->gateway,
0,
existing->metric,
existing->mss);
/* It's now hopefully in platform. Take a note so that we
* don't attempt to add it again. */
g_array_append_val (plat_routes, *existing);
}
if (route.ifindex == ifindex) {
/* Clean up. */
nm_platform_ip4_route_delete (route.ifindex,
route.network,
route.plen,
route.metric);
}
}
if (!known_routes) {
g_array_free (routes, TRUE);
if (!known_routes)
return TRUE;
}
/* Add missing routes */
for (i_type = 0, success = TRUE; i_type < 2 && success; i_type++) {
for (i = 0; i < known_routes->len && success; i++) {
known_route = &g_array_index (known_routes, NMPlatformIP4Route, i);
g_assert (known_route->ifindex);
if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (known_route))
continue;
@ -123,8 +190,8 @@ nm_route_manager_ip4_route_sync (NMRouteManager *self, int ifindex, const GArray
}
/* Ignore routes that already exist */
if (!array_contains_ip4_route (routes, known_route)) {
success = nm_platform_ip4_route_add (ifindex,
if (!array_get_ip4_route (routes, 0, known_route)) {
success = nm_platform_ip4_route_add (known_route->ifindex,
known_route->source,
known_route->network,
known_route->plen,
@ -138,10 +205,12 @@ nm_route_manager_ip4_route_sync (NMRouteManager *self, int ifindex, const GArray
success = TRUE;
}
}
if (!array_get_ip4_route (routes, known_route->ifindex, known_route))
g_array_append_val (routes, *known_route);
}
}
g_array_free (routes, TRUE);
return success;
}
@ -161,32 +230,74 @@ nm_route_manager_ip4_route_sync (NMRouteManager *self, int ifindex, const GArray
gboolean
nm_route_manager_ip6_route_sync (NMRouteManager *self, int ifindex, const GArray *known_routes)
{
GArray *routes;
NMPlatformIP6Route *route;
NMRouteManagerPrivate *priv = NM_ROUTE_MANAGER_GET_PRIVATE (self);
GArray *plat_routes, *routes = priv->ip6_routes;
NMPlatformIP6Route route;
const NMPlatformIP6Route *known_route;
const NMPlatformIP6Route *existing;
gboolean success;
int i, i_type;
/* Delete unknown routes */
routes = nm_platform_ip6_route_get_all (ifindex, NM_PLATFORM_GET_ROUTE_MODE_NO_DEFAULT);
for (i = 0; i < routes->len; i++) {
route = &g_array_index (routes, NMPlatformIP6Route, i);
route->ifindex = 0;
if (!array_contains_ip6_route (known_routes, route))
nm_platform_ip6_route_delete (ifindex, route->network, route->plen, route->metric);
/* Learn about routes that platform knows but we don't. */
plat_routes = nm_platform_ip6_route_get_all (0, NM_PLATFORM_GET_ROUTE_MODE_NO_DEFAULT);
for (i = 0; i < plat_routes->len; i++) {
existing = &g_array_index (plat_routes, NMPlatformIP6Route, i);
if (!array_get_ip6_route (routes, existing->ifindex, existing))
g_array_append_val (routes, *existing);
}
if (!known_routes) {
g_array_free (routes, TRUE);
for (i = 0; i < routes->len;) {
route = g_array_index (routes, NMPlatformIP6Route, i);
if (route.ifindex == ifindex) {
/* Our route. Keep it? */
if (array_get_ip6_route (known_routes, route.ifindex, &route)) {
i++;
continue;
}
g_array_remove_index (routes, i);
} else {
i++;
}
existing = array_get_ip6_route (routes, 0, &route);
if ( existing
&& !array_get_ip6_route (plat_routes, existing->ifindex, existing)) {
/* The route that should already exist is not there.
* Try to add it. */
nm_platform_ip6_route_add (existing->ifindex,
existing->source,
existing->network,
existing->plen,
existing->gateway,
existing->metric,
existing->mss);
/* It's now hopefully in platform. Take a note so that we
* don't attempt to add it again. */
g_array_append_val (plat_routes, *existing);
}
if (route.ifindex == ifindex) {
/* Clean up. */
nm_platform_ip6_route_delete (route.ifindex,
route.network,
route.plen,
route.metric);
}
}
if (!known_routes)
return TRUE;
}
/* Add missing routes */
for (i_type = 0, success = TRUE; i_type < 2 && success; i_type++) {
for (i = 0; i < known_routes->len && success; i++) {
known_route = &g_array_index (known_routes, NMPlatformIP6Route, i);
g_assert (known_route->ifindex);
if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (known_route))
continue;
@ -197,8 +308,8 @@ nm_route_manager_ip6_route_sync (NMRouteManager *self, int ifindex, const GArray
}
/* Ignore routes that already exist */
if (!array_contains_ip6_route (routes, known_route)) {
success = nm_platform_ip6_route_add (ifindex,
if (!array_get_ip6_route (routes, 0, known_route)) {
success = nm_platform_ip6_route_add (known_route->ifindex,
known_route->source,
known_route->network,
known_route->plen,
@ -211,10 +322,12 @@ nm_route_manager_ip6_route_sync (NMRouteManager *self, int ifindex, const GArray
success = TRUE;
}
}
if (!array_get_ip6_route (routes, known_route->ifindex, known_route))
g_array_append_val (routes, *known_route);
}
}
g_array_free (routes, TRUE);
return success;
}
@ -238,9 +351,30 @@ nm_route_manager_get ()
static void
nm_route_manager_init (NMRouteManager *self)
{
NMRouteManagerPrivate *priv = NM_ROUTE_MANAGER_GET_PRIVATE (self);
priv->ip4_routes = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP4Route));
priv->ip6_routes = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP6Route));
}
static void
finalize (GObject *object)
{
NMRouteManagerPrivate *priv = NM_ROUTE_MANAGER_GET_PRIVATE (object);
g_array_free (priv->ip4_routes, TRUE);
g_array_free (priv->ip6_routes, TRUE);
G_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);
g_type_class_add_private (klass, sizeof (NMRouteManagerPrivate));
/* virtual methods */
object_class->finalize = finalize;
}