summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLubomir Rintel <lkundrak@v3.sk>2015-01-05 18:12:15 +0100
committerLubomir Rintel <lkundrak@v3.sk>2015-02-27 16:48:28 +0100
commit4c3ba29b4041a0644c7605898ac9c56efc2dafb2 (patch)
treec8ca92a73e9c0d0856e0a1f0f3d02ec4ae25daff
parent7c52d094ed33a2c1a9549a6b4917a68845cab251 (diff)
downloadNetworkManager-4c3ba29b4041a0644c7605898ac9c56efc2dafb2.tar.gz
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
-rw-r--r--src/nm-route-manager.c210
1 files changed, 172 insertions, 38 deletions
diff --git a/src/nm-route-manager.c b/src/nm-route-manager.c
index a1fae89575..925de7760f 100644
--- a/src/nm-route-manager.c
+++ b/src/nm-route-manager.c
@@ -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 (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++;
+ }
- if (!array_contains_ip4_route (known_routes, route))
- (void) nm_platform_ip4_route_delete (ifindex, route->network, route->plen, route->metric);
+ 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;
+ /* 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 (!array_contains_ip6_route (known_routes, route))
- nm_platform_ip6_route_delete (ifindex, route->network, route->plen, route->metric);
+ 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) {
- 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, 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;
}