mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2026-05-01 04:28:00 +02:00
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:
parent
7c52d094ed
commit
4c3ba29b40
1 changed files with 173 additions and 39 deletions
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue