platform: avoid conflicts when reinstalling the device-route

Since f32075d2fc, we remove the kernel
added IPv4 device route, and re-add it with appropriate metric.

This could potentially replace existing, conflicting routes. Be more
careful and only take any action when we don't have a conflicting
route and when we add the address for the first time.

The motivation for this was libreswan which might install a VPN route
for a subnet that we also have configured on an interface. But the route
conflict could happen easily for other reasons, for example if you
configure a conflicting route manually.

Don't replace the device route if we have any indication that
a conflict could arise.

https://bugzilla.gnome.org/show_bug.cgi?id=723178
This commit is contained in:
Thomas Haller
2014-11-26 00:10:04 +01:00
parent e439478ccd
commit 5849c97c03
4 changed files with 102 additions and 33 deletions

View File

@@ -103,7 +103,7 @@ G_DEFINE_TYPE (NMLinuxPlatform, nm_linux_platform, NM_TYPE_PLATFORM)
static const char *to_string_object (NMPlatform *platform, struct nl_object *obj);
static gboolean _address_match (struct rtnl_addr *addr, int family, int ifindex);
static gboolean _route_match (struct rtnl_route *rtnlroute, int family, int ifindex);
static gboolean _route_match (struct rtnl_route *rtnlroute, int family, int ifindex, gboolean include_proto_kernel);
void
nm_linux_platform_setup (void)
@@ -1668,7 +1668,7 @@ announce_object (NMPlatform *platform, const struct nl_object *object, NMPlatfor
{
NMPlatformIP4Route route;
if (!_route_match ((struct rtnl_route *) object, AF_INET, 0)) {
if (!_route_match ((struct rtnl_route *) object, AF_INET, 0, FALSE)) {
nm_log_dbg (LOGD_PLATFORM, "skip announce unmatching IP4 route %s", to_string_ip4_route ((struct rtnl_route *) object));
return;
}
@@ -1680,7 +1680,7 @@ announce_object (NMPlatform *platform, const struct nl_object *object, NMPlatfor
{
NMPlatformIP6Route route;
if (!_route_match ((struct rtnl_route *) object, AF_INET6, 0)) {
if (!_route_match ((struct rtnl_route *) object, AF_INET6, 0, FALSE)) {
nm_log_dbg (LOGD_PLATFORM, "skip announce unmatching IP6 route %s", to_string_ip6_route ((struct rtnl_route *) object));
return;
}
@@ -3632,10 +3632,53 @@ ip6_address_exists (NMPlatform *platform, int ifindex, struct in6_addr addr, int
return ip_address_exists (platform, AF_INET6, ifindex, &addr, plen);
}
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);
NMPlatformIP4Address addr_candidate;
NMPlatformIP4Route route_candidate;
struct nl_object *object;
guint32 device_network;
for (object = nl_cache_get_first (priv->address_cache); object; object = nl_cache_get_next (object)) {
if (_address_match ((struct rtnl_addr *) object, AF_INET, 0)) {
if (init_ip4_address (&addr_candidate, (struct rtnl_addr *) object))
if ( addr_candidate.plen == address->plen
&& addr_candidate.address == address->address) {
/* If we already have the same address installed on any interface,
* we back off.
* Perform this check first, as we expect to have significantly less
* addresses to search. */
return FALSE;
}
}
}
device_network = nm_utils_ip4_address_clear_host_address (address->address, address->plen);
for (object = nl_cache_get_first (priv->route_cache); object; object = nl_cache_get_next (object)) {
if (_route_match ((struct rtnl_route *) object, AF_INET, 0, TRUE)) {
if (init_ip4_route (&route_candidate, (struct rtnl_route *) object)) {
if ( route_candidate.network == device_network
&& route_candidate.plen == address->plen
&& ( route_candidate.metric == 0
|| route_candidate.metric == device_route_metric)) {
/* There is already any route with metric 0 or the metric we want to install
* for the same subnet. */
return FALSE;
}
}
}
}
return TRUE;
}
/******************************************************************/
static gboolean
_route_match (struct rtnl_route *rtnlroute, int family, int ifindex)
_route_match (struct rtnl_route *rtnlroute, int family, int ifindex, gboolean include_proto_kernel)
{
struct rtnl_nexthop *nexthop;
@@ -3643,7 +3686,7 @@ _route_match (struct rtnl_route *rtnlroute, int family, int ifindex)
if (rtnl_route_get_type (rtnlroute) != RTN_UNICAST ||
rtnl_route_get_table (rtnlroute) != RT_TABLE_MAIN ||
rtnl_route_get_protocol (rtnlroute) == RTPROT_KERNEL ||
(!include_proto_kernel && rtnl_route_get_protocol (rtnlroute) == RTPROT_KERNEL) ||
rtnl_route_get_family (rtnlroute) != family ||
rtnl_route_get_nnexthops (rtnlroute) != 1 ||
rtnl_route_get_flags (rtnlroute) & RTM_F_CLONED)
@@ -3669,7 +3712,7 @@ ip4_route_get_all (NMPlatform *platform, int ifindex, NMPlatformGetRouteMode mod
routes = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP4Route));
for (object = nl_cache_get_first (priv->route_cache); object; object = nl_cache_get_next (object)) {
if (_route_match ((struct rtnl_route *) object, AF_INET, ifindex)) {
if (_route_match ((struct rtnl_route *) object, AF_INET, ifindex, FALSE)) {
if (_rtnl_route_is_default ((struct rtnl_route *) object)) {
if (mode == NM_PLATFORM_GET_ROUTE_MODE_NO_DEFAULT)
continue;
@@ -3698,7 +3741,7 @@ ip6_route_get_all (NMPlatform *platform, int ifindex, NMPlatformGetRouteMode mod
routes = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP6Route));
for (object = nl_cache_get_first (priv->route_cache); object; object = nl_cache_get_next (object)) {
if (_route_match ((struct rtnl_route *) object, AF_INET6, ifindex)) {
if (_route_match ((struct rtnl_route *) object, AF_INET6, ifindex, FALSE)) {
if (_rtnl_route_is_default ((struct rtnl_route *) object)) {
if (mode == NM_PLATFORM_GET_ROUTE_MODE_NO_DEFAULT)
continue;
@@ -3805,7 +3848,7 @@ route_search_cache (struct nl_cache *cache, int family, int ifindex, const void
struct nl_addr *dst;
struct rtnl_route *rtnlroute = (struct rtnl_route *) object;
if (!_route_match (rtnlroute, family, ifindex))
if (!_route_match (rtnlroute, family, ifindex, FALSE))
continue;
if (metric && metric != rtnl_route_get_priority (rtnlroute))
@@ -4362,6 +4405,8 @@ 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;