diff options
author | Lubomir Rintel <lkundrak@v3.sk> | 2015-01-05 18:12:15 +0100 |
---|---|---|
committer | Lubomir Rintel <lkundrak@v3.sk> | 2015-02-27 16:48:28 +0100 |
commit | 4c3ba29b4041a0644c7605898ac9c56efc2dafb2 (patch) | |
tree | c8ca92a73e9c0d0856e0a1f0f3d02ec4ae25daff | |
parent | 7c52d094ed33a2c1a9549a6b4917a68845cab251 (diff) | |
download | NetworkManager-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.c | 210 |
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; } |