2013-03-27 22:23:24 +01:00
|
|
|
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
|
|
|
|
|
/* nm-linux-platform.c - Linux kernel & udev network configuration layer
|
|
|
|
|
*
|
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
|
* the Free Software Foundation; either version 2, or (at your option)
|
|
|
|
|
* any later version.
|
|
|
|
|
*
|
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU General Public License along
|
|
|
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
|
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
|
|
|
*
|
|
|
|
|
* Copyright (C) 2012-2013 Red Hat, Inc.
|
|
|
|
|
*/
|
2014-11-13 10:07:02 -05:00
|
|
|
#include "config.h"
|
2013-03-27 22:23:24 +01:00
|
|
|
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
#include <sys/socket.h>
|
2013-04-03 16:10:38 +02:00
|
|
|
#include <fcntl.h>
|
2014-02-23 14:22:32 +01:00
|
|
|
#include <dlfcn.h>
|
2013-03-27 22:23:24 +01:00
|
|
|
#include <netinet/icmp6.h>
|
|
|
|
|
#include <netinet/in.h>
|
2013-05-21 12:49:24 -03:00
|
|
|
#include <linux/ip.h>
|
2013-03-27 22:23:24 +01:00
|
|
|
#include <linux/if_arp.h>
|
2013-05-06 09:16:17 -04:00
|
|
|
#include <linux/if_link.h>
|
2013-04-25 15:46:39 -04:00
|
|
|
#include <linux/if_tun.h>
|
2013-05-21 12:49:24 -03:00
|
|
|
#include <linux/if_tunnel.h>
|
2013-03-27 22:53:55 +01:00
|
|
|
#include <sys/ioctl.h>
|
|
|
|
|
#include <linux/sockios.h>
|
|
|
|
|
#include <linux/ethtool.h>
|
2013-05-20 15:38:54 -03:00
|
|
|
#include <linux/mii.h>
|
2013-03-27 22:23:24 +01:00
|
|
|
#include <netlink/netlink.h>
|
|
|
|
|
#include <netlink/object.h>
|
|
|
|
|
#include <netlink/cache.h>
|
|
|
|
|
#include <netlink/route/link.h>
|
|
|
|
|
#include <netlink/route/link/vlan.h>
|
2013-03-27 22:23:24 +01:00
|
|
|
#include <netlink/route/addr.h>
|
2013-03-27 22:23:24 +01:00
|
|
|
#include <netlink/route/route.h>
|
2013-05-29 12:00:50 -03:00
|
|
|
#include <gudev/gudev.h>
|
2013-03-27 22:23:24 +01:00
|
|
|
|
2014-09-29 17:58:44 +02:00
|
|
|
#if HAVE_LIBNL_INET6_ADDR_GEN_MODE || HAVE_LIBNL_INET6_TOKEN
|
2014-07-24 15:57:08 -05:00
|
|
|
#include <netlink/route/link/inet6.h>
|
2014-09-29 17:58:44 +02:00
|
|
|
#if HAVE_LIBNL_INET6_ADDR_GEN_MODE && HAVE_KERNEL_INET6_ADDR_GEN_MODE
|
2014-07-24 15:57:08 -05:00
|
|
|
#include <linux/if_link.h>
|
|
|
|
|
#else
|
|
|
|
|
#define IN6_ADDR_GEN_MODE_EUI64 0
|
|
|
|
|
#define IN6_ADDR_GEN_MODE_NONE 1
|
|
|
|
|
#endif
|
|
|
|
|
#endif
|
|
|
|
|
|
2014-10-29 17:23:46 +01:00
|
|
|
#include "gsystem-local-alloc.h"
|
2015-02-22 11:54:03 +01:00
|
|
|
#include "nm-core-internal.h"
|
2013-12-10 19:04:22 +01:00
|
|
|
#include "NetworkManagerUtils.h"
|
2013-03-27 22:23:24 +01:00
|
|
|
#include "nm-linux-platform.h"
|
2014-01-21 11:04:26 +01:00
|
|
|
#include "NetworkManagerUtils.h"
|
|
|
|
|
#include "nm-utils.h"
|
2013-03-27 22:23:24 +01:00
|
|
|
#include "nm-logging.h"
|
2013-10-16 12:29:13 -05:00
|
|
|
#include "wifi/wifi-utils.h"
|
2014-02-04 14:27:03 +01:00
|
|
|
#include "wifi/wifi-utils-wext.h"
|
2013-03-27 22:23:24 +01:00
|
|
|
|
|
|
|
|
/* This is only included for the translation of VLAN flags */
|
|
|
|
|
#include "nm-setting-vlan.h"
|
|
|
|
|
|
|
|
|
|
#define debug(...) nm_log_dbg (LOGD_PLATFORM, __VA_ARGS__)
|
|
|
|
|
#define warning(...) nm_log_warn (LOGD_PLATFORM, __VA_ARGS__)
|
|
|
|
|
#define error(...) nm_log_err (LOGD_PLATFORM, __VA_ARGS__)
|
|
|
|
|
|
2014-02-23 14:22:32 +01:00
|
|
|
|
|
|
|
|
struct libnl_vtable
|
|
|
|
|
{
|
|
|
|
|
void *handle;
|
|
|
|
|
|
|
|
|
|
int (*f_nl_has_capability) (int capability);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2013-03-27 22:23:24 +01:00
|
|
|
typedef struct {
|
|
|
|
|
struct nl_sock *nlh;
|
|
|
|
|
struct nl_sock *nlh_event;
|
|
|
|
|
struct nl_cache *link_cache;
|
2013-03-27 22:23:24 +01:00
|
|
|
struct nl_cache *address_cache;
|
2013-03-27 22:23:24 +01:00
|
|
|
struct nl_cache *route_cache;
|
2013-03-27 22:23:24 +01:00
|
|
|
GIOChannel *event_channel;
|
|
|
|
|
guint event_id;
|
2013-05-29 12:00:50 -03:00
|
|
|
|
|
|
|
|
GUdevClient *udev_client;
|
|
|
|
|
GHashTable *udev_devices;
|
2014-01-07 17:21:12 +01:00
|
|
|
|
2014-02-04 14:27:03 +01:00
|
|
|
GHashTable *wifi_data;
|
|
|
|
|
|
2014-01-07 17:21:12 +01:00
|
|
|
int support_kernel_extended_ifa_flags;
|
2014-07-24 15:57:08 -05:00
|
|
|
int support_user_ipv6ll;
|
2013-03-27 22:23:24 +01:00
|
|
|
} NMLinuxPlatformPrivate;
|
|
|
|
|
|
|
|
|
|
#define NM_LINUX_PLATFORM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_LINUX_PLATFORM, NMLinuxPlatformPrivate))
|
|
|
|
|
|
|
|
|
|
G_DEFINE_TYPE (NMLinuxPlatform, nm_linux_platform, NM_TYPE_PLATFORM)
|
|
|
|
|
|
2014-02-17 14:20:44 +01:00
|
|
|
static const char *to_string_object (NMPlatform *platform, struct nl_object *obj);
|
2014-06-24 13:47:25 +02:00
|
|
|
static gboolean _address_match (struct rtnl_addr *addr, int family, int ifindex);
|
2014-11-26 00:10:04 +01:00
|
|
|
static gboolean _route_match (struct rtnl_route *rtnlroute, int family, int ifindex, gboolean include_proto_kernel);
|
2014-02-17 14:20:44 +01:00
|
|
|
|
2013-03-27 22:23:24 +01:00
|
|
|
void
|
|
|
|
|
nm_linux_platform_setup (void)
|
|
|
|
|
{
|
|
|
|
|
nm_platform_setup (NM_TYPE_LINUX_PLATFORM);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
|
|
|
|
2014-02-23 14:22:32 +01:00
|
|
|
static int
|
|
|
|
|
_nl_f_nl_has_capability (int capability)
|
|
|
|
|
{
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct libnl_vtable *
|
2014-10-29 10:44:31 +01:00
|
|
|
_nl_get_vtable (void)
|
2014-02-23 14:22:32 +01:00
|
|
|
{
|
|
|
|
|
static struct libnl_vtable vtable;
|
|
|
|
|
|
|
|
|
|
if (G_UNLIKELY (!vtable.f_nl_has_capability)) {
|
|
|
|
|
void *handle;
|
|
|
|
|
|
|
|
|
|
handle = dlopen ("libnl-3.so", RTLD_LAZY | RTLD_NOLOAD);
|
|
|
|
|
if (handle) {
|
|
|
|
|
vtable.handle = handle;
|
|
|
|
|
vtable.f_nl_has_capability = dlsym (handle, "nl_has_capability");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!vtable.f_nl_has_capability)
|
|
|
|
|
vtable.f_nl_has_capability = &_nl_f_nl_has_capability;
|
|
|
|
|
|
|
|
|
|
g_return_val_if_fail (vtable.handle, &vtable);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return &vtable;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
_nl_has_capability (int capability)
|
|
|
|
|
{
|
|
|
|
|
return (_nl_get_vtable ()->f_nl_has_capability) (capability);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
|
|
|
|
platform: fix preferred and valid lifetimes for addresses from netlink/kernel
The kernel tells the address lifetimes in the 'struct ifa_cacheinfo'
attribute. This contains two timestamps (cstamp and tstamp) and two
relative lifetimes (ifa_prefered and ifa_valid).
The timestamps are equal to clock_gettime(CLOCK_MONOTONIC) scale in
1/100th of a second (wrapping every 497 days).
The preferred/valid times are re-adjusted everytime when sending the
message and count down as the time goes by. In other words, they are
anchored relatively to the moment of when kernel creates the netlink
message.
As platform is caching the rtnl_addr object, the information of *when* the
lifetimes started counting is not available.
This patch fixes reading these values by hacking the libnl object
when it gets received, so that valid and preferred are instead absolute
expiration timestamps in scale nm_utils_get_monotonic_timestamp_s() --
which NM internally is used for address timestamps.
There are two minor downsides to this hack:
- the valid and preferred properties of a cached rtnl_addr object have
an unexpected meaning, i.e. they are absolute and in a different time
scale.
- later when converting rtnl_addr to NMPlatformIPAddress, the base
timestamp is set to "1", i.e. an NMPlatformIPAddress has no knowledge
of when the address was created or last modified. The timestamp
property of NMPlatformIPAddress is solely there to anchor the relative
timestamps lifetime and preferred. Do not use it for anything
else.
Another reason the timestamp property is meaningless is that
its scale nm_utils_get_monotonic_timestamp_s() starts counting at
process start. So addresses that existed before would have a negative
or zero timestamp, which we avoid. This in turn could be solved by either
allowing negative timestamps or by shifting
nm_utils_get_monotonic_timestamp_*(). Both is viable, but not
necessary (ATM), because the age of an address has no other apparent
use then to anchor the relative timestamps.
Another implication is, that we potentially could get rid of the
timestamp completely, and insteat make preferred and lifetime be
absolute expiries.
This will be fixed properly later, by not caching libnl objects but instead
native NMPlatform objects. For those we have full control over their properties.
https://bugzilla.gnome.org/show_bug.cgi?id=727382
Signed-off-by: Thomas Haller <thaller@redhat.com>
2014-04-02 13:35:21 +02:00
|
|
|
static guint32
|
|
|
|
|
_get_expiry (guint32 now_s, guint32 lifetime_s)
|
|
|
|
|
{
|
|
|
|
|
gint64 t = ((gint64) now_s) + ((gint64) lifetime_s);
|
|
|
|
|
|
|
|
|
|
return MIN (t, NM_PLATFORM_LIFETIME_PERMANENT - 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* The rtnl_addr object contains relative lifetimes @valid and @preferred
|
|
|
|
|
* that count in seconds, starting from the moment when the kernel constructed
|
|
|
|
|
* the netlink message.
|
|
|
|
|
*
|
|
|
|
|
* There is also a field rtnl_addr_last_update_time(), which is the absolute
|
|
|
|
|
* time in 1/100th of a second of clock_gettime (CLOCK_MONOTONIC) when the address
|
|
|
|
|
* was modified (wrapping every 497 days).
|
|
|
|
|
* Immediately at the time when the address was last modified, #NOW and @last_update_time
|
|
|
|
|
* are the same, so (only) in that case @valid and @preferred are anchored at @last_update_time.
|
|
|
|
|
* However, this is not true in general. As time goes by, whenever kernel sends a new address
|
|
|
|
|
* via netlink, the lifetimes keep counting down.
|
|
|
|
|
*
|
|
|
|
|
* As we cache the rtnl_addr object we must know the absolute expiries.
|
|
|
|
|
* As a hack, modify the relative timestamps valid and preferred into absolute
|
|
|
|
|
* timestamps of scale nm_utils_get_monotonic_timestamp_s().
|
|
|
|
|
**/
|
|
|
|
|
static void
|
|
|
|
|
_rtnl_addr_hack_lifetimes_rel_to_abs (struct rtnl_addr *rtnladdr)
|
|
|
|
|
{
|
|
|
|
|
guint32 a_valid = rtnl_addr_get_valid_lifetime (rtnladdr);
|
|
|
|
|
guint32 a_preferred = rtnl_addr_get_preferred_lifetime (rtnladdr);
|
|
|
|
|
guint32 now;
|
|
|
|
|
|
|
|
|
|
if (a_valid == NM_PLATFORM_LIFETIME_PERMANENT &&
|
|
|
|
|
a_preferred == NM_PLATFORM_LIFETIME_PERMANENT)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
now = (guint32) nm_utils_get_monotonic_timestamp_s ();
|
|
|
|
|
|
|
|
|
|
if (a_preferred > a_valid)
|
|
|
|
|
a_preferred = a_valid;
|
|
|
|
|
|
|
|
|
|
if (a_valid != NM_PLATFORM_LIFETIME_PERMANENT)
|
|
|
|
|
rtnl_addr_set_valid_lifetime (rtnladdr, _get_expiry (now, a_valid));
|
|
|
|
|
rtnl_addr_set_preferred_lifetime (rtnladdr, _get_expiry (now, a_preferred));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
|
|
|
|
2013-03-27 22:23:24 +01:00
|
|
|
/* libnl library workarounds and additions */
|
|
|
|
|
|
|
|
|
|
/* Automatic deallocation of local variables */
|
2013-08-02 14:25:07 +02:00
|
|
|
#define auto_nl_cache __attribute__((cleanup(put_nl_cache)))
|
|
|
|
|
static void
|
|
|
|
|
put_nl_cache (void *ptr)
|
|
|
|
|
{
|
|
|
|
|
struct nl_cache **cache = ptr;
|
|
|
|
|
|
|
|
|
|
if (cache && *cache) {
|
|
|
|
|
nl_cache_free (*cache);
|
|
|
|
|
*cache = NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-27 22:23:24 +01:00
|
|
|
#define auto_nl_object __attribute__((cleanup(put_nl_object)))
|
|
|
|
|
static void
|
|
|
|
|
put_nl_object (void *ptr)
|
|
|
|
|
{
|
|
|
|
|
struct nl_object **object = ptr;
|
|
|
|
|
|
|
|
|
|
if (object && *object) {
|
|
|
|
|
nl_object_put (*object);
|
|
|
|
|
*object = NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-27 22:23:24 +01:00
|
|
|
#define auto_nl_addr __attribute__((cleanup(put_nl_addr)))
|
|
|
|
|
static void
|
|
|
|
|
put_nl_addr (void *ptr)
|
|
|
|
|
{
|
|
|
|
|
struct nl_addr **object = ptr;
|
|
|
|
|
|
|
|
|
|
if (object && *object) {
|
|
|
|
|
nl_addr_put (*object);
|
|
|
|
|
*object = NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-04-29 16:00:48 +02:00
|
|
|
/*******************************************************************/
|
|
|
|
|
|
|
|
|
|
/* wrap the libnl alloc functions and abort on out-of-memory*/
|
|
|
|
|
|
|
|
|
|
static struct nl_addr *
|
|
|
|
|
_nm_nl_addr_build (int family, const void *buf, size_t size)
|
|
|
|
|
{
|
|
|
|
|
struct nl_addr *addr;
|
|
|
|
|
|
|
|
|
|
addr = nl_addr_build (family, (void *) buf, size);
|
|
|
|
|
if (!addr)
|
|
|
|
|
g_error ("nl_addr_build() failed with out of memory");
|
|
|
|
|
|
|
|
|
|
return addr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct rtnl_link *
|
|
|
|
|
_nm_rtnl_link_alloc (int ifindex, const char*name)
|
|
|
|
|
{
|
|
|
|
|
struct rtnl_link *rtnllink;
|
|
|
|
|
|
|
|
|
|
rtnllink = rtnl_link_alloc ();
|
|
|
|
|
if (!rtnllink)
|
|
|
|
|
g_error ("rtnl_link_alloc() failed with out of memory");
|
|
|
|
|
|
|
|
|
|
if (ifindex > 0)
|
|
|
|
|
rtnl_link_set_ifindex (rtnllink, ifindex);
|
|
|
|
|
if (name)
|
|
|
|
|
rtnl_link_set_name (rtnllink, name);
|
|
|
|
|
return rtnllink;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct rtnl_addr *
|
|
|
|
|
_nm_rtnl_addr_alloc (int ifindex)
|
|
|
|
|
{
|
|
|
|
|
struct rtnl_addr *rtnladdr;
|
|
|
|
|
|
|
|
|
|
rtnladdr = rtnl_addr_alloc ();
|
|
|
|
|
if (!rtnladdr)
|
|
|
|
|
g_error ("rtnl_addr_alloc() failed with out of memory");
|
|
|
|
|
if (ifindex > 0)
|
|
|
|
|
rtnl_addr_set_ifindex (rtnladdr, ifindex);
|
|
|
|
|
return rtnladdr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct rtnl_route *
|
2014-10-29 10:44:31 +01:00
|
|
|
_nm_rtnl_route_alloc (void)
|
2014-04-29 16:00:48 +02:00
|
|
|
{
|
|
|
|
|
struct rtnl_route *rtnlroute = rtnl_route_alloc ();
|
|
|
|
|
|
|
|
|
|
if (!rtnlroute)
|
|
|
|
|
g_error ("rtnl_route_alloc() failed with out of memory");
|
|
|
|
|
return rtnlroute;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct rtnl_nexthop *
|
2014-10-29 10:44:31 +01:00
|
|
|
_nm_rtnl_route_nh_alloc (void)
|
2014-04-29 16:00:48 +02:00
|
|
|
{
|
|
|
|
|
struct rtnl_nexthop *nexthop;
|
|
|
|
|
|
|
|
|
|
nexthop = rtnl_route_nh_alloc ();
|
|
|
|
|
if (!nexthop)
|
|
|
|
|
g_error ("rtnl_route_nh_alloc () failed with out of memory");
|
|
|
|
|
return nexthop;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*******************************************************************/
|
2013-03-27 22:23:24 +01:00
|
|
|
|
2013-03-27 22:23:24 +01:00
|
|
|
/* rtnl_addr_set_prefixlen fails to update the nl_addr prefixlen */
|
|
|
|
|
static void
|
|
|
|
|
nm_rtnl_addr_set_prefixlen (struct rtnl_addr *rtnladdr, int plen)
|
|
|
|
|
{
|
|
|
|
|
struct nl_addr *nladdr;
|
|
|
|
|
|
|
|
|
|
rtnl_addr_set_prefixlen (rtnladdr, plen);
|
|
|
|
|
|
|
|
|
|
nladdr = rtnl_addr_get_local (rtnladdr);
|
|
|
|
|
if (nladdr)
|
|
|
|
|
nl_addr_set_prefixlen (nladdr, plen);
|
|
|
|
|
}
|
|
|
|
|
#define rtnl_addr_set_prefixlen nm_rtnl_addr_set_prefixlen
|
|
|
|
|
|
2013-03-27 22:23:24 +01:00
|
|
|
typedef enum {
|
2014-04-05 12:39:00 +02:00
|
|
|
OBJECT_TYPE_UNKNOWN,
|
|
|
|
|
OBJECT_TYPE_LINK,
|
|
|
|
|
OBJECT_TYPE_IP4_ADDRESS,
|
|
|
|
|
OBJECT_TYPE_IP6_ADDRESS,
|
|
|
|
|
OBJECT_TYPE_IP4_ROUTE,
|
|
|
|
|
OBJECT_TYPE_IP6_ROUTE,
|
|
|
|
|
__OBJECT_TYPE_LAST,
|
2013-03-27 22:23:24 +01:00
|
|
|
} ObjectType;
|
|
|
|
|
|
|
|
|
|
static ObjectType
|
|
|
|
|
object_type_from_nl_object (const struct nl_object *object)
|
|
|
|
|
{
|
2014-02-11 22:10:05 +01:00
|
|
|
const char *type_str;
|
2013-07-27 00:42:10 +02:00
|
|
|
|
2014-02-11 22:10:05 +01:00
|
|
|
if (!object || !(type_str = nl_object_get_type (object)))
|
2014-04-05 12:39:00 +02:00
|
|
|
return OBJECT_TYPE_UNKNOWN;
|
2013-03-27 22:23:24 +01:00
|
|
|
|
2013-07-27 00:42:10 +02:00
|
|
|
if (!strcmp (type_str, "route/link"))
|
2014-04-05 12:39:00 +02:00
|
|
|
return OBJECT_TYPE_LINK;
|
2013-07-27 00:42:10 +02:00
|
|
|
else if (!strcmp (type_str, "route/addr")) {
|
2013-03-27 22:23:24 +01:00
|
|
|
switch (rtnl_addr_get_family ((struct rtnl_addr *) object)) {
|
|
|
|
|
case AF_INET:
|
2014-04-05 12:39:00 +02:00
|
|
|
return OBJECT_TYPE_IP4_ADDRESS;
|
2013-03-27 22:23:24 +01:00
|
|
|
case AF_INET6:
|
2014-04-05 12:39:00 +02:00
|
|
|
return OBJECT_TYPE_IP6_ADDRESS;
|
2013-03-27 22:23:24 +01:00
|
|
|
default:
|
2014-04-05 12:39:00 +02:00
|
|
|
return OBJECT_TYPE_UNKNOWN;
|
2013-03-27 22:23:24 +01:00
|
|
|
}
|
2013-07-27 00:42:10 +02:00
|
|
|
} else if (!strcmp (type_str, "route/route")) {
|
2013-03-27 22:23:24 +01:00
|
|
|
switch (rtnl_route_get_family ((struct rtnl_route *) object)) {
|
|
|
|
|
case AF_INET:
|
2014-04-05 12:39:00 +02:00
|
|
|
return OBJECT_TYPE_IP4_ROUTE;
|
2013-03-27 22:23:24 +01:00
|
|
|
case AF_INET6:
|
2014-04-05 12:39:00 +02:00
|
|
|
return OBJECT_TYPE_IP6_ROUTE;
|
2013-03-27 22:23:24 +01:00
|
|
|
default:
|
2014-04-05 12:39:00 +02:00
|
|
|
return OBJECT_TYPE_UNKNOWN;
|
2013-03-27 22:23:24 +01:00
|
|
|
}
|
2013-03-27 22:23:24 +01:00
|
|
|
} else
|
2014-04-05 12:39:00 +02:00
|
|
|
return OBJECT_TYPE_UNKNOWN;
|
2013-03-27 22:23:24 +01:00
|
|
|
}
|
|
|
|
|
|
platform: fix caching for link types
This bug was present since a long time, however libnl3-v3.2.23
(commit fdd1ba220dd7b780400e9d0652cde80e59f63572) changed the returned
family of bridge link objects, which breaks NetworkManager.
This resulted in error messages such as:
DBG<4> object.c:207 nl_object_get: New reference to object 0x19c34b0, total 2
DBG<5> route/link.c:895 link_keygen: link 0x19c34b0 key (dev 9 fam 7) keysz 8, hash 0x2b2
DBG<2> hashtable.c:127 nl_hash_table_add: Warning: Add to hashtable found duplicate...
DBG<4> object.c:221 nl_object_put: Returned object reference 0x19c34b0, 1 remaining
NetworkManager[17745]: <error> [1392114373.475432] [platform/nm-linux-platform.c:1328] event_notification(): netlink cache error: Object exists
Even before the change of libnl, I saw the following error lines
<debug> [...] [platform/nm-linux-platform.c:1216] event_notification(): netlink event (type 16) for link: virbr0 (4)
<error> [...] [platform/nm-linux-platform.c:1265] event_notification(): netlink cache error: Object exists
Hence, the caching mechanism for libnl objects already had a bug.
For rtnl link objects, the identifier consists of family and ifindex.
Since in upper layers, we don't easily know the family, we need a way to find
the objects inside the cache. We do this, by only caching links of family
AF_UNSPEC.
Objects that we receive via event_notification() are never cached. They are only used
to trigger refetching the kernel_object. Their family is irrelevant, we
only need to know, that something about this ifindex changed.
For objects retrieved via get_kernel_object(), we only get link objects of
family AF_UNSPEC or AF_BRIDGE. In any case, we reset (coerce) their family
before caching. This way, inside the link cache, there are only objects with
(coerced) family AF_UNSPEC. We loose the information, which family the
link had, however we don't need it anyway.
https://bugzilla.gnome.org/show_bug.cgi?id=719905
https://bugzilla.redhat.com/show_bug.cgi?id=1063290
Duplicates:
https://bugzilla.gnome.org/show_bug.cgi?id=724225
https://bugzilla.redhat.com/show_bug.cgi?id=1063800
Signed-off-by: Thomas Haller <thaller@redhat.com>
2014-02-11 20:16:03 +01:00
|
|
|
static void
|
|
|
|
|
_nl_link_family_unset (struct nl_object *obj, int *family)
|
|
|
|
|
{
|
2014-04-05 12:39:00 +02:00
|
|
|
if (!obj || object_type_from_nl_object (obj) != OBJECT_TYPE_LINK)
|
platform: fix caching for link types
This bug was present since a long time, however libnl3-v3.2.23
(commit fdd1ba220dd7b780400e9d0652cde80e59f63572) changed the returned
family of bridge link objects, which breaks NetworkManager.
This resulted in error messages such as:
DBG<4> object.c:207 nl_object_get: New reference to object 0x19c34b0, total 2
DBG<5> route/link.c:895 link_keygen: link 0x19c34b0 key (dev 9 fam 7) keysz 8, hash 0x2b2
DBG<2> hashtable.c:127 nl_hash_table_add: Warning: Add to hashtable found duplicate...
DBG<4> object.c:221 nl_object_put: Returned object reference 0x19c34b0, 1 remaining
NetworkManager[17745]: <error> [1392114373.475432] [platform/nm-linux-platform.c:1328] event_notification(): netlink cache error: Object exists
Even before the change of libnl, I saw the following error lines
<debug> [...] [platform/nm-linux-platform.c:1216] event_notification(): netlink event (type 16) for link: virbr0 (4)
<error> [...] [platform/nm-linux-platform.c:1265] event_notification(): netlink cache error: Object exists
Hence, the caching mechanism for libnl objects already had a bug.
For rtnl link objects, the identifier consists of family and ifindex.
Since in upper layers, we don't easily know the family, we need a way to find
the objects inside the cache. We do this, by only caching links of family
AF_UNSPEC.
Objects that we receive via event_notification() are never cached. They are only used
to trigger refetching the kernel_object. Their family is irrelevant, we
only need to know, that something about this ifindex changed.
For objects retrieved via get_kernel_object(), we only get link objects of
family AF_UNSPEC or AF_BRIDGE. In any case, we reset (coerce) their family
before caching. This way, inside the link cache, there are only objects with
(coerced) family AF_UNSPEC. We loose the information, which family the
link had, however we don't need it anyway.
https://bugzilla.gnome.org/show_bug.cgi?id=719905
https://bugzilla.redhat.com/show_bug.cgi?id=1063290
Duplicates:
https://bugzilla.gnome.org/show_bug.cgi?id=724225
https://bugzilla.redhat.com/show_bug.cgi?id=1063800
Signed-off-by: Thomas Haller <thaller@redhat.com>
2014-02-11 20:16:03 +01:00
|
|
|
*family = AF_UNSPEC;
|
|
|
|
|
else {
|
|
|
|
|
*family = rtnl_link_get_family ((struct rtnl_link *) obj);
|
|
|
|
|
|
|
|
|
|
/* Always explicitly set the family to AF_UNSPEC, even if rtnl_link_get_family() might
|
|
|
|
|
* already return %AF_UNSPEC. The reason is, that %AF_UNSPEC is the default family
|
|
|
|
|
* and libnl nl_object_identical() function will only succeed, if the family is
|
|
|
|
|
* explicitly set (which we cannot be sure, unless setting it). */
|
|
|
|
|
rtnl_link_set_family ((struct rtnl_link *) obj, AF_UNSPEC);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* In our link cache, we coerce the family of all link objects to AF_UNSPEC.
|
|
|
|
|
* Thus, before searching for an object, we fixup @needle to have the right
|
|
|
|
|
* id (by resetting the family). */
|
2013-03-27 22:23:24 +01:00
|
|
|
static struct nl_object *
|
|
|
|
|
nm_nl_cache_search (struct nl_cache *cache, struct nl_object *needle)
|
|
|
|
|
{
|
platform: fix caching for link types
This bug was present since a long time, however libnl3-v3.2.23
(commit fdd1ba220dd7b780400e9d0652cde80e59f63572) changed the returned
family of bridge link objects, which breaks NetworkManager.
This resulted in error messages such as:
DBG<4> object.c:207 nl_object_get: New reference to object 0x19c34b0, total 2
DBG<5> route/link.c:895 link_keygen: link 0x19c34b0 key (dev 9 fam 7) keysz 8, hash 0x2b2
DBG<2> hashtable.c:127 nl_hash_table_add: Warning: Add to hashtable found duplicate...
DBG<4> object.c:221 nl_object_put: Returned object reference 0x19c34b0, 1 remaining
NetworkManager[17745]: <error> [1392114373.475432] [platform/nm-linux-platform.c:1328] event_notification(): netlink cache error: Object exists
Even before the change of libnl, I saw the following error lines
<debug> [...] [platform/nm-linux-platform.c:1216] event_notification(): netlink event (type 16) for link: virbr0 (4)
<error> [...] [platform/nm-linux-platform.c:1265] event_notification(): netlink cache error: Object exists
Hence, the caching mechanism for libnl objects already had a bug.
For rtnl link objects, the identifier consists of family and ifindex.
Since in upper layers, we don't easily know the family, we need a way to find
the objects inside the cache. We do this, by only caching links of family
AF_UNSPEC.
Objects that we receive via event_notification() are never cached. They are only used
to trigger refetching the kernel_object. Their family is irrelevant, we
only need to know, that something about this ifindex changed.
For objects retrieved via get_kernel_object(), we only get link objects of
family AF_UNSPEC or AF_BRIDGE. In any case, we reset (coerce) their family
before caching. This way, inside the link cache, there are only objects with
(coerced) family AF_UNSPEC. We loose the information, which family the
link had, however we don't need it anyway.
https://bugzilla.gnome.org/show_bug.cgi?id=719905
https://bugzilla.redhat.com/show_bug.cgi?id=1063290
Duplicates:
https://bugzilla.gnome.org/show_bug.cgi?id=724225
https://bugzilla.redhat.com/show_bug.cgi?id=1063800
Signed-off-by: Thomas Haller <thaller@redhat.com>
2014-02-11 20:16:03 +01:00
|
|
|
int family;
|
|
|
|
|
struct nl_object *obj;
|
|
|
|
|
|
|
|
|
|
_nl_link_family_unset (needle, &family);
|
|
|
|
|
obj = nl_cache_search (cache, needle);
|
|
|
|
|
if (family != AF_UNSPEC) {
|
|
|
|
|
/* restore the family of the @needle instance. If the family was
|
|
|
|
|
* unset before, we cannot make it unset again. Thus, in that case
|
|
|
|
|
* we cannot undo _nl_link_family_unset() entirely. */
|
|
|
|
|
rtnl_link_set_family ((struct rtnl_link *) needle, family);
|
|
|
|
|
}
|
2013-03-27 22:23:24 +01:00
|
|
|
|
platform: fix caching for link types
This bug was present since a long time, however libnl3-v3.2.23
(commit fdd1ba220dd7b780400e9d0652cde80e59f63572) changed the returned
family of bridge link objects, which breaks NetworkManager.
This resulted in error messages such as:
DBG<4> object.c:207 nl_object_get: New reference to object 0x19c34b0, total 2
DBG<5> route/link.c:895 link_keygen: link 0x19c34b0 key (dev 9 fam 7) keysz 8, hash 0x2b2
DBG<2> hashtable.c:127 nl_hash_table_add: Warning: Add to hashtable found duplicate...
DBG<4> object.c:221 nl_object_put: Returned object reference 0x19c34b0, 1 remaining
NetworkManager[17745]: <error> [1392114373.475432] [platform/nm-linux-platform.c:1328] event_notification(): netlink cache error: Object exists
Even before the change of libnl, I saw the following error lines
<debug> [...] [platform/nm-linux-platform.c:1216] event_notification(): netlink event (type 16) for link: virbr0 (4)
<error> [...] [platform/nm-linux-platform.c:1265] event_notification(): netlink cache error: Object exists
Hence, the caching mechanism for libnl objects already had a bug.
For rtnl link objects, the identifier consists of family and ifindex.
Since in upper layers, we don't easily know the family, we need a way to find
the objects inside the cache. We do this, by only caching links of family
AF_UNSPEC.
Objects that we receive via event_notification() are never cached. They are only used
to trigger refetching the kernel_object. Their family is irrelevant, we
only need to know, that something about this ifindex changed.
For objects retrieved via get_kernel_object(), we only get link objects of
family AF_UNSPEC or AF_BRIDGE. In any case, we reset (coerce) their family
before caching. This way, inside the link cache, there are only objects with
(coerced) family AF_UNSPEC. We loose the information, which family the
link had, however we don't need it anyway.
https://bugzilla.gnome.org/show_bug.cgi?id=719905
https://bugzilla.redhat.com/show_bug.cgi?id=1063290
Duplicates:
https://bugzilla.gnome.org/show_bug.cgi?id=724225
https://bugzilla.redhat.com/show_bug.cgi?id=1063800
Signed-off-by: Thomas Haller <thaller@redhat.com>
2014-02-11 20:16:03 +01:00
|
|
|
return obj;
|
2013-03-27 22:23:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Ask the kernel for an object identical (as in nl_cache_identical) to the
|
|
|
|
|
* needle argument. This is a kernel counterpart for nl_cache_search.
|
|
|
|
|
*
|
platform: fix caching for link types
This bug was present since a long time, however libnl3-v3.2.23
(commit fdd1ba220dd7b780400e9d0652cde80e59f63572) changed the returned
family of bridge link objects, which breaks NetworkManager.
This resulted in error messages such as:
DBG<4> object.c:207 nl_object_get: New reference to object 0x19c34b0, total 2
DBG<5> route/link.c:895 link_keygen: link 0x19c34b0 key (dev 9 fam 7) keysz 8, hash 0x2b2
DBG<2> hashtable.c:127 nl_hash_table_add: Warning: Add to hashtable found duplicate...
DBG<4> object.c:221 nl_object_put: Returned object reference 0x19c34b0, 1 remaining
NetworkManager[17745]: <error> [1392114373.475432] [platform/nm-linux-platform.c:1328] event_notification(): netlink cache error: Object exists
Even before the change of libnl, I saw the following error lines
<debug> [...] [platform/nm-linux-platform.c:1216] event_notification(): netlink event (type 16) for link: virbr0 (4)
<error> [...] [platform/nm-linux-platform.c:1265] event_notification(): netlink cache error: Object exists
Hence, the caching mechanism for libnl objects already had a bug.
For rtnl link objects, the identifier consists of family and ifindex.
Since in upper layers, we don't easily know the family, we need a way to find
the objects inside the cache. We do this, by only caching links of family
AF_UNSPEC.
Objects that we receive via event_notification() are never cached. They are only used
to trigger refetching the kernel_object. Their family is irrelevant, we
only need to know, that something about this ifindex changed.
For objects retrieved via get_kernel_object(), we only get link objects of
family AF_UNSPEC or AF_BRIDGE. In any case, we reset (coerce) their family
before caching. This way, inside the link cache, there are only objects with
(coerced) family AF_UNSPEC. We loose the information, which family the
link had, however we don't need it anyway.
https://bugzilla.gnome.org/show_bug.cgi?id=719905
https://bugzilla.redhat.com/show_bug.cgi?id=1063290
Duplicates:
https://bugzilla.gnome.org/show_bug.cgi?id=724225
https://bugzilla.redhat.com/show_bug.cgi?id=1063800
Signed-off-by: Thomas Haller <thaller@redhat.com>
2014-02-11 20:16:03 +01:00
|
|
|
* The returned object must be freed by the caller with nl_object_put().
|
2013-03-27 22:23:24 +01:00
|
|
|
*/
|
|
|
|
|
static struct nl_object *
|
|
|
|
|
get_kernel_object (struct nl_sock *sock, struct nl_object *needle)
|
|
|
|
|
{
|
2014-02-11 20:12:12 +01:00
|
|
|
struct nl_object *object = NULL;
|
|
|
|
|
ObjectType type = object_type_from_nl_object (needle);
|
2013-03-27 22:23:24 +01:00
|
|
|
|
2014-02-11 20:12:12 +01:00
|
|
|
switch (type) {
|
2014-04-05 12:39:00 +02:00
|
|
|
case OBJECT_TYPE_LINK:
|
2013-03-27 22:23:24 +01:00
|
|
|
{
|
|
|
|
|
int ifindex = rtnl_link_get_ifindex ((struct rtnl_link *) needle);
|
|
|
|
|
const char *name = rtnl_link_get_name ((struct rtnl_link *) needle);
|
|
|
|
|
int nle;
|
|
|
|
|
|
2014-02-11 20:12:12 +01:00
|
|
|
nle = rtnl_link_get_kernel (sock, ifindex, name, (struct rtnl_link **) &object);
|
2013-03-27 22:23:24 +01:00
|
|
|
switch (nle) {
|
|
|
|
|
case -NLE_SUCCESS:
|
2014-02-11 20:12:12 +01:00
|
|
|
if (nm_logging_enabled (LOGL_DEBUG, LOGD_PLATFORM)) {
|
|
|
|
|
name = rtnl_link_get_name ((struct rtnl_link *) object);
|
|
|
|
|
debug ("get_kernel_object for link: %s (%d, family %d)",
|
|
|
|
|
name ? name : "(unknown)",
|
|
|
|
|
rtnl_link_get_ifindex ((struct rtnl_link *) object),
|
|
|
|
|
rtnl_link_get_family ((struct rtnl_link *) object));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_nl_link_family_unset (object, &nle);
|
|
|
|
|
return object;
|
2013-03-27 22:23:24 +01:00
|
|
|
case -NLE_NODEV:
|
2014-02-11 20:12:12 +01:00
|
|
|
debug ("get_kernel_object for link %s (%d) had no result",
|
|
|
|
|
name ? name : "(unknown)", ifindex);
|
2013-03-27 22:23:24 +01:00
|
|
|
return NULL;
|
|
|
|
|
default:
|
2014-02-11 20:12:12 +01:00
|
|
|
error ("get_kernel_object for link %s (%d) failed: %s (%d)",
|
|
|
|
|
name ? name : "(unknown)", ifindex, nl_geterror (nle), nle);
|
2013-03-27 22:23:24 +01:00
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-04-05 12:39:00 +02:00
|
|
|
case OBJECT_TYPE_IP4_ADDRESS:
|
|
|
|
|
case OBJECT_TYPE_IP6_ADDRESS:
|
|
|
|
|
case OBJECT_TYPE_IP4_ROUTE:
|
|
|
|
|
case OBJECT_TYPE_IP6_ROUTE:
|
2013-03-27 22:23:24 +01:00
|
|
|
/* Fallback to a one-time cache allocation. */
|
|
|
|
|
{
|
|
|
|
|
struct nl_cache *cache;
|
|
|
|
|
int nle;
|
|
|
|
|
|
2014-05-29 13:08:40 +02:00
|
|
|
/* FIXME: every time we refresh *one* object, we request an
|
|
|
|
|
* entire dump. E.g. check_cache_items() gets O(n2) complexitly. */
|
|
|
|
|
|
2013-03-27 22:23:24 +01:00
|
|
|
nle = nl_cache_alloc_and_fill (
|
|
|
|
|
nl_cache_ops_lookup (nl_object_get_type (needle)),
|
|
|
|
|
sock, &cache);
|
2014-02-11 20:12:12 +01:00
|
|
|
if (nle) {
|
|
|
|
|
error ("get_kernel_object for type %d failed: %s (%d)",
|
|
|
|
|
type, nl_geterror (nle), nle);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-27 22:23:24 +01:00
|
|
|
object = nl_cache_search (cache, needle);
|
|
|
|
|
|
2013-04-15 13:46:50 +02:00
|
|
|
nl_cache_free (cache);
|
2014-02-11 20:12:12 +01:00
|
|
|
|
platform: fix preferred and valid lifetimes for addresses from netlink/kernel
The kernel tells the address lifetimes in the 'struct ifa_cacheinfo'
attribute. This contains two timestamps (cstamp and tstamp) and two
relative lifetimes (ifa_prefered and ifa_valid).
The timestamps are equal to clock_gettime(CLOCK_MONOTONIC) scale in
1/100th of a second (wrapping every 497 days).
The preferred/valid times are re-adjusted everytime when sending the
message and count down as the time goes by. In other words, they are
anchored relatively to the moment of when kernel creates the netlink
message.
As platform is caching the rtnl_addr object, the information of *when* the
lifetimes started counting is not available.
This patch fixes reading these values by hacking the libnl object
when it gets received, so that valid and preferred are instead absolute
expiration timestamps in scale nm_utils_get_monotonic_timestamp_s() --
which NM internally is used for address timestamps.
There are two minor downsides to this hack:
- the valid and preferred properties of a cached rtnl_addr object have
an unexpected meaning, i.e. they are absolute and in a different time
scale.
- later when converting rtnl_addr to NMPlatformIPAddress, the base
timestamp is set to "1", i.e. an NMPlatformIPAddress has no knowledge
of when the address was created or last modified. The timestamp
property of NMPlatformIPAddress is solely there to anchor the relative
timestamps lifetime and preferred. Do not use it for anything
else.
Another reason the timestamp property is meaningless is that
its scale nm_utils_get_monotonic_timestamp_s() starts counting at
process start. So addresses that existed before would have a negative
or zero timestamp, which we avoid. This in turn could be solved by either
allowing negative timestamps or by shifting
nm_utils_get_monotonic_timestamp_*(). Both is viable, but not
necessary (ATM), because the age of an address has no other apparent
use then to anchor the relative timestamps.
Another implication is, that we potentially could get rid of the
timestamp completely, and insteat make preferred and lifetime be
absolute expiries.
This will be fixed properly later, by not caching libnl objects but instead
native NMPlatform objects. For those we have full control over their properties.
https://bugzilla.gnome.org/show_bug.cgi?id=727382
Signed-off-by: Thomas Haller <thaller@redhat.com>
2014-04-02 13:35:21 +02:00
|
|
|
if (object && (type == OBJECT_TYPE_IP4_ADDRESS || type == OBJECT_TYPE_IP6_ADDRESS))
|
|
|
|
|
_rtnl_addr_hack_lifetimes_rel_to_abs ((struct rtnl_addr *) object);
|
|
|
|
|
|
2014-02-11 20:12:12 +01:00
|
|
|
if (object)
|
|
|
|
|
debug ("get_kernel_object for type %d returned %p", type, object);
|
|
|
|
|
else
|
|
|
|
|
debug ("get_kernel_object for type %d had no result", type);
|
2013-03-27 22:23:24 +01:00
|
|
|
return object;
|
|
|
|
|
}
|
2014-02-11 22:10:05 +01:00
|
|
|
default:
|
|
|
|
|
g_return_val_if_reached (NULL);
|
|
|
|
|
return NULL;
|
2013-03-27 22:23:24 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* libnl 3.2 doesn't seem to provide such a generic way to add libnl-route objects. */
|
2013-06-19 13:27:53 +02:00
|
|
|
static int
|
2013-03-27 22:23:24 +01:00
|
|
|
add_kernel_object (struct nl_sock *sock, struct nl_object *object)
|
|
|
|
|
{
|
|
|
|
|
switch (object_type_from_nl_object (object)) {
|
2014-04-05 12:39:00 +02:00
|
|
|
case OBJECT_TYPE_LINK:
|
2013-03-27 22:23:24 +01:00
|
|
|
return rtnl_link_add (sock, (struct rtnl_link *) object, NLM_F_CREATE);
|
2014-04-05 12:39:00 +02:00
|
|
|
case OBJECT_TYPE_IP4_ADDRESS:
|
|
|
|
|
case OBJECT_TYPE_IP6_ADDRESS:
|
2013-07-30 00:03:48 +02:00
|
|
|
return rtnl_addr_add (sock, (struct rtnl_addr *) object, NLM_F_CREATE | NLM_F_REPLACE);
|
2014-04-05 12:39:00 +02:00
|
|
|
case OBJECT_TYPE_IP4_ROUTE:
|
|
|
|
|
case OBJECT_TYPE_IP6_ROUTE:
|
2013-08-01 00:13:10 +02:00
|
|
|
return rtnl_route_add (sock, (struct rtnl_route *) object, NLM_F_CREATE | NLM_F_REPLACE);
|
2013-03-27 22:23:24 +01:00
|
|
|
default:
|
2014-02-11 22:10:05 +01:00
|
|
|
g_return_val_if_reached (-NLE_INVAL);
|
|
|
|
|
return -NLE_INVAL;
|
2013-03-27 22:23:24 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-05-06 09:16:17 -04:00
|
|
|
/* nm_rtnl_link_parse_info_data(): Re-fetches a link from the kernel
|
|
|
|
|
* and parses its IFLA_INFO_DATA using a caller-provided parser.
|
|
|
|
|
*
|
|
|
|
|
* Code is stolen from rtnl_link_get_kernel(), nl_pickup(), and link_msg_parser().
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
typedef int (*NMNLInfoDataParser) (struct nlattr *info_data, gpointer parser_data);
|
|
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
|
NMNLInfoDataParser parser;
|
|
|
|
|
gpointer parser_data;
|
|
|
|
|
} NMNLInfoDataClosure;
|
|
|
|
|
|
|
|
|
|
static struct nla_policy info_data_link_policy[IFLA_MAX + 1] = {
|
2014-03-03 10:08:23 -05:00
|
|
|
[IFLA_LINKINFO] = { .type = NLA_NESTED },
|
2013-05-06 09:16:17 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static struct nla_policy info_data_link_info_policy[IFLA_INFO_MAX + 1] = {
|
2014-03-03 10:08:23 -05:00
|
|
|
[IFLA_INFO_DATA] = { .type = NLA_NESTED },
|
2013-05-06 09:16:17 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
info_data_parser (struct nl_msg *msg, void *arg)
|
|
|
|
|
{
|
|
|
|
|
NMNLInfoDataClosure *closure = arg;
|
|
|
|
|
struct nlmsghdr *n = nlmsg_hdr (msg);
|
|
|
|
|
struct nlattr *tb[IFLA_MAX + 1];
|
|
|
|
|
struct nlattr *li[IFLA_INFO_MAX + 1];
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
if (!nlmsg_valid_hdr (n, sizeof (struct ifinfomsg)))
|
|
|
|
|
return -NLE_MSG_TOOSHORT;
|
|
|
|
|
|
|
|
|
|
err = nlmsg_parse (n, sizeof (struct ifinfomsg), tb, IFLA_MAX, info_data_link_policy);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
|
|
|
|
|
if (!tb[IFLA_LINKINFO])
|
|
|
|
|
return -NLE_MISSING_ATTR;
|
|
|
|
|
|
|
|
|
|
err = nla_parse_nested (li, IFLA_INFO_MAX, tb[IFLA_LINKINFO], info_data_link_info_policy);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
|
|
|
|
|
if (!li[IFLA_INFO_DATA])
|
|
|
|
|
return -NLE_MISSING_ATTR;
|
|
|
|
|
|
|
|
|
|
return closure->parser (li[IFLA_INFO_DATA], closure->parser_data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
nm_rtnl_link_parse_info_data (struct nl_sock *sk, int ifindex,
|
|
|
|
|
NMNLInfoDataParser parser, gpointer parser_data)
|
|
|
|
|
{
|
|
|
|
|
NMNLInfoDataClosure data = { .parser = parser, .parser_data = parser_data };
|
|
|
|
|
struct nl_msg *msg = NULL;
|
|
|
|
|
struct nl_cb *cb;
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
err = rtnl_link_build_get_request (ifindex, NULL, &msg);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
|
|
|
|
|
err = nl_send_auto (sk, msg);
|
|
|
|
|
nlmsg_free (msg);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
|
|
|
|
|
cb = nl_cb_clone (nl_socket_get_cb (sk));
|
|
|
|
|
if (cb == NULL)
|
|
|
|
|
return -NLE_NOMEM;
|
|
|
|
|
nl_cb_set (cb, NL_CB_VALID, NL_CB_CUSTOM, info_data_parser, &data);
|
|
|
|
|
|
|
|
|
|
err = nl_recvmsgs (sk, cb);
|
|
|
|
|
nl_cb_put (cb);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
|
|
|
|
|
nl_wait_for_ack (sk);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-27 22:23:24 +01:00
|
|
|
/******************************************************************/
|
|
|
|
|
|
2013-09-06 19:39:11 -05:00
|
|
|
static gboolean
|
|
|
|
|
ethtool_get (const char *name, gpointer edata)
|
|
|
|
|
{
|
|
|
|
|
struct ifreq ifr;
|
|
|
|
|
int fd;
|
|
|
|
|
|
|
|
|
|
memset (&ifr, 0, sizeof (ifr));
|
|
|
|
|
strncpy (ifr.ifr_name, name, IFNAMSIZ);
|
|
|
|
|
ifr.ifr_data = edata;
|
|
|
|
|
|
|
|
|
|
fd = socket (PF_INET, SOCK_DGRAM, 0);
|
|
|
|
|
if (fd < 0) {
|
|
|
|
|
error ("ethtool: Could not open socket.");
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ioctl (fd, SIOCETHTOOL, &ifr) < 0) {
|
|
|
|
|
debug ("ethtool: Request failed: %s", strerror (errno));
|
|
|
|
|
close (fd);
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
close (fd);
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
ethtool_get_stringset_index (const char *ifname, int stringset_id, const char *string)
|
|
|
|
|
{
|
2014-07-15 20:18:15 +02:00
|
|
|
gs_free struct ethtool_sset_info *info = NULL;
|
|
|
|
|
gs_free struct ethtool_gstrings *strings = NULL;
|
2013-09-06 19:39:11 -05:00
|
|
|
guint32 len, i;
|
|
|
|
|
|
|
|
|
|
info = g_malloc0 (sizeof (*info) + sizeof (guint32));
|
|
|
|
|
info->cmd = ETHTOOL_GSSET_INFO;
|
|
|
|
|
info->reserved = 0;
|
|
|
|
|
info->sset_mask = 1ULL << stringset_id;
|
|
|
|
|
|
|
|
|
|
if (!ethtool_get (ifname, info))
|
|
|
|
|
return -1;
|
|
|
|
|
if (!info->sset_mask)
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
len = info->data[0];
|
|
|
|
|
|
|
|
|
|
strings = g_malloc0 (sizeof (*strings) + len * ETH_GSTRING_LEN);
|
|
|
|
|
strings->cmd = ETHTOOL_GSTRINGS;
|
|
|
|
|
strings->string_set = stringset_id;
|
|
|
|
|
strings->len = len;
|
|
|
|
|
if (!ethtool_get (ifname, strings))
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < len; i++) {
|
|
|
|
|
if (!strcmp ((char *) &strings->data[i * ETH_GSTRING_LEN], string))
|
|
|
|
|
return i;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
|
|
|
|
2014-01-07 17:21:12 +01:00
|
|
|
static void
|
|
|
|
|
_check_support_kernel_extended_ifa_flags_init (NMLinuxPlatformPrivate *priv, struct nl_msg *msg)
|
|
|
|
|
{
|
|
|
|
|
struct nlmsghdr *msg_hdr = nlmsg_hdr (msg);
|
|
|
|
|
|
|
|
|
|
g_return_if_fail (priv->support_kernel_extended_ifa_flags == 0);
|
|
|
|
|
g_return_if_fail (msg_hdr->nlmsg_type == RTM_NEWADDR);
|
|
|
|
|
|
|
|
|
|
/* the extended address flags are only set for AF_INET6 */
|
|
|
|
|
if (((struct ifaddrmsg *) nlmsg_data (msg_hdr))->ifa_family != AF_INET6)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
/* see if the nl_msg contains the IFA_FLAGS attribute. If it does,
|
|
|
|
|
* we assume, that the kernel supports extended flags, IFA_F_MANAGETEMPADDR
|
|
|
|
|
* and IFA_F_NOPREFIXROUTE (they were added together).
|
|
|
|
|
**/
|
|
|
|
|
priv->support_kernel_extended_ifa_flags =
|
|
|
|
|
nlmsg_find_attr (msg_hdr, sizeof (struct ifaddrmsg), 8 /* IFA_FLAGS */)
|
|
|
|
|
? 1 : -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
check_support_kernel_extended_ifa_flags (NMPlatform *platform)
|
|
|
|
|
{
|
|
|
|
|
NMLinuxPlatformPrivate *priv;
|
|
|
|
|
|
|
|
|
|
g_return_val_if_fail (NM_IS_LINUX_PLATFORM (platform), FALSE);
|
|
|
|
|
|
|
|
|
|
priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
|
|
|
|
|
|
|
|
|
|
if (priv->support_kernel_extended_ifa_flags == 0) {
|
2014-02-25 16:19:43 +01:00
|
|
|
nm_log_warn (LOGD_PLATFORM, "Unable to detect kernel support for extended IFA_FLAGS. Assume no kernel support.");
|
2014-01-07 17:21:12 +01:00
|
|
|
priv->support_kernel_extended_ifa_flags = -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return priv->support_kernel_extended_ifa_flags > 0;
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-24 15:57:08 -05:00
|
|
|
static gboolean
|
|
|
|
|
check_support_user_ipv6ll (NMPlatform *platform)
|
|
|
|
|
{
|
|
|
|
|
NMLinuxPlatformPrivate *priv;
|
|
|
|
|
|
|
|
|
|
g_return_val_if_fail (NM_IS_LINUX_PLATFORM (platform), FALSE);
|
|
|
|
|
|
|
|
|
|
priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
|
|
|
|
|
|
|
|
|
|
if (priv->support_user_ipv6ll == 0) {
|
|
|
|
|
nm_log_warn (LOGD_PLATFORM, "Unable to detect kernel support for IFLA_INET6_ADDR_GEN_MODE. Assume no kernel support.");
|
|
|
|
|
priv->support_user_ipv6ll = -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return priv->support_user_ipv6ll > 0;
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-07 17:21:12 +01:00
|
|
|
|
2013-03-27 22:23:24 +01:00
|
|
|
/* Object type specific utilities */
|
|
|
|
|
|
|
|
|
|
static const char *
|
|
|
|
|
type_to_string (NMLinkType type)
|
|
|
|
|
{
|
2013-04-25 15:46:39 -04:00
|
|
|
/* Note that this only has to support virtual types */
|
2013-03-27 22:23:24 +01:00
|
|
|
switch (type) {
|
|
|
|
|
case NM_LINK_TYPE_DUMMY:
|
|
|
|
|
return "dummy";
|
2013-05-21 12:49:24 -03:00
|
|
|
case NM_LINK_TYPE_GRE:
|
|
|
|
|
return "gre";
|
|
|
|
|
case NM_LINK_TYPE_GRETAP:
|
|
|
|
|
return "gretap";
|
2013-04-25 15:46:39 -04:00
|
|
|
case NM_LINK_TYPE_IFB:
|
|
|
|
|
return "ifb";
|
2013-05-06 09:16:17 -04:00
|
|
|
case NM_LINK_TYPE_MACVLAN:
|
|
|
|
|
return "macvlan";
|
|
|
|
|
case NM_LINK_TYPE_MACVTAP:
|
|
|
|
|
return "macvtap";
|
2013-04-25 15:46:39 -04:00
|
|
|
case NM_LINK_TYPE_TAP:
|
|
|
|
|
return "tap";
|
|
|
|
|
case NM_LINK_TYPE_TUN:
|
|
|
|
|
return "tun";
|
2013-05-03 13:55:51 -04:00
|
|
|
case NM_LINK_TYPE_VETH:
|
|
|
|
|
return "veth";
|
2013-04-25 15:46:39 -04:00
|
|
|
case NM_LINK_TYPE_VLAN:
|
|
|
|
|
return "vlan";
|
2013-06-04 10:31:22 -03:00
|
|
|
case NM_LINK_TYPE_VXLAN:
|
|
|
|
|
return "vxlan";
|
2013-03-27 22:53:55 +01:00
|
|
|
case NM_LINK_TYPE_BRIDGE:
|
|
|
|
|
return "bridge";
|
|
|
|
|
case NM_LINK_TYPE_BOND:
|
|
|
|
|
return "bond";
|
|
|
|
|
case NM_LINK_TYPE_TEAM:
|
|
|
|
|
return "team";
|
2013-03-27 22:23:24 +01:00
|
|
|
default:
|
|
|
|
|
g_warning ("Wrong type: %d", type);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-26 11:43:08 -04:00
|
|
|
#define return_type(t, name) \
|
|
|
|
|
G_STMT_START { \
|
|
|
|
|
if (out_name) \
|
|
|
|
|
*out_name = name; \
|
|
|
|
|
return t; \
|
|
|
|
|
} G_STMT_END
|
|
|
|
|
|
2013-03-27 22:23:24 +01:00
|
|
|
static NMLinkType
|
platform: fix link_type_from_udev() to use ifname from libnl
When an interface gets renamed, we first receive a libnl update with
the changed interface name.
This results in the following chain of calls:
- event_notification()
- announce_object()
- link_init()
- link_extract_type()
- link_type_from_udev()
Then link_type_from_udev() looks up the name in the udev data (getting
the previous name, because we did not yet recieve the udev notification)
and passes the name to wifi_utils_is_wifi(), which eventually calls
nm_platform_link_get_ifindex() -- doing a lookup by the old name.
Fix this, by passing the ifname from libnl to link_type_from_udev().
Also, change hack_empty_master_iff_lower_up() because it is called
from event_notification(), at a moment when the link cache possibly
does not yet know the ifindex -- so that the call chain to
link_extract_type(), link_type_from_udev(), wifi_utils_is_wifi()
again might lead to lookup for something that does not yet exist.
Note, that in this case the name would not yet exist, because we
did not yet put the libnl object into the link cache.
Signed-off-by: Thomas Haller <thaller@redhat.com>
2014-03-06 21:11:47 +01:00
|
|
|
link_type_from_udev (NMPlatform *platform, int ifindex, const char *ifname, int arptype, const char **out_name)
|
2013-05-29 12:00:50 -03:00
|
|
|
{
|
|
|
|
|
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
|
|
|
|
|
GUdevDevice *udev_device;
|
platform: fix link_type_from_udev() to use ifname from libnl
When an interface gets renamed, we first receive a libnl update with
the changed interface name.
This results in the following chain of calls:
- event_notification()
- announce_object()
- link_init()
- link_extract_type()
- link_type_from_udev()
Then link_type_from_udev() looks up the name in the udev data (getting
the previous name, because we did not yet recieve the udev notification)
and passes the name to wifi_utils_is_wifi(), which eventually calls
nm_platform_link_get_ifindex() -- doing a lookup by the old name.
Fix this, by passing the ifname from libnl to link_type_from_udev().
Also, change hack_empty_master_iff_lower_up() because it is called
from event_notification(), at a moment when the link cache possibly
does not yet know the ifindex -- so that the call chain to
link_extract_type(), link_type_from_udev(), wifi_utils_is_wifi()
again might lead to lookup for something that does not yet exist.
Note, that in this case the name would not yet exist, because we
did not yet put the libnl object into the link cache.
Signed-off-by: Thomas Haller <thaller@redhat.com>
2014-03-06 21:11:47 +01:00
|
|
|
const char *prop, *sysfs_path;
|
|
|
|
|
|
|
|
|
|
g_assert (ifname);
|
2013-05-29 12:00:50 -03:00
|
|
|
|
|
|
|
|
udev_device = g_hash_table_lookup (priv->udev_devices, GINT_TO_POINTER (ifindex));
|
|
|
|
|
if (!udev_device)
|
|
|
|
|
return_type (NM_LINK_TYPE_UNKNOWN, "unknown");
|
|
|
|
|
|
2014-04-21 14:39:55 -05:00
|
|
|
if ( g_udev_device_get_property (udev_device, "ID_NM_OLPC_MESH")
|
|
|
|
|
|| g_udev_device_get_sysfs_attr (udev_device, "anycast_mask"))
|
2013-05-29 12:00:50 -03:00
|
|
|
return_type (NM_LINK_TYPE_OLPC_MESH, "olpc-mesh");
|
|
|
|
|
|
|
|
|
|
prop = g_udev_device_get_property (udev_device, "DEVTYPE");
|
2013-10-16 12:29:13 -05:00
|
|
|
sysfs_path = g_udev_device_get_sysfs_path (udev_device);
|
2014-11-14 11:05:16 -06:00
|
|
|
if (wifi_utils_is_wifi (ifname, sysfs_path, prop))
|
2013-05-29 12:00:50 -03:00
|
|
|
return_type (NM_LINK_TYPE_WIFI, "wifi");
|
2013-08-07 12:35:05 -05:00
|
|
|
else if (g_strcmp0 (prop, "wwan") == 0)
|
|
|
|
|
return_type (NM_LINK_TYPE_WWAN_ETHERNET, "wwan");
|
2013-10-18 11:12:40 -05:00
|
|
|
else if (g_strcmp0 (prop, "wimax") == 0)
|
|
|
|
|
return_type (NM_LINK_TYPE_WIMAX, "wimax");
|
2013-08-07 12:35:05 -05:00
|
|
|
|
|
|
|
|
if (arptype == ARPHRD_ETHER)
|
|
|
|
|
return_type (NM_LINK_TYPE_ETHERNET, "ethernet");
|
2013-05-29 12:00:50 -03:00
|
|
|
|
2013-08-07 12:35:05 -05:00
|
|
|
return_type (NM_LINK_TYPE_UNKNOWN, "unknown");
|
2013-05-29 12:00:50 -03:00
|
|
|
}
|
|
|
|
|
|
core: don't wait for udev to find created software devices
NM knows software devices it creates exist immediately, and it knows
it can use them immediately instead of waiting for udev to find them.
Ideally we'd wait for udev to find all devices, to allow tags and
other rules to run, but when creating software devices they must
currently be available for use immediately.
Bridges and bonds are required to be IFF_UP before they can be
activated (see their is_available() implementation). But during
activation code paths, specifically in nm_manager_activate_connection(),
the interface is created and immediately checked for availability
to activate (since the creation was a result of the user requesting
device activation). That availability check fails, because the
device is not visible outside NMPlatform (because it hasn't been
found by udev yet, because we haven't gotten back to the event
loop) and thus nm_platform_link_is_up() fails, failing the
availability check. Thus the device activation ends in error.
What should really happen is that device activation shouldn't
be synchronous; a new NMActiveConnection should be created for
any activation request, which can then wait until all the
resources it needs for activation are available (device found,
master devices created, etc). That's going to take more work
though.
2013-08-04 22:08:48 -05:00
|
|
|
static gboolean
|
2013-10-22 17:11:24 +02:00
|
|
|
link_is_software (struct rtnl_link *rtnllink)
|
core: don't wait for udev to find created software devices
NM knows software devices it creates exist immediately, and it knows
it can use them immediately instead of waiting for udev to find them.
Ideally we'd wait for udev to find all devices, to allow tags and
other rules to run, but when creating software devices they must
currently be available for use immediately.
Bridges and bonds are required to be IFF_UP before they can be
activated (see their is_available() implementation). But during
activation code paths, specifically in nm_manager_activate_connection(),
the interface is created and immediately checked for availability
to activate (since the creation was a result of the user requesting
device activation). That availability check fails, because the
device is not visible outside NMPlatform (because it hasn't been
found by udev yet, because we haven't gotten back to the event
loop) and thus nm_platform_link_is_up() fails, failing the
availability check. Thus the device activation ends in error.
What should really happen is that device activation shouldn't
be synchronous; a new NMActiveConnection should be created for
any activation request, which can then wait until all the
resources it needs for activation are available (device found,
master devices created, etc). That's going to take more work
though.
2013-08-04 22:08:48 -05:00
|
|
|
{
|
2013-09-16 13:43:28 -04:00
|
|
|
const char *type;
|
core: don't wait for udev to find created software devices
NM knows software devices it creates exist immediately, and it knows
it can use them immediately instead of waiting for udev to find them.
Ideally we'd wait for udev to find all devices, to allow tags and
other rules to run, but when creating software devices they must
currently be available for use immediately.
Bridges and bonds are required to be IFF_UP before they can be
activated (see their is_available() implementation). But during
activation code paths, specifically in nm_manager_activate_connection(),
the interface is created and immediately checked for availability
to activate (since the creation was a result of the user requesting
device activation). That availability check fails, because the
device is not visible outside NMPlatform (because it hasn't been
found by udev yet, because we haven't gotten back to the event
loop) and thus nm_platform_link_is_up() fails, failing the
availability check. Thus the device activation ends in error.
What should really happen is that device activation shouldn't
be synchronous; a new NMActiveConnection should be created for
any activation request, which can then wait until all the
resources it needs for activation are available (device found,
master devices created, etc). That's going to take more work
though.
2013-08-04 22:08:48 -05:00
|
|
|
|
|
|
|
|
/* FIXME: replace somehow with NMLinkType or nm_platform_is_software(), but
|
|
|
|
|
* solve the infinite callstack problems that getting the type of a TUN/TAP
|
|
|
|
|
* device causes.
|
|
|
|
|
*/
|
2013-09-16 13:43:28 -04:00
|
|
|
|
2013-10-22 17:11:24 +02:00
|
|
|
if ( rtnl_link_get_arptype (rtnllink) == ARPHRD_INFINIBAND
|
|
|
|
|
&& strchr (rtnl_link_get_name (rtnllink), '.'))
|
2013-09-16 13:43:28 -04:00
|
|
|
return TRUE;
|
|
|
|
|
|
2013-10-22 17:11:24 +02:00
|
|
|
type = rtnl_link_get_type (rtnllink);
|
core: don't wait for udev to find created software devices
NM knows software devices it creates exist immediately, and it knows
it can use them immediately instead of waiting for udev to find them.
Ideally we'd wait for udev to find all devices, to allow tags and
other rules to run, but when creating software devices they must
currently be available for use immediately.
Bridges and bonds are required to be IFF_UP before they can be
activated (see their is_available() implementation). But during
activation code paths, specifically in nm_manager_activate_connection(),
the interface is created and immediately checked for availability
to activate (since the creation was a result of the user requesting
device activation). That availability check fails, because the
device is not visible outside NMPlatform (because it hasn't been
found by udev yet, because we haven't gotten back to the event
loop) and thus nm_platform_link_is_up() fails, failing the
availability check. Thus the device activation ends in error.
What should really happen is that device activation shouldn't
be synchronous; a new NMActiveConnection should be created for
any activation request, which can then wait until all the
resources it needs for activation are available (device found,
master devices created, etc). That's going to take more work
though.
2013-08-04 22:08:48 -05:00
|
|
|
if (type == NULL)
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
if (!strcmp (type, "dummy") ||
|
|
|
|
|
!strcmp (type, "gre") ||
|
|
|
|
|
!strcmp (type, "gretap") ||
|
|
|
|
|
!strcmp (type, "macvlan") ||
|
|
|
|
|
!strcmp (type, "macvtap") ||
|
|
|
|
|
!strcmp (type, "tun") ||
|
|
|
|
|
!strcmp (type, "veth") ||
|
|
|
|
|
!strcmp (type, "vlan") ||
|
2013-06-04 10:31:22 -03:00
|
|
|
!strcmp (type, "vxlan") ||
|
core: don't wait for udev to find created software devices
NM knows software devices it creates exist immediately, and it knows
it can use them immediately instead of waiting for udev to find them.
Ideally we'd wait for udev to find all devices, to allow tags and
other rules to run, but when creating software devices they must
currently be available for use immediately.
Bridges and bonds are required to be IFF_UP before they can be
activated (see their is_available() implementation). But during
activation code paths, specifically in nm_manager_activate_connection(),
the interface is created and immediately checked for availability
to activate (since the creation was a result of the user requesting
device activation). That availability check fails, because the
device is not visible outside NMPlatform (because it hasn't been
found by udev yet, because we haven't gotten back to the event
loop) and thus nm_platform_link_is_up() fails, failing the
availability check. Thus the device activation ends in error.
What should really happen is that device activation shouldn't
be synchronous; a new NMActiveConnection should be created for
any activation request, which can then wait until all the
resources it needs for activation are available (device found,
master devices created, etc). That's going to take more work
though.
2013-08-04 22:08:48 -05:00
|
|
|
!strcmp (type, "bridge") ||
|
|
|
|
|
!strcmp (type, "bond") ||
|
|
|
|
|
!strcmp (type, "team"))
|
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
2013-09-06 19:39:11 -05:00
|
|
|
static const char *
|
|
|
|
|
ethtool_get_driver (const char *ifname)
|
|
|
|
|
{
|
2013-09-25 13:41:21 -05:00
|
|
|
struct ethtool_drvinfo drvinfo = { 0 };
|
2013-09-06 19:39:11 -05:00
|
|
|
|
2014-04-21 14:12:39 -05:00
|
|
|
g_return_val_if_fail (ifname != NULL, NULL);
|
|
|
|
|
|
2013-09-06 19:39:11 -05:00
|
|
|
drvinfo.cmd = ETHTOOL_GDRVINFO;
|
|
|
|
|
if (!ethtool_get (ifname, &drvinfo))
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
if (!*drvinfo.driver)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
return g_intern_string (drvinfo.driver);
|
|
|
|
|
}
|
|
|
|
|
|
core: don't wait for udev to find created software devices
NM knows software devices it creates exist immediately, and it knows
it can use them immediately instead of waiting for udev to find them.
Ideally we'd wait for udev to find all devices, to allow tags and
other rules to run, but when creating software devices they must
currently be available for use immediately.
Bridges and bonds are required to be IFF_UP before they can be
activated (see their is_available() implementation). But during
activation code paths, specifically in nm_manager_activate_connection(),
the interface is created and immediately checked for availability
to activate (since the creation was a result of the user requesting
device activation). That availability check fails, because the
device is not visible outside NMPlatform (because it hasn't been
found by udev yet, because we haven't gotten back to the event
loop) and thus nm_platform_link_is_up() fails, failing the
availability check. Thus the device activation ends in error.
What should really happen is that device activation shouldn't
be synchronous; a new NMActiveConnection should be created for
any activation request, which can then wait until all the
resources it needs for activation are available (device found,
master devices created, etc). That's going to take more work
though.
2013-08-04 22:08:48 -05:00
|
|
|
static gboolean
|
2013-09-16 13:40:39 -04:00
|
|
|
link_is_announceable (NMPlatform *platform, struct rtnl_link *rtnllink)
|
core: don't wait for udev to find created software devices
NM knows software devices it creates exist immediately, and it knows
it can use them immediately instead of waiting for udev to find them.
Ideally we'd wait for udev to find all devices, to allow tags and
other rules to run, but when creating software devices they must
currently be available for use immediately.
Bridges and bonds are required to be IFF_UP before they can be
activated (see their is_available() implementation). But during
activation code paths, specifically in nm_manager_activate_connection(),
the interface is created and immediately checked for availability
to activate (since the creation was a result of the user requesting
device activation). That availability check fails, because the
device is not visible outside NMPlatform (because it hasn't been
found by udev yet, because we haven't gotten back to the event
loop) and thus nm_platform_link_is_up() fails, failing the
availability check. Thus the device activation ends in error.
What should really happen is that device activation shouldn't
be synchronous; a new NMActiveConnection should be created for
any activation request, which can then wait until all the
resources it needs for activation are available (device found,
master devices created, etc). That's going to take more work
though.
2013-08-04 22:08:48 -05:00
|
|
|
{
|
|
|
|
|
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
|
|
|
|
|
|
|
|
|
|
/* Software devices are always visible outside the platform */
|
|
|
|
|
if (link_is_software (rtnllink))
|
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
|
|
/* Hardware devices must be found by udev so rules get run and tags set */
|
|
|
|
|
if (g_hash_table_lookup (priv->udev_devices,
|
|
|
|
|
GINT_TO_POINTER (rtnl_link_get_ifindex (rtnllink))))
|
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
2013-05-29 12:00:50 -03:00
|
|
|
static NMLinkType
|
|
|
|
|
link_extract_type (NMPlatform *platform, struct rtnl_link *rtnllink, const char **out_name)
|
2013-03-27 22:23:24 +01:00
|
|
|
{
|
|
|
|
|
const char *type;
|
|
|
|
|
|
|
|
|
|
if (!rtnllink)
|
2013-05-10 21:40:44 +02:00
|
|
|
return_type (NM_LINK_TYPE_NONE, NULL);
|
2013-03-27 22:23:24 +01:00
|
|
|
|
|
|
|
|
type = rtnl_link_get_type (rtnllink);
|
|
|
|
|
|
2013-04-25 15:46:39 -04:00
|
|
|
if (!type) {
|
2013-05-29 12:00:50 -03:00
|
|
|
int arptype = rtnl_link_get_arptype (rtnllink);
|
2013-09-06 19:39:11 -05:00
|
|
|
const char *driver;
|
platform: fix link_type_from_udev() to use ifname from libnl
When an interface gets renamed, we first receive a libnl update with
the changed interface name.
This results in the following chain of calls:
- event_notification()
- announce_object()
- link_init()
- link_extract_type()
- link_type_from_udev()
Then link_type_from_udev() looks up the name in the udev data (getting
the previous name, because we did not yet recieve the udev notification)
and passes the name to wifi_utils_is_wifi(), which eventually calls
nm_platform_link_get_ifindex() -- doing a lookup by the old name.
Fix this, by passing the ifname from libnl to link_type_from_udev().
Also, change hack_empty_master_iff_lower_up() because it is called
from event_notification(), at a moment when the link cache possibly
does not yet know the ifindex -- so that the call chain to
link_extract_type(), link_type_from_udev(), wifi_utils_is_wifi()
again might lead to lookup for something that does not yet exist.
Note, that in this case the name would not yet exist, because we
did not yet put the libnl object into the link cache.
Signed-off-by: Thomas Haller <thaller@redhat.com>
2014-03-06 21:11:47 +01:00
|
|
|
const char *ifname;
|
|
|
|
|
|
2013-05-29 12:00:50 -03:00
|
|
|
if (arptype == ARPHRD_LOOPBACK)
|
2013-04-26 11:43:08 -04:00
|
|
|
return_type (NM_LINK_TYPE_LOOPBACK, "loopback");
|
2013-06-11 10:27:42 -03:00
|
|
|
else if (arptype == ARPHRD_INFINIBAND)
|
|
|
|
|
return_type (NM_LINK_TYPE_INFINIBAND, "infiniband");
|
platform: fix link_type_from_udev() to use ifname from libnl
When an interface gets renamed, we first receive a libnl update with
the changed interface name.
This results in the following chain of calls:
- event_notification()
- announce_object()
- link_init()
- link_extract_type()
- link_type_from_udev()
Then link_type_from_udev() looks up the name in the udev data (getting
the previous name, because we did not yet recieve the udev notification)
and passes the name to wifi_utils_is_wifi(), which eventually calls
nm_platform_link_get_ifindex() -- doing a lookup by the old name.
Fix this, by passing the ifname from libnl to link_type_from_udev().
Also, change hack_empty_master_iff_lower_up() because it is called
from event_notification(), at a moment when the link cache possibly
does not yet know the ifindex -- so that the call chain to
link_extract_type(), link_type_from_udev(), wifi_utils_is_wifi()
again might lead to lookup for something that does not yet exist.
Note, that in this case the name would not yet exist, because we
did not yet put the libnl object into the link cache.
Signed-off-by: Thomas Haller <thaller@redhat.com>
2014-03-06 21:11:47 +01:00
|
|
|
|
|
|
|
|
ifname = rtnl_link_get_name (rtnllink);
|
2014-04-21 14:12:39 -05:00
|
|
|
if (!ifname)
|
|
|
|
|
return_type (NM_LINK_TYPE_UNKNOWN, type);
|
|
|
|
|
|
platform: fix link_type_from_udev() to use ifname from libnl
When an interface gets renamed, we first receive a libnl update with
the changed interface name.
This results in the following chain of calls:
- event_notification()
- announce_object()
- link_init()
- link_extract_type()
- link_type_from_udev()
Then link_type_from_udev() looks up the name in the udev data (getting
the previous name, because we did not yet recieve the udev notification)
and passes the name to wifi_utils_is_wifi(), which eventually calls
nm_platform_link_get_ifindex() -- doing a lookup by the old name.
Fix this, by passing the ifname from libnl to link_type_from_udev().
Also, change hack_empty_master_iff_lower_up() because it is called
from event_notification(), at a moment when the link cache possibly
does not yet know the ifindex -- so that the call chain to
link_extract_type(), link_type_from_udev(), wifi_utils_is_wifi()
again might lead to lookup for something that does not yet exist.
Note, that in this case the name would not yet exist, because we
did not yet put the libnl object into the link cache.
Signed-off-by: Thomas Haller <thaller@redhat.com>
2014-03-06 21:11:47 +01:00
|
|
|
if (arptype == 256) {
|
2013-04-25 15:46:39 -04:00
|
|
|
/* Some s390 CTC-type devices report 256 for the encapsulation type
|
2013-05-29 12:00:50 -03:00
|
|
|
* for some reason, but we need to call them Ethernet. FIXME: use
|
2013-04-25 15:46:39 -04:00
|
|
|
* something other than interface name to detect CTC here.
|
|
|
|
|
*/
|
platform: fix link_type_from_udev() to use ifname from libnl
When an interface gets renamed, we first receive a libnl update with
the changed interface name.
This results in the following chain of calls:
- event_notification()
- announce_object()
- link_init()
- link_extract_type()
- link_type_from_udev()
Then link_type_from_udev() looks up the name in the udev data (getting
the previous name, because we did not yet recieve the udev notification)
and passes the name to wifi_utils_is_wifi(), which eventually calls
nm_platform_link_get_ifindex() -- doing a lookup by the old name.
Fix this, by passing the ifname from libnl to link_type_from_udev().
Also, change hack_empty_master_iff_lower_up() because it is called
from event_notification(), at a moment when the link cache possibly
does not yet know the ifindex -- so that the call chain to
link_extract_type(), link_type_from_udev(), wifi_utils_is_wifi()
again might lead to lookup for something that does not yet exist.
Note, that in this case the name would not yet exist, because we
did not yet put the libnl object into the link cache.
Signed-off-by: Thomas Haller <thaller@redhat.com>
2014-03-06 21:11:47 +01:00
|
|
|
if (g_str_has_prefix (ifname, "ctc"))
|
2013-04-25 15:46:39 -04:00
|
|
|
return_type (NM_LINK_TYPE_ETHERNET, "ethernet");
|
2013-09-06 19:39:11 -05:00
|
|
|
}
|
|
|
|
|
|
platform: fix link_type_from_udev() to use ifname from libnl
When an interface gets renamed, we first receive a libnl update with
the changed interface name.
This results in the following chain of calls:
- event_notification()
- announce_object()
- link_init()
- link_extract_type()
- link_type_from_udev()
Then link_type_from_udev() looks up the name in the udev data (getting
the previous name, because we did not yet recieve the udev notification)
and passes the name to wifi_utils_is_wifi(), which eventually calls
nm_platform_link_get_ifindex() -- doing a lookup by the old name.
Fix this, by passing the ifname from libnl to link_type_from_udev().
Also, change hack_empty_master_iff_lower_up() because it is called
from event_notification(), at a moment when the link cache possibly
does not yet know the ifindex -- so that the call chain to
link_extract_type(), link_type_from_udev(), wifi_utils_is_wifi()
again might lead to lookup for something that does not yet exist.
Note, that in this case the name would not yet exist, because we
did not yet put the libnl object into the link cache.
Signed-off-by: Thomas Haller <thaller@redhat.com>
2014-03-06 21:11:47 +01:00
|
|
|
driver = ethtool_get_driver (ifname);
|
2013-09-06 19:39:11 -05:00
|
|
|
if (!g_strcmp0 (driver, "openvswitch"))
|
|
|
|
|
return_type (NM_LINK_TYPE_OPENVSWITCH, "openvswitch");
|
|
|
|
|
|
|
|
|
|
return link_type_from_udev (platform,
|
|
|
|
|
rtnl_link_get_ifindex (rtnllink),
|
platform: fix link_type_from_udev() to use ifname from libnl
When an interface gets renamed, we first receive a libnl update with
the changed interface name.
This results in the following chain of calls:
- event_notification()
- announce_object()
- link_init()
- link_extract_type()
- link_type_from_udev()
Then link_type_from_udev() looks up the name in the udev data (getting
the previous name, because we did not yet recieve the udev notification)
and passes the name to wifi_utils_is_wifi(), which eventually calls
nm_platform_link_get_ifindex() -- doing a lookup by the old name.
Fix this, by passing the ifname from libnl to link_type_from_udev().
Also, change hack_empty_master_iff_lower_up() because it is called
from event_notification(), at a moment when the link cache possibly
does not yet know the ifindex -- so that the call chain to
link_extract_type(), link_type_from_udev(), wifi_utils_is_wifi()
again might lead to lookup for something that does not yet exist.
Note, that in this case the name would not yet exist, because we
did not yet put the libnl object into the link cache.
Signed-off-by: Thomas Haller <thaller@redhat.com>
2014-03-06 21:11:47 +01:00
|
|
|
ifname,
|
2013-09-06 19:39:11 -05:00
|
|
|
arptype,
|
|
|
|
|
out_name);
|
2013-06-11 10:27:42 -03:00
|
|
|
} else if (!strcmp (type, "dummy"))
|
2013-04-26 11:43:08 -04:00
|
|
|
return_type (NM_LINK_TYPE_DUMMY, "dummy");
|
2013-05-21 12:49:24 -03:00
|
|
|
else if (!strcmp (type, "gre"))
|
|
|
|
|
return_type (NM_LINK_TYPE_GRE, "gre");
|
|
|
|
|
else if (!strcmp (type, "gretap"))
|
|
|
|
|
return_type (NM_LINK_TYPE_GRETAP, "gretap");
|
2013-04-25 15:46:39 -04:00
|
|
|
else if (!strcmp (type, "ifb"))
|
|
|
|
|
return_type (NM_LINK_TYPE_IFB, "ifb");
|
2013-05-06 09:16:17 -04:00
|
|
|
else if (!strcmp (type, "macvlan"))
|
|
|
|
|
return_type (NM_LINK_TYPE_MACVLAN, "macvlan");
|
|
|
|
|
else if (!strcmp (type, "macvtap"))
|
|
|
|
|
return_type (NM_LINK_TYPE_MACVTAP, "macvtap");
|
2013-04-25 15:46:39 -04:00
|
|
|
else if (!strcmp (type, "tun")) {
|
|
|
|
|
NMPlatformTunProperties props;
|
2014-01-21 11:04:26 +01:00
|
|
|
guint flags;
|
2013-04-25 15:46:39 -04:00
|
|
|
|
2014-01-21 11:04:26 +01:00
|
|
|
if (nm_platform_tun_get_properties (rtnl_link_get_ifindex (rtnllink), &props)) {
|
|
|
|
|
if (!g_strcmp0 (props.mode, "tap"))
|
|
|
|
|
return_type (NM_LINK_TYPE_TAP, "tap");
|
|
|
|
|
if (!g_strcmp0 (props.mode, "tun"))
|
|
|
|
|
return_type (NM_LINK_TYPE_TUN, "tun");
|
|
|
|
|
}
|
|
|
|
|
flags = rtnl_link_get_flags (rtnllink);
|
|
|
|
|
|
|
|
|
|
nm_log_dbg (LOGD_PLATFORM, "Failed to read tun properties for interface %d (link flags: %X)",
|
|
|
|
|
rtnl_link_get_ifindex (rtnllink), flags);
|
|
|
|
|
|
|
|
|
|
/* try guessing the type using the link flags instead... */
|
|
|
|
|
if (flags & IFF_POINTOPOINT)
|
2013-04-25 15:46:39 -04:00
|
|
|
return_type (NM_LINK_TYPE_TUN, "tun");
|
2014-01-21 11:04:26 +01:00
|
|
|
return_type (NM_LINK_TYPE_TAP, "tap");
|
2013-04-25 15:46:39 -04:00
|
|
|
} else if (!strcmp (type, "veth"))
|
2013-05-03 13:55:51 -04:00
|
|
|
return_type (NM_LINK_TYPE_VETH, "veth");
|
2013-04-25 15:46:39 -04:00
|
|
|
else if (!strcmp (type, "vlan"))
|
|
|
|
|
return_type (NM_LINK_TYPE_VLAN, "vlan");
|
2013-06-04 10:31:22 -03:00
|
|
|
else if (!strcmp (type, "vxlan"))
|
|
|
|
|
return_type (NM_LINK_TYPE_VXLAN, "vxlan");
|
2013-04-26 11:43:08 -04:00
|
|
|
else if (!strcmp (type, "bridge"))
|
|
|
|
|
return_type (NM_LINK_TYPE_BRIDGE, "bridge");
|
|
|
|
|
else if (!strcmp (type, "bond"))
|
|
|
|
|
return_type (NM_LINK_TYPE_BOND, "bond");
|
|
|
|
|
else if (!strcmp (type, "team"))
|
|
|
|
|
return_type (NM_LINK_TYPE_TEAM, "team");
|
2013-05-29 12:00:50 -03:00
|
|
|
|
2013-06-07 11:05:06 -03:00
|
|
|
return_type (NM_LINK_TYPE_UNKNOWN, type);
|
2013-05-29 12:00:50 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const char *
|
|
|
|
|
udev_get_driver (NMPlatform *platform, GUdevDevice *device, int ifindex)
|
|
|
|
|
{
|
|
|
|
|
GUdevDevice *parent = NULL, *grandparent = NULL;
|
|
|
|
|
const char *driver, *subsys;
|
|
|
|
|
|
|
|
|
|
driver = g_udev_device_get_driver (device);
|
|
|
|
|
if (driver)
|
|
|
|
|
return driver;
|
|
|
|
|
|
|
|
|
|
/* Try the parent */
|
|
|
|
|
parent = g_udev_device_get_parent (device);
|
|
|
|
|
if (parent) {
|
|
|
|
|
driver = g_udev_device_get_driver (parent);
|
|
|
|
|
if (!driver) {
|
|
|
|
|
/* Try the grandparent if it's an ibmebus device or if the
|
|
|
|
|
* subsys is NULL which usually indicates some sort of
|
|
|
|
|
* platform device like a 'gadget' net interface.
|
|
|
|
|
*/
|
|
|
|
|
subsys = g_udev_device_get_subsystem (parent);
|
|
|
|
|
if ( (g_strcmp0 (subsys, "ibmebus") == 0)
|
|
|
|
|
|| (subsys == NULL)) {
|
|
|
|
|
grandparent = g_udev_device_get_parent (parent);
|
|
|
|
|
if (grandparent) {
|
|
|
|
|
driver = g_udev_device_get_driver (grandparent);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Intern the string so we don't have to worry about memory
|
|
|
|
|
* management in NMPlatformLink.
|
|
|
|
|
*/
|
|
|
|
|
if (driver)
|
|
|
|
|
driver = g_intern_string (driver);
|
|
|
|
|
|
|
|
|
|
g_clear_object (&parent);
|
|
|
|
|
g_clear_object (&grandparent);
|
|
|
|
|
|
|
|
|
|
return driver;
|
2013-03-27 22:23:24 +01:00
|
|
|
}
|
|
|
|
|
|
2014-02-17 12:48:05 +01:00
|
|
|
static gboolean
|
2014-02-17 12:50:52 +01:00
|
|
|
init_link (NMPlatform *platform, NMPlatformLink *info, struct rtnl_link *rtnllink)
|
2013-03-27 22:23:24 +01:00
|
|
|
{
|
2013-05-29 12:00:50 -03:00
|
|
|
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
|
|
|
|
|
GUdevDevice *udev_device;
|
2014-04-15 14:34:40 +02:00
|
|
|
const char *name;
|
2013-05-29 12:00:50 -03:00
|
|
|
|
2014-02-17 12:48:05 +01:00
|
|
|
g_return_val_if_fail (rtnllink, FALSE);
|
2013-03-27 22:23:24 +01:00
|
|
|
|
2014-04-15 14:34:40 +02:00
|
|
|
name = rtnl_link_get_name (rtnllink);
|
2014-02-17 12:48:05 +01:00
|
|
|
memset (info, 0, sizeof (*info));
|
2013-03-27 22:23:24 +01:00
|
|
|
|
|
|
|
|
info->ifindex = rtnl_link_get_ifindex (rtnllink);
|
2014-04-15 14:34:40 +02:00
|
|
|
if (name)
|
|
|
|
|
g_strlcpy (info->name, name, sizeof (info->name));
|
|
|
|
|
else
|
|
|
|
|
info->name[0] = '\0';
|
2013-05-29 12:00:50 -03:00
|
|
|
info->type = link_extract_type (platform, rtnllink, &info->type_name);
|
2013-03-27 22:23:24 +01:00
|
|
|
info->up = !!(rtnl_link_get_flags (rtnllink) & IFF_UP);
|
|
|
|
|
info->connected = !!(rtnl_link_get_flags (rtnllink) & IFF_LOWER_UP);
|
|
|
|
|
info->arp = !(rtnl_link_get_flags (rtnllink) & IFF_NOARP);
|
2013-03-27 22:53:55 +01:00
|
|
|
info->master = rtnl_link_get_master (rtnllink);
|
2013-06-03 11:49:55 -03:00
|
|
|
info->parent = rtnl_link_get_link (rtnllink);
|
2013-04-15 21:48:12 +02:00
|
|
|
info->mtu = rtnl_link_get_mtu (rtnllink);
|
2013-05-29 12:00:50 -03:00
|
|
|
|
|
|
|
|
udev_device = g_hash_table_lookup (priv->udev_devices, GINT_TO_POINTER (info->ifindex));
|
|
|
|
|
if (udev_device) {
|
|
|
|
|
info->driver = udev_get_driver (platform, udev_device, info->ifindex);
|
|
|
|
|
if (!info->driver)
|
2015-02-18 16:04:43 +01:00
|
|
|
info->driver = g_intern_string (rtnl_link_get_type (rtnllink));
|
2013-09-06 19:39:11 -05:00
|
|
|
if (!info->driver)
|
|
|
|
|
info->driver = ethtool_get_driver (info->name);
|
2013-05-29 12:00:50 -03:00
|
|
|
if (!info->driver)
|
|
|
|
|
info->driver = "unknown";
|
|
|
|
|
info->udi = g_udev_device_get_sysfs_path (udev_device);
|
|
|
|
|
}
|
2014-02-17 12:48:05 +01:00
|
|
|
|
|
|
|
|
return TRUE;
|
2013-03-27 22:53:55 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Hack: Empty bridges and bonds have IFF_LOWER_UP flag and therefore they break
|
|
|
|
|
* the carrier detection. This hack makes nm-platform think they don't have the
|
|
|
|
|
* IFF_LOWER_UP flag. This seems to also apply to bonds (specifically) with all
|
|
|
|
|
* slaves down.
|
|
|
|
|
*
|
|
|
|
|
* Note: This is still a bit racy but when NetworkManager asks for enslaving a slave,
|
|
|
|
|
* nm-platform will do that synchronously and will immediately ask for both master
|
|
|
|
|
* and slave information after the enslaving request. After the synchronous call, the
|
|
|
|
|
* master carrier is already updated with the slave carrier in mind.
|
|
|
|
|
*
|
|
|
|
|
* https://bugzilla.redhat.com/show_bug.cgi?id=910348
|
|
|
|
|
*/
|
|
|
|
|
static void
|
|
|
|
|
hack_empty_master_iff_lower_up (NMPlatform *platform, struct nl_object *object)
|
|
|
|
|
{
|
|
|
|
|
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
|
|
|
|
|
struct rtnl_link *rtnllink;
|
|
|
|
|
int ifindex;
|
|
|
|
|
struct nl_object *slave;
|
platform: fix link_type_from_udev() to use ifname from libnl
When an interface gets renamed, we first receive a libnl update with
the changed interface name.
This results in the following chain of calls:
- event_notification()
- announce_object()
- link_init()
- link_extract_type()
- link_type_from_udev()
Then link_type_from_udev() looks up the name in the udev data (getting
the previous name, because we did not yet recieve the udev notification)
and passes the name to wifi_utils_is_wifi(), which eventually calls
nm_platform_link_get_ifindex() -- doing a lookup by the old name.
Fix this, by passing the ifname from libnl to link_type_from_udev().
Also, change hack_empty_master_iff_lower_up() because it is called
from event_notification(), at a moment when the link cache possibly
does not yet know the ifindex -- so that the call chain to
link_extract_type(), link_type_from_udev(), wifi_utils_is_wifi()
again might lead to lookup for something that does not yet exist.
Note, that in this case the name would not yet exist, because we
did not yet put the libnl object into the link cache.
Signed-off-by: Thomas Haller <thaller@redhat.com>
2014-03-06 21:11:47 +01:00
|
|
|
const char *type;
|
2013-03-27 22:53:55 +01:00
|
|
|
|
|
|
|
|
if (!object)
|
|
|
|
|
return;
|
|
|
|
|
if (strcmp (nl_object_get_type (object), "route/link"))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
rtnllink = (struct rtnl_link *) object;
|
|
|
|
|
|
|
|
|
|
ifindex = rtnl_link_get_ifindex (rtnllink);
|
|
|
|
|
|
platform: fix link_type_from_udev() to use ifname from libnl
When an interface gets renamed, we first receive a libnl update with
the changed interface name.
This results in the following chain of calls:
- event_notification()
- announce_object()
- link_init()
- link_extract_type()
- link_type_from_udev()
Then link_type_from_udev() looks up the name in the udev data (getting
the previous name, because we did not yet recieve the udev notification)
and passes the name to wifi_utils_is_wifi(), which eventually calls
nm_platform_link_get_ifindex() -- doing a lookup by the old name.
Fix this, by passing the ifname from libnl to link_type_from_udev().
Also, change hack_empty_master_iff_lower_up() because it is called
from event_notification(), at a moment when the link cache possibly
does not yet know the ifindex -- so that the call chain to
link_extract_type(), link_type_from_udev(), wifi_utils_is_wifi()
again might lead to lookup for something that does not yet exist.
Note, that in this case the name would not yet exist, because we
did not yet put the libnl object into the link cache.
Signed-off-by: Thomas Haller <thaller@redhat.com>
2014-03-06 21:11:47 +01:00
|
|
|
type = rtnl_link_get_type (rtnllink);
|
|
|
|
|
if (!type || (strcmp (type, "bridge") != 0 && strcmp (type, "bond") != 0))
|
2013-03-27 22:53:55 +01:00
|
|
|
return;
|
platform: fix link_type_from_udev() to use ifname from libnl
When an interface gets renamed, we first receive a libnl update with
the changed interface name.
This results in the following chain of calls:
- event_notification()
- announce_object()
- link_init()
- link_extract_type()
- link_type_from_udev()
Then link_type_from_udev() looks up the name in the udev data (getting
the previous name, because we did not yet recieve the udev notification)
and passes the name to wifi_utils_is_wifi(), which eventually calls
nm_platform_link_get_ifindex() -- doing a lookup by the old name.
Fix this, by passing the ifname from libnl to link_type_from_udev().
Also, change hack_empty_master_iff_lower_up() because it is called
from event_notification(), at a moment when the link cache possibly
does not yet know the ifindex -- so that the call chain to
link_extract_type(), link_type_from_udev(), wifi_utils_is_wifi()
again might lead to lookup for something that does not yet exist.
Note, that in this case the name would not yet exist, because we
did not yet put the libnl object into the link cache.
Signed-off-by: Thomas Haller <thaller@redhat.com>
2014-03-06 21:11:47 +01:00
|
|
|
|
|
|
|
|
for (slave = nl_cache_get_first (priv->link_cache); slave; slave = nl_cache_get_next (slave)) {
|
|
|
|
|
struct rtnl_link *rtnlslave = (struct rtnl_link *) slave;
|
|
|
|
|
if (rtnl_link_get_master (rtnlslave) == ifindex
|
|
|
|
|
&& rtnl_link_get_flags (rtnlslave) & IFF_LOWER_UP)
|
|
|
|
|
return;
|
2013-03-27 22:53:55 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rtnl_link_unset_flags (rtnllink, IFF_LOWER_UP);
|
2013-03-27 22:23:24 +01:00
|
|
|
}
|
|
|
|
|
|
2014-06-07 12:52:22 +02:00
|
|
|
static guint32
|
|
|
|
|
_get_remaining_time (guint32 start_timestamp, guint32 end_timestamp)
|
|
|
|
|
{
|
|
|
|
|
/* Return the remaining time between @start_timestamp until @end_timestamp.
|
|
|
|
|
*
|
|
|
|
|
* If @end_timestamp is NM_PLATFORM_LIFETIME_PERMANENT, it returns
|
|
|
|
|
* NM_PLATFORM_LIFETIME_PERMANENT. If @start_timestamp already passed
|
|
|
|
|
* @end_timestamp it returns 0. Beware, NMPlatformIPAddress treats a @lifetime
|
|
|
|
|
* of 0 as permanent.
|
|
|
|
|
*/
|
|
|
|
|
if (end_timestamp == NM_PLATFORM_LIFETIME_PERMANENT)
|
|
|
|
|
return NM_PLATFORM_LIFETIME_PERMANENT;
|
|
|
|
|
if (start_timestamp >= end_timestamp)
|
|
|
|
|
return 0;
|
|
|
|
|
return end_timestamp - start_timestamp;
|
|
|
|
|
}
|
|
|
|
|
|
2014-06-07 13:52:01 +02:00
|
|
|
/* _timestamp_nl_to_ms:
|
|
|
|
|
* @timestamp_nl: a timestamp from ifa_cacheinfo.
|
|
|
|
|
* @monotonic_ms: *now* in CLOCK_MONOTONIC. Needed to estimate the current
|
|
|
|
|
* uptime and how often timestamp_nl wrapped.
|
|
|
|
|
*
|
|
|
|
|
* Convert the timestamp from ifa_cacheinfo to CLOCK_MONOTONIC milliseconds.
|
|
|
|
|
* The ifa_cacheinfo fields tstamp and cstamp contains timestamps that counts
|
|
|
|
|
* with in 1/100th of a second of clock_gettime(CLOCK_MONOTONIC). However,
|
|
|
|
|
* the uint32 counter wraps every 497 days of uptime, so we have to compensate
|
|
|
|
|
* for that. */
|
|
|
|
|
static gint64
|
|
|
|
|
_timestamp_nl_to_ms (guint32 timestamp_nl, gint64 monotonic_ms)
|
|
|
|
|
{
|
|
|
|
|
const gint64 WRAP_INTERVAL = (((gint64) G_MAXUINT32) + 1) * (1000 / 100);
|
|
|
|
|
gint64 timestamp_nl_ms;
|
|
|
|
|
|
|
|
|
|
/* convert timestamp from 1/100th of a second to msec. */
|
|
|
|
|
timestamp_nl_ms = ((gint64) timestamp_nl) * (1000 / 100);
|
|
|
|
|
|
|
|
|
|
/* timestamp wraps every 497 days. Try to compensate for that.*/
|
|
|
|
|
if (timestamp_nl_ms > monotonic_ms) {
|
|
|
|
|
/* timestamp_nl_ms is in the future. Truncate it to *now* */
|
|
|
|
|
timestamp_nl_ms = monotonic_ms;
|
|
|
|
|
} else if (monotonic_ms >= WRAP_INTERVAL) {
|
|
|
|
|
timestamp_nl_ms += (monotonic_ms / WRAP_INTERVAL) * WRAP_INTERVAL;
|
|
|
|
|
if (timestamp_nl_ms > monotonic_ms)
|
|
|
|
|
timestamp_nl_ms -= WRAP_INTERVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return timestamp_nl_ms;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static guint32
|
|
|
|
|
_rtnl_addr_last_update_time_to_nm (const struct rtnl_addr *rtnladdr)
|
|
|
|
|
{
|
|
|
|
|
guint32 last_update_time = rtnl_addr_get_last_update_time ((struct rtnl_addr *) rtnladdr);
|
|
|
|
|
struct timespec tp;
|
|
|
|
|
gint64 now_nl, now_nm, result;
|
|
|
|
|
|
|
|
|
|
/* timestamp is unset. Default to 1. */
|
|
|
|
|
if (!last_update_time)
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
/* do all the calculations in milliseconds scale */
|
|
|
|
|
|
|
|
|
|
clock_gettime (CLOCK_MONOTONIC, &tp);
|
|
|
|
|
now_nm = nm_utils_get_monotonic_timestamp_ms ();
|
|
|
|
|
now_nl = (((gint64) tp.tv_sec) * ((gint64) 1000)) +
|
|
|
|
|
(tp.tv_nsec / (NM_UTILS_NS_PER_SECOND/1000));
|
|
|
|
|
|
|
|
|
|
result = now_nm - (now_nl - _timestamp_nl_to_ms (last_update_time, now_nl));
|
|
|
|
|
|
|
|
|
|
/* converting the last_update_time into nm_utils_get_monotonic_timestamp_ms() scale is
|
|
|
|
|
* a good guess but fails in the following situations:
|
|
|
|
|
*
|
|
|
|
|
* - If the address existed before start of the process, the timestamp in nm scale would
|
|
|
|
|
* be negative or zero. In this case we default to 1.
|
|
|
|
|
* - during hibernation, the CLOCK_MONOTONIC/last_update_time drifts from
|
|
|
|
|
* nm_utils_get_monotonic_timestamp_ms() scale.
|
|
|
|
|
*/
|
|
|
|
|
if (result <= 1000)
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
if (result > now_nm)
|
|
|
|
|
return now_nm / 1000;
|
|
|
|
|
|
|
|
|
|
return result / 1000;
|
|
|
|
|
}
|
|
|
|
|
|
platform: fix preferred and valid lifetimes for addresses from netlink/kernel
The kernel tells the address lifetimes in the 'struct ifa_cacheinfo'
attribute. This contains two timestamps (cstamp and tstamp) and two
relative lifetimes (ifa_prefered and ifa_valid).
The timestamps are equal to clock_gettime(CLOCK_MONOTONIC) scale in
1/100th of a second (wrapping every 497 days).
The preferred/valid times are re-adjusted everytime when sending the
message and count down as the time goes by. In other words, they are
anchored relatively to the moment of when kernel creates the netlink
message.
As platform is caching the rtnl_addr object, the information of *when* the
lifetimes started counting is not available.
This patch fixes reading these values by hacking the libnl object
when it gets received, so that valid and preferred are instead absolute
expiration timestamps in scale nm_utils_get_monotonic_timestamp_s() --
which NM internally is used for address timestamps.
There are two minor downsides to this hack:
- the valid and preferred properties of a cached rtnl_addr object have
an unexpected meaning, i.e. they are absolute and in a different time
scale.
- later when converting rtnl_addr to NMPlatformIPAddress, the base
timestamp is set to "1", i.e. an NMPlatformIPAddress has no knowledge
of when the address was created or last modified. The timestamp
property of NMPlatformIPAddress is solely there to anchor the relative
timestamps lifetime and preferred. Do not use it for anything
else.
Another reason the timestamp property is meaningless is that
its scale nm_utils_get_monotonic_timestamp_s() starts counting at
process start. So addresses that existed before would have a negative
or zero timestamp, which we avoid. This in turn could be solved by either
allowing negative timestamps or by shifting
nm_utils_get_monotonic_timestamp_*(). Both is viable, but not
necessary (ATM), because the age of an address has no other apparent
use then to anchor the relative timestamps.
Another implication is, that we potentially could get rid of the
timestamp completely, and insteat make preferred and lifetime be
absolute expiries.
This will be fixed properly later, by not caching libnl objects but instead
native NMPlatform objects. For those we have full control over their properties.
https://bugzilla.gnome.org/show_bug.cgi?id=727382
Signed-off-by: Thomas Haller <thaller@redhat.com>
2014-04-02 13:35:21 +02:00
|
|
|
static void
|
|
|
|
|
_init_ip_address_lifetime (NMPlatformIPAddress *address, const struct rtnl_addr *rtnladdr)
|
|
|
|
|
{
|
|
|
|
|
guint32 a_valid = rtnl_addr_get_valid_lifetime ((struct rtnl_addr *) rtnladdr);
|
|
|
|
|
guint32 a_preferred = rtnl_addr_get_preferred_lifetime ((struct rtnl_addr *) rtnladdr);
|
|
|
|
|
|
|
|
|
|
/* the meaning of the valid and preferred lifetimes is different from the
|
|
|
|
|
* original meaning. See _rtnl_addr_hack_lifetimes_rel_to_abs().
|
|
|
|
|
* Beware: this function expects hacked rtnl_addr objects.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (a_valid == NM_PLATFORM_LIFETIME_PERMANENT &&
|
|
|
|
|
a_preferred == NM_PLATFORM_LIFETIME_PERMANENT) {
|
|
|
|
|
address->timestamp = 0;
|
|
|
|
|
address->lifetime = NM_PLATFORM_LIFETIME_PERMANENT;
|
|
|
|
|
address->preferred = NM_PLATFORM_LIFETIME_PERMANENT;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* The valies are hacked and absolute expiry times. They must
|
|
|
|
|
* be positive and preferred<=valid. */
|
|
|
|
|
g_assert (a_preferred <= a_valid &&
|
|
|
|
|
a_valid > 0 &&
|
|
|
|
|
a_preferred > 0);
|
|
|
|
|
|
2014-06-07 12:52:22 +02:00
|
|
|
if (a_valid <= 1) {
|
|
|
|
|
/* Since we want to have positive @timestamp and @valid != 0,
|
|
|
|
|
* we must handle this case special. */
|
|
|
|
|
address->timestamp = 1;
|
|
|
|
|
address->lifetime = 1; /* Extend the lifetime by one second */
|
|
|
|
|
address->preferred = 0; /* no longer preferred. */
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2014-06-07 13:52:01 +02:00
|
|
|
/* _rtnl_addr_last_update_time_to_nm() might be wrong, so don't rely on
|
|
|
|
|
* timestamp to have any meaning beyond anchoring the relative durations
|
|
|
|
|
* @lifetime and @preferred.
|
platform: fix preferred and valid lifetimes for addresses from netlink/kernel
The kernel tells the address lifetimes in the 'struct ifa_cacheinfo'
attribute. This contains two timestamps (cstamp and tstamp) and two
relative lifetimes (ifa_prefered and ifa_valid).
The timestamps are equal to clock_gettime(CLOCK_MONOTONIC) scale in
1/100th of a second (wrapping every 497 days).
The preferred/valid times are re-adjusted everytime when sending the
message and count down as the time goes by. In other words, they are
anchored relatively to the moment of when kernel creates the netlink
message.
As platform is caching the rtnl_addr object, the information of *when* the
lifetimes started counting is not available.
This patch fixes reading these values by hacking the libnl object
when it gets received, so that valid and preferred are instead absolute
expiration timestamps in scale nm_utils_get_monotonic_timestamp_s() --
which NM internally is used for address timestamps.
There are two minor downsides to this hack:
- the valid and preferred properties of a cached rtnl_addr object have
an unexpected meaning, i.e. they are absolute and in a different time
scale.
- later when converting rtnl_addr to NMPlatformIPAddress, the base
timestamp is set to "1", i.e. an NMPlatformIPAddress has no knowledge
of when the address was created or last modified. The timestamp
property of NMPlatformIPAddress is solely there to anchor the relative
timestamps lifetime and preferred. Do not use it for anything
else.
Another reason the timestamp property is meaningless is that
its scale nm_utils_get_monotonic_timestamp_s() starts counting at
process start. So addresses that existed before would have a negative
or zero timestamp, which we avoid. This in turn could be solved by either
allowing negative timestamps or by shifting
nm_utils_get_monotonic_timestamp_*(). Both is viable, but not
necessary (ATM), because the age of an address has no other apparent
use then to anchor the relative timestamps.
Another implication is, that we potentially could get rid of the
timestamp completely, and insteat make preferred and lifetime be
absolute expiries.
This will be fixed properly later, by not caching libnl objects but instead
native NMPlatform objects. For those we have full control over their properties.
https://bugzilla.gnome.org/show_bug.cgi?id=727382
Signed-off-by: Thomas Haller <thaller@redhat.com>
2014-04-02 13:35:21 +02:00
|
|
|
*/
|
2014-06-07 13:52:01 +02:00
|
|
|
address->timestamp = _rtnl_addr_last_update_time_to_nm (rtnladdr);
|
platform: fix preferred and valid lifetimes for addresses from netlink/kernel
The kernel tells the address lifetimes in the 'struct ifa_cacheinfo'
attribute. This contains two timestamps (cstamp and tstamp) and two
relative lifetimes (ifa_prefered and ifa_valid).
The timestamps are equal to clock_gettime(CLOCK_MONOTONIC) scale in
1/100th of a second (wrapping every 497 days).
The preferred/valid times are re-adjusted everytime when sending the
message and count down as the time goes by. In other words, they are
anchored relatively to the moment of when kernel creates the netlink
message.
As platform is caching the rtnl_addr object, the information of *when* the
lifetimes started counting is not available.
This patch fixes reading these values by hacking the libnl object
when it gets received, so that valid and preferred are instead absolute
expiration timestamps in scale nm_utils_get_monotonic_timestamp_s() --
which NM internally is used for address timestamps.
There are two minor downsides to this hack:
- the valid and preferred properties of a cached rtnl_addr object have
an unexpected meaning, i.e. they are absolute and in a different time
scale.
- later when converting rtnl_addr to NMPlatformIPAddress, the base
timestamp is set to "1", i.e. an NMPlatformIPAddress has no knowledge
of when the address was created or last modified. The timestamp
property of NMPlatformIPAddress is solely there to anchor the relative
timestamps lifetime and preferred. Do not use it for anything
else.
Another reason the timestamp property is meaningless is that
its scale nm_utils_get_monotonic_timestamp_s() starts counting at
process start. So addresses that existed before would have a negative
or zero timestamp, which we avoid. This in turn could be solved by either
allowing negative timestamps or by shifting
nm_utils_get_monotonic_timestamp_*(). Both is viable, but not
necessary (ATM), because the age of an address has no other apparent
use then to anchor the relative timestamps.
Another implication is, that we potentially could get rid of the
timestamp completely, and insteat make preferred and lifetime be
absolute expiries.
This will be fixed properly later, by not caching libnl objects but instead
native NMPlatform objects. For those we have full control over their properties.
https://bugzilla.gnome.org/show_bug.cgi?id=727382
Signed-off-by: Thomas Haller <thaller@redhat.com>
2014-04-02 13:35:21 +02:00
|
|
|
|
2014-06-07 13:52:01 +02:00
|
|
|
/* We would expect @timestamp to be less then @a_valid. Just to be sure,
|
|
|
|
|
* fix it up. */
|
|
|
|
|
address->timestamp = MIN (address->timestamp, a_valid - 1);
|
2014-06-07 12:52:22 +02:00
|
|
|
address->lifetime = _get_remaining_time (address->timestamp, a_valid);
|
|
|
|
|
address->preferred = _get_remaining_time (address->timestamp, a_preferred);
|
platform: fix preferred and valid lifetimes for addresses from netlink/kernel
The kernel tells the address lifetimes in the 'struct ifa_cacheinfo'
attribute. This contains two timestamps (cstamp and tstamp) and two
relative lifetimes (ifa_prefered and ifa_valid).
The timestamps are equal to clock_gettime(CLOCK_MONOTONIC) scale in
1/100th of a second (wrapping every 497 days).
The preferred/valid times are re-adjusted everytime when sending the
message and count down as the time goes by. In other words, they are
anchored relatively to the moment of when kernel creates the netlink
message.
As platform is caching the rtnl_addr object, the information of *when* the
lifetimes started counting is not available.
This patch fixes reading these values by hacking the libnl object
when it gets received, so that valid and preferred are instead absolute
expiration timestamps in scale nm_utils_get_monotonic_timestamp_s() --
which NM internally is used for address timestamps.
There are two minor downsides to this hack:
- the valid and preferred properties of a cached rtnl_addr object have
an unexpected meaning, i.e. they are absolute and in a different time
scale.
- later when converting rtnl_addr to NMPlatformIPAddress, the base
timestamp is set to "1", i.e. an NMPlatformIPAddress has no knowledge
of when the address was created or last modified. The timestamp
property of NMPlatformIPAddress is solely there to anchor the relative
timestamps lifetime and preferred. Do not use it for anything
else.
Another reason the timestamp property is meaningless is that
its scale nm_utils_get_monotonic_timestamp_s() starts counting at
process start. So addresses that existed before would have a negative
or zero timestamp, which we avoid. This in turn could be solved by either
allowing negative timestamps or by shifting
nm_utils_get_monotonic_timestamp_*(). Both is viable, but not
necessary (ATM), because the age of an address has no other apparent
use then to anchor the relative timestamps.
Another implication is, that we potentially could get rid of the
timestamp completely, and insteat make preferred and lifetime be
absolute expiries.
This will be fixed properly later, by not caching libnl objects but instead
native NMPlatform objects. For those we have full control over their properties.
https://bugzilla.gnome.org/show_bug.cgi?id=727382
Signed-off-by: Thomas Haller <thaller@redhat.com>
2014-04-02 13:35:21 +02:00
|
|
|
}
|
|
|
|
|
|
2014-02-17 12:48:05 +01:00
|
|
|
static gboolean
|
2013-03-27 22:23:24 +01:00
|
|
|
init_ip4_address (NMPlatformIP4Address *address, struct rtnl_addr *rtnladdr)
|
|
|
|
|
{
|
|
|
|
|
struct nl_addr *nladdr = rtnl_addr_get_local (rtnladdr);
|
2013-12-02 10:20:26 -05:00
|
|
|
struct nl_addr *nlpeer = rtnl_addr_get_peer (rtnladdr);
|
2014-02-19 16:10:59 -05:00
|
|
|
const char *label;
|
2013-03-27 22:23:24 +01:00
|
|
|
|
2014-02-17 12:48:05 +01:00
|
|
|
g_return_val_if_fail (nladdr, FALSE);
|
2013-03-27 22:23:24 +01:00
|
|
|
|
|
|
|
|
memset (address, 0, sizeof (*address));
|
|
|
|
|
|
2014-10-13 11:52:29 +02:00
|
|
|
address->source = NM_IP_CONFIG_SOURCE_KERNEL;
|
2013-03-27 22:23:24 +01:00
|
|
|
address->ifindex = rtnl_addr_get_ifindex (rtnladdr);
|
|
|
|
|
address->plen = rtnl_addr_get_prefixlen (rtnladdr);
|
platform: fix preferred and valid lifetimes for addresses from netlink/kernel
The kernel tells the address lifetimes in the 'struct ifa_cacheinfo'
attribute. This contains two timestamps (cstamp and tstamp) and two
relative lifetimes (ifa_prefered and ifa_valid).
The timestamps are equal to clock_gettime(CLOCK_MONOTONIC) scale in
1/100th of a second (wrapping every 497 days).
The preferred/valid times are re-adjusted everytime when sending the
message and count down as the time goes by. In other words, they are
anchored relatively to the moment of when kernel creates the netlink
message.
As platform is caching the rtnl_addr object, the information of *when* the
lifetimes started counting is not available.
This patch fixes reading these values by hacking the libnl object
when it gets received, so that valid and preferred are instead absolute
expiration timestamps in scale nm_utils_get_monotonic_timestamp_s() --
which NM internally is used for address timestamps.
There are two minor downsides to this hack:
- the valid and preferred properties of a cached rtnl_addr object have
an unexpected meaning, i.e. they are absolute and in a different time
scale.
- later when converting rtnl_addr to NMPlatformIPAddress, the base
timestamp is set to "1", i.e. an NMPlatformIPAddress has no knowledge
of when the address was created or last modified. The timestamp
property of NMPlatformIPAddress is solely there to anchor the relative
timestamps lifetime and preferred. Do not use it for anything
else.
Another reason the timestamp property is meaningless is that
its scale nm_utils_get_monotonic_timestamp_s() starts counting at
process start. So addresses that existed before would have a negative
or zero timestamp, which we avoid. This in turn could be solved by either
allowing negative timestamps or by shifting
nm_utils_get_monotonic_timestamp_*(). Both is viable, but not
necessary (ATM), because the age of an address has no other apparent
use then to anchor the relative timestamps.
Another implication is, that we potentially could get rid of the
timestamp completely, and insteat make preferred and lifetime be
absolute expiries.
This will be fixed properly later, by not caching libnl objects but instead
native NMPlatform objects. For those we have full control over their properties.
https://bugzilla.gnome.org/show_bug.cgi?id=727382
Signed-off-by: Thomas Haller <thaller@redhat.com>
2014-04-02 13:35:21 +02:00
|
|
|
_init_ip_address_lifetime ((NMPlatformIPAddress *) address, rtnladdr);
|
2014-02-17 12:48:05 +01:00
|
|
|
if (!nladdr || nl_addr_get_len (nladdr) != sizeof (address->address)) {
|
|
|
|
|
g_return_val_if_reached (FALSE);
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
2013-03-27 22:23:24 +01:00
|
|
|
memcpy (&address->address, nl_addr_get_binary_addr (nladdr), sizeof (address->address));
|
2013-12-02 10:20:26 -05:00
|
|
|
if (nlpeer) {
|
2014-02-17 12:48:05 +01:00
|
|
|
if (nl_addr_get_len (nlpeer) != sizeof (address->peer_address)) {
|
|
|
|
|
g_return_val_if_reached (FALSE);
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
2013-12-02 10:20:26 -05:00
|
|
|
memcpy (&address->peer_address, nl_addr_get_binary_addr (nlpeer), sizeof (address->peer_address));
|
|
|
|
|
}
|
2014-02-19 16:10:59 -05:00
|
|
|
label = rtnl_addr_get_label (rtnladdr);
|
2014-03-26 12:43:24 -04:00
|
|
|
/* Check for ':'; we're only interested in labels used as interface aliases */
|
|
|
|
|
if (label && strchr (label, ':'))
|
2014-02-19 16:10:59 -05:00
|
|
|
g_strlcpy (address->label, label, sizeof (address->label));
|
2014-02-17 12:48:05 +01:00
|
|
|
|
|
|
|
|
return TRUE;
|
2013-03-27 22:23:24 +01:00
|
|
|
}
|
|
|
|
|
|
2014-02-17 12:48:05 +01:00
|
|
|
static gboolean
|
2013-03-27 22:23:24 +01:00
|
|
|
init_ip6_address (NMPlatformIP6Address *address, struct rtnl_addr *rtnladdr)
|
|
|
|
|
{
|
|
|
|
|
struct nl_addr *nladdr = rtnl_addr_get_local (rtnladdr);
|
2013-12-02 10:20:26 -05:00
|
|
|
struct nl_addr *nlpeer = rtnl_addr_get_peer (rtnladdr);
|
2013-03-27 22:23:24 +01:00
|
|
|
|
|
|
|
|
memset (address, 0, sizeof (*address));
|
|
|
|
|
|
2014-10-13 11:52:29 +02:00
|
|
|
address->source = NM_IP_CONFIG_SOURCE_KERNEL;
|
2013-03-27 22:23:24 +01:00
|
|
|
address->ifindex = rtnl_addr_get_ifindex (rtnladdr);
|
|
|
|
|
address->plen = rtnl_addr_get_prefixlen (rtnladdr);
|
platform: fix preferred and valid lifetimes for addresses from netlink/kernel
The kernel tells the address lifetimes in the 'struct ifa_cacheinfo'
attribute. This contains two timestamps (cstamp and tstamp) and two
relative lifetimes (ifa_prefered and ifa_valid).
The timestamps are equal to clock_gettime(CLOCK_MONOTONIC) scale in
1/100th of a second (wrapping every 497 days).
The preferred/valid times are re-adjusted everytime when sending the
message and count down as the time goes by. In other words, they are
anchored relatively to the moment of when kernel creates the netlink
message.
As platform is caching the rtnl_addr object, the information of *when* the
lifetimes started counting is not available.
This patch fixes reading these values by hacking the libnl object
when it gets received, so that valid and preferred are instead absolute
expiration timestamps in scale nm_utils_get_monotonic_timestamp_s() --
which NM internally is used for address timestamps.
There are two minor downsides to this hack:
- the valid and preferred properties of a cached rtnl_addr object have
an unexpected meaning, i.e. they are absolute and in a different time
scale.
- later when converting rtnl_addr to NMPlatformIPAddress, the base
timestamp is set to "1", i.e. an NMPlatformIPAddress has no knowledge
of when the address was created or last modified. The timestamp
property of NMPlatformIPAddress is solely there to anchor the relative
timestamps lifetime and preferred. Do not use it for anything
else.
Another reason the timestamp property is meaningless is that
its scale nm_utils_get_monotonic_timestamp_s() starts counting at
process start. So addresses that existed before would have a negative
or zero timestamp, which we avoid. This in turn could be solved by either
allowing negative timestamps or by shifting
nm_utils_get_monotonic_timestamp_*(). Both is viable, but not
necessary (ATM), because the age of an address has no other apparent
use then to anchor the relative timestamps.
Another implication is, that we potentially could get rid of the
timestamp completely, and insteat make preferred and lifetime be
absolute expiries.
This will be fixed properly later, by not caching libnl objects but instead
native NMPlatform objects. For those we have full control over their properties.
https://bugzilla.gnome.org/show_bug.cgi?id=727382
Signed-off-by: Thomas Haller <thaller@redhat.com>
2014-04-02 13:35:21 +02:00
|
|
|
_init_ip_address_lifetime ((NMPlatformIPAddress *) address, rtnladdr);
|
2013-10-15 20:44:59 +02:00
|
|
|
address->flags = rtnl_addr_get_flags (rtnladdr);
|
2014-02-17 12:48:05 +01:00
|
|
|
if (!nladdr || nl_addr_get_len (nladdr) != sizeof (address->address)) {
|
|
|
|
|
g_return_val_if_reached (FALSE);
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
2013-03-27 22:23:24 +01:00
|
|
|
memcpy (&address->address, nl_addr_get_binary_addr (nladdr), sizeof (address->address));
|
2013-12-02 10:20:26 -05:00
|
|
|
if (nlpeer) {
|
2014-02-17 12:48:05 +01:00
|
|
|
if (nl_addr_get_len (nlpeer) != sizeof (address->peer_address)) {
|
|
|
|
|
g_return_val_if_reached (FALSE);
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
2013-12-02 10:20:26 -05:00
|
|
|
memcpy (&address->peer_address, nl_addr_get_binary_addr (nlpeer), sizeof (address->peer_address));
|
|
|
|
|
}
|
2014-02-17 12:48:05 +01:00
|
|
|
|
|
|
|
|
return TRUE;
|
2013-03-27 22:23:24 +01:00
|
|
|
}
|
|
|
|
|
|
2014-05-08 14:50:02 -04:00
|
|
|
static guint
|
2014-10-13 11:52:29 +02:00
|
|
|
source_to_rtprot (NMIPConfigSource source)
|
2014-05-08 14:50:02 -04:00
|
|
|
{
|
|
|
|
|
switch (source) {
|
2014-10-13 11:52:29 +02:00
|
|
|
case NM_IP_CONFIG_SOURCE_UNKNOWN:
|
2014-05-08 14:50:02 -04:00
|
|
|
return RTPROT_UNSPEC;
|
2014-10-13 11:52:29 +02:00
|
|
|
case NM_IP_CONFIG_SOURCE_KERNEL:
|
2014-05-08 14:50:02 -04:00
|
|
|
return RTPROT_KERNEL;
|
2014-10-13 11:52:29 +02:00
|
|
|
case NM_IP_CONFIG_SOURCE_DHCP:
|
2014-05-08 14:50:02 -04:00
|
|
|
return RTPROT_DHCP;
|
2014-10-13 11:52:29 +02:00
|
|
|
case NM_IP_CONFIG_SOURCE_RDISC:
|
2014-05-08 14:50:02 -04:00
|
|
|
return RTPROT_RA;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
return RTPROT_STATIC;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-10-13 11:52:29 +02:00
|
|
|
static NMIPConfigSource
|
2014-05-08 14:50:02 -04:00
|
|
|
rtprot_to_source (guint rtprot)
|
|
|
|
|
{
|
|
|
|
|
switch (rtprot) {
|
|
|
|
|
case RTPROT_UNSPEC:
|
2014-10-13 11:52:29 +02:00
|
|
|
return NM_IP_CONFIG_SOURCE_UNKNOWN;
|
2014-05-08 14:50:02 -04:00
|
|
|
case RTPROT_REDIRECT:
|
|
|
|
|
case RTPROT_KERNEL:
|
2014-10-13 11:52:29 +02:00
|
|
|
return NM_IP_CONFIG_SOURCE_KERNEL;
|
2014-05-08 14:50:02 -04:00
|
|
|
case RTPROT_RA:
|
2014-10-13 11:52:29 +02:00
|
|
|
return NM_IP_CONFIG_SOURCE_RDISC;
|
2014-05-08 14:50:02 -04:00
|
|
|
case RTPROT_DHCP:
|
2014-10-13 11:52:29 +02:00
|
|
|
return NM_IP_CONFIG_SOURCE_DHCP;
|
2014-05-08 14:50:02 -04:00
|
|
|
|
|
|
|
|
default:
|
2014-10-13 11:52:29 +02:00
|
|
|
return NM_IP_CONFIG_SOURCE_USER;
|
2014-05-08 14:50:02 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-10-14 19:02:50 +02:00
|
|
|
static gboolean
|
|
|
|
|
_rtnl_route_is_default (const struct rtnl_route *rtnlroute)
|
|
|
|
|
{
|
|
|
|
|
struct nl_addr *dst;
|
|
|
|
|
|
|
|
|
|
return rtnlroute
|
|
|
|
|
&& (dst = rtnl_route_get_dst ((struct rtnl_route *) rtnlroute))
|
|
|
|
|
&& nl_addr_get_prefixlen (dst) == 0;
|
|
|
|
|
}
|
|
|
|
|
|
2013-08-12 17:25:29 +02:00
|
|
|
static gboolean
|
2013-03-27 22:23:24 +01:00
|
|
|
init_ip4_route (NMPlatformIP4Route *route, struct rtnl_route *rtnlroute)
|
|
|
|
|
{
|
|
|
|
|
struct nl_addr *dst, *gw;
|
|
|
|
|
struct rtnl_nexthop *nexthop;
|
|
|
|
|
|
2013-08-12 17:25:29 +02:00
|
|
|
memset (route, 0, sizeof (*route));
|
|
|
|
|
|
|
|
|
|
/* Multi-hop routes not supported. */
|
|
|
|
|
if (rtnl_route_get_nnexthops (rtnlroute) != 1)
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
2013-03-27 22:23:24 +01:00
|
|
|
nexthop = rtnl_route_nexthop_n (rtnlroute, 0);
|
|
|
|
|
dst = rtnl_route_get_dst (rtnlroute);
|
|
|
|
|
gw = rtnl_route_nh_get_gateway (nexthop);
|
|
|
|
|
|
|
|
|
|
route->ifindex = rtnl_route_nh_get_ifindex (nexthop);
|
|
|
|
|
route->plen = nl_addr_get_prefixlen (dst);
|
2013-06-19 15:35:53 +02:00
|
|
|
/* Workaround on previous workaround for libnl default route prefixlen bug. */
|
|
|
|
|
if (nl_addr_get_len (dst)) {
|
2014-02-17 12:48:05 +01:00
|
|
|
if (nl_addr_get_len (dst) != sizeof (route->network)) {
|
|
|
|
|
g_return_val_if_reached (FALSE);
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
2013-06-19 15:35:53 +02:00
|
|
|
memcpy (&route->network, nl_addr_get_binary_addr (dst), sizeof (route->network));
|
|
|
|
|
}
|
|
|
|
|
if (gw) {
|
2014-02-17 12:48:05 +01:00
|
|
|
if (nl_addr_get_len (gw) != sizeof (route->network)) {
|
|
|
|
|
g_return_val_if_reached (FALSE);
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
2013-03-27 22:23:24 +01:00
|
|
|
memcpy (&route->gateway, nl_addr_get_binary_addr (gw), sizeof (route->gateway));
|
2013-06-19 15:35:53 +02:00
|
|
|
}
|
2013-03-27 22:23:24 +01:00
|
|
|
route->metric = rtnl_route_get_priority (rtnlroute);
|
2013-05-02 08:42:13 +02:00
|
|
|
rtnl_route_get_metric (rtnlroute, RTAX_ADVMSS, &route->mss);
|
2014-05-08 14:50:02 -04:00
|
|
|
route->source = rtprot_to_source (rtnl_route_get_protocol (rtnlroute));
|
2013-08-12 17:25:29 +02:00
|
|
|
|
|
|
|
|
return TRUE;
|
2013-03-27 22:23:24 +01:00
|
|
|
}
|
|
|
|
|
|
2013-08-12 17:25:29 +02:00
|
|
|
static gboolean
|
2013-03-27 22:23:24 +01:00
|
|
|
init_ip6_route (NMPlatformIP6Route *route, struct rtnl_route *rtnlroute)
|
|
|
|
|
{
|
|
|
|
|
struct nl_addr *dst, *gw;
|
|
|
|
|
struct rtnl_nexthop *nexthop;
|
|
|
|
|
|
2013-08-12 17:25:29 +02:00
|
|
|
memset (route, 0, sizeof (*route));
|
|
|
|
|
|
|
|
|
|
/* Multi-hop routes not supported. */
|
|
|
|
|
if (rtnl_route_get_nnexthops (rtnlroute) != 1)
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
2013-03-27 22:23:24 +01:00
|
|
|
nexthop = rtnl_route_nexthop_n (rtnlroute, 0);
|
|
|
|
|
dst = rtnl_route_get_dst (rtnlroute);
|
|
|
|
|
gw = rtnl_route_nh_get_gateway (nexthop);
|
|
|
|
|
|
|
|
|
|
route->ifindex = rtnl_route_nh_get_ifindex (nexthop);
|
|
|
|
|
route->plen = nl_addr_get_prefixlen (dst);
|
2013-06-19 15:35:53 +02:00
|
|
|
/* Workaround on previous workaround for libnl default route prefixlen bug. */
|
|
|
|
|
if (nl_addr_get_len (dst)) {
|
2014-02-17 12:48:05 +01:00
|
|
|
if (nl_addr_get_len (dst) != sizeof (route->network)) {
|
|
|
|
|
g_return_val_if_reached (FALSE);
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
2013-06-19 15:35:53 +02:00
|
|
|
memcpy (&route->network, nl_addr_get_binary_addr (dst), sizeof (route->network));
|
|
|
|
|
}
|
|
|
|
|
if (gw) {
|
2014-02-17 12:48:05 +01:00
|
|
|
if (nl_addr_get_len (gw) != sizeof (route->network)) {
|
|
|
|
|
g_return_val_if_reached (FALSE);
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
2013-03-27 22:23:24 +01:00
|
|
|
memcpy (&route->gateway, nl_addr_get_binary_addr (gw), sizeof (route->gateway));
|
2013-06-19 15:35:53 +02:00
|
|
|
}
|
2013-03-27 22:23:24 +01:00
|
|
|
route->metric = rtnl_route_get_priority (rtnlroute);
|
2013-05-02 08:42:13 +02:00
|
|
|
rtnl_route_get_metric (rtnlroute, RTAX_ADVMSS, &route->mss);
|
2014-05-08 14:50:02 -04:00
|
|
|
route->source = rtprot_to_source (rtnl_route_get_protocol (rtnlroute));
|
2013-08-12 17:25:29 +02:00
|
|
|
|
|
|
|
|
return TRUE;
|
2013-03-27 22:23:24 +01:00
|
|
|
}
|
|
|
|
|
|
2014-02-17 14:20:44 +01:00
|
|
|
static char to_string_buffer[255];
|
|
|
|
|
|
|
|
|
|
#define SET_AND_RETURN_STRING_BUFFER(...) \
|
|
|
|
|
G_STMT_START { \
|
|
|
|
|
g_snprintf (to_string_buffer, sizeof (to_string_buffer), ## __VA_ARGS__); \
|
|
|
|
|
g_return_val_if_reached (to_string_buffer); \
|
|
|
|
|
return to_string_buffer; \
|
|
|
|
|
} G_STMT_END
|
|
|
|
|
|
|
|
|
|
static const char *
|
|
|
|
|
to_string_link (NMPlatform *platform, struct rtnl_link *obj)
|
|
|
|
|
{
|
|
|
|
|
NMPlatformLink pl_obj;
|
|
|
|
|
|
|
|
|
|
if (init_link (platform, &pl_obj, obj))
|
|
|
|
|
return nm_platform_link_to_string (&pl_obj);
|
|
|
|
|
SET_AND_RETURN_STRING_BUFFER ("(invalid link %p)", obj);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const char *
|
|
|
|
|
to_string_ip4_address (struct rtnl_addr *obj)
|
|
|
|
|
{
|
|
|
|
|
NMPlatformIP4Address pl_obj;
|
|
|
|
|
|
|
|
|
|
if (init_ip4_address (&pl_obj, obj))
|
|
|
|
|
return nm_platform_ip4_address_to_string (&pl_obj);
|
|
|
|
|
SET_AND_RETURN_STRING_BUFFER ("(invalid ip4 address %p)", obj);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const char *
|
|
|
|
|
to_string_ip6_address (struct rtnl_addr *obj)
|
|
|
|
|
{
|
|
|
|
|
NMPlatformIP6Address pl_obj;
|
|
|
|
|
|
|
|
|
|
if (init_ip6_address (&pl_obj, obj))
|
|
|
|
|
return nm_platform_ip6_address_to_string (&pl_obj);
|
|
|
|
|
SET_AND_RETURN_STRING_BUFFER ("(invalid ip6 address %p)", obj);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const char *
|
|
|
|
|
to_string_ip4_route (struct rtnl_route *obj)
|
|
|
|
|
{
|
|
|
|
|
NMPlatformIP4Route pl_obj;
|
|
|
|
|
|
|
|
|
|
if (init_ip4_route (&pl_obj, obj))
|
|
|
|
|
return nm_platform_ip4_route_to_string (&pl_obj);
|
|
|
|
|
SET_AND_RETURN_STRING_BUFFER ("(invalid ip4 route %p)", obj);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const char *
|
|
|
|
|
to_string_ip6_route (struct rtnl_route *obj)
|
|
|
|
|
{
|
|
|
|
|
NMPlatformIP6Route pl_obj;
|
|
|
|
|
|
|
|
|
|
if (init_ip6_route (&pl_obj, obj))
|
|
|
|
|
return nm_platform_ip6_route_to_string (&pl_obj);
|
|
|
|
|
SET_AND_RETURN_STRING_BUFFER ("(invalid ip6 route %p)", obj);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const char *
|
|
|
|
|
to_string_object_with_type (NMPlatform *platform, struct nl_object *obj, ObjectType type)
|
|
|
|
|
{
|
|
|
|
|
switch (type) {
|
2014-04-05 12:39:00 +02:00
|
|
|
case OBJECT_TYPE_LINK:
|
2014-02-17 14:20:44 +01:00
|
|
|
return to_string_link (platform, (struct rtnl_link *) obj);
|
2014-04-05 12:39:00 +02:00
|
|
|
case OBJECT_TYPE_IP4_ADDRESS:
|
2014-02-17 14:20:44 +01:00
|
|
|
return to_string_ip4_address ((struct rtnl_addr *) obj);
|
2014-04-05 12:39:00 +02:00
|
|
|
case OBJECT_TYPE_IP6_ADDRESS:
|
2014-02-17 14:20:44 +01:00
|
|
|
return to_string_ip6_address ((struct rtnl_addr *) obj);
|
2014-04-05 12:39:00 +02:00
|
|
|
case OBJECT_TYPE_IP4_ROUTE:
|
2014-02-17 14:20:44 +01:00
|
|
|
return to_string_ip4_route ((struct rtnl_route *) obj);
|
2014-04-05 12:39:00 +02:00
|
|
|
case OBJECT_TYPE_IP6_ROUTE:
|
2014-02-17 14:20:44 +01:00
|
|
|
return to_string_ip6_route ((struct rtnl_route *) obj);
|
|
|
|
|
default:
|
|
|
|
|
SET_AND_RETURN_STRING_BUFFER ("(unknown netlink object %p)", obj);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const char *
|
|
|
|
|
to_string_object (NMPlatform *platform, struct nl_object *obj)
|
|
|
|
|
{
|
|
|
|
|
return to_string_object_with_type (platform, obj, object_type_from_nl_object (obj));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#undef SET_AND_RETURN_STRING_BUFFER
|
|
|
|
|
|
2013-03-27 22:23:24 +01:00
|
|
|
/******************************************************************/
|
|
|
|
|
|
|
|
|
|
/* Object and cache manipulation */
|
|
|
|
|
|
2014-04-05 12:39:00 +02:00
|
|
|
static const char *signal_by_type_and_status[__OBJECT_TYPE_LAST] = {
|
|
|
|
|
[OBJECT_TYPE_LINK] = NM_PLATFORM_SIGNAL_LINK_CHANGED,
|
|
|
|
|
[OBJECT_TYPE_IP4_ADDRESS] = NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED,
|
|
|
|
|
[OBJECT_TYPE_IP6_ADDRESS] = NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED,
|
|
|
|
|
[OBJECT_TYPE_IP4_ROUTE] = NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED,
|
|
|
|
|
[OBJECT_TYPE_IP6_ROUTE] = NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED,
|
2013-03-27 22:23:24 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static struct nl_cache *
|
2014-02-13 16:08:36 +01:00
|
|
|
choose_cache_by_type (NMPlatform *platform, ObjectType object_type)
|
2013-03-27 22:23:24 +01:00
|
|
|
{
|
|
|
|
|
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
|
|
|
|
|
|
2014-02-13 16:08:36 +01:00
|
|
|
switch (object_type) {
|
2014-04-05 12:39:00 +02:00
|
|
|
case OBJECT_TYPE_LINK:
|
2013-03-27 22:23:24 +01:00
|
|
|
return priv->link_cache;
|
2014-04-05 12:39:00 +02:00
|
|
|
case OBJECT_TYPE_IP4_ADDRESS:
|
|
|
|
|
case OBJECT_TYPE_IP6_ADDRESS:
|
2013-03-27 22:23:24 +01:00
|
|
|
return priv->address_cache;
|
2014-04-05 12:39:00 +02:00
|
|
|
case OBJECT_TYPE_IP4_ROUTE:
|
|
|
|
|
case OBJECT_TYPE_IP6_ROUTE:
|
2013-03-27 22:23:24 +01:00
|
|
|
return priv->route_cache;
|
2013-03-27 22:23:24 +01:00
|
|
|
default:
|
2014-02-11 22:10:05 +01:00
|
|
|
g_return_val_if_reached (NULL);
|
|
|
|
|
return NULL;
|
2013-03-27 22:23:24 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-13 16:08:36 +01:00
|
|
|
static struct nl_cache *
|
|
|
|
|
choose_cache (NMPlatform *platform, struct nl_object *object)
|
|
|
|
|
{
|
|
|
|
|
return choose_cache_by_type (platform, object_type_from_nl_object (object));
|
|
|
|
|
}
|
|
|
|
|
|
2013-08-02 14:25:07 +02:00
|
|
|
static gboolean
|
|
|
|
|
object_has_ifindex (struct nl_object *object, int ifindex)
|
2013-07-24 10:38:43 +02:00
|
|
|
{
|
|
|
|
|
switch (object_type_from_nl_object (object)) {
|
2014-04-05 12:39:00 +02:00
|
|
|
case OBJECT_TYPE_IP4_ADDRESS:
|
|
|
|
|
case OBJECT_TYPE_IP6_ADDRESS:
|
2013-08-02 14:25:07 +02:00
|
|
|
return ifindex == rtnl_addr_get_ifindex ((struct rtnl_addr *) object);
|
2014-04-05 12:39:00 +02:00
|
|
|
case OBJECT_TYPE_IP4_ROUTE:
|
|
|
|
|
case OBJECT_TYPE_IP6_ROUTE:
|
2013-07-24 10:38:43 +02:00
|
|
|
{
|
|
|
|
|
struct rtnl_route *rtnlroute = (struct rtnl_route *) object;
|
|
|
|
|
struct rtnl_nexthop *nexthop;
|
|
|
|
|
|
|
|
|
|
if (rtnl_route_get_nnexthops (rtnlroute) != 1)
|
2013-08-02 14:25:07 +02:00
|
|
|
return FALSE;
|
2013-07-24 10:38:43 +02:00
|
|
|
nexthop = rtnl_route_nexthop_n (rtnlroute, 0);
|
2013-08-02 14:25:07 +02:00
|
|
|
|
|
|
|
|
return ifindex == rtnl_route_nh_get_ifindex (nexthop);
|
2013-07-24 10:38:43 +02:00
|
|
|
}
|
|
|
|
|
default:
|
2013-08-02 14:25:07 +02:00
|
|
|
g_assert_not_reached ();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-08-02 18:51:06 +02:00
|
|
|
static gboolean refresh_object (NMPlatform *platform, struct nl_object *object, gboolean removed, NMPlatformReason reason);
|
2013-08-02 14:25:07 +02:00
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
check_cache_items (NMPlatform *platform, struct nl_cache *cache, int ifindex)
|
|
|
|
|
{
|
|
|
|
|
auto_nl_cache struct nl_cache *cloned_cache = nl_cache_clone (cache);
|
|
|
|
|
struct nl_object *object;
|
2014-05-29 10:50:45 +02:00
|
|
|
GPtrArray *objects_to_refresh = g_ptr_array_new_with_free_func ((GDestroyNotify) nl_object_put);
|
|
|
|
|
guint i;
|
2013-08-02 14:25:07 +02:00
|
|
|
|
|
|
|
|
for (object = nl_cache_get_first (cloned_cache); object; object = nl_cache_get_next (object)) {
|
2014-05-29 10:50:45 +02:00
|
|
|
if (object_has_ifindex (object, ifindex)) {
|
|
|
|
|
nl_object_get (object);
|
|
|
|
|
g_ptr_array_add (objects_to_refresh, object);
|
|
|
|
|
}
|
2013-07-24 10:38:43 +02:00
|
|
|
}
|
2014-05-29 10:50:45 +02:00
|
|
|
|
|
|
|
|
for (i = 0; i < objects_to_refresh->len; i++)
|
2014-05-30 20:15:55 +02:00
|
|
|
refresh_object (platform, objects_to_refresh->pdata[i], TRUE, NM_PLATFORM_REASON_CACHE_CHECK);
|
2014-05-29 10:50:45 +02:00
|
|
|
|
|
|
|
|
g_ptr_array_free (objects_to_refresh, TRUE);
|
2013-07-24 10:38:43 +02:00
|
|
|
}
|
|
|
|
|
|
2013-03-27 22:23:24 +01:00
|
|
|
static void
|
2014-03-07 19:04:38 +01:00
|
|
|
announce_object (NMPlatform *platform, const struct nl_object *object, NMPlatformSignalChangeType change_type, NMPlatformReason reason)
|
2013-03-27 22:23:24 +01:00
|
|
|
{
|
2013-07-24 10:38:43 +02:00
|
|
|
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
|
2013-03-27 22:23:24 +01:00
|
|
|
ObjectType object_type = object_type_from_nl_object (object);
|
2014-03-07 19:04:38 +01:00
|
|
|
const char *sig = signal_by_type_and_status[object_type];
|
2013-03-27 22:23:24 +01:00
|
|
|
|
|
|
|
|
switch (object_type) {
|
2014-04-05 12:39:00 +02:00
|
|
|
case OBJECT_TYPE_LINK:
|
2013-03-27 22:23:24 +01:00
|
|
|
{
|
core: don't wait for udev to find created software devices
NM knows software devices it creates exist immediately, and it knows
it can use them immediately instead of waiting for udev to find them.
Ideally we'd wait for udev to find all devices, to allow tags and
other rules to run, but when creating software devices they must
currently be available for use immediately.
Bridges and bonds are required to be IFF_UP before they can be
activated (see their is_available() implementation). But during
activation code paths, specifically in nm_manager_activate_connection(),
the interface is created and immediately checked for availability
to activate (since the creation was a result of the user requesting
device activation). That availability check fails, because the
device is not visible outside NMPlatform (because it hasn't been
found by udev yet, because we haven't gotten back to the event
loop) and thus nm_platform_link_is_up() fails, failing the
availability check. Thus the device activation ends in error.
What should really happen is that device activation shouldn't
be synchronous; a new NMActiveConnection should be created for
any activation request, which can then wait until all the
resources it needs for activation are available (device found,
master devices created, etc). That's going to take more work
though.
2013-08-04 22:08:48 -05:00
|
|
|
struct rtnl_link *rtnl_link = (struct rtnl_link *) object;
|
2014-07-24 15:57:08 -05:00
|
|
|
NMPlatformLink device;
|
|
|
|
|
|
|
|
|
|
#if HAVE_LIBNL_INET6_ADDR_GEN_MODE
|
|
|
|
|
/* If we ever see a link with valid IPv6 link-local address
|
|
|
|
|
* generation modes, the kernel supports it.
|
|
|
|
|
*/
|
|
|
|
|
if (priv->support_user_ipv6ll == 0) {
|
|
|
|
|
uint8_t mode;
|
|
|
|
|
|
|
|
|
|
if (rtnl_link_inet6_get_addr_gen_mode (rtnl_link, &mode) == 0)
|
|
|
|
|
priv->support_user_ipv6ll = 1;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
2013-03-27 22:23:24 +01:00
|
|
|
|
2014-02-17 12:48:05 +01:00
|
|
|
if (!init_link (platform, &device, rtnl_link))
|
|
|
|
|
return;
|
2013-07-24 10:38:43 +02:00
|
|
|
|
core: don't wait for udev to find created software devices
NM knows software devices it creates exist immediately, and it knows
it can use them immediately instead of waiting for udev to find them.
Ideally we'd wait for udev to find all devices, to allow tags and
other rules to run, but when creating software devices they must
currently be available for use immediately.
Bridges and bonds are required to be IFF_UP before they can be
activated (see their is_available() implementation). But during
activation code paths, specifically in nm_manager_activate_connection(),
the interface is created and immediately checked for availability
to activate (since the creation was a result of the user requesting
device activation). That availability check fails, because the
device is not visible outside NMPlatform (because it hasn't been
found by udev yet, because we haven't gotten back to the event
loop) and thus nm_platform_link_is_up() fails, failing the
availability check. Thus the device activation ends in error.
What should really happen is that device activation shouldn't
be synchronous; a new NMActiveConnection should be created for
any activation request, which can then wait until all the
resources it needs for activation are available (device found,
master devices created, etc). That's going to take more work
though.
2013-08-04 22:08:48 -05:00
|
|
|
/* Skip hardware devices not yet discovered by udev. They will be
|
|
|
|
|
* announced by udev_device_added(). This doesn't apply to removed
|
|
|
|
|
* devices, as those come either from udev_device_removed(),
|
|
|
|
|
* event_notification() or link_delete() which block the announcment
|
|
|
|
|
* themselves when appropriate.
|
2013-07-26 17:03:39 +02:00
|
|
|
*/
|
2014-03-07 19:04:38 +01:00
|
|
|
switch (change_type) {
|
|
|
|
|
case NM_PLATFORM_SIGNAL_ADDED:
|
|
|
|
|
case NM_PLATFORM_SIGNAL_CHANGED:
|
core: don't wait for udev to find created software devices
NM knows software devices it creates exist immediately, and it knows
it can use them immediately instead of waiting for udev to find them.
Ideally we'd wait for udev to find all devices, to allow tags and
other rules to run, but when creating software devices they must
currently be available for use immediately.
Bridges and bonds are required to be IFF_UP before they can be
activated (see their is_available() implementation). But during
activation code paths, specifically in nm_manager_activate_connection(),
the interface is created and immediately checked for availability
to activate (since the creation was a result of the user requesting
device activation). That availability check fails, because the
device is not visible outside NMPlatform (because it hasn't been
found by udev yet, because we haven't gotten back to the event
loop) and thus nm_platform_link_is_up() fails, failing the
availability check. Thus the device activation ends in error.
What should really happen is that device activation shouldn't
be synchronous; a new NMActiveConnection should be created for
any activation request, which can then wait until all the
resources it needs for activation are available (device found,
master devices created, etc). That's going to take more work
though.
2013-08-04 22:08:48 -05:00
|
|
|
if (!link_is_software (rtnl_link) && !device.driver)
|
2013-07-26 17:03:39 +02:00
|
|
|
return;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2013-08-02 14:25:07 +02:00
|
|
|
/* Link deletion or setting down is sometimes accompanied by address
|
|
|
|
|
* and/or route deletion.
|
|
|
|
|
*
|
|
|
|
|
* More precisely, kernel removes routes when interface goes !IFF_UP and
|
|
|
|
|
* removes both addresses and routes when interface is removed.
|
2013-07-24 10:38:43 +02:00
|
|
|
*/
|
2014-03-07 19:04:38 +01:00
|
|
|
switch (change_type) {
|
|
|
|
|
case NM_PLATFORM_SIGNAL_CHANGED:
|
2013-07-24 10:38:43 +02:00
|
|
|
if (!device.connected)
|
2013-08-02 14:25:07 +02:00
|
|
|
check_cache_items (platform, priv->route_cache, device.ifindex);
|
2013-07-24 10:38:43 +02:00
|
|
|
break;
|
2014-03-07 19:04:38 +01:00
|
|
|
case NM_PLATFORM_SIGNAL_REMOVED:
|
2013-08-02 14:25:07 +02:00
|
|
|
check_cache_items (platform, priv->address_cache, device.ifindex);
|
|
|
|
|
check_cache_items (platform, priv->route_cache, device.ifindex);
|
2014-02-04 14:27:03 +01:00
|
|
|
g_hash_table_remove (priv->wifi_data, GINT_TO_POINTER (device.ifindex));
|
2013-07-24 10:38:43 +02:00
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
2013-07-26 17:03:39 +02:00
|
|
|
|
2014-03-07 19:04:38 +01:00
|
|
|
g_signal_emit_by_name (platform, sig, device.ifindex, &device, change_type, reason);
|
2013-03-27 22:23:24 +01:00
|
|
|
}
|
|
|
|
|
return;
|
2014-04-05 12:39:00 +02:00
|
|
|
case OBJECT_TYPE_IP4_ADDRESS:
|
2013-03-27 22:23:24 +01:00
|
|
|
{
|
|
|
|
|
NMPlatformIP4Address address;
|
|
|
|
|
|
2013-08-02 14:25:07 +02:00
|
|
|
/* Address deletion is sometimes accompanied by route deletion. We need to
|
|
|
|
|
* check all routes belonging to the same interface.
|
|
|
|
|
*/
|
2014-03-07 19:04:38 +01:00
|
|
|
switch (change_type) {
|
|
|
|
|
case NM_PLATFORM_SIGNAL_REMOVED:
|
2014-07-21 13:04:08 -05:00
|
|
|
check_cache_items (platform,
|
|
|
|
|
priv->route_cache,
|
|
|
|
|
rtnl_addr_get_ifindex ((struct rtnl_addr *) object));
|
2013-08-02 14:25:07 +02:00
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2014-06-24 13:47:25 +02:00
|
|
|
if (!_address_match ((struct rtnl_addr *) object, AF_INET, 0)) {
|
|
|
|
|
nm_log_dbg (LOGD_PLATFORM, "skip announce unmatching IP4 address %s", to_string_ip4_address ((struct rtnl_addr *) object));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (!init_ip4_address (&address, (struct rtnl_addr *) object))
|
|
|
|
|
return;
|
2014-03-07 19:04:38 +01:00
|
|
|
g_signal_emit_by_name (platform, sig, address.ifindex, &address, change_type, reason);
|
2013-03-27 22:23:24 +01:00
|
|
|
}
|
|
|
|
|
return;
|
2014-04-05 12:39:00 +02:00
|
|
|
case OBJECT_TYPE_IP6_ADDRESS:
|
2013-03-27 22:23:24 +01:00
|
|
|
{
|
|
|
|
|
NMPlatformIP6Address address;
|
|
|
|
|
|
2014-06-24 13:47:25 +02:00
|
|
|
if (!_address_match ((struct rtnl_addr *) object, AF_INET6, 0)) {
|
|
|
|
|
nm_log_dbg (LOGD_PLATFORM, "skip announce unmatching IP6 address %s", to_string_ip6_address ((struct rtnl_addr *) object));
|
|
|
|
|
return;
|
|
|
|
|
}
|
2014-02-17 12:48:05 +01:00
|
|
|
if (!init_ip6_address (&address, (struct rtnl_addr *) object))
|
|
|
|
|
return;
|
2014-03-07 19:04:38 +01:00
|
|
|
g_signal_emit_by_name (platform, sig, address.ifindex, &address, change_type, reason);
|
2013-03-27 22:23:24 +01:00
|
|
|
}
|
|
|
|
|
return;
|
2014-04-05 12:39:00 +02:00
|
|
|
case OBJECT_TYPE_IP4_ROUTE:
|
2013-03-27 22:23:24 +01:00
|
|
|
{
|
|
|
|
|
NMPlatformIP4Route route;
|
|
|
|
|
|
2015-01-11 17:42:46 +01:00
|
|
|
if (reason == _NM_PLATFORM_REASON_CACHE_CHECK_INTERNAL)
|
|
|
|
|
return;
|
|
|
|
|
|
2014-11-26 00:10:04 +01:00
|
|
|
if (!_route_match ((struct rtnl_route *) object, AF_INET, 0, FALSE)) {
|
2014-06-24 13:47:25 +02:00
|
|
|
nm_log_dbg (LOGD_PLATFORM, "skip announce unmatching IP4 route %s", to_string_ip4_route ((struct rtnl_route *) object));
|
|
|
|
|
return;
|
|
|
|
|
}
|
2013-08-12 17:25:29 +02:00
|
|
|
if (init_ip4_route (&route, (struct rtnl_route *) object))
|
2014-03-07 19:04:38 +01:00
|
|
|
g_signal_emit_by_name (platform, sig, route.ifindex, &route, change_type, reason);
|
2013-03-27 22:23:24 +01:00
|
|
|
}
|
|
|
|
|
return;
|
2014-04-05 12:39:00 +02:00
|
|
|
case OBJECT_TYPE_IP6_ROUTE:
|
2013-03-27 22:23:24 +01:00
|
|
|
{
|
|
|
|
|
NMPlatformIP6Route route;
|
|
|
|
|
|
2015-01-11 17:42:46 +01:00
|
|
|
if (reason == _NM_PLATFORM_REASON_CACHE_CHECK_INTERNAL)
|
|
|
|
|
return;
|
|
|
|
|
|
2014-11-26 00:10:04 +01:00
|
|
|
if (!_route_match ((struct rtnl_route *) object, AF_INET6, 0, FALSE)) {
|
2014-06-24 13:47:25 +02:00
|
|
|
nm_log_dbg (LOGD_PLATFORM, "skip announce unmatching IP6 route %s", to_string_ip6_route ((struct rtnl_route *) object));
|
|
|
|
|
return;
|
|
|
|
|
}
|
2013-08-12 17:25:29 +02:00
|
|
|
if (init_ip6_route (&route, (struct rtnl_route *) object))
|
2014-03-07 19:04:38 +01:00
|
|
|
g_signal_emit_by_name (platform, sig, route.ifindex, &route, change_type, reason);
|
2013-03-27 22:23:24 +01:00
|
|
|
}
|
|
|
|
|
return;
|
2013-03-27 22:23:24 +01:00
|
|
|
default:
|
2014-02-11 22:10:05 +01:00
|
|
|
g_return_if_reached ();
|
2013-03-27 22:23:24 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-27 22:53:55 +01:00
|
|
|
static struct nl_object * build_rtnl_link (int ifindex, const char *name, NMLinkType type);
|
|
|
|
|
|
2013-03-27 22:23:24 +01:00
|
|
|
static gboolean
|
2013-08-02 18:51:06 +02:00
|
|
|
refresh_object (NMPlatform *platform, struct nl_object *object, gboolean removed, NMPlatformReason reason)
|
2013-03-27 22:23:24 +01:00
|
|
|
{
|
|
|
|
|
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
|
|
|
|
|
auto_nl_object struct nl_object *cached_object = NULL;
|
|
|
|
|
auto_nl_object struct nl_object *kernel_object = NULL;
|
|
|
|
|
struct nl_cache *cache;
|
2013-08-02 00:43:12 +02:00
|
|
|
int nle;
|
2013-03-27 22:23:24 +01:00
|
|
|
|
|
|
|
|
cache = choose_cache (platform, object);
|
platform: fix caching for link types
This bug was present since a long time, however libnl3-v3.2.23
(commit fdd1ba220dd7b780400e9d0652cde80e59f63572) changed the returned
family of bridge link objects, which breaks NetworkManager.
This resulted in error messages such as:
DBG<4> object.c:207 nl_object_get: New reference to object 0x19c34b0, total 2
DBG<5> route/link.c:895 link_keygen: link 0x19c34b0 key (dev 9 fam 7) keysz 8, hash 0x2b2
DBG<2> hashtable.c:127 nl_hash_table_add: Warning: Add to hashtable found duplicate...
DBG<4> object.c:221 nl_object_put: Returned object reference 0x19c34b0, 1 remaining
NetworkManager[17745]: <error> [1392114373.475432] [platform/nm-linux-platform.c:1328] event_notification(): netlink cache error: Object exists
Even before the change of libnl, I saw the following error lines
<debug> [...] [platform/nm-linux-platform.c:1216] event_notification(): netlink event (type 16) for link: virbr0 (4)
<error> [...] [platform/nm-linux-platform.c:1265] event_notification(): netlink cache error: Object exists
Hence, the caching mechanism for libnl objects already had a bug.
For rtnl link objects, the identifier consists of family and ifindex.
Since in upper layers, we don't easily know the family, we need a way to find
the objects inside the cache. We do this, by only caching links of family
AF_UNSPEC.
Objects that we receive via event_notification() are never cached. They are only used
to trigger refetching the kernel_object. Their family is irrelevant, we
only need to know, that something about this ifindex changed.
For objects retrieved via get_kernel_object(), we only get link objects of
family AF_UNSPEC or AF_BRIDGE. In any case, we reset (coerce) their family
before caching. This way, inside the link cache, there are only objects with
(coerced) family AF_UNSPEC. We loose the information, which family the
link had, however we don't need it anyway.
https://bugzilla.gnome.org/show_bug.cgi?id=719905
https://bugzilla.redhat.com/show_bug.cgi?id=1063290
Duplicates:
https://bugzilla.gnome.org/show_bug.cgi?id=724225
https://bugzilla.redhat.com/show_bug.cgi?id=1063800
Signed-off-by: Thomas Haller <thaller@redhat.com>
2014-02-11 20:16:03 +01:00
|
|
|
cached_object = nm_nl_cache_search (cache, object);
|
2013-03-27 22:23:24 +01:00
|
|
|
kernel_object = get_kernel_object (priv->nlh, object);
|
|
|
|
|
|
2013-08-02 00:43:12 +02:00
|
|
|
if (removed) {
|
|
|
|
|
if (kernel_object)
|
|
|
|
|
return TRUE;
|
2013-03-27 22:23:24 +01:00
|
|
|
|
2013-08-02 00:43:12 +02:00
|
|
|
/* Only announce object if it was still in the cache. */
|
|
|
|
|
if (cached_object) {
|
|
|
|
|
nl_cache_remove (cached_object);
|
2013-03-27 22:53:55 +01:00
|
|
|
|
2014-03-07 19:04:38 +01:00
|
|
|
announce_object (platform, cached_object, NM_PLATFORM_SIGNAL_REMOVED, reason);
|
2013-08-02 00:43:12 +02:00
|
|
|
}
|
|
|
|
|
} else {
|
platform: avoid storing unknown netlink object types (bgo #742928)
Testing WWAN connections through a Nokia Series 40 phone, addresses of family
AF_PHONET end up triggering an assert() in object_has_ifindex(), just because
object_type_from_nl_object() only handles AF_INET and AF_INET6 address.
In order to avoid this kind of problems, we'll try to make sure that the object
caches kept by NM only store known object types.
(fixup by dcbw to use cached passed to cache_remove_unknown())
https://bugzilla.gnome.org/show_bug.cgi?id=742928
Connect: ppp0 <--> /dev/ttyACM0
nm-pppd-plugin-Message: nm-ppp-plugin: (nm_phasechange): status 5 / phase 'establish'
NetworkManager[27434]: <info> (ppp0): new Generic device (driver: 'unknown' ifindex: 12)
NetworkManager[27434]: <info> (ppp0): exported as /org/freedesktop/NetworkManager/Devices/4
[Thread 0x7ffff1ecf700 (LWP 27439) exited]
NetworkManager[27434]: <info> (ttyACM0): device state change: ip-config -> deactivating (reason 'user-requested') [70 110 39]
Terminating on signal 15
nm-pppd-plugin-Message: nm-ppp-plugin: (nm_phasechange): status 10 / phase 'terminate'
**
NetworkManager:ERROR:platform/nm-linux-platform.c:1534:object_has_ifindex: code should not be reached
Program received signal SIGABRT, Aborted.
0x00007ffff4692a97 in raise () from /usr/lib/libc.so.6
(gdb) bt
#0 0x00007ffff4692a97 in raise () from /usr/lib/libc.so.6
#1 0x00007ffff4693e6a in abort () from /usr/lib/libc.so.6
#2 0x00007ffff4c8d7f5 in g_assertion_message () from /usr/lib/libglib-2.0.so.0
#3 0x00007ffff4c8d88a in g_assertion_message_expr () from /usr/lib/libglib-2.0.so.0
#4 0x0000000000472b91 in object_has_ifindex (object=0x8a8320, ifindex=12) at platform/nm-linux-platform.c:1534
#5 0x0000000000472bec in check_cache_items (platform=0x7fe8a0, cache=0x7fda30, ifindex=12) at platform/nm-linux-platform.c:1549
#6 0x0000000000472de3 in announce_object (platform=0x7fe8a0, object=0x8a8c30, change_type=NM_PLATFORM_SIGNAL_REMOVED, reason=NM_PLATFORM_REASON_EXTERNAL) at platform/nm-linux-platform.c:1617
#7 0x0000000000473dd2 in event_notification (msg=0x8a7970, user_data=0x7fe8a0) at platform/nm-linux-platform.c:1992
#8 0x00007ffff5ee14de in nl_recvmsgs_report () from /usr/lib/libnl-3.so.200
#9 0x00007ffff5ee1849 in nl_recvmsgs () from /usr/lib/libnl-3.so.200
#10 0x00000000004794df in event_handler (channel=0x7fc930, io_condition=G_IO_IN, user_data=0x7fe8a0) at platform/nm-linux-platform.c:4152
#11 0x00007ffff4c6791d in g_main_context_dispatch () from /usr/lib/libglib-2.0.so.0
#12 0x00007ffff4c67cf8 in ?? () from /usr/lib/libglib-2.0.so.0
#13 0x00007ffff4c68022 in g_main_loop_run () from /usr/lib/libglib-2.0.so.0
#14 0x00000000004477ee in main (argc=1, argv=0x7fffffffeaa8) at main.c:447
(gdb) fr 4
#4 0x0000000000472b91 in object_has_ifindex (object=0x8a8320, ifindex=12) at platform/nm-linux-platform.c:1534
1534 g_assert_not_reached ();
2015-01-15 09:18:07 +01:00
|
|
|
ObjectType type;
|
|
|
|
|
|
2013-08-23 19:08:27 +02:00
|
|
|
if (!kernel_object)
|
|
|
|
|
return FALSE;
|
2013-08-02 00:43:12 +02:00
|
|
|
|
platform: avoid storing unknown netlink object types (bgo #742928)
Testing WWAN connections through a Nokia Series 40 phone, addresses of family
AF_PHONET end up triggering an assert() in object_has_ifindex(), just because
object_type_from_nl_object() only handles AF_INET and AF_INET6 address.
In order to avoid this kind of problems, we'll try to make sure that the object
caches kept by NM only store known object types.
(fixup by dcbw to use cached passed to cache_remove_unknown())
https://bugzilla.gnome.org/show_bug.cgi?id=742928
Connect: ppp0 <--> /dev/ttyACM0
nm-pppd-plugin-Message: nm-ppp-plugin: (nm_phasechange): status 5 / phase 'establish'
NetworkManager[27434]: <info> (ppp0): new Generic device (driver: 'unknown' ifindex: 12)
NetworkManager[27434]: <info> (ppp0): exported as /org/freedesktop/NetworkManager/Devices/4
[Thread 0x7ffff1ecf700 (LWP 27439) exited]
NetworkManager[27434]: <info> (ttyACM0): device state change: ip-config -> deactivating (reason 'user-requested') [70 110 39]
Terminating on signal 15
nm-pppd-plugin-Message: nm-ppp-plugin: (nm_phasechange): status 10 / phase 'terminate'
**
NetworkManager:ERROR:platform/nm-linux-platform.c:1534:object_has_ifindex: code should not be reached
Program received signal SIGABRT, Aborted.
0x00007ffff4692a97 in raise () from /usr/lib/libc.so.6
(gdb) bt
#0 0x00007ffff4692a97 in raise () from /usr/lib/libc.so.6
#1 0x00007ffff4693e6a in abort () from /usr/lib/libc.so.6
#2 0x00007ffff4c8d7f5 in g_assertion_message () from /usr/lib/libglib-2.0.so.0
#3 0x00007ffff4c8d88a in g_assertion_message_expr () from /usr/lib/libglib-2.0.so.0
#4 0x0000000000472b91 in object_has_ifindex (object=0x8a8320, ifindex=12) at platform/nm-linux-platform.c:1534
#5 0x0000000000472bec in check_cache_items (platform=0x7fe8a0, cache=0x7fda30, ifindex=12) at platform/nm-linux-platform.c:1549
#6 0x0000000000472de3 in announce_object (platform=0x7fe8a0, object=0x8a8c30, change_type=NM_PLATFORM_SIGNAL_REMOVED, reason=NM_PLATFORM_REASON_EXTERNAL) at platform/nm-linux-platform.c:1617
#7 0x0000000000473dd2 in event_notification (msg=0x8a7970, user_data=0x7fe8a0) at platform/nm-linux-platform.c:1992
#8 0x00007ffff5ee14de in nl_recvmsgs_report () from /usr/lib/libnl-3.so.200
#9 0x00007ffff5ee1849 in nl_recvmsgs () from /usr/lib/libnl-3.so.200
#10 0x00000000004794df in event_handler (channel=0x7fc930, io_condition=G_IO_IN, user_data=0x7fe8a0) at platform/nm-linux-platform.c:4152
#11 0x00007ffff4c6791d in g_main_context_dispatch () from /usr/lib/libglib-2.0.so.0
#12 0x00007ffff4c67cf8 in ?? () from /usr/lib/libglib-2.0.so.0
#13 0x00007ffff4c68022 in g_main_loop_run () from /usr/lib/libglib-2.0.so.0
#14 0x00000000004477ee in main (argc=1, argv=0x7fffffffeaa8) at main.c:447
(gdb) fr 4
#4 0x0000000000472b91 in object_has_ifindex (object=0x8a8320, ifindex=12) at platform/nm-linux-platform.c:1534
1534 g_assert_not_reached ();
2015-01-15 09:18:07 +01:00
|
|
|
/* Unsupported object types should never have reached the caches */
|
|
|
|
|
type = object_type_from_nl_object (kernel_object);
|
|
|
|
|
g_assert (type != OBJECT_TYPE_UNKNOWN);
|
|
|
|
|
|
2013-08-02 00:43:12 +02:00
|
|
|
hack_empty_master_iff_lower_up (platform, kernel_object);
|
2013-03-27 22:23:24 +01:00
|
|
|
|
2013-08-02 00:43:12 +02:00
|
|
|
if (cached_object)
|
|
|
|
|
nl_cache_remove (cached_object);
|
|
|
|
|
nle = nl_cache_add (cache, kernel_object);
|
2013-08-23 19:08:27 +02:00
|
|
|
if (nle) {
|
|
|
|
|
nm_log_dbg (LOGD_PLATFORM, "refresh_object(reason %d) failed during nl_cache_add with %d", reason, nle);
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
2013-03-27 22:23:24 +01:00
|
|
|
|
2014-03-07 19:04:38 +01:00
|
|
|
announce_object (platform, kernel_object, cached_object ? NM_PLATFORM_SIGNAL_CHANGED : NM_PLATFORM_SIGNAL_ADDED, reason);
|
2013-03-27 22:53:55 +01:00
|
|
|
|
2013-08-02 00:43:12 +02:00
|
|
|
/* Refresh the master device (even on enslave/release) */
|
platform: avoid storing unknown netlink object types (bgo #742928)
Testing WWAN connections through a Nokia Series 40 phone, addresses of family
AF_PHONET end up triggering an assert() in object_has_ifindex(), just because
object_type_from_nl_object() only handles AF_INET and AF_INET6 address.
In order to avoid this kind of problems, we'll try to make sure that the object
caches kept by NM only store known object types.
(fixup by dcbw to use cached passed to cache_remove_unknown())
https://bugzilla.gnome.org/show_bug.cgi?id=742928
Connect: ppp0 <--> /dev/ttyACM0
nm-pppd-plugin-Message: nm-ppp-plugin: (nm_phasechange): status 5 / phase 'establish'
NetworkManager[27434]: <info> (ppp0): new Generic device (driver: 'unknown' ifindex: 12)
NetworkManager[27434]: <info> (ppp0): exported as /org/freedesktop/NetworkManager/Devices/4
[Thread 0x7ffff1ecf700 (LWP 27439) exited]
NetworkManager[27434]: <info> (ttyACM0): device state change: ip-config -> deactivating (reason 'user-requested') [70 110 39]
Terminating on signal 15
nm-pppd-plugin-Message: nm-ppp-plugin: (nm_phasechange): status 10 / phase 'terminate'
**
NetworkManager:ERROR:platform/nm-linux-platform.c:1534:object_has_ifindex: code should not be reached
Program received signal SIGABRT, Aborted.
0x00007ffff4692a97 in raise () from /usr/lib/libc.so.6
(gdb) bt
#0 0x00007ffff4692a97 in raise () from /usr/lib/libc.so.6
#1 0x00007ffff4693e6a in abort () from /usr/lib/libc.so.6
#2 0x00007ffff4c8d7f5 in g_assertion_message () from /usr/lib/libglib-2.0.so.0
#3 0x00007ffff4c8d88a in g_assertion_message_expr () from /usr/lib/libglib-2.0.so.0
#4 0x0000000000472b91 in object_has_ifindex (object=0x8a8320, ifindex=12) at platform/nm-linux-platform.c:1534
#5 0x0000000000472bec in check_cache_items (platform=0x7fe8a0, cache=0x7fda30, ifindex=12) at platform/nm-linux-platform.c:1549
#6 0x0000000000472de3 in announce_object (platform=0x7fe8a0, object=0x8a8c30, change_type=NM_PLATFORM_SIGNAL_REMOVED, reason=NM_PLATFORM_REASON_EXTERNAL) at platform/nm-linux-platform.c:1617
#7 0x0000000000473dd2 in event_notification (msg=0x8a7970, user_data=0x7fe8a0) at platform/nm-linux-platform.c:1992
#8 0x00007ffff5ee14de in nl_recvmsgs_report () from /usr/lib/libnl-3.so.200
#9 0x00007ffff5ee1849 in nl_recvmsgs () from /usr/lib/libnl-3.so.200
#10 0x00000000004794df in event_handler (channel=0x7fc930, io_condition=G_IO_IN, user_data=0x7fe8a0) at platform/nm-linux-platform.c:4152
#11 0x00007ffff4c6791d in g_main_context_dispatch () from /usr/lib/libglib-2.0.so.0
#12 0x00007ffff4c67cf8 in ?? () from /usr/lib/libglib-2.0.so.0
#13 0x00007ffff4c68022 in g_main_loop_run () from /usr/lib/libglib-2.0.so.0
#14 0x00000000004477ee in main (argc=1, argv=0x7fffffffeaa8) at main.c:447
(gdb) fr 4
#4 0x0000000000472b91 in object_has_ifindex (object=0x8a8320, ifindex=12) at platform/nm-linux-platform.c:1534
1534 g_assert_not_reached ();
2015-01-15 09:18:07 +01:00
|
|
|
if (type == OBJECT_TYPE_LINK) {
|
2013-08-02 00:43:12 +02:00
|
|
|
int kernel_master = rtnl_link_get_master ((struct rtnl_link *) kernel_object);
|
|
|
|
|
int cached_master = cached_object ? rtnl_link_get_master ((struct rtnl_link *) cached_object) : 0;
|
|
|
|
|
struct nl_object *master_object;
|
|
|
|
|
|
|
|
|
|
if (kernel_master) {
|
|
|
|
|
master_object = build_rtnl_link (kernel_master, NULL, NM_LINK_TYPE_NONE);
|
2013-08-02 18:51:06 +02:00
|
|
|
refresh_object (platform, master_object, FALSE, NM_PLATFORM_REASON_INTERNAL);
|
2013-08-02 00:43:12 +02:00
|
|
|
nl_object_put (master_object);
|
|
|
|
|
}
|
|
|
|
|
if (cached_master && cached_master != kernel_master) {
|
|
|
|
|
master_object = build_rtnl_link (cached_master, NULL, NM_LINK_TYPE_NONE);
|
2013-08-02 18:51:06 +02:00
|
|
|
refresh_object (platform, master_object, FALSE, NM_PLATFORM_REASON_INTERNAL);
|
2013-08-02 00:43:12 +02:00
|
|
|
nl_object_put (master_object);
|
|
|
|
|
}
|
2013-03-27 22:53:55 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-27 22:23:24 +01:00
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Decreases the reference count if @obj for convenience */
|
|
|
|
|
static gboolean
|
|
|
|
|
add_object (NMPlatform *platform, struct nl_object *obj)
|
|
|
|
|
{
|
|
|
|
|
auto_nl_object struct nl_object *object = obj;
|
|
|
|
|
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
|
2013-08-02 00:43:12 +02:00
|
|
|
int nle;
|
2013-11-13 20:06:39 -06:00
|
|
|
struct nl_dump_params dp = {
|
|
|
|
|
.dp_type = NL_DUMP_DETAILS,
|
|
|
|
|
.dp_fd = stderr,
|
|
|
|
|
};
|
2013-03-27 22:23:24 +01:00
|
|
|
|
2014-02-13 15:11:05 +01:00
|
|
|
g_return_val_if_fail (object, FALSE);
|
|
|
|
|
|
2013-08-02 00:43:12 +02:00
|
|
|
nle = add_kernel_object (priv->nlh, object);
|
|
|
|
|
|
|
|
|
|
/* NLE_EXIST is considered equivalent to success to avoid race conditions. You
|
|
|
|
|
* never know when something sends an identical object just before
|
|
|
|
|
* NetworkManager.
|
|
|
|
|
*/
|
|
|
|
|
switch (nle) {
|
|
|
|
|
case -NLE_SUCCESS:
|
|
|
|
|
case -NLE_EXIST:
|
|
|
|
|
break;
|
|
|
|
|
default:
|
2014-02-17 14:20:44 +01:00
|
|
|
error ("Netlink error adding %s: %s", to_string_object (platform, object), nl_geterror (nle));
|
2013-11-13 20:06:39 -06:00
|
|
|
nl_object_dump (object, &dp);
|
2013-08-02 00:43:12 +02:00
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
2013-08-02 18:51:06 +02:00
|
|
|
return refresh_object (platform, object, FALSE, NM_PLATFORM_REASON_INTERNAL);
|
2013-03-27 22:23:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Decreases the reference count if @obj for convenience */
|
|
|
|
|
static gboolean
|
2014-07-26 21:58:57 +02:00
|
|
|
delete_object (NMPlatform *platform, struct nl_object *object, gboolean do_refresh_object)
|
2013-03-27 22:23:24 +01:00
|
|
|
{
|
|
|
|
|
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
|
2014-02-13 15:11:05 +01:00
|
|
|
int object_type;
|
2013-06-21 02:32:34 +02:00
|
|
|
int nle;
|
2014-07-26 21:58:57 +02:00
|
|
|
gboolean result = FALSE;
|
2013-03-27 22:23:24 +01:00
|
|
|
|
2014-07-26 21:58:57 +02:00
|
|
|
object_type = object_type_from_nl_object (object);
|
2014-04-05 12:39:00 +02:00
|
|
|
g_return_val_if_fail (object_type != OBJECT_TYPE_UNKNOWN, FALSE);
|
2013-03-27 22:23:24 +01:00
|
|
|
|
2014-02-13 15:11:05 +01:00
|
|
|
switch (object_type) {
|
2014-04-05 12:39:00 +02:00
|
|
|
case OBJECT_TYPE_LINK:
|
2014-02-13 15:11:05 +01:00
|
|
|
nle = rtnl_link_delete (priv->nlh, (struct rtnl_link *) object);
|
|
|
|
|
break;
|
2014-04-05 12:39:00 +02:00
|
|
|
case OBJECT_TYPE_IP4_ADDRESS:
|
|
|
|
|
case OBJECT_TYPE_IP6_ADDRESS:
|
2014-02-13 15:11:05 +01:00
|
|
|
nle = rtnl_addr_delete (priv->nlh, (struct rtnl_addr *) object, 0);
|
|
|
|
|
break;
|
2014-04-05 12:39:00 +02:00
|
|
|
case OBJECT_TYPE_IP4_ROUTE:
|
|
|
|
|
case OBJECT_TYPE_IP6_ROUTE:
|
2014-02-13 15:11:05 +01:00
|
|
|
nle = rtnl_route_delete (priv->nlh, (struct rtnl_route *) object, 0);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
g_assert_not_reached ();
|
|
|
|
|
}
|
2013-06-21 02:32:34 +02:00
|
|
|
|
|
|
|
|
switch (nle) {
|
|
|
|
|
case -NLE_SUCCESS:
|
2014-02-13 15:11:05 +01:00
|
|
|
break;
|
2013-06-21 02:32:34 +02:00
|
|
|
case -NLE_OBJ_NOTFOUND:
|
2014-02-13 15:11:05 +01:00
|
|
|
debug("delete_object failed with \"%s\" (%d), meaning the object was already removed",
|
|
|
|
|
nl_geterror (nle), nle);
|
2013-06-21 02:32:34 +02:00
|
|
|
break;
|
2014-06-03 15:45:09 +02:00
|
|
|
case -NLE_FAILURE:
|
|
|
|
|
if (object_type == OBJECT_TYPE_IP6_ADDRESS) {
|
|
|
|
|
/* On RHEL7 kernel, deleting a non existing address fails with ENXIO (which libnl maps to NLE_FAILURE) */
|
|
|
|
|
debug("delete_object for address failed with \"%s\" (%d), meaning the address was already removed",
|
|
|
|
|
nl_geterror (nle), nle);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
goto DEFAULT;
|
2014-02-13 15:11:05 +01:00
|
|
|
case -NLE_NOADDR:
|
2014-04-05 12:39:00 +02:00
|
|
|
if (object_type == OBJECT_TYPE_IP4_ADDRESS || object_type == OBJECT_TYPE_IP6_ADDRESS) {
|
2014-02-13 15:11:05 +01:00
|
|
|
debug("delete_object for address failed with \"%s\" (%d), meaning the address was already removed",
|
|
|
|
|
nl_geterror (nle), nle);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2014-06-03 15:45:09 +02:00
|
|
|
goto DEFAULT;
|
|
|
|
|
DEFAULT:
|
2013-06-21 02:32:34 +02:00
|
|
|
default:
|
2014-07-26 21:58:57 +02:00
|
|
|
error ("Netlink error deleting %s: %s (%d)", to_string_object (platform, object), nl_geterror (nle), nle);
|
|
|
|
|
goto out;
|
2013-06-21 02:32:34 +02:00
|
|
|
}
|
2013-03-27 22:23:24 +01:00
|
|
|
|
platform: fix lookup of routes and deletion of IPv4 routes
When doing a lookup for an libnl route, the cache comparison function
for routes takes into account 'family', 'tos', 'table', 'dst', and 'prio'.
In NetworkManager we don't use all of these properties for a route, so
at several places when doing a cache lookup we don't have all identifying
properties. Usually we only have 'family' and 'dst' ('table' is
implicit 0, because NM does currently not care about any other tables).
The problem is that NM sees routes with different 'tos', 'prio', but it
cannot look them up in the cache. Add a hack to search the cache
fuzzy.
This is similar to the hack for link, where the identifying properties
are 'family' and 'ifindex', but we only have 'ifindex' at hand. However,
contrary to this hack, we coerce the 'family' to AF_UNSPEC for every link cache
operation. This is not viable in this case, because we internally need
the 'tos' field.
We need the 'tos' field because when deleting an IPv4 route, the 'tos' field must
match. See fib_table_delete(). This was already partially fixed by commit
f0daf90298d1bd9cafac7b9c02dc905327e0b85a, but before the lookup to the
cached object would fail for any non-zero 'tos'.
Signed-off-by: Thomas Haller <thaller@redhat.com>
2014-05-28 18:46:12 +02:00
|
|
|
if (do_refresh_object)
|
|
|
|
|
refresh_object (platform, object, TRUE, NM_PLATFORM_REASON_INTERNAL);
|
2013-03-27 22:23:24 +01:00
|
|
|
|
2014-07-26 21:58:57 +02:00
|
|
|
result = TRUE;
|
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
nl_object_put (object);
|
|
|
|
|
return result;
|
2013-03-27 22:23:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
ref_object (struct nl_object *obj, void *data)
|
|
|
|
|
{
|
|
|
|
|
struct nl_object **out = data;
|
|
|
|
|
|
|
|
|
|
nl_object_get (obj);
|
|
|
|
|
*out = obj;
|
|
|
|
|
}
|
|
|
|
|
|
2014-04-02 16:54:08 +02:00
|
|
|
static gboolean
|
|
|
|
|
_rtnl_addr_timestamps_equal_fuzzy (guint32 ts1, guint32 ts2)
|
|
|
|
|
{
|
|
|
|
|
guint32 diff;
|
|
|
|
|
|
|
|
|
|
if (ts1 == ts2)
|
|
|
|
|
return TRUE;
|
|
|
|
|
if (ts1 == NM_PLATFORM_LIFETIME_PERMANENT ||
|
|
|
|
|
ts2 == NM_PLATFORM_LIFETIME_PERMANENT)
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
/** accept the timestamps as equal if they are within two seconds. */
|
|
|
|
|
diff = ts1 > ts2 ? ts1 - ts2 : ts2 - ts1;
|
|
|
|
|
return diff <= 2;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-29 17:58:44 +02:00
|
|
|
static gboolean
|
|
|
|
|
nm_nl_object_diff (ObjectType type, struct nl_object *_a, struct nl_object *_b)
|
|
|
|
|
{
|
|
|
|
|
if (nl_object_diff (_a, _b)) {
|
|
|
|
|
/* libnl thinks objects are different*/
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-29 17:58:44 +02:00
|
|
|
#if HAVE_LIBNL_INET6_TOKEN
|
|
|
|
|
/* libnl ignores PROTINFO changes in object without AF assigned */
|
|
|
|
|
if (type == OBJECT_TYPE_LINK) {
|
|
|
|
|
struct rtnl_addr *a = (struct rtnl_addr *) _a;
|
|
|
|
|
struct rtnl_addr *b = (struct rtnl_addr *) _b;
|
|
|
|
|
auto_nl_addr struct nl_addr *token_a = NULL;
|
|
|
|
|
auto_nl_addr struct nl_addr *token_b = NULL;
|
|
|
|
|
|
|
|
|
|
if (rtnl_link_inet6_get_token ((struct rtnl_link *) a, &token_a) != 0)
|
|
|
|
|
token_a = NULL;
|
|
|
|
|
if (rtnl_link_inet6_get_token ((struct rtnl_link *) b, &token_b) != 0)
|
|
|
|
|
token_b = NULL;
|
|
|
|
|
|
|
|
|
|
if (token_a && token_b) {
|
|
|
|
|
if (nl_addr_get_family (token_a) == AF_INET6 &&
|
|
|
|
|
nl_addr_get_family (token_b) == AF_INET6 &&
|
|
|
|
|
nl_addr_get_len (token_a) == sizeof (struct in6_addr) &&
|
|
|
|
|
nl_addr_get_len (token_b) == sizeof (struct in6_addr) &&
|
|
|
|
|
memcmp (nl_addr_get_binary_addr (token_a),
|
|
|
|
|
nl_addr_get_binary_addr (token_b),
|
|
|
|
|
sizeof (struct in6_addr))) {
|
|
|
|
|
/* Token changed */
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
} else if (token_a != token_b) {
|
|
|
|
|
/* Token added or removed (?). */
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2014-09-29 17:58:44 +02:00
|
|
|
if (type == OBJECT_TYPE_IP4_ADDRESS || type == OBJECT_TYPE_IP6_ADDRESS) {
|
|
|
|
|
struct rtnl_addr *a = (struct rtnl_addr *) _a;
|
|
|
|
|
struct rtnl_addr *b = (struct rtnl_addr *) _b;
|
|
|
|
|
|
|
|
|
|
/* libnl nl_object_diff() ignores differences in timestamp. Let's care about
|
|
|
|
|
* them (if they are large enough).
|
|
|
|
|
*
|
|
|
|
|
* Note that these valid and preferred timestamps are absolute, after
|
|
|
|
|
* _rtnl_addr_hack_lifetimes_rel_to_abs(). */
|
|
|
|
|
if ( !_rtnl_addr_timestamps_equal_fuzzy (rtnl_addr_get_preferred_lifetime (a),
|
|
|
|
|
rtnl_addr_get_preferred_lifetime (b))
|
|
|
|
|
|| !_rtnl_addr_timestamps_equal_fuzzy (rtnl_addr_get_valid_lifetime (a),
|
|
|
|
|
rtnl_addr_get_valid_lifetime (b)))
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-27 22:23:24 +01:00
|
|
|
/* This function does all the magic to avoid race conditions caused
|
|
|
|
|
* by concurrent usage of synchronous commands and an asynchronous cache. This
|
|
|
|
|
* might be a nice future addition to libnl but it requires to do all operations
|
|
|
|
|
* through the cache manager. In this case, nm-linux-platform serves as the
|
|
|
|
|
* cache manager instead of the one provided by libnl.
|
|
|
|
|
*/
|
|
|
|
|
static int
|
|
|
|
|
event_notification (struct nl_msg *msg, gpointer user_data)
|
|
|
|
|
{
|
|
|
|
|
NMPlatform *platform = NM_PLATFORM (user_data);
|
|
|
|
|
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
|
|
|
|
|
struct nl_cache *cache;
|
|
|
|
|
auto_nl_object struct nl_object *object = NULL;
|
|
|
|
|
auto_nl_object struct nl_object *cached_object = NULL;
|
|
|
|
|
auto_nl_object struct nl_object *kernel_object = NULL;
|
|
|
|
|
int event;
|
|
|
|
|
int nle;
|
2014-04-02 16:54:08 +02:00
|
|
|
ObjectType type;
|
2013-03-27 22:23:24 +01:00
|
|
|
|
|
|
|
|
event = nlmsg_hdr (msg)->nlmsg_type;
|
2014-01-07 17:21:12 +01:00
|
|
|
|
|
|
|
|
if (priv->support_kernel_extended_ifa_flags == 0 && event == RTM_NEWADDR) {
|
|
|
|
|
/* if kernel support for extended ifa flags is still undecided, use the opportunity
|
|
|
|
|
* now and use @msg to decide it. This saves a blocking net link request.
|
|
|
|
|
**/
|
|
|
|
|
_check_support_kernel_extended_ifa_flags_init (priv, msg);
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-27 22:23:24 +01:00
|
|
|
nl_msg_parse (msg, ref_object, &object);
|
|
|
|
|
g_return_val_if_fail (object, NL_OK);
|
|
|
|
|
|
2014-04-02 16:54:08 +02:00
|
|
|
type = object_type_from_nl_object (object);
|
|
|
|
|
|
2014-02-11 20:12:12 +01:00
|
|
|
if (nm_logging_enabled (LOGL_DEBUG, LOGD_PLATFORM)) {
|
2014-04-02 16:54:08 +02:00
|
|
|
if (type == OBJECT_TYPE_LINK) {
|
2014-02-11 20:12:12 +01:00
|
|
|
const char *name = rtnl_link_get_name ((struct rtnl_link *) object);
|
|
|
|
|
|
|
|
|
|
debug ("netlink event (type %d) for link: %s (%d, family %d)",
|
|
|
|
|
event, name ? name : "(unknown)",
|
|
|
|
|
rtnl_link_get_ifindex ((struct rtnl_link *) object),
|
|
|
|
|
rtnl_link_get_family ((struct rtnl_link *) object));
|
|
|
|
|
} else
|
|
|
|
|
debug ("netlink event (type %d)", event);
|
|
|
|
|
}
|
|
|
|
|
|
2014-04-02 16:54:08 +02:00
|
|
|
cache = choose_cache_by_type (platform, type);
|
platform: fix caching for link types
This bug was present since a long time, however libnl3-v3.2.23
(commit fdd1ba220dd7b780400e9d0652cde80e59f63572) changed the returned
family of bridge link objects, which breaks NetworkManager.
This resulted in error messages such as:
DBG<4> object.c:207 nl_object_get: New reference to object 0x19c34b0, total 2
DBG<5> route/link.c:895 link_keygen: link 0x19c34b0 key (dev 9 fam 7) keysz 8, hash 0x2b2
DBG<2> hashtable.c:127 nl_hash_table_add: Warning: Add to hashtable found duplicate...
DBG<4> object.c:221 nl_object_put: Returned object reference 0x19c34b0, 1 remaining
NetworkManager[17745]: <error> [1392114373.475432] [platform/nm-linux-platform.c:1328] event_notification(): netlink cache error: Object exists
Even before the change of libnl, I saw the following error lines
<debug> [...] [platform/nm-linux-platform.c:1216] event_notification(): netlink event (type 16) for link: virbr0 (4)
<error> [...] [platform/nm-linux-platform.c:1265] event_notification(): netlink cache error: Object exists
Hence, the caching mechanism for libnl objects already had a bug.
For rtnl link objects, the identifier consists of family and ifindex.
Since in upper layers, we don't easily know the family, we need a way to find
the objects inside the cache. We do this, by only caching links of family
AF_UNSPEC.
Objects that we receive via event_notification() are never cached. They are only used
to trigger refetching the kernel_object. Their family is irrelevant, we
only need to know, that something about this ifindex changed.
For objects retrieved via get_kernel_object(), we only get link objects of
family AF_UNSPEC or AF_BRIDGE. In any case, we reset (coerce) their family
before caching. This way, inside the link cache, there are only objects with
(coerced) family AF_UNSPEC. We loose the information, which family the
link had, however we don't need it anyway.
https://bugzilla.gnome.org/show_bug.cgi?id=719905
https://bugzilla.redhat.com/show_bug.cgi?id=1063290
Duplicates:
https://bugzilla.gnome.org/show_bug.cgi?id=724225
https://bugzilla.redhat.com/show_bug.cgi?id=1063800
Signed-off-by: Thomas Haller <thaller@redhat.com>
2014-02-11 20:16:03 +01:00
|
|
|
cached_object = nm_nl_cache_search (cache, object);
|
2013-03-27 22:23:24 +01:00
|
|
|
kernel_object = get_kernel_object (priv->nlh, object);
|
|
|
|
|
|
2013-03-27 22:53:55 +01:00
|
|
|
hack_empty_master_iff_lower_up (platform, kernel_object);
|
|
|
|
|
|
2013-03-27 22:23:24 +01:00
|
|
|
/* Removed object */
|
|
|
|
|
switch (event) {
|
|
|
|
|
case RTM_DELLINK:
|
2013-03-27 22:23:24 +01:00
|
|
|
case RTM_DELADDR:
|
2013-07-24 10:05:41 +02:00
|
|
|
case RTM_DELROUTE:
|
2013-03-27 22:23:24 +01:00
|
|
|
/* Ignore inconsistent deletion
|
|
|
|
|
*
|
|
|
|
|
* Quick external deletion and addition can be occasionally
|
|
|
|
|
* seen as just a change.
|
|
|
|
|
*/
|
|
|
|
|
if (kernel_object)
|
|
|
|
|
return NL_OK;
|
|
|
|
|
/* Ignore internal deletion */
|
|
|
|
|
if (!cached_object)
|
|
|
|
|
return NL_OK;
|
|
|
|
|
|
|
|
|
|
nl_cache_remove (cached_object);
|
2014-04-17 14:57:55 +02:00
|
|
|
/* Don't announce removed interfaces that are not recognized by
|
2013-07-26 17:03:39 +02:00
|
|
|
* udev. They were either not yet discovered or they have been
|
|
|
|
|
* already removed and announced.
|
|
|
|
|
*/
|
|
|
|
|
if (event == RTM_DELLINK) {
|
platform: fix caching for link types
This bug was present since a long time, however libnl3-v3.2.23
(commit fdd1ba220dd7b780400e9d0652cde80e59f63572) changed the returned
family of bridge link objects, which breaks NetworkManager.
This resulted in error messages such as:
DBG<4> object.c:207 nl_object_get: New reference to object 0x19c34b0, total 2
DBG<5> route/link.c:895 link_keygen: link 0x19c34b0 key (dev 9 fam 7) keysz 8, hash 0x2b2
DBG<2> hashtable.c:127 nl_hash_table_add: Warning: Add to hashtable found duplicate...
DBG<4> object.c:221 nl_object_put: Returned object reference 0x19c34b0, 1 remaining
NetworkManager[17745]: <error> [1392114373.475432] [platform/nm-linux-platform.c:1328] event_notification(): netlink cache error: Object exists
Even before the change of libnl, I saw the following error lines
<debug> [...] [platform/nm-linux-platform.c:1216] event_notification(): netlink event (type 16) for link: virbr0 (4)
<error> [...] [platform/nm-linux-platform.c:1265] event_notification(): netlink cache error: Object exists
Hence, the caching mechanism for libnl objects already had a bug.
For rtnl link objects, the identifier consists of family and ifindex.
Since in upper layers, we don't easily know the family, we need a way to find
the objects inside the cache. We do this, by only caching links of family
AF_UNSPEC.
Objects that we receive via event_notification() are never cached. They are only used
to trigger refetching the kernel_object. Their family is irrelevant, we
only need to know, that something about this ifindex changed.
For objects retrieved via get_kernel_object(), we only get link objects of
family AF_UNSPEC or AF_BRIDGE. In any case, we reset (coerce) their family
before caching. This way, inside the link cache, there are only objects with
(coerced) family AF_UNSPEC. We loose the information, which family the
link had, however we don't need it anyway.
https://bugzilla.gnome.org/show_bug.cgi?id=719905
https://bugzilla.redhat.com/show_bug.cgi?id=1063290
Duplicates:
https://bugzilla.gnome.org/show_bug.cgi?id=724225
https://bugzilla.redhat.com/show_bug.cgi?id=1063800
Signed-off-by: Thomas Haller <thaller@redhat.com>
2014-02-11 20:16:03 +01:00
|
|
|
if (!link_is_announceable (platform, (struct rtnl_link *) cached_object))
|
2013-07-26 17:03:39 +02:00
|
|
|
return NL_OK;
|
|
|
|
|
}
|
2014-03-07 19:04:38 +01:00
|
|
|
announce_object (platform, cached_object, NM_PLATFORM_SIGNAL_REMOVED, NM_PLATFORM_REASON_EXTERNAL);
|
2013-03-27 22:23:24 +01:00
|
|
|
|
|
|
|
|
return NL_OK;
|
|
|
|
|
case RTM_NEWLINK:
|
2013-03-27 22:23:24 +01:00
|
|
|
case RTM_NEWADDR:
|
2013-07-24 10:05:41 +02:00
|
|
|
case RTM_NEWROUTE:
|
2013-03-27 22:23:24 +01:00
|
|
|
/* Ignore inconsistent addition or change (kernel will send a good one)
|
|
|
|
|
*
|
|
|
|
|
* Quick sequence of RTM_NEWLINK notifications can be occasionally
|
|
|
|
|
* collapsed to just one addition or deletion, depending of whether we
|
|
|
|
|
* already have the object in cache.
|
|
|
|
|
*/
|
|
|
|
|
if (!kernel_object)
|
|
|
|
|
return NL_OK;
|
platform: avoid storing unknown netlink object types (bgo #742928)
Testing WWAN connections through a Nokia Series 40 phone, addresses of family
AF_PHONET end up triggering an assert() in object_has_ifindex(), just because
object_type_from_nl_object() only handles AF_INET and AF_INET6 address.
In order to avoid this kind of problems, we'll try to make sure that the object
caches kept by NM only store known object types.
(fixup by dcbw to use cached passed to cache_remove_unknown())
https://bugzilla.gnome.org/show_bug.cgi?id=742928
Connect: ppp0 <--> /dev/ttyACM0
nm-pppd-plugin-Message: nm-ppp-plugin: (nm_phasechange): status 5 / phase 'establish'
NetworkManager[27434]: <info> (ppp0): new Generic device (driver: 'unknown' ifindex: 12)
NetworkManager[27434]: <info> (ppp0): exported as /org/freedesktop/NetworkManager/Devices/4
[Thread 0x7ffff1ecf700 (LWP 27439) exited]
NetworkManager[27434]: <info> (ttyACM0): device state change: ip-config -> deactivating (reason 'user-requested') [70 110 39]
Terminating on signal 15
nm-pppd-plugin-Message: nm-ppp-plugin: (nm_phasechange): status 10 / phase 'terminate'
**
NetworkManager:ERROR:platform/nm-linux-platform.c:1534:object_has_ifindex: code should not be reached
Program received signal SIGABRT, Aborted.
0x00007ffff4692a97 in raise () from /usr/lib/libc.so.6
(gdb) bt
#0 0x00007ffff4692a97 in raise () from /usr/lib/libc.so.6
#1 0x00007ffff4693e6a in abort () from /usr/lib/libc.so.6
#2 0x00007ffff4c8d7f5 in g_assertion_message () from /usr/lib/libglib-2.0.so.0
#3 0x00007ffff4c8d88a in g_assertion_message_expr () from /usr/lib/libglib-2.0.so.0
#4 0x0000000000472b91 in object_has_ifindex (object=0x8a8320, ifindex=12) at platform/nm-linux-platform.c:1534
#5 0x0000000000472bec in check_cache_items (platform=0x7fe8a0, cache=0x7fda30, ifindex=12) at platform/nm-linux-platform.c:1549
#6 0x0000000000472de3 in announce_object (platform=0x7fe8a0, object=0x8a8c30, change_type=NM_PLATFORM_SIGNAL_REMOVED, reason=NM_PLATFORM_REASON_EXTERNAL) at platform/nm-linux-platform.c:1617
#7 0x0000000000473dd2 in event_notification (msg=0x8a7970, user_data=0x7fe8a0) at platform/nm-linux-platform.c:1992
#8 0x00007ffff5ee14de in nl_recvmsgs_report () from /usr/lib/libnl-3.so.200
#9 0x00007ffff5ee1849 in nl_recvmsgs () from /usr/lib/libnl-3.so.200
#10 0x00000000004794df in event_handler (channel=0x7fc930, io_condition=G_IO_IN, user_data=0x7fe8a0) at platform/nm-linux-platform.c:4152
#11 0x00007ffff4c6791d in g_main_context_dispatch () from /usr/lib/libglib-2.0.so.0
#12 0x00007ffff4c67cf8 in ?? () from /usr/lib/libglib-2.0.so.0
#13 0x00007ffff4c68022 in g_main_loop_run () from /usr/lib/libglib-2.0.so.0
#14 0x00000000004477ee in main (argc=1, argv=0x7fffffffeaa8) at main.c:447
(gdb) fr 4
#4 0x0000000000472b91 in object_has_ifindex (object=0x8a8320, ifindex=12) at platform/nm-linux-platform.c:1534
1534 g_assert_not_reached ();
2015-01-15 09:18:07 +01:00
|
|
|
|
|
|
|
|
/* Ignore unsupported object types (e.g. AF_PHONET family addresses) */
|
|
|
|
|
if (type == OBJECT_TYPE_UNKNOWN)
|
|
|
|
|
return NL_OK;
|
|
|
|
|
|
2013-03-27 22:23:24 +01:00
|
|
|
/* Handle external addition */
|
|
|
|
|
if (!cached_object) {
|
|
|
|
|
nle = nl_cache_add (cache, kernel_object);
|
|
|
|
|
if (nle) {
|
|
|
|
|
error ("netlink cache error: %s", nl_geterror (nle));
|
|
|
|
|
return NL_OK;
|
|
|
|
|
}
|
2014-03-07 19:04:38 +01:00
|
|
|
announce_object (platform, kernel_object, NM_PLATFORM_SIGNAL_ADDED, NM_PLATFORM_REASON_EXTERNAL);
|
2013-03-27 22:23:24 +01:00
|
|
|
return NL_OK;
|
|
|
|
|
}
|
|
|
|
|
/* Ignore non-change
|
|
|
|
|
*
|
|
|
|
|
* This also catches notifications for internal addition or change, unless
|
|
|
|
|
* another action occured very soon after it.
|
|
|
|
|
*/
|
2014-09-29 17:58:44 +02:00
|
|
|
if (!nm_nl_object_diff (type, kernel_object, cached_object))
|
|
|
|
|
return NL_OK;
|
|
|
|
|
|
2013-03-27 22:23:24 +01:00
|
|
|
/* Handle external change */
|
|
|
|
|
nl_cache_remove (cached_object);
|
|
|
|
|
nle = nl_cache_add (cache, kernel_object);
|
|
|
|
|
if (nle) {
|
|
|
|
|
error ("netlink cache error: %s", nl_geterror (nle));
|
|
|
|
|
return NL_OK;
|
|
|
|
|
}
|
2014-03-07 19:04:38 +01:00
|
|
|
announce_object (platform, kernel_object, NM_PLATFORM_SIGNAL_CHANGED, NM_PLATFORM_REASON_EXTERNAL);
|
2013-03-27 22:23:24 +01:00
|
|
|
|
|
|
|
|
return NL_OK;
|
|
|
|
|
default:
|
|
|
|
|
error ("Unknown netlink event: %d", event);
|
|
|
|
|
return NL_OK;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
|
|
|
|
2014-03-04 20:15:28 +01:00
|
|
|
static void
|
|
|
|
|
_log_dbg_sysctl_set_impl (const char *path, const char *value)
|
|
|
|
|
{
|
|
|
|
|
GError *error = NULL;
|
|
|
|
|
char *contents, *contents_escaped;
|
|
|
|
|
char *value_escaped = g_strescape (value, NULL);
|
|
|
|
|
|
|
|
|
|
if (!g_file_get_contents (path, &contents, NULL, &error)) {
|
|
|
|
|
debug ("sysctl: setting '%s' to '%s' (current value cannot be read: %s)", path, value_escaped, error->message);
|
|
|
|
|
g_clear_error (&error);
|
|
|
|
|
} else {
|
|
|
|
|
g_strstrip (contents);
|
|
|
|
|
contents_escaped = g_strescape (contents, NULL);
|
|
|
|
|
if (strcmp (contents, value) == 0)
|
|
|
|
|
debug ("sysctl: setting '%s' to '%s' (current value is identical)", path, value_escaped);
|
|
|
|
|
else
|
|
|
|
|
debug ("sysctl: setting '%s' to '%s' (current value is '%s')", path, value_escaped, contents_escaped);
|
|
|
|
|
g_free (contents);
|
|
|
|
|
g_free (contents_escaped);
|
|
|
|
|
}
|
|
|
|
|
g_free (value_escaped);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define _log_dbg_sysctl_set(path, value) \
|
|
|
|
|
G_STMT_START { \
|
|
|
|
|
if (nm_logging_enabled (LOGL_DEBUG, LOGD_PLATFORM)) { \
|
|
|
|
|
_log_dbg_sysctl_set_impl (path, value); \
|
|
|
|
|
} \
|
|
|
|
|
} G_STMT_END
|
|
|
|
|
|
2013-04-03 16:10:38 +02:00
|
|
|
static gboolean
|
|
|
|
|
sysctl_set (NMPlatform *platform, const char *path, const char *value)
|
|
|
|
|
{
|
|
|
|
|
int fd, len, nwrote, tries;
|
|
|
|
|
char *actual;
|
|
|
|
|
|
|
|
|
|
g_return_val_if_fail (path != NULL, FALSE);
|
|
|
|
|
g_return_val_if_fail (value != NULL, FALSE);
|
|
|
|
|
|
2014-03-04 21:14:30 +01:00
|
|
|
/* Don't write outside known locations */
|
|
|
|
|
g_assert (g_str_has_prefix (path, "/proc/sys/")
|
|
|
|
|
|| g_str_has_prefix (path, "/sys/"));
|
|
|
|
|
/* Don't write to suspicious locations */
|
2014-03-24 12:34:43 +01:00
|
|
|
g_assert (!strstr (path, "/../"));
|
2014-03-04 21:14:30 +01:00
|
|
|
|
2013-04-03 16:10:38 +02:00
|
|
|
fd = open (path, O_WRONLY | O_TRUNC);
|
|
|
|
|
if (fd == -1) {
|
2014-02-25 13:23:07 -05:00
|
|
|
if (errno == ENOENT) {
|
|
|
|
|
debug ("sysctl: failed to open '%s': (%d) %s",
|
|
|
|
|
path, errno, strerror (errno));
|
|
|
|
|
} else {
|
|
|
|
|
error ("sysctl: failed to open '%s': (%d) %s",
|
|
|
|
|
path, errno, strerror (errno));
|
|
|
|
|
}
|
2013-04-03 16:10:38 +02:00
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-04 20:15:28 +01:00
|
|
|
_log_dbg_sysctl_set (path, value);
|
2013-04-03 16:10:38 +02:00
|
|
|
|
|
|
|
|
/* Most sysfs and sysctl options don't care about a trailing LF, while some
|
|
|
|
|
* (like infiniband) do. So always add the LF. Also, neither sysfs nor
|
|
|
|
|
* sysctl support partial writes so the LF must be added to the string we're
|
|
|
|
|
* about to write.
|
|
|
|
|
*/
|
|
|
|
|
actual = g_strdup_printf ("%s\n", value);
|
|
|
|
|
|
|
|
|
|
/* Try to write the entire value three times if a partial write occurs */
|
|
|
|
|
len = strlen (actual);
|
|
|
|
|
for (tries = 0, nwrote = 0; tries < 3 && nwrote != len; tries++) {
|
|
|
|
|
nwrote = write (fd, actual, len);
|
|
|
|
|
if (nwrote == -1) {
|
|
|
|
|
if (errno == EINTR) {
|
2014-02-25 13:23:07 -05:00
|
|
|
debug ("sysctl: interrupted, will try again");
|
2013-04-03 16:10:38 +02:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-02-25 13:23:07 -05:00
|
|
|
if (nwrote == -1 && errno != EEXIST) {
|
2013-04-03 16:10:38 +02:00
|
|
|
error ("sysctl: failed to set '%s' to '%s': (%d) %s",
|
2014-02-25 13:23:07 -05:00
|
|
|
path, value, errno, strerror (errno));
|
|
|
|
|
} else if (nwrote < len) {
|
|
|
|
|
error ("sysctl: failed to set '%s' to '%s' after three attempts",
|
|
|
|
|
path, value);
|
2013-04-03 16:10:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
g_free (actual);
|
|
|
|
|
close (fd);
|
|
|
|
|
return (nwrote == len);
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-04 20:15:28 +01:00
|
|
|
static GHashTable *sysctl_get_prev_values;
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
_log_dbg_sysctl_get_impl (const char *path, const char *contents)
|
|
|
|
|
{
|
|
|
|
|
const char *prev_value = NULL;
|
|
|
|
|
|
|
|
|
|
if (!sysctl_get_prev_values)
|
|
|
|
|
sysctl_get_prev_values = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
|
|
|
|
|
else
|
|
|
|
|
prev_value = g_hash_table_lookup (sysctl_get_prev_values, path);
|
|
|
|
|
|
|
|
|
|
if (prev_value) {
|
|
|
|
|
if (strcmp (prev_value, contents) != 0) {
|
|
|
|
|
char *contents_escaped = g_strescape (contents, NULL);
|
|
|
|
|
char *prev_value_escaped = g_strescape (prev_value, NULL);
|
|
|
|
|
|
|
|
|
|
debug ("sysctl: reading '%s': '%s' (changed from '%s' on last read)", path, contents_escaped, prev_value_escaped);
|
|
|
|
|
g_free (contents_escaped);
|
|
|
|
|
g_free (prev_value_escaped);
|
|
|
|
|
g_hash_table_insert (sysctl_get_prev_values, g_strdup (path), g_strdup (contents));
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
char *contents_escaped = g_strescape (contents, NULL);
|
|
|
|
|
|
|
|
|
|
debug ("sysctl: reading '%s': '%s'", path, contents_escaped);
|
|
|
|
|
g_free (contents_escaped);
|
|
|
|
|
g_hash_table_insert (sysctl_get_prev_values, g_strdup (path), g_strdup (contents));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define _log_dbg_sysctl_get(path, contents) \
|
|
|
|
|
G_STMT_START { \
|
|
|
|
|
if (nm_logging_enabled (LOGL_DEBUG, LOGD_PLATFORM)) { \
|
|
|
|
|
_log_dbg_sysctl_get_impl (path, contents); \
|
|
|
|
|
} else if (sysctl_get_prev_values) { \
|
|
|
|
|
g_hash_table_destroy (sysctl_get_prev_values); \
|
|
|
|
|
sysctl_get_prev_values = NULL; \
|
|
|
|
|
} \
|
|
|
|
|
} G_STMT_END
|
|
|
|
|
|
2013-04-03 16:10:38 +02:00
|
|
|
static char *
|
2014-02-25 13:23:07 -05:00
|
|
|
sysctl_get (NMPlatform *platform, const char *path)
|
2013-04-03 16:10:38 +02:00
|
|
|
{
|
|
|
|
|
GError *error = NULL;
|
|
|
|
|
char *contents;
|
|
|
|
|
|
2014-03-04 21:14:30 +01:00
|
|
|
/* Don't write outside known locations */
|
|
|
|
|
g_assert (g_str_has_prefix (path, "/proc/sys/")
|
|
|
|
|
|| g_str_has_prefix (path, "/sys/"));
|
|
|
|
|
/* Don't write to suspicious locations */
|
2014-03-24 12:34:43 +01:00
|
|
|
g_assert (!strstr (path, "/../"));
|
2014-03-04 21:14:30 +01:00
|
|
|
|
2013-04-03 16:10:38 +02:00
|
|
|
if (!g_file_get_contents (path, &contents, NULL, &error)) {
|
2014-02-25 13:23:07 -05:00
|
|
|
/* We assume FAILED means EOPNOTSUP */
|
|
|
|
|
if ( g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT)
|
|
|
|
|
|| g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_FAILED))
|
|
|
|
|
debug ("error reading %s: %s", path, error->message);
|
|
|
|
|
else
|
2014-01-21 11:23:16 +01:00
|
|
|
error ("error reading %s: %s", path, error->message);
|
2013-09-23 18:15:21 +02:00
|
|
|
g_clear_error (&error);
|
2013-04-03 16:10:38 +02:00
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-04 20:15:28 +01:00
|
|
|
g_strstrip (contents);
|
|
|
|
|
|
|
|
|
|
_log_dbg_sysctl_get (path, contents);
|
|
|
|
|
|
|
|
|
|
return contents;
|
2013-04-03 16:10:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
|
|
|
|
2013-03-27 22:23:24 +01:00
|
|
|
static GArray *
|
|
|
|
|
link_get_all (NMPlatform *platform)
|
|
|
|
|
{
|
|
|
|
|
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
|
2014-03-12 10:30:03 +01:00
|
|
|
GArray *links = g_array_sized_new (FALSE, FALSE, sizeof (NMPlatformLink), nl_cache_nitems (priv->link_cache));
|
2013-03-27 22:23:24 +01:00
|
|
|
NMPlatformLink device;
|
|
|
|
|
struct nl_object *object;
|
|
|
|
|
|
|
|
|
|
for (object = nl_cache_get_first (priv->link_cache); object; object = nl_cache_get_next (object)) {
|
core: don't wait for udev to find created software devices
NM knows software devices it creates exist immediately, and it knows
it can use them immediately instead of waiting for udev to find them.
Ideally we'd wait for udev to find all devices, to allow tags and
other rules to run, but when creating software devices they must
currently be available for use immediately.
Bridges and bonds are required to be IFF_UP before they can be
activated (see their is_available() implementation). But during
activation code paths, specifically in nm_manager_activate_connection(),
the interface is created and immediately checked for availability
to activate (since the creation was a result of the user requesting
device activation). That availability check fails, because the
device is not visible outside NMPlatform (because it hasn't been
found by udev yet, because we haven't gotten back to the event
loop) and thus nm_platform_link_is_up() fails, failing the
availability check. Thus the device activation ends in error.
What should really happen is that device activation shouldn't
be synchronous; a new NMActiveConnection should be created for
any activation request, which can then wait until all the
resources it needs for activation are available (device found,
master devices created, etc). That's going to take more work
though.
2013-08-04 22:08:48 -05:00
|
|
|
struct rtnl_link *rtnl_link = (struct rtnl_link *) object;
|
2013-06-06 11:41:30 -05:00
|
|
|
|
2013-09-16 13:40:39 -04:00
|
|
|
if (link_is_announceable (platform, rtnl_link)) {
|
2014-02-17 12:48:05 +01:00
|
|
|
if (init_link (platform, &device, rtnl_link))
|
|
|
|
|
g_array_append_val (links, device);
|
2013-06-06 11:41:30 -05:00
|
|
|
}
|
2013-03-27 22:23:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return links;
|
|
|
|
|
}
|
|
|
|
|
|
2014-04-22 16:02:15 +02:00
|
|
|
static gboolean
|
2014-08-21 10:35:16 +02:00
|
|
|
_nm_platform_link_get (NMPlatform *platform, int ifindex, NMPlatformLink *l)
|
2014-04-22 16:02:15 +02:00
|
|
|
{
|
|
|
|
|
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
|
2014-06-06 20:10:36 +02:00
|
|
|
auto_nl_object struct rtnl_link *rtnllink = NULL;
|
2014-04-22 16:02:15 +02:00
|
|
|
|
|
|
|
|
rtnllink = rtnl_link_get (priv->link_cache, ifindex);
|
|
|
|
|
if (rtnllink) {
|
|
|
|
|
if (link_is_announceable (platform, rtnllink)) {
|
2014-08-21 10:35:16 +02:00
|
|
|
if (init_link (platform, l, rtnllink))
|
2014-04-22 16:02:15 +02:00
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-27 22:23:24 +01:00
|
|
|
static struct nl_object *
|
|
|
|
|
build_rtnl_link (int ifindex, const char *name, NMLinkType type)
|
|
|
|
|
{
|
|
|
|
|
struct rtnl_link *rtnllink;
|
|
|
|
|
int nle;
|
|
|
|
|
|
2014-04-29 16:00:48 +02:00
|
|
|
rtnllink = _nm_rtnl_link_alloc (ifindex, name);
|
2013-03-27 22:23:24 +01:00
|
|
|
if (type) {
|
|
|
|
|
nle = rtnl_link_set_type (rtnllink, type_to_string (type));
|
|
|
|
|
g_assert (!nle);
|
|
|
|
|
}
|
|
|
|
|
return (struct nl_object *) rtnllink;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
2014-05-13 18:13:52 +02:00
|
|
|
link_add (NMPlatform *platform, const char *name, NMLinkType type, const void *address, size_t address_len)
|
2013-03-27 22:23:24 +01:00
|
|
|
{
|
2014-08-21 10:35:16 +02:00
|
|
|
struct nl_object *l;
|
2013-05-09 10:51:27 -05:00
|
|
|
|
2013-04-26 21:20:57 +02:00
|
|
|
if (type == NM_LINK_TYPE_BOND) {
|
|
|
|
|
/* When the kernel loads the bond module, either via explicit modprobe
|
|
|
|
|
* or automatically in response to creating a bond master, it will also
|
|
|
|
|
* create a 'bond0' interface. Since the bond we're about to create may
|
|
|
|
|
* or may not be named 'bond0' prevent potential confusion about a bond
|
|
|
|
|
* that the user didn't want by telling the bonding module not to create
|
|
|
|
|
* bond0 automatically.
|
|
|
|
|
*/
|
|
|
|
|
if (!g_file_test ("/sys/class/net/bonding_masters", G_FILE_TEST_EXISTS))
|
2015-01-09 11:49:25 +01:00
|
|
|
nm_utils_modprobe (NULL, "bonding", "max_bonds=0", NULL);
|
2013-04-26 21:20:57 +02:00
|
|
|
}
|
|
|
|
|
|
2014-03-05 10:56:16 +01:00
|
|
|
debug ("link: add link '%s' of type '%s' (%d)",
|
|
|
|
|
name, type_to_string (type), (int) type);
|
|
|
|
|
|
2014-08-21 10:35:16 +02:00
|
|
|
l = build_rtnl_link (0, name, type);
|
2014-05-13 18:13:52 +02:00
|
|
|
|
|
|
|
|
g_assert ( (address != NULL) ^ (address_len == 0) );
|
|
|
|
|
if (address) {
|
|
|
|
|
auto_nl_addr struct nl_addr *nladdr = _nm_nl_addr_build (AF_LLC, address, address_len);
|
|
|
|
|
|
2014-08-21 10:35:16 +02:00
|
|
|
rtnl_link_set_addr ((struct rtnl_link *) l, nladdr);
|
2014-05-13 18:13:52 +02:00
|
|
|
}
|
2014-08-21 10:35:16 +02:00
|
|
|
return add_object (platform, l);
|
2013-03-27 22:23:24 +01:00
|
|
|
}
|
|
|
|
|
|
2013-05-17 22:09:19 +02:00
|
|
|
static struct rtnl_link *
|
|
|
|
|
link_get (NMPlatform *platform, int ifindex)
|
|
|
|
|
{
|
|
|
|
|
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
|
2014-02-11 13:08:23 +01:00
|
|
|
struct rtnl_link *rtnllink = rtnl_link_get (priv->link_cache, ifindex);
|
2013-05-17 22:09:19 +02:00
|
|
|
|
core: don't wait for udev to find created software devices
NM knows software devices it creates exist immediately, and it knows
it can use them immediately instead of waiting for udev to find them.
Ideally we'd wait for udev to find all devices, to allow tags and
other rules to run, but when creating software devices they must
currently be available for use immediately.
Bridges and bonds are required to be IFF_UP before they can be
activated (see their is_available() implementation). But during
activation code paths, specifically in nm_manager_activate_connection(),
the interface is created and immediately checked for availability
to activate (since the creation was a result of the user requesting
device activation). That availability check fails, because the
device is not visible outside NMPlatform (because it hasn't been
found by udev yet, because we haven't gotten back to the event
loop) and thus nm_platform_link_is_up() fails, failing the
availability check. Thus the device activation ends in error.
What should really happen is that device activation shouldn't
be synchronous; a new NMActiveConnection should be created for
any activation request, which can then wait until all the
resources it needs for activation are available (device found,
master devices created, etc). That's going to take more work
though.
2013-08-04 22:08:48 -05:00
|
|
|
if (!rtnllink) {
|
|
|
|
|
platform->error = NM_PLATFORM_ERROR_NOT_FOUND;
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* physical interfaces must be found by udev before they can be used */
|
2013-09-16 13:40:39 -04:00
|
|
|
if (!link_is_announceable (platform, rtnllink)) {
|
2013-05-17 22:09:19 +02:00
|
|
|
platform->error = NM_PLATFORM_ERROR_NOT_FOUND;
|
2014-02-11 13:08:23 +01:00
|
|
|
rtnl_link_put (rtnllink);
|
2013-07-26 17:03:39 +02:00
|
|
|
return NULL;
|
|
|
|
|
}
|
2013-05-17 22:09:19 +02:00
|
|
|
|
|
|
|
|
return rtnllink;
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-27 22:23:24 +01:00
|
|
|
static gboolean
|
|
|
|
|
link_change (NMPlatform *platform, int ifindex, struct rtnl_link *change)
|
|
|
|
|
{
|
|
|
|
|
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
|
2013-05-17 22:09:19 +02:00
|
|
|
auto_nl_object struct rtnl_link *rtnllink = link_get (platform, ifindex);
|
|
|
|
|
int nle;
|
2013-03-27 22:23:24 +01:00
|
|
|
|
2013-05-17 22:09:19 +02:00
|
|
|
if (!rtnllink)
|
2013-03-27 22:23:24 +01:00
|
|
|
return FALSE;
|
2014-04-21 14:12:39 -05:00
|
|
|
g_return_val_if_fail (rtnl_link_get_ifindex (change) > 0, FALSE);
|
2013-03-27 22:23:24 +01:00
|
|
|
|
2013-05-17 22:09:19 +02:00
|
|
|
nle = rtnl_link_change (priv->nlh, rtnllink, change, 0);
|
|
|
|
|
|
2013-08-02 00:43:12 +02:00
|
|
|
/* NLE_EXIST is considered equivalent to success to avoid race conditions. You
|
|
|
|
|
* never know when something sends an identical object just before
|
|
|
|
|
* NetworkManager.
|
|
|
|
|
*
|
|
|
|
|
* When netlink returns NLE_OBJ_NOTFOUND, it usually means it failed to find
|
2013-05-24 00:29:21 +02:00
|
|
|
* firmware for the device, especially on nm_platform_link_set_up ().
|
|
|
|
|
* This is basically the same check as in the original code and could
|
|
|
|
|
* potentially be improved.
|
|
|
|
|
*/
|
2013-08-02 00:43:12 +02:00
|
|
|
switch (nle) {
|
|
|
|
|
case -NLE_SUCCESS:
|
|
|
|
|
case -NLE_EXIST:
|
|
|
|
|
break;
|
|
|
|
|
case -NLE_OBJ_NOTFOUND:
|
2014-02-17 14:20:44 +01:00
|
|
|
error ("Firmware not found for changing link %s; Netlink error: %s)", to_string_link (platform, change), nl_geterror (nle));
|
2013-06-19 12:28:48 -05:00
|
|
|
platform->error = NM_PLATFORM_ERROR_NO_FIRMWARE;
|
|
|
|
|
return FALSE;
|
2013-08-02 00:43:12 +02:00
|
|
|
default:
|
2014-02-17 14:20:44 +01:00
|
|
|
error ("Netlink error changing link %s: %s", to_string_link (platform, change), nl_geterror (nle));
|
2013-08-02 00:43:12 +02:00
|
|
|
return FALSE;
|
2013-06-19 12:28:48 -05:00
|
|
|
}
|
2013-05-24 00:29:21 +02:00
|
|
|
|
2013-08-02 18:51:06 +02:00
|
|
|
return refresh_object (platform, (struct nl_object *) rtnllink, FALSE, NM_PLATFORM_REASON_INTERNAL);
|
2013-03-27 22:23:24 +01:00
|
|
|
}
|
|
|
|
|
|
2013-03-27 22:23:24 +01:00
|
|
|
static gboolean
|
|
|
|
|
link_delete (NMPlatform *platform, int ifindex)
|
|
|
|
|
{
|
2013-07-26 17:03:39 +02:00
|
|
|
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
|
core: don't wait for udev to find created software devices
NM knows software devices it creates exist immediately, and it knows
it can use them immediately instead of waiting for udev to find them.
Ideally we'd wait for udev to find all devices, to allow tags and
other rules to run, but when creating software devices they must
currently be available for use immediately.
Bridges and bonds are required to be IFF_UP before they can be
activated (see their is_available() implementation). But during
activation code paths, specifically in nm_manager_activate_connection(),
the interface is created and immediately checked for availability
to activate (since the creation was a result of the user requesting
device activation). That availability check fails, because the
device is not visible outside NMPlatform (because it hasn't been
found by udev yet, because we haven't gotten back to the event
loop) and thus nm_platform_link_is_up() fails, failing the
availability check. Thus the device activation ends in error.
What should really happen is that device activation shouldn't
be synchronous; a new NMActiveConnection should be created for
any activation request, which can then wait until all the
resources it needs for activation are available (device found,
master devices created, etc). That's going to take more work
though.
2013-08-04 22:08:48 -05:00
|
|
|
struct rtnl_link *rtnllink = rtnl_link_get (priv->link_cache, ifindex);
|
2013-07-26 17:03:39 +02:00
|
|
|
|
core: don't wait for udev to find created software devices
NM knows software devices it creates exist immediately, and it knows
it can use them immediately instead of waiting for udev to find them.
Ideally we'd wait for udev to find all devices, to allow tags and
other rules to run, but when creating software devices they must
currently be available for use immediately.
Bridges and bonds are required to be IFF_UP before they can be
activated (see their is_available() implementation). But during
activation code paths, specifically in nm_manager_activate_connection(),
the interface is created and immediately checked for availability
to activate (since the creation was a result of the user requesting
device activation). That availability check fails, because the
device is not visible outside NMPlatform (because it hasn't been
found by udev yet, because we haven't gotten back to the event
loop) and thus nm_platform_link_is_up() fails, failing the
availability check. Thus the device activation ends in error.
What should really happen is that device activation shouldn't
be synchronous; a new NMActiveConnection should be created for
any activation request, which can then wait until all the
resources it needs for activation are available (device found,
master devices created, etc). That's going to take more work
though.
2013-08-04 22:08:48 -05:00
|
|
|
if (!rtnllink) {
|
2013-07-26 17:03:39 +02:00
|
|
|
platform->error = NM_PLATFORM_ERROR_NOT_FOUND;
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
platform: fix lookup of routes and deletion of IPv4 routes
When doing a lookup for an libnl route, the cache comparison function
for routes takes into account 'family', 'tos', 'table', 'dst', and 'prio'.
In NetworkManager we don't use all of these properties for a route, so
at several places when doing a cache lookup we don't have all identifying
properties. Usually we only have 'family' and 'dst' ('table' is
implicit 0, because NM does currently not care about any other tables).
The problem is that NM sees routes with different 'tos', 'prio', but it
cannot look them up in the cache. Add a hack to search the cache
fuzzy.
This is similar to the hack for link, where the identifying properties
are 'family' and 'ifindex', but we only have 'ifindex' at hand. However,
contrary to this hack, we coerce the 'family' to AF_UNSPEC for every link cache
operation. This is not viable in this case, because we internally need
the 'tos' field.
We need the 'tos' field because when deleting an IPv4 route, the 'tos' field must
match. See fib_table_delete(). This was already partially fixed by commit
f0daf90298d1bd9cafac7b9c02dc905327e0b85a, but before the lookup to the
cached object would fail for any non-zero 'tos'.
Signed-off-by: Thomas Haller <thaller@redhat.com>
2014-05-28 18:46:12 +02:00
|
|
|
return delete_object (platform, build_rtnl_link (ifindex, NULL, NM_LINK_TYPE_NONE), TRUE);
|
2013-03-27 22:23:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
link_get_ifindex (NMPlatform *platform, const char *ifname)
|
|
|
|
|
{
|
|
|
|
|
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
|
|
|
|
|
|
|
|
|
|
return rtnl_link_name2i (priv->link_cache, ifname);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const char *
|
|
|
|
|
link_get_name (NMPlatform *platform, int ifindex)
|
|
|
|
|
{
|
|
|
|
|
auto_nl_object struct rtnl_link *rtnllink = link_get (platform, ifindex);
|
|
|
|
|
|
|
|
|
|
return rtnllink ? rtnl_link_get_name (rtnllink) : NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static NMLinkType
|
|
|
|
|
link_get_type (NMPlatform *platform, int ifindex)
|
|
|
|
|
{
|
|
|
|
|
auto_nl_object struct rtnl_link *rtnllink = link_get (platform, ifindex);
|
|
|
|
|
|
2013-05-29 12:00:50 -03:00
|
|
|
return link_extract_type (platform, rtnllink, NULL);
|
2013-04-26 11:43:08 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const char *
|
|
|
|
|
link_get_type_name (NMPlatform *platform, int ifindex)
|
|
|
|
|
{
|
|
|
|
|
auto_nl_object struct rtnl_link *rtnllink = link_get (platform, ifindex);
|
|
|
|
|
const char *type;
|
|
|
|
|
|
2013-05-29 12:00:50 -03:00
|
|
|
link_extract_type (platform, rtnllink, &type);
|
2013-04-26 11:43:08 -04:00
|
|
|
return type;
|
2013-03-27 22:23:24 +01:00
|
|
|
}
|
|
|
|
|
|
2013-03-27 22:23:24 +01:00
|
|
|
static guint32
|
|
|
|
|
link_get_flags (NMPlatform *platform, int ifindex)
|
|
|
|
|
{
|
2013-05-17 22:09:19 +02:00
|
|
|
auto_nl_object struct rtnl_link *rtnllink = link_get (platform, ifindex);
|
2013-03-27 22:23:24 +01:00
|
|
|
|
2013-05-17 22:09:19 +02:00
|
|
|
if (!rtnllink)
|
2013-03-27 22:23:24 +01:00
|
|
|
return IFF_NOARP;
|
|
|
|
|
|
|
|
|
|
return rtnl_link_get_flags (rtnllink);
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-11 13:58:00 +01:00
|
|
|
static gboolean
|
|
|
|
|
link_refresh (NMPlatform *platform, int ifindex)
|
|
|
|
|
{
|
2014-04-29 16:00:48 +02:00
|
|
|
auto_nl_object struct rtnl_link *rtnllink = _nm_rtnl_link_alloc (ifindex, NULL);
|
2014-02-11 13:58:00 +01:00
|
|
|
|
2014-04-29 16:00:48 +02:00
|
|
|
return refresh_object (platform, (struct nl_object *) rtnllink, FALSE, NM_PLATFORM_REASON_EXTERNAL);
|
2014-02-11 13:58:00 +01:00
|
|
|
}
|
|
|
|
|
|
2013-03-27 22:23:24 +01:00
|
|
|
static gboolean
|
|
|
|
|
link_is_up (NMPlatform *platform, int ifindex)
|
|
|
|
|
{
|
|
|
|
|
return !!(link_get_flags (platform, ifindex) & IFF_UP);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
link_is_connected (NMPlatform *platform, int ifindex)
|
|
|
|
|
{
|
|
|
|
|
return !!(link_get_flags (platform, ifindex) & IFF_LOWER_UP);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
link_uses_arp (NMPlatform *platform, int ifindex)
|
|
|
|
|
{
|
|
|
|
|
return !(link_get_flags (platform, ifindex) & IFF_NOARP);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
link_change_flags (NMPlatform *platform, int ifindex, unsigned int flags, gboolean value)
|
|
|
|
|
{
|
2014-04-29 16:00:48 +02:00
|
|
|
auto_nl_object struct rtnl_link *change = _nm_rtnl_link_alloc (ifindex, NULL);
|
2013-03-27 22:23:24 +01:00
|
|
|
|
|
|
|
|
if (value)
|
|
|
|
|
rtnl_link_set_flags (change, flags);
|
|
|
|
|
else
|
|
|
|
|
rtnl_link_unset_flags (change, flags);
|
|
|
|
|
|
2014-03-05 10:56:16 +01:00
|
|
|
if (nm_logging_enabled (LOGL_DEBUG, LOGD_PLATFORM)) {
|
|
|
|
|
char buf[512];
|
|
|
|
|
|
|
|
|
|
rtnl_link_flags2str (flags, buf, sizeof (buf));
|
|
|
|
|
debug ("link: change %d: flags %s '%s' (%d)", ifindex, value ? "set" : "unset", buf, flags);
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-27 22:23:24 +01:00
|
|
|
return link_change (platform, ifindex, change);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
link_set_up (NMPlatform *platform, int ifindex)
|
|
|
|
|
{
|
|
|
|
|
return link_change_flags (platform, ifindex, IFF_UP, TRUE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
link_set_down (NMPlatform *platform, int ifindex)
|
|
|
|
|
{
|
|
|
|
|
return link_change_flags (platform, ifindex, IFF_UP, FALSE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
link_set_arp (NMPlatform *platform, int ifindex)
|
|
|
|
|
{
|
|
|
|
|
return link_change_flags (platform, ifindex, IFF_NOARP, FALSE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
link_set_noarp (NMPlatform *platform, int ifindex)
|
|
|
|
|
{
|
|
|
|
|
return link_change_flags (platform, ifindex, IFF_NOARP, TRUE);
|
|
|
|
|
}
|
|
|
|
|
|
2014-10-13 08:26:52 +02:00
|
|
|
static gboolean
|
|
|
|
|
link_get_ipv6_token (NMPlatform *platform, int ifindex, NMUtilsIPv6IfaceId *iid)
|
|
|
|
|
{
|
|
|
|
|
#if HAVE_LIBNL_INET6_TOKEN
|
|
|
|
|
auto_nl_object struct rtnl_link *rtnllink = link_get (platform, ifindex);
|
|
|
|
|
struct nl_addr *nladdr;
|
|
|
|
|
struct in6_addr *addr;
|
|
|
|
|
|
|
|
|
|
if (rtnllink &&
|
|
|
|
|
(rtnl_link_inet6_get_token (rtnllink, &nladdr)) == 0) {
|
|
|
|
|
if (nl_addr_get_family (nladdr) != AF_INET6 ||
|
|
|
|
|
nl_addr_get_len (nladdr) != sizeof (struct in6_addr)) {
|
|
|
|
|
nl_addr_put (nladdr);
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
addr = nl_addr_get_binary_addr (nladdr);
|
|
|
|
|
iid->id_u8[7] = addr->s6_addr[15];
|
|
|
|
|
iid->id_u8[6] = addr->s6_addr[14];
|
|
|
|
|
iid->id_u8[5] = addr->s6_addr[13];
|
|
|
|
|
iid->id_u8[4] = addr->s6_addr[12];
|
|
|
|
|
iid->id_u8[3] = addr->s6_addr[11];
|
|
|
|
|
iid->id_u8[2] = addr->s6_addr[10];
|
|
|
|
|
iid->id_u8[1] = addr->s6_addr[9];
|
|
|
|
|
iid->id_u8[0] = addr->s6_addr[8];
|
|
|
|
|
nl_addr_put (nladdr);
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-24 15:57:08 -05:00
|
|
|
static gboolean
|
|
|
|
|
link_get_user_ipv6ll_enabled (NMPlatform *platform, int ifindex)
|
|
|
|
|
{
|
|
|
|
|
#if HAVE_LIBNL_INET6_ADDR_GEN_MODE
|
|
|
|
|
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
|
|
|
|
|
|
|
|
|
|
if (priv->support_user_ipv6ll > 0) {
|
|
|
|
|
auto_nl_object struct rtnl_link *rtnllink = link_get (platform, ifindex);
|
|
|
|
|
uint8_t mode = 0;
|
|
|
|
|
|
|
|
|
|
if (rtnllink) {
|
|
|
|
|
if (rtnl_link_inet6_get_addr_gen_mode (rtnllink, &mode) != 0) {
|
|
|
|
|
/* Default to "disabled" on error */
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
return mode == IN6_ADDR_GEN_MODE_NONE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
link_set_user_ipv6ll_enabled (NMPlatform *platform, int ifindex, gboolean enabled)
|
|
|
|
|
{
|
|
|
|
|
#if HAVE_LIBNL_INET6_ADDR_GEN_MODE
|
|
|
|
|
if (check_support_user_ipv6ll (platform)) {
|
|
|
|
|
auto_nl_object struct rtnl_link *change = _nm_rtnl_link_alloc (ifindex, NULL);
|
|
|
|
|
guint8 mode = enabled ? IN6_ADDR_GEN_MODE_NONE : IN6_ADDR_GEN_MODE_EUI64;
|
|
|
|
|
char buf[32];
|
|
|
|
|
|
|
|
|
|
rtnl_link_inet6_set_addr_gen_mode (change, mode);
|
|
|
|
|
debug ("link: change %d: set IPv6 address generation mode to %s",
|
|
|
|
|
ifindex, rtnl_link_inet6_addrgenmode2str (mode, buf, sizeof (buf)));
|
|
|
|
|
return link_change (platform, ifindex, change);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-27 22:53:55 +01:00
|
|
|
static gboolean
|
2013-05-20 15:38:54 -03:00
|
|
|
supports_ethtool_carrier_detect (const char *ifname)
|
2013-03-27 22:53:55 +01:00
|
|
|
{
|
|
|
|
|
struct ethtool_cmd edata = { .cmd = ETHTOOL_GLINK };
|
|
|
|
|
|
2013-04-17 12:27:00 +02:00
|
|
|
/* We ignore the result. If the ETHTOOL_GLINK call succeeded, then we
|
|
|
|
|
* assume the device supports carrier-detect, otherwise we assume it
|
|
|
|
|
* doesn't.
|
|
|
|
|
*/
|
2013-05-20 15:38:54 -03:00
|
|
|
return ethtool_get (ifname, &edata);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
supports_mii_carrier_detect (const char *ifname)
|
|
|
|
|
{
|
|
|
|
|
int fd;
|
|
|
|
|
struct ifreq ifr;
|
|
|
|
|
struct mii_ioctl_data *mii;
|
|
|
|
|
gboolean supports_mii = FALSE;
|
|
|
|
|
|
|
|
|
|
fd = socket (PF_INET, SOCK_DGRAM, 0);
|
|
|
|
|
if (fd < 0) {
|
|
|
|
|
nm_log_err (LOGD_PLATFORM, "couldn't open control socket.");
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
memset (&ifr, 0, sizeof (struct ifreq));
|
|
|
|
|
strncpy (ifr.ifr_name, ifname, IFNAMSIZ);
|
|
|
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
|
if (ioctl (fd, SIOCGMIIPHY, &ifr) < 0) {
|
|
|
|
|
nm_log_dbg (LOGD_PLATFORM, "SIOCGMIIPHY failed: %d", errno);
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If we can read the BMSR register, we assume that the card supports MII link detection */
|
|
|
|
|
mii = (struct mii_ioctl_data *) &ifr.ifr_ifru;
|
|
|
|
|
mii->reg_num = MII_BMSR;
|
|
|
|
|
|
|
|
|
|
if (ioctl (fd, SIOCGMIIREG, &ifr) == 0) {
|
|
|
|
|
nm_log_dbg (LOGD_PLATFORM, "SIOCGMIIREG result 0x%X", mii->val_out);
|
|
|
|
|
supports_mii = TRUE;
|
|
|
|
|
} else {
|
|
|
|
|
nm_log_dbg (LOGD_PLATFORM, "SIOCGMIIREG failed: %d", errno);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
close (fd);
|
|
|
|
|
nm_log_dbg (LOGD_PLATFORM, "MII %s supported", supports_mii ? "is" : "not");
|
|
|
|
|
return supports_mii;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
link_supports_carrier_detect (NMPlatform *platform, int ifindex)
|
|
|
|
|
{
|
|
|
|
|
const char *name = nm_platform_link_get_name (ifindex);
|
|
|
|
|
|
|
|
|
|
if (!name)
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
/* We use netlink for the actual carrier detection, but netlink can't tell
|
|
|
|
|
* us whether the device actually supports carrier detection in the first
|
|
|
|
|
* place. We assume any device that does implements one of these two APIs.
|
|
|
|
|
*/
|
|
|
|
|
return supports_ethtool_carrier_detect (name) || supports_mii_carrier_detect (name);
|
2013-03-27 22:53:55 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
link_supports_vlans (NMPlatform *platform, int ifindex)
|
|
|
|
|
{
|
|
|
|
|
auto_nl_object struct rtnl_link *rtnllink = link_get (platform, ifindex);
|
|
|
|
|
const char *name = nm_platform_link_get_name (ifindex);
|
2014-07-15 20:18:15 +02:00
|
|
|
gs_free struct ethtool_gfeatures *features = NULL;
|
2013-10-22 17:11:24 +02:00
|
|
|
int idx, block, bit, size;
|
2013-03-27 22:53:55 +01:00
|
|
|
|
2013-04-17 12:30:09 +02:00
|
|
|
/* Only ARPHRD_ETHER links can possibly support VLANs. */
|
2013-03-27 22:53:55 +01:00
|
|
|
if (!rtnllink || rtnl_link_get_arptype (rtnllink) != ARPHRD_ETHER)
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
2013-05-06 12:10:01 -04:00
|
|
|
if (!name)
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
2013-10-22 17:11:24 +02:00
|
|
|
idx = ethtool_get_stringset_index (name, ETH_SS_FEATURES, "vlan-challenged");
|
|
|
|
|
if (idx == -1) {
|
2013-05-06 12:10:01 -04:00
|
|
|
debug ("vlan-challenged ethtool feature does not exist?");
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
2013-10-22 17:11:24 +02:00
|
|
|
block = idx / 32;
|
|
|
|
|
bit = idx % 32;
|
2013-05-06 12:10:01 -04:00
|
|
|
size = block + 1;
|
|
|
|
|
|
|
|
|
|
features = g_malloc0 (sizeof (*features) + size * sizeof (struct ethtool_get_features_block));
|
|
|
|
|
features->cmd = ETHTOOL_GFEATURES;
|
|
|
|
|
features->size = size;
|
|
|
|
|
|
|
|
|
|
if (!ethtool_get (name, features))
|
2013-03-27 22:53:55 +01:00
|
|
|
return FALSE;
|
|
|
|
|
|
2013-05-06 12:10:01 -04:00
|
|
|
return !(features->features[block].active & (1 << bit));
|
2013-03-27 22:53:55 +01:00
|
|
|
}
|
|
|
|
|
|
2013-03-27 22:53:55 +01:00
|
|
|
static gboolean
|
|
|
|
|
link_set_address (NMPlatform *platform, int ifindex, gconstpointer address, size_t length)
|
|
|
|
|
{
|
2014-04-29 16:00:48 +02:00
|
|
|
auto_nl_object struct rtnl_link *change = _nm_rtnl_link_alloc (ifindex, NULL);
|
|
|
|
|
auto_nl_addr struct nl_addr *nladdr = _nm_nl_addr_build (AF_LLC, address, length);
|
2013-03-27 22:53:55 +01:00
|
|
|
|
|
|
|
|
rtnl_link_set_addr (change, nladdr);
|
|
|
|
|
|
2014-03-05 10:56:16 +01:00
|
|
|
if (nm_logging_enabled (LOGL_DEBUG, LOGD_PLATFORM)) {
|
2014-07-04 15:59:19 -04:00
|
|
|
char *mac = nm_utils_hwaddr_ntoa (address, length);
|
2014-03-05 10:56:16 +01:00
|
|
|
|
|
|
|
|
debug ("link: change %d: address %s (%lu bytes)", ifindex, mac, (unsigned long) length);
|
|
|
|
|
g_free (mac);
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-27 22:53:55 +01:00
|
|
|
return link_change (platform, ifindex, change);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gconstpointer
|
|
|
|
|
link_get_address (NMPlatform *platform, int ifindex, size_t *length)
|
|
|
|
|
{
|
|
|
|
|
auto_nl_object struct rtnl_link *rtnllink = link_get (platform, ifindex);
|
|
|
|
|
struct nl_addr *nladdr;
|
2014-06-13 15:47:29 +02:00
|
|
|
size_t l = 0;
|
|
|
|
|
gconstpointer a = NULL;
|
|
|
|
|
|
|
|
|
|
if (rtnllink &&
|
|
|
|
|
(nladdr = rtnl_link_get_addr (rtnllink))) {
|
|
|
|
|
l = nl_addr_get_len (nladdr);
|
|
|
|
|
if (l > NM_UTILS_HWADDR_LEN_MAX) {
|
|
|
|
|
if (length)
|
|
|
|
|
*length = 0;
|
|
|
|
|
g_return_val_if_reached (NULL);
|
|
|
|
|
} else if (l > 0)
|
|
|
|
|
a = nl_addr_get_binary_addr (nladdr);
|
|
|
|
|
}
|
2013-03-27 22:53:55 +01:00
|
|
|
|
|
|
|
|
if (length)
|
2014-06-13 15:47:29 +02:00
|
|
|
*length = l;
|
|
|
|
|
return a;
|
2013-03-27 22:53:55 +01:00
|
|
|
}
|
|
|
|
|
|
2013-04-15 21:48:12 +02:00
|
|
|
static gboolean
|
|
|
|
|
link_set_mtu (NMPlatform *platform, int ifindex, guint32 mtu)
|
|
|
|
|
{
|
2014-04-29 16:00:48 +02:00
|
|
|
auto_nl_object struct rtnl_link *change = _nm_rtnl_link_alloc (ifindex, NULL);
|
2013-04-15 21:48:12 +02:00
|
|
|
|
|
|
|
|
rtnl_link_set_mtu (change, mtu);
|
2014-03-05 10:56:16 +01:00
|
|
|
debug ("link: change %d: mtu %lu", ifindex, (unsigned long)mtu);
|
|
|
|
|
|
2013-04-15 21:48:12 +02:00
|
|
|
return link_change (platform, ifindex, change);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static guint32
|
|
|
|
|
link_get_mtu (NMPlatform *platform, int ifindex)
|
|
|
|
|
{
|
|
|
|
|
auto_nl_object struct rtnl_link *rtnllink = link_get (platform, ifindex);
|
|
|
|
|
|
|
|
|
|
return rtnllink ? rtnl_link_get_mtu (rtnllink) : 0;
|
|
|
|
|
}
|
|
|
|
|
|
2013-10-11 14:59:26 -04:00
|
|
|
static char *
|
|
|
|
|
link_get_physical_port_id (NMPlatform *platform, int ifindex)
|
|
|
|
|
{
|
|
|
|
|
const char *ifname;
|
|
|
|
|
char *path, *id;
|
|
|
|
|
|
|
|
|
|
ifname = nm_platform_link_get_name (ifindex);
|
|
|
|
|
if (!ifname)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
2014-03-12 12:49:34 +01:00
|
|
|
ifname = ASSERT_VALID_PATH_COMPONENT (ifname);
|
|
|
|
|
|
2014-01-22 13:20:18 -05:00
|
|
|
path = g_strdup_printf ("/sys/class/net/%s/phys_port_id", ifname);
|
2014-02-25 13:23:07 -05:00
|
|
|
id = sysctl_get (platform, path);
|
2013-10-11 14:59:26 -04:00
|
|
|
g_free (path);
|
|
|
|
|
|
|
|
|
|
return id;
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-27 22:53:55 +01:00
|
|
|
static int
|
|
|
|
|
vlan_add (NMPlatform *platform, const char *name, int parent, int vlan_id, guint32 vlan_flags)
|
|
|
|
|
{
|
|
|
|
|
struct nl_object *object = build_rtnl_link (0, name, NM_LINK_TYPE_VLAN);
|
|
|
|
|
struct rtnl_link *rtnllink = (struct rtnl_link *) object;
|
|
|
|
|
unsigned int kernel_flags;
|
|
|
|
|
|
|
|
|
|
kernel_flags = 0;
|
|
|
|
|
if (vlan_flags & NM_VLAN_FLAG_REORDER_HEADERS)
|
|
|
|
|
kernel_flags |= VLAN_FLAG_REORDER_HDR;
|
|
|
|
|
if (vlan_flags & NM_VLAN_FLAG_GVRP)
|
|
|
|
|
kernel_flags |= VLAN_FLAG_GVRP;
|
|
|
|
|
if (vlan_flags & NM_VLAN_FLAG_LOOSE_BINDING)
|
|
|
|
|
kernel_flags |= VLAN_FLAG_LOOSE_BINDING;
|
|
|
|
|
|
|
|
|
|
rtnl_link_set_link (rtnllink, parent);
|
|
|
|
|
rtnl_link_vlan_set_id (rtnllink, vlan_id);
|
2013-10-20 18:12:45 +02:00
|
|
|
rtnl_link_vlan_set_flags (rtnllink, kernel_flags);
|
2013-03-27 22:53:55 +01:00
|
|
|
|
2014-03-05 10:56:16 +01:00
|
|
|
debug ("link: add vlan '%s', parent %d, vlan id %d, flags %X (native: %X)",
|
|
|
|
|
name, parent, vlan_id, (unsigned int) vlan_flags, kernel_flags);
|
|
|
|
|
|
2013-03-27 22:53:55 +01:00
|
|
|
return add_object (platform, object);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
vlan_get_info (NMPlatform *platform, int ifindex, int *parent, int *vlan_id)
|
|
|
|
|
{
|
|
|
|
|
auto_nl_object struct rtnl_link *rtnllink = link_get (platform, ifindex);
|
|
|
|
|
|
|
|
|
|
if (parent)
|
|
|
|
|
*parent = rtnllink ? rtnl_link_get_link (rtnllink) : 0;
|
|
|
|
|
if (vlan_id)
|
|
|
|
|
*vlan_id = rtnllink ? rtnl_link_vlan_get_id (rtnllink) : 0;
|
|
|
|
|
|
|
|
|
|
return !!rtnllink;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
vlan_set_ingress_map (NMPlatform *platform, int ifindex, int from, int to)
|
|
|
|
|
{
|
2013-09-03 14:02:59 -04:00
|
|
|
/* We have to use link_get() because a "blank" rtnl_link won't have the
|
|
|
|
|
* right data structures to be able to call rtnl_link_vlan_set_ingress_map()
|
|
|
|
|
* on it. (Likewise below in vlan_set_egress_map().)
|
|
|
|
|
*/
|
|
|
|
|
auto_nl_object struct rtnl_link *change = link_get (platform, ifindex);
|
2013-03-27 22:53:55 +01:00
|
|
|
|
2013-09-03 14:02:59 -04:00
|
|
|
if (!change)
|
|
|
|
|
return FALSE;
|
|
|
|
|
rtnl_link_vlan_set_ingress_map (change, from, to);
|
2013-03-27 22:53:55 +01:00
|
|
|
|
2014-03-05 10:56:16 +01:00
|
|
|
debug ("link: change %d: vlan ingress map %d -> %d", ifindex, from, to);
|
|
|
|
|
|
2013-03-27 22:53:55 +01:00
|
|
|
return link_change (platform, ifindex, change);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
vlan_set_egress_map (NMPlatform *platform, int ifindex, int from, int to)
|
|
|
|
|
{
|
2013-09-03 14:02:59 -04:00
|
|
|
auto_nl_object struct rtnl_link *change = link_get (platform, ifindex);
|
2013-03-27 22:53:55 +01:00
|
|
|
|
2013-09-03 14:02:59 -04:00
|
|
|
if (!change)
|
|
|
|
|
return FALSE;
|
2013-03-27 22:53:55 +01:00
|
|
|
rtnl_link_vlan_set_egress_map (change, from, to);
|
|
|
|
|
|
2014-03-05 10:56:16 +01:00
|
|
|
debug ("link: change %d: vlan egress map %d -> %d", ifindex, from, to);
|
|
|
|
|
|
2013-03-27 22:53:55 +01:00
|
|
|
return link_change (platform, ifindex, change);
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-27 22:53:55 +01:00
|
|
|
static gboolean
|
2013-08-02 00:43:12 +02:00
|
|
|
link_enslave (NMPlatform *platform, int master, int slave)
|
2013-03-27 22:53:55 +01:00
|
|
|
{
|
2014-04-29 16:00:48 +02:00
|
|
|
auto_nl_object struct rtnl_link *change = _nm_rtnl_link_alloc (slave, NULL);
|
2013-03-27 22:53:55 +01:00
|
|
|
|
2013-08-02 00:43:12 +02:00
|
|
|
rtnl_link_set_master (change, master);
|
2014-03-05 10:56:16 +01:00
|
|
|
debug ("link: change %d: enslave to master %d", slave, master);
|
|
|
|
|
|
2013-08-02 00:43:12 +02:00
|
|
|
return link_change (platform, slave, change);
|
2013-03-27 22:53:55 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
link_release (NMPlatform *platform, int master, int slave)
|
|
|
|
|
{
|
2013-08-02 00:43:12 +02:00
|
|
|
return link_enslave (platform, 0, slave);
|
2013-03-27 22:53:55 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
link_get_master (NMPlatform *platform, int slave)
|
|
|
|
|
{
|
2013-05-17 22:09:19 +02:00
|
|
|
auto_nl_object struct rtnl_link *rtnllink = link_get (platform, slave);
|
2013-03-27 22:53:55 +01:00
|
|
|
|
2013-05-17 22:09:19 +02:00
|
|
|
return rtnllink ? rtnl_link_get_master (rtnllink) : 0;
|
2013-03-27 22:53:55 +01:00
|
|
|
}
|
|
|
|
|
|
2013-04-04 17:07:47 +02:00
|
|
|
static char *
|
|
|
|
|
link_option_path (int master, const char *category, const char *option)
|
|
|
|
|
{
|
|
|
|
|
const char *name = nm_platform_link_get_name (master);
|
|
|
|
|
|
|
|
|
|
if (!name || !category || !option)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
2014-03-12 12:49:34 +01:00
|
|
|
return g_strdup_printf ("/sys/class/net/%s/%s/%s",
|
|
|
|
|
ASSERT_VALID_PATH_COMPONENT (name),
|
|
|
|
|
ASSERT_VALID_PATH_COMPONENT (category),
|
|
|
|
|
ASSERT_VALID_PATH_COMPONENT (option));
|
2013-04-04 17:07:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
link_set_option (int master, const char *category, const char *option, const char *value)
|
|
|
|
|
{
|
2014-07-15 20:18:15 +02:00
|
|
|
gs_free char *path = link_option_path (master, category, option);
|
2013-04-04 17:07:47 +02:00
|
|
|
|
|
|
|
|
return path && nm_platform_sysctl_set (path, value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char *
|
|
|
|
|
link_get_option (int master, const char *category, const char *option)
|
|
|
|
|
{
|
2014-07-15 20:18:15 +02:00
|
|
|
gs_free char *path = link_option_path (master, category, option);
|
2013-04-04 17:07:47 +02:00
|
|
|
|
2014-02-25 13:23:07 -05:00
|
|
|
return path ? nm_platform_sysctl_get (path) : NULL;
|
2013-04-04 17:07:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const char *
|
|
|
|
|
master_category (NMPlatform *platform, int master)
|
|
|
|
|
{
|
|
|
|
|
switch (link_get_type (platform, master)) {
|
|
|
|
|
case NM_LINK_TYPE_BRIDGE:
|
|
|
|
|
return "bridge";
|
|
|
|
|
case NM_LINK_TYPE_BOND:
|
|
|
|
|
return "bonding";
|
|
|
|
|
default:
|
2014-02-11 22:10:05 +01:00
|
|
|
g_return_val_if_reached (NULL);
|
|
|
|
|
return NULL;
|
2013-04-04 17:07:47 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const char *
|
|
|
|
|
slave_category (NMPlatform *platform, int slave)
|
|
|
|
|
{
|
|
|
|
|
int master = link_get_master (platform, slave);
|
|
|
|
|
|
2013-11-06 20:52:30 -06:00
|
|
|
if (master <= 0) {
|
2013-04-04 17:07:47 +02:00
|
|
|
platform->error = NM_PLATFORM_ERROR_NOT_SLAVE;
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (link_get_type (platform, master)) {
|
|
|
|
|
case NM_LINK_TYPE_BRIDGE:
|
|
|
|
|
return "brport";
|
|
|
|
|
default:
|
2014-02-11 22:10:05 +01:00
|
|
|
g_return_val_if_reached (NULL);
|
|
|
|
|
return NULL;
|
2013-04-04 17:07:47 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
master_set_option (NMPlatform *platform, int master, const char *option, const char *value)
|
|
|
|
|
{
|
|
|
|
|
return link_set_option (master, master_category (platform, master), option, value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char *
|
|
|
|
|
master_get_option (NMPlatform *platform, int master, const char *option)
|
|
|
|
|
{
|
|
|
|
|
return link_get_option (master, master_category (platform, master), option);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
slave_set_option (NMPlatform *platform, int slave, const char *option, const char *value)
|
|
|
|
|
{
|
|
|
|
|
return link_set_option (slave, slave_category (platform, slave), option, value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char *
|
|
|
|
|
slave_get_option (NMPlatform *platform, int slave, const char *option)
|
|
|
|
|
{
|
|
|
|
|
return link_get_option (slave, slave_category (platform, slave), option);
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-10 16:21:08 -03:00
|
|
|
static gboolean
|
|
|
|
|
infiniband_partition_add (NMPlatform *platform, int parent, int p_key)
|
|
|
|
|
{
|
|
|
|
|
const char *parent_name;
|
|
|
|
|
char *path, *id;
|
|
|
|
|
gboolean success;
|
|
|
|
|
|
|
|
|
|
parent_name = nm_platform_link_get_name (parent);
|
|
|
|
|
g_return_val_if_fail (parent_name != NULL, FALSE);
|
|
|
|
|
|
2014-03-12 12:49:34 +01:00
|
|
|
path = g_strdup_printf ("/sys/class/net/%s/create_child", ASSERT_VALID_PATH_COMPONENT (parent_name));
|
2013-06-10 16:21:08 -03:00
|
|
|
id = g_strdup_printf ("0x%04x", p_key);
|
|
|
|
|
success = nm_platform_sysctl_set (path, id);
|
|
|
|
|
g_free (id);
|
|
|
|
|
g_free (path);
|
|
|
|
|
|
2013-09-16 13:43:28 -04:00
|
|
|
if (success) {
|
2014-07-15 20:18:15 +02:00
|
|
|
gs_free char *ifname = g_strdup_printf ("%s.%04x", parent_name, p_key);
|
2014-04-29 16:00:48 +02:00
|
|
|
auto_nl_object struct rtnl_link *rtnllink = _nm_rtnl_link_alloc (0, ifname);
|
2013-09-16 13:43:28 -04:00
|
|
|
|
|
|
|
|
success = refresh_object (platform, (struct nl_object *) rtnllink, FALSE, NM_PLATFORM_REASON_INTERNAL);
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-10 16:21:08 -03:00
|
|
|
return success;
|
|
|
|
|
}
|
|
|
|
|
|
2013-05-03 13:55:51 -04:00
|
|
|
static gboolean
|
|
|
|
|
veth_get_properties (NMPlatform *platform, int ifindex, NMPlatformVethProperties *props)
|
|
|
|
|
{
|
|
|
|
|
const char *ifname;
|
2014-07-15 20:18:15 +02:00
|
|
|
gs_free struct ethtool_stats *stats = NULL;
|
2013-05-03 13:55:51 -04:00
|
|
|
int peer_ifindex_stat;
|
|
|
|
|
|
|
|
|
|
ifname = nm_platform_link_get_name (ifindex);
|
|
|
|
|
if (!ifname)
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
peer_ifindex_stat = ethtool_get_stringset_index (ifname, ETH_SS_STATS, "peer_ifindex");
|
|
|
|
|
if (peer_ifindex_stat == -1) {
|
|
|
|
|
debug ("%s: peer_ifindex ethtool stat does not exist?", ifname);
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
stats = g_malloc0 (sizeof (*stats) + (peer_ifindex_stat + 1) * sizeof (guint64));
|
|
|
|
|
stats->cmd = ETHTOOL_GSTATS;
|
|
|
|
|
stats->n_stats = peer_ifindex_stat + 1;
|
|
|
|
|
if (!ethtool_get (ifname, stats))
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
props->peer = stats->data[peer_ifindex_stat];
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-25 15:46:39 -04:00
|
|
|
static gboolean
|
|
|
|
|
tun_get_properties (NMPlatform *platform, int ifindex, NMPlatformTunProperties *props)
|
|
|
|
|
{
|
|
|
|
|
const char *ifname;
|
|
|
|
|
char *path, *val;
|
2014-01-21 11:04:26 +01:00
|
|
|
gboolean success = TRUE;
|
|
|
|
|
|
|
|
|
|
g_return_val_if_fail (props, FALSE);
|
|
|
|
|
|
|
|
|
|
memset (props, 0, sizeof (*props));
|
|
|
|
|
props->owner = -1;
|
|
|
|
|
props->group = -1;
|
2013-04-25 15:46:39 -04:00
|
|
|
|
|
|
|
|
ifname = nm_platform_link_get_name (ifindex);
|
2014-01-21 11:04:26 +01:00
|
|
|
if (!ifname || !nm_utils_iface_valid_name (ifname))
|
2013-04-25 15:46:39 -04:00
|
|
|
return FALSE;
|
2014-03-12 12:49:34 +01:00
|
|
|
ifname = ASSERT_VALID_PATH_COMPONENT (ifname);
|
2013-04-25 15:46:39 -04:00
|
|
|
|
|
|
|
|
path = g_strdup_printf ("/sys/class/net/%s/owner", ifname);
|
2014-02-25 13:23:07 -05:00
|
|
|
val = nm_platform_sysctl_get (path);
|
2013-04-25 15:46:39 -04:00
|
|
|
g_free (path);
|
2014-01-21 11:04:26 +01:00
|
|
|
if (val) {
|
2015-02-22 11:55:31 +01:00
|
|
|
props->owner = _nm_utils_ascii_str_to_int64 (val, 10, -1, G_MAXINT64, -1);
|
2014-01-21 11:04:26 +01:00
|
|
|
if (errno)
|
|
|
|
|
success = FALSE;
|
|
|
|
|
g_free (val);
|
|
|
|
|
} else
|
|
|
|
|
success = FALSE;
|
2013-04-25 15:46:39 -04:00
|
|
|
|
|
|
|
|
path = g_strdup_printf ("/sys/class/net/%s/group", ifname);
|
2014-02-25 13:23:07 -05:00
|
|
|
val = nm_platform_sysctl_get (path);
|
2013-04-25 15:46:39 -04:00
|
|
|
g_free (path);
|
2014-01-21 11:04:26 +01:00
|
|
|
if (val) {
|
2015-02-22 11:55:31 +01:00
|
|
|
props->group = _nm_utils_ascii_str_to_int64 (val, 10, -1, G_MAXINT64, -1);
|
2014-01-21 11:04:26 +01:00
|
|
|
if (errno)
|
|
|
|
|
success = FALSE;
|
|
|
|
|
g_free (val);
|
|
|
|
|
} else
|
|
|
|
|
success = FALSE;
|
2013-04-25 15:46:39 -04:00
|
|
|
|
|
|
|
|
path = g_strdup_printf ("/sys/class/net/%s/tun_flags", ifname);
|
2014-02-25 13:23:07 -05:00
|
|
|
val = nm_platform_sysctl_get (path);
|
2013-04-25 15:46:39 -04:00
|
|
|
g_free (path);
|
2014-01-21 11:04:26 +01:00
|
|
|
if (val) {
|
|
|
|
|
gint64 flags;
|
|
|
|
|
|
2015-02-22 11:55:31 +01:00
|
|
|
flags = _nm_utils_ascii_str_to_int64 (val, 16, 0, G_MAXINT64, 0);
|
2014-01-21 11:04:26 +01:00
|
|
|
if (!errno) {
|
|
|
|
|
#ifndef IFF_MULTI_QUEUE
|
|
|
|
|
const int IFF_MULTI_QUEUE = 0x0100;
|
2013-06-04 12:01:38 -03:00
|
|
|
#endif
|
2014-11-19 18:38:13 -06:00
|
|
|
props->mode = ((flags & (IFF_TUN | IFF_TAP)) == IFF_TUN) ? "tun" : "tap";
|
2014-01-21 11:04:26 +01:00
|
|
|
props->no_pi = !!(flags & IFF_NO_PI);
|
|
|
|
|
props->vnet_hdr = !!(flags & IFF_VNET_HDR);
|
|
|
|
|
props->multi_queue = !!(flags & IFF_MULTI_QUEUE);
|
|
|
|
|
} else
|
|
|
|
|
success = FALSE;
|
|
|
|
|
g_free (val);
|
|
|
|
|
} else
|
|
|
|
|
success = FALSE;
|
2013-04-25 15:46:39 -04:00
|
|
|
|
2014-01-21 11:04:26 +01:00
|
|
|
return success;
|
2013-04-25 15:46:39 -04:00
|
|
|
}
|
|
|
|
|
|
2013-05-06 09:16:17 -04:00
|
|
|
static const struct nla_policy macvlan_info_policy[IFLA_MACVLAN_MAX + 1] = {
|
|
|
|
|
[IFLA_MACVLAN_MODE] = { .type = NLA_U32 },
|
2014-02-20 14:28:30 -05:00
|
|
|
#ifdef MACVLAN_FLAG_NOPROMISC
|
2013-05-06 09:16:17 -04:00
|
|
|
[IFLA_MACVLAN_FLAGS] = { .type = NLA_U16 },
|
2013-06-04 12:01:38 -03:00
|
|
|
#endif
|
2013-05-06 09:16:17 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
macvlan_info_data_parser (struct nlattr *info_data, gpointer parser_data)
|
|
|
|
|
{
|
|
|
|
|
NMPlatformMacvlanProperties *props = parser_data;
|
|
|
|
|
struct nlattr *tb[IFLA_MACVLAN_MAX + 1];
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
err = nla_parse_nested (tb, IFLA_MACVLAN_MAX, info_data,
|
|
|
|
|
(struct nla_policy *) macvlan_info_policy);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
|
|
|
|
|
switch (nla_get_u32 (tb[IFLA_MACVLAN_MODE])) {
|
|
|
|
|
case MACVLAN_MODE_PRIVATE:
|
|
|
|
|
props->mode = "private";
|
|
|
|
|
break;
|
|
|
|
|
case MACVLAN_MODE_VEPA:
|
|
|
|
|
props->mode = "vepa";
|
|
|
|
|
break;
|
|
|
|
|
case MACVLAN_MODE_BRIDGE:
|
|
|
|
|
props->mode = "bridge";
|
|
|
|
|
break;
|
|
|
|
|
case MACVLAN_MODE_PASSTHRU:
|
|
|
|
|
props->mode = "passthru";
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
return -NLE_PARSE_ERR;
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-04 12:01:38 -03:00
|
|
|
#ifdef MACVLAN_FLAG_NOPROMISC
|
2013-05-06 09:16:17 -04:00
|
|
|
props->no_promisc = !!(nla_get_u16 (tb[IFLA_MACVLAN_FLAGS]) & MACVLAN_FLAG_NOPROMISC);
|
2013-06-04 12:01:38 -03:00
|
|
|
#else
|
|
|
|
|
props->no_promisc = FALSE;
|
|
|
|
|
#endif
|
2013-05-06 09:16:17 -04:00
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
macvlan_get_properties (NMPlatform *platform, int ifindex, NMPlatformMacvlanProperties *props)
|
|
|
|
|
{
|
|
|
|
|
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
|
2013-08-07 15:47:47 -05:00
|
|
|
auto_nl_object struct rtnl_link *rtnllink = NULL;
|
2013-05-06 09:16:17 -04:00
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
rtnllink = link_get (platform, ifindex);
|
|
|
|
|
if (!rtnllink)
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
props->parent_ifindex = rtnl_link_get_link (rtnllink);
|
|
|
|
|
|
|
|
|
|
err = nm_rtnl_link_parse_info_data (priv->nlh, ifindex,
|
|
|
|
|
macvlan_info_data_parser, props);
|
2014-02-20 14:40:35 -05:00
|
|
|
if (err != 0) {
|
|
|
|
|
warning ("(%s) could not read properties: %s",
|
|
|
|
|
rtnl_link_get_name (rtnllink), nl_geterror (err));
|
|
|
|
|
}
|
2013-05-06 09:16:17 -04:00
|
|
|
return (err == 0);
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-04 10:31:22 -03:00
|
|
|
/* The installed kernel headers might not have VXLAN stuff at all, or
|
|
|
|
|
* they might have the original properties, but not PORT, GROUP6, or LOCAL6.
|
|
|
|
|
* So until we depend on kernel >= 3.11, we just ignore the actual enum
|
|
|
|
|
* in if_link.h and define the values ourselves.
|
|
|
|
|
*/
|
|
|
|
|
#define IFLA_VXLAN_UNSPEC 0
|
|
|
|
|
#define IFLA_VXLAN_ID 1
|
|
|
|
|
#define IFLA_VXLAN_GROUP 2
|
|
|
|
|
#define IFLA_VXLAN_LINK 3
|
|
|
|
|
#define IFLA_VXLAN_LOCAL 4
|
|
|
|
|
#define IFLA_VXLAN_TTL 5
|
|
|
|
|
#define IFLA_VXLAN_TOS 6
|
|
|
|
|
#define IFLA_VXLAN_LEARNING 7
|
|
|
|
|
#define IFLA_VXLAN_AGEING 8
|
|
|
|
|
#define IFLA_VXLAN_LIMIT 9
|
|
|
|
|
#define IFLA_VXLAN_PORT_RANGE 10
|
|
|
|
|
#define IFLA_VXLAN_PROXY 11
|
|
|
|
|
#define IFLA_VXLAN_RSC 12
|
|
|
|
|
#define IFLA_VXLAN_L2MISS 13
|
|
|
|
|
#define IFLA_VXLAN_L3MISS 14
|
|
|
|
|
#define IFLA_VXLAN_PORT 15
|
|
|
|
|
#define IFLA_VXLAN_GROUP6 16
|
|
|
|
|
#define IFLA_VXLAN_LOCAL6 17
|
|
|
|
|
#undef IFLA_VXLAN_MAX
|
|
|
|
|
#define IFLA_VXLAN_MAX IFLA_VXLAN_LOCAL6
|
|
|
|
|
|
2014-09-25 16:44:09 +02:00
|
|
|
/* older kernel header might not contain 'struct ifla_vxlan_port_range'.
|
|
|
|
|
* Redefine it. */
|
|
|
|
|
struct nm_ifla_vxlan_port_range {
|
|
|
|
|
guint16 low;
|
|
|
|
|
guint16 high;
|
|
|
|
|
};
|
|
|
|
|
|
2013-06-04 10:31:22 -03:00
|
|
|
static const struct nla_policy vxlan_info_policy[IFLA_VXLAN_MAX + 1] = {
|
|
|
|
|
[IFLA_VXLAN_ID] = { .type = NLA_U32 },
|
|
|
|
|
[IFLA_VXLAN_GROUP] = { .type = NLA_U32 },
|
|
|
|
|
[IFLA_VXLAN_GROUP6] = { .type = NLA_UNSPEC,
|
|
|
|
|
.minlen = sizeof (struct in6_addr) },
|
|
|
|
|
[IFLA_VXLAN_LINK] = { .type = NLA_U32 },
|
|
|
|
|
[IFLA_VXLAN_LOCAL] = { .type = NLA_U32 },
|
|
|
|
|
[IFLA_VXLAN_LOCAL6] = { .type = NLA_UNSPEC,
|
|
|
|
|
.minlen = sizeof (struct in6_addr) },
|
|
|
|
|
[IFLA_VXLAN_TOS] = { .type = NLA_U8 },
|
|
|
|
|
[IFLA_VXLAN_TTL] = { .type = NLA_U8 },
|
|
|
|
|
[IFLA_VXLAN_LEARNING] = { .type = NLA_U8 },
|
|
|
|
|
[IFLA_VXLAN_AGEING] = { .type = NLA_U32 },
|
|
|
|
|
[IFLA_VXLAN_LIMIT] = { .type = NLA_U32 },
|
|
|
|
|
[IFLA_VXLAN_PORT_RANGE] = { .type = NLA_UNSPEC,
|
2014-09-25 16:44:09 +02:00
|
|
|
.minlen = sizeof (struct nm_ifla_vxlan_port_range) },
|
2013-06-04 10:31:22 -03:00
|
|
|
[IFLA_VXLAN_PROXY] = { .type = NLA_U8 },
|
|
|
|
|
[IFLA_VXLAN_RSC] = { .type = NLA_U8 },
|
|
|
|
|
[IFLA_VXLAN_L2MISS] = { .type = NLA_U8 },
|
|
|
|
|
[IFLA_VXLAN_L3MISS] = { .type = NLA_U8 },
|
|
|
|
|
[IFLA_VXLAN_PORT] = { .type = NLA_U16 },
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
vxlan_info_data_parser (struct nlattr *info_data, gpointer parser_data)
|
|
|
|
|
{
|
|
|
|
|
NMPlatformVxlanProperties *props = parser_data;
|
|
|
|
|
struct nlattr *tb[IFLA_VXLAN_MAX + 1];
|
2014-09-25 16:44:09 +02:00
|
|
|
struct nm_ifla_vxlan_port_range *range;
|
2013-06-04 10:31:22 -03:00
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
err = nla_parse_nested (tb, IFLA_VXLAN_MAX, info_data,
|
|
|
|
|
(struct nla_policy *) vxlan_info_policy);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
|
|
|
|
|
memset (props, 0, sizeof (*props));
|
|
|
|
|
|
2014-09-25 16:44:09 +02:00
|
|
|
if (tb[IFLA_VXLAN_LINK])
|
|
|
|
|
props->parent_ifindex = nla_get_u32 (tb[IFLA_VXLAN_LINK]);
|
|
|
|
|
if (tb[IFLA_VXLAN_ID])
|
|
|
|
|
props->id = nla_get_u32 (tb[IFLA_VXLAN_ID]);
|
2013-06-04 10:31:22 -03:00
|
|
|
if (tb[IFLA_VXLAN_GROUP])
|
|
|
|
|
props->group = nla_get_u32 (tb[IFLA_VXLAN_GROUP]);
|
|
|
|
|
if (tb[IFLA_VXLAN_LOCAL])
|
|
|
|
|
props->local = nla_get_u32 (tb[IFLA_VXLAN_LOCAL]);
|
|
|
|
|
if (tb[IFLA_VXLAN_GROUP6])
|
|
|
|
|
memcpy (&props->group6, nla_data (tb[IFLA_VXLAN_GROUP6]), sizeof (props->group6));
|
|
|
|
|
if (tb[IFLA_VXLAN_LOCAL6])
|
|
|
|
|
memcpy (&props->local6, nla_data (tb[IFLA_VXLAN_LOCAL6]), sizeof (props->local6));
|
|
|
|
|
|
2014-09-25 16:44:09 +02:00
|
|
|
if (tb[IFLA_VXLAN_AGEING])
|
|
|
|
|
props->ageing = nla_get_u32 (tb[IFLA_VXLAN_AGEING]);
|
|
|
|
|
if (tb[IFLA_VXLAN_LIMIT])
|
|
|
|
|
props->limit = nla_get_u32 (tb[IFLA_VXLAN_LIMIT]);
|
|
|
|
|
if (tb[IFLA_VXLAN_TOS])
|
|
|
|
|
props->tos = nla_get_u8 (tb[IFLA_VXLAN_TOS]);
|
|
|
|
|
if (tb[IFLA_VXLAN_TTL])
|
|
|
|
|
props->ttl = nla_get_u8 (tb[IFLA_VXLAN_TTL]);
|
|
|
|
|
|
|
|
|
|
if (tb[IFLA_VXLAN_PORT])
|
|
|
|
|
props->dst_port = nla_get_u16 (tb[IFLA_VXLAN_PORT]);
|
|
|
|
|
|
|
|
|
|
if (tb[IFLA_VXLAN_PORT_RANGE]) {
|
|
|
|
|
range = nla_data (tb[IFLA_VXLAN_PORT_RANGE]);
|
|
|
|
|
props->src_port_min = range->low;
|
|
|
|
|
props->src_port_max = range->high;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (tb[IFLA_VXLAN_LEARNING])
|
|
|
|
|
props->learning = !!nla_get_u8 (tb[IFLA_VXLAN_LEARNING]);
|
|
|
|
|
if (tb[IFLA_VXLAN_PROXY])
|
|
|
|
|
props->proxy = !!nla_get_u8 (tb[IFLA_VXLAN_PROXY]);
|
|
|
|
|
if (tb[IFLA_VXLAN_RSC])
|
|
|
|
|
props->rsc = !!nla_get_u8 (tb[IFLA_VXLAN_RSC]);
|
|
|
|
|
if (tb[IFLA_VXLAN_L2MISS])
|
|
|
|
|
props->l2miss = !!nla_get_u8 (tb[IFLA_VXLAN_L2MISS]);
|
|
|
|
|
if (tb[IFLA_VXLAN_L3MISS])
|
|
|
|
|
props->l3miss = !!nla_get_u8 (tb[IFLA_VXLAN_L3MISS]);
|
2013-06-04 10:31:22 -03:00
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
vxlan_get_properties (NMPlatform *platform, int ifindex, NMPlatformVxlanProperties *props)
|
|
|
|
|
{
|
|
|
|
|
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
err = nm_rtnl_link_parse_info_data (priv->nlh, ifindex,
|
|
|
|
|
vxlan_info_data_parser, props);
|
|
|
|
|
if (err != 0) {
|
|
|
|
|
warning ("(%s) could not read properties: %s",
|
|
|
|
|
link_get_name (platform, ifindex), nl_geterror (err));
|
|
|
|
|
}
|
|
|
|
|
return (err == 0);
|
|
|
|
|
}
|
|
|
|
|
|
2013-05-21 12:49:24 -03:00
|
|
|
static const struct nla_policy gre_info_policy[IFLA_GRE_MAX + 1] = {
|
2014-03-03 10:08:23 -05:00
|
|
|
[IFLA_GRE_LINK] = { .type = NLA_U32 },
|
|
|
|
|
[IFLA_GRE_IFLAGS] = { .type = NLA_U16 },
|
|
|
|
|
[IFLA_GRE_OFLAGS] = { .type = NLA_U16 },
|
|
|
|
|
[IFLA_GRE_IKEY] = { .type = NLA_U32 },
|
|
|
|
|
[IFLA_GRE_OKEY] = { .type = NLA_U32 },
|
|
|
|
|
[IFLA_GRE_LOCAL] = { .type = NLA_U32 },
|
|
|
|
|
[IFLA_GRE_REMOTE] = { .type = NLA_U32 },
|
|
|
|
|
[IFLA_GRE_TTL] = { .type = NLA_U8 },
|
|
|
|
|
[IFLA_GRE_TOS] = { .type = NLA_U8 },
|
|
|
|
|
[IFLA_GRE_PMTUDISC] = { .type = NLA_U8 },
|
2013-05-21 12:49:24 -03:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
gre_info_data_parser (struct nlattr *info_data, gpointer parser_data)
|
|
|
|
|
{
|
|
|
|
|
NMPlatformGreProperties *props = parser_data;
|
|
|
|
|
struct nlattr *tb[IFLA_GRE_MAX + 1];
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
err = nla_parse_nested (tb, IFLA_GRE_MAX, info_data,
|
|
|
|
|
(struct nla_policy *) gre_info_policy);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
|
|
|
|
|
props->parent_ifindex = tb[IFLA_GRE_LINK] ? nla_get_u32 (tb[IFLA_GRE_LINK]) : 0;
|
|
|
|
|
props->input_flags = nla_get_u16 (tb[IFLA_GRE_IFLAGS]);
|
|
|
|
|
props->output_flags = nla_get_u16 (tb[IFLA_GRE_OFLAGS]);
|
|
|
|
|
props->input_key = (props->input_flags & GRE_KEY) ? nla_get_u32 (tb[IFLA_GRE_IKEY]) : 0;
|
|
|
|
|
props->output_key = (props->output_flags & GRE_KEY) ? nla_get_u32 (tb[IFLA_GRE_OKEY]) : 0;
|
|
|
|
|
props->local = nla_get_u32 (tb[IFLA_GRE_LOCAL]);
|
|
|
|
|
props->remote = nla_get_u32 (tb[IFLA_GRE_REMOTE]);
|
|
|
|
|
props->tos = nla_get_u8 (tb[IFLA_GRE_TOS]);
|
|
|
|
|
props->ttl = nla_get_u8 (tb[IFLA_GRE_TTL]);
|
2014-02-20 14:23:06 -05:00
|
|
|
props->path_mtu_discovery = !!nla_get_u8 (tb[IFLA_GRE_PMTUDISC]);
|
2013-05-21 12:49:24 -03:00
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
gre_get_properties (NMPlatform *platform, int ifindex, NMPlatformGreProperties *props)
|
|
|
|
|
{
|
|
|
|
|
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
err = nm_rtnl_link_parse_info_data (priv->nlh, ifindex,
|
|
|
|
|
gre_info_data_parser, props);
|
2014-02-20 14:40:35 -05:00
|
|
|
if (err != 0) {
|
|
|
|
|
warning ("(%s) could not read properties: %s",
|
|
|
|
|
link_get_name (platform, ifindex), nl_geterror (err));
|
|
|
|
|
}
|
2013-05-21 12:49:24 -03:00
|
|
|
return (err == 0);
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-04 14:27:03 +01:00
|
|
|
static WifiData *
|
|
|
|
|
wifi_get_wifi_data (NMPlatform *platform, int ifindex)
|
|
|
|
|
{
|
|
|
|
|
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
|
|
|
|
|
WifiData *wifi_data;
|
|
|
|
|
|
|
|
|
|
wifi_data = g_hash_table_lookup (priv->wifi_data, GINT_TO_POINTER (ifindex));
|
|
|
|
|
if (!wifi_data) {
|
|
|
|
|
NMLinkType type;
|
|
|
|
|
const char *ifname;
|
|
|
|
|
|
|
|
|
|
type = link_get_type (platform, ifindex);
|
|
|
|
|
ifname = link_get_name (platform, ifindex);
|
|
|
|
|
|
|
|
|
|
if (type == NM_LINK_TYPE_WIFI)
|
|
|
|
|
wifi_data = wifi_utils_init (ifname, ifindex, TRUE);
|
|
|
|
|
else if (type == NM_LINK_TYPE_OLPC_MESH) {
|
|
|
|
|
/* The kernel driver now uses nl80211, but we force use of WEXT because
|
|
|
|
|
* the cfg80211 interactions are not quite ready to support access to
|
|
|
|
|
* mesh control through nl80211 just yet.
|
|
|
|
|
*/
|
|
|
|
|
#if HAVE_WEXT
|
|
|
|
|
wifi_data = wifi_wext_init (ifname, ifindex, FALSE);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (wifi_data)
|
|
|
|
|
g_hash_table_insert (priv->wifi_data, GINT_TO_POINTER (ifindex), wifi_data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return wifi_data;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
wifi_get_capabilities (NMPlatform *platform, int ifindex, NMDeviceWifiCapabilities *caps)
|
|
|
|
|
{
|
|
|
|
|
WifiData *wifi_data = wifi_get_wifi_data (platform, ifindex);
|
|
|
|
|
|
|
|
|
|
if (!wifi_data)
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
if (caps)
|
|
|
|
|
*caps = wifi_utils_get_caps (wifi_data);
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
2014-07-07 12:04:14 -04:00
|
|
|
wifi_get_bssid (NMPlatform *platform, int ifindex, guint8 *bssid)
|
2014-02-04 14:27:03 +01:00
|
|
|
{
|
|
|
|
|
WifiData *wifi_data = wifi_get_wifi_data (platform, ifindex);
|
|
|
|
|
|
|
|
|
|
if (!wifi_data)
|
|
|
|
|
return FALSE;
|
|
|
|
|
return wifi_utils_get_bssid (wifi_data, bssid);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static GByteArray *
|
|
|
|
|
wifi_get_ssid (NMPlatform *platform, int ifindex)
|
|
|
|
|
{
|
|
|
|
|
WifiData *wifi_data = wifi_get_wifi_data (platform, ifindex);
|
|
|
|
|
|
|
|
|
|
if (!wifi_data)
|
|
|
|
|
return NULL;
|
|
|
|
|
return wifi_utils_get_ssid (wifi_data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static guint32
|
|
|
|
|
wifi_get_frequency (NMPlatform *platform, int ifindex)
|
|
|
|
|
{
|
|
|
|
|
WifiData *wifi_data = wifi_get_wifi_data (platform, ifindex);
|
|
|
|
|
|
|
|
|
|
if (!wifi_data)
|
|
|
|
|
return 0;
|
|
|
|
|
return wifi_utils_get_freq (wifi_data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
wifi_get_quality (NMPlatform *platform, int ifindex)
|
|
|
|
|
{
|
|
|
|
|
WifiData *wifi_data = wifi_get_wifi_data (platform, ifindex);
|
|
|
|
|
|
|
|
|
|
if (!wifi_data)
|
|
|
|
|
return FALSE;
|
|
|
|
|
return wifi_utils_get_qual (wifi_data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static guint32
|
|
|
|
|
wifi_get_rate (NMPlatform *platform, int ifindex)
|
|
|
|
|
{
|
|
|
|
|
WifiData *wifi_data = wifi_get_wifi_data (platform, ifindex);
|
|
|
|
|
|
|
|
|
|
if (!wifi_data)
|
|
|
|
|
return FALSE;
|
|
|
|
|
return wifi_utils_get_rate (wifi_data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static NM80211Mode
|
|
|
|
|
wifi_get_mode (NMPlatform *platform, int ifindex)
|
|
|
|
|
{
|
|
|
|
|
WifiData *wifi_data = wifi_get_wifi_data (platform, ifindex);
|
|
|
|
|
|
|
|
|
|
if (!wifi_data)
|
|
|
|
|
return NM_802_11_MODE_UNKNOWN;
|
|
|
|
|
|
|
|
|
|
return wifi_utils_get_mode (wifi_data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
wifi_set_mode (NMPlatform *platform, int ifindex, NM80211Mode mode)
|
|
|
|
|
{
|
|
|
|
|
WifiData *wifi_data = wifi_get_wifi_data (platform, ifindex);
|
|
|
|
|
|
|
|
|
|
if (wifi_data)
|
|
|
|
|
wifi_utils_set_mode (wifi_data, mode);
|
|
|
|
|
}
|
|
|
|
|
|
2014-10-23 14:19:59 -04:00
|
|
|
static void
|
|
|
|
|
wifi_set_powersave (NMPlatform *platform, int ifindex, guint32 powersave)
|
|
|
|
|
{
|
|
|
|
|
WifiData *wifi_data = wifi_get_wifi_data (platform, ifindex);
|
|
|
|
|
|
|
|
|
|
if (wifi_data)
|
|
|
|
|
wifi_utils_set_powersave (wifi_data, powersave);
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-04 14:27:03 +01:00
|
|
|
static guint32
|
|
|
|
|
wifi_find_frequency (NMPlatform *platform, int ifindex, const guint32 *freqs)
|
|
|
|
|
{
|
|
|
|
|
WifiData *wifi_data = wifi_get_wifi_data (platform, ifindex);
|
|
|
|
|
|
|
|
|
|
if (!wifi_data)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
return wifi_utils_find_freq (wifi_data, freqs);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
wifi_indicate_addressing_running (NMPlatform *platform, int ifindex, gboolean running)
|
|
|
|
|
{
|
|
|
|
|
WifiData *wifi_data = wifi_get_wifi_data (platform, ifindex);
|
|
|
|
|
|
|
|
|
|
if (wifi_data)
|
|
|
|
|
wifi_utils_indicate_addressing_running (wifi_data, running);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static guint32
|
|
|
|
|
mesh_get_channel (NMPlatform *platform, int ifindex)
|
|
|
|
|
{
|
|
|
|
|
WifiData *wifi_data = wifi_get_wifi_data (platform, ifindex);
|
|
|
|
|
|
|
|
|
|
if (!wifi_data)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
return wifi_utils_get_mesh_channel (wifi_data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
mesh_set_channel (NMPlatform *platform, int ifindex, guint32 channel)
|
|
|
|
|
{
|
|
|
|
|
WifiData *wifi_data = wifi_get_wifi_data (platform, ifindex);
|
|
|
|
|
|
|
|
|
|
if (!wifi_data)
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
return wifi_utils_set_mesh_channel (wifi_data, channel);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
2014-06-26 10:42:11 -04:00
|
|
|
mesh_set_ssid (NMPlatform *platform, int ifindex, const guint8 *ssid, gsize len)
|
2014-02-04 14:27:03 +01:00
|
|
|
{
|
|
|
|
|
WifiData *wifi_data = wifi_get_wifi_data (platform, ifindex);
|
|
|
|
|
|
|
|
|
|
if (!wifi_data)
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
2014-06-26 10:42:11 -04:00
|
|
|
return wifi_utils_set_mesh_ssid (wifi_data, ssid, len);
|
2014-02-04 14:27:03 +01:00
|
|
|
}
|
|
|
|
|
|
2014-02-05 11:56:44 +01:00
|
|
|
static gboolean
|
|
|
|
|
link_get_wake_on_lan (NMPlatform *platform, int ifindex)
|
|
|
|
|
{
|
|
|
|
|
NMLinkType type = link_get_type (platform, ifindex);
|
|
|
|
|
|
|
|
|
|
if (type == NM_LINK_TYPE_ETHERNET) {
|
|
|
|
|
struct ethtool_wolinfo wol;
|
|
|
|
|
|
|
|
|
|
memset (&wol, 0, sizeof (wol));
|
|
|
|
|
wol.cmd = ETHTOOL_GWOL;
|
|
|
|
|
if (!ethtool_get (link_get_name (platform, ifindex), &wol))
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
return wol.wolopts != 0;
|
|
|
|
|
} else if (type == NM_LINK_TYPE_WIFI) {
|
|
|
|
|
WifiData *wifi_data = wifi_get_wifi_data (platform, ifindex);
|
|
|
|
|
|
|
|
|
|
if (!wifi_data)
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
return wifi_utils_get_wowlan (wifi_data);
|
|
|
|
|
} else
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-27 22:23:24 +01:00
|
|
|
/******************************************************************/
|
|
|
|
|
|
2014-03-07 16:25:28 +01:00
|
|
|
static gboolean
|
|
|
|
|
_address_match (struct rtnl_addr *addr, int family, int ifindex)
|
2013-03-27 22:23:24 +01:00
|
|
|
{
|
2014-03-07 16:25:28 +01:00
|
|
|
g_return_val_if_fail (addr, FALSE);
|
2013-03-27 22:23:24 +01:00
|
|
|
|
2014-03-07 16:25:28 +01:00
|
|
|
return rtnl_addr_get_family (addr) == family &&
|
2014-06-24 13:47:25 +02:00
|
|
|
(ifindex == 0 || rtnl_addr_get_ifindex (addr) == ifindex);
|
2013-03-27 22:23:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static GArray *
|
|
|
|
|
ip4_address_get_all (NMPlatform *platform, int ifindex)
|
|
|
|
|
{
|
|
|
|
|
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
|
|
|
|
|
GArray *addresses;
|
|
|
|
|
NMPlatformIP4Address address;
|
|
|
|
|
struct nl_object *object;
|
|
|
|
|
|
2014-03-12 10:30:03 +01:00
|
|
|
addresses = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP4Address));
|
2013-03-27 22:23:24 +01:00
|
|
|
|
|
|
|
|
for (object = nl_cache_get_first (priv->address_cache); object; object = nl_cache_get_next (object)) {
|
2014-03-07 16:25:28 +01:00
|
|
|
if (_address_match ((struct rtnl_addr *) object, AF_INET, ifindex)) {
|
2014-04-03 10:09:47 +02:00
|
|
|
if (init_ip4_address (&address, (struct rtnl_addr *) object))
|
2014-02-17 12:48:05 +01:00
|
|
|
g_array_append_val (addresses, address);
|
2013-03-27 22:23:24 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return addresses;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static GArray *
|
|
|
|
|
ip6_address_get_all (NMPlatform *platform, int ifindex)
|
|
|
|
|
{
|
|
|
|
|
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
|
|
|
|
|
GArray *addresses;
|
|
|
|
|
NMPlatformIP6Address address;
|
|
|
|
|
struct nl_object *object;
|
|
|
|
|
|
2014-03-12 10:30:03 +01:00
|
|
|
addresses = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP6Address));
|
2013-03-27 22:23:24 +01:00
|
|
|
|
|
|
|
|
for (object = nl_cache_get_first (priv->address_cache); object; object = nl_cache_get_next (object)) {
|
2014-03-07 16:25:28 +01:00
|
|
|
if (_address_match ((struct rtnl_addr *) object, AF_INET6, ifindex)) {
|
2014-04-03 10:09:47 +02:00
|
|
|
if (init_ip6_address (&address, (struct rtnl_addr *) object))
|
2014-02-17 12:48:05 +01:00
|
|
|
g_array_append_val (addresses, address);
|
2013-03-27 22:23:24 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return addresses;
|
|
|
|
|
}
|
|
|
|
|
|
2014-04-20 16:54:59 +02:00
|
|
|
#define IPV4LL_NETWORK (htonl (0xA9FE0000L))
|
|
|
|
|
#define IPV4LL_NETMASK (htonl (0xFFFF0000L))
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
ip4_is_link_local (const struct in_addr *src)
|
|
|
|
|
{
|
|
|
|
|
return (src->s_addr & IPV4LL_NETMASK) == IPV4LL_NETWORK;
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-27 22:23:24 +01:00
|
|
|
static struct nl_object *
|
2013-12-02 10:20:26 -05:00
|
|
|
build_rtnl_addr (int family,
|
|
|
|
|
int ifindex,
|
|
|
|
|
gconstpointer addr,
|
|
|
|
|
gconstpointer peer_addr,
|
|
|
|
|
int plen,
|
|
|
|
|
guint32 lifetime,
|
|
|
|
|
guint32 preferred,
|
2014-02-19 16:10:59 -05:00
|
|
|
guint flags,
|
|
|
|
|
const char *label)
|
2013-03-27 22:23:24 +01:00
|
|
|
{
|
2014-04-29 16:00:48 +02:00
|
|
|
auto_nl_addr struct rtnl_addr *rtnladdr = _nm_rtnl_addr_alloc (ifindex);
|
|
|
|
|
struct rtnl_addr *rtnladdr_copy;
|
2013-03-27 22:23:24 +01:00
|
|
|
int addrlen = family == AF_INET ? sizeof (in_addr_t) : sizeof (struct in6_addr);
|
2014-04-29 16:00:48 +02:00
|
|
|
auto_nl_addr struct nl_addr *nladdr = _nm_nl_addr_build (family, addr, addrlen);
|
2013-03-27 22:23:24 +01:00
|
|
|
int nle;
|
|
|
|
|
|
2013-12-03 11:42:28 -06:00
|
|
|
/* IP address */
|
2013-03-27 22:23:24 +01:00
|
|
|
nle = rtnl_addr_set_local (rtnladdr, nladdr);
|
2014-04-29 16:00:48 +02:00
|
|
|
if (nle) {
|
|
|
|
|
error ("build_rtnl_addr(): rtnl_addr_set_local failed with %s (%d)", nl_geterror (nle), nle);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2013-12-02 10:20:26 -05:00
|
|
|
|
2014-04-20 16:54:59 +02:00
|
|
|
/* Tighten scope (IPv4 only) */
|
|
|
|
|
if (family == AF_INET && ip4_is_link_local (addr))
|
|
|
|
|
rtnl_addr_set_scope (rtnladdr, rtnl_str2scope ("link"));
|
|
|
|
|
|
2013-12-03 11:42:28 -06:00
|
|
|
/* IPv4 Broadcast address */
|
|
|
|
|
if (family == AF_INET) {
|
2014-03-07 16:33:40 +01:00
|
|
|
in_addr_t bcast;
|
2013-12-05 09:48:46 +01:00
|
|
|
auto_nl_addr struct nl_addr *bcaddr = NULL;
|
2013-12-03 11:42:28 -06:00
|
|
|
|
2014-03-07 16:33:40 +01:00
|
|
|
bcast = *((in_addr_t *) addr) | ~nm_utils_ip4_prefix_to_netmask (plen);
|
2014-04-29 16:00:48 +02:00
|
|
|
bcaddr = _nm_nl_addr_build (family, &bcast, addrlen);
|
2013-12-03 11:42:28 -06:00
|
|
|
g_assert (bcaddr);
|
|
|
|
|
rtnl_addr_set_broadcast (rtnladdr, bcaddr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Peer/point-to-point address */
|
2013-12-02 10:20:26 -05:00
|
|
|
if (peer_addr) {
|
2014-04-29 16:00:48 +02:00
|
|
|
auto_nl_addr struct nl_addr *nlpeer = _nm_nl_addr_build (family, peer_addr, addrlen);
|
2013-12-02 10:20:26 -05:00
|
|
|
|
|
|
|
|
nle = rtnl_addr_set_peer (rtnladdr, nlpeer);
|
2014-04-29 16:00:48 +02:00
|
|
|
if (nle && nle != -NLE_AF_NOSUPPORT) {
|
|
|
|
|
/* IPv6 doesn't support peer addresses yet */
|
|
|
|
|
error ("build_rtnl_addr(): rtnl_addr_set_peer failed with %s (%d)", nl_geterror (nle), nle);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2013-12-02 10:20:26 -05:00
|
|
|
}
|
|
|
|
|
|
2013-03-27 22:23:24 +01:00
|
|
|
rtnl_addr_set_prefixlen (rtnladdr, plen);
|
2013-06-29 11:30:11 +02:00
|
|
|
if (lifetime) {
|
platform: fix preferred and valid lifetimes for addresses from netlink/kernel
The kernel tells the address lifetimes in the 'struct ifa_cacheinfo'
attribute. This contains two timestamps (cstamp and tstamp) and two
relative lifetimes (ifa_prefered and ifa_valid).
The timestamps are equal to clock_gettime(CLOCK_MONOTONIC) scale in
1/100th of a second (wrapping every 497 days).
The preferred/valid times are re-adjusted everytime when sending the
message and count down as the time goes by. In other words, they are
anchored relatively to the moment of when kernel creates the netlink
message.
As platform is caching the rtnl_addr object, the information of *when* the
lifetimes started counting is not available.
This patch fixes reading these values by hacking the libnl object
when it gets received, so that valid and preferred are instead absolute
expiration timestamps in scale nm_utils_get_monotonic_timestamp_s() --
which NM internally is used for address timestamps.
There are two minor downsides to this hack:
- the valid and preferred properties of a cached rtnl_addr object have
an unexpected meaning, i.e. they are absolute and in a different time
scale.
- later when converting rtnl_addr to NMPlatformIPAddress, the base
timestamp is set to "1", i.e. an NMPlatformIPAddress has no knowledge
of when the address was created or last modified. The timestamp
property of NMPlatformIPAddress is solely there to anchor the relative
timestamps lifetime and preferred. Do not use it for anything
else.
Another reason the timestamp property is meaningless is that
its scale nm_utils_get_monotonic_timestamp_s() starts counting at
process start. So addresses that existed before would have a negative
or zero timestamp, which we avoid. This in turn could be solved by either
allowing negative timestamps or by shifting
nm_utils_get_monotonic_timestamp_*(). Both is viable, but not
necessary (ATM), because the age of an address has no other apparent
use then to anchor the relative timestamps.
Another implication is, that we potentially could get rid of the
timestamp completely, and insteat make preferred and lifetime be
absolute expiries.
This will be fixed properly later, by not caching libnl objects but instead
native NMPlatform objects. For those we have full control over their properties.
https://bugzilla.gnome.org/show_bug.cgi?id=727382
Signed-off-by: Thomas Haller <thaller@redhat.com>
2014-04-02 13:35:21 +02:00
|
|
|
/* note that here we set the relative timestamps (ticking from *now*).
|
|
|
|
|
* Contrary to the rtnl_addr objects from our cache, which have absolute
|
|
|
|
|
* timestamps (see _rtnl_addr_hack_lifetimes_rel_to_abs()).
|
|
|
|
|
*
|
|
|
|
|
* This is correct, because we only use build_rtnl_addr() for
|
|
|
|
|
* add_object(), delete_object() and cache search (ip_address_exists). */
|
2013-06-29 11:30:11 +02:00
|
|
|
rtnl_addr_set_valid_lifetime (rtnladdr, lifetime);
|
|
|
|
|
rtnl_addr_set_preferred_lifetime (rtnladdr, preferred);
|
|
|
|
|
}
|
2014-04-04 16:14:46 +02:00
|
|
|
if (flags) {
|
|
|
|
|
if ((flags & ~0xFF) && !check_support_kernel_extended_ifa_flags (nm_platform_get ())) {
|
|
|
|
|
/* Older kernels don't accept unknown netlink attributes.
|
|
|
|
|
*
|
|
|
|
|
* With commit libnl commit 5206c050504f8676a24854519b9c351470fb7cc6, libnl will only set
|
|
|
|
|
* the extended address flags attribute IFA_FLAGS when necessary (> 8 bit). But it's up to
|
|
|
|
|
* us not to shove those extended flags on to older kernels.
|
|
|
|
|
*
|
|
|
|
|
* Just silently clear them. The kernel should ignore those unknown flags anyway. */
|
|
|
|
|
flags &= 0xFF;
|
|
|
|
|
}
|
2013-10-15 20:44:59 +02:00
|
|
|
rtnl_addr_set_flags (rtnladdr, flags);
|
2014-04-04 16:14:46 +02:00
|
|
|
}
|
2014-02-19 16:10:59 -05:00
|
|
|
if (label && *label)
|
|
|
|
|
rtnl_addr_set_label (rtnladdr, label);
|
2013-03-27 22:23:24 +01:00
|
|
|
|
2014-04-29 16:00:48 +02:00
|
|
|
rtnladdr_copy = rtnladdr;
|
|
|
|
|
rtnladdr = NULL;
|
|
|
|
|
return (struct nl_object *) rtnladdr_copy;
|
2013-03-27 22:23:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
2013-12-02 10:20:26 -05:00
|
|
|
ip4_address_add (NMPlatform *platform,
|
|
|
|
|
int ifindex,
|
|
|
|
|
in_addr_t addr,
|
|
|
|
|
in_addr_t peer_addr,
|
|
|
|
|
int plen,
|
|
|
|
|
guint32 lifetime,
|
2014-02-19 16:10:59 -05:00
|
|
|
guint32 preferred,
|
|
|
|
|
const char *label)
|
2013-03-27 22:23:24 +01:00
|
|
|
{
|
2013-12-02 10:20:26 -05:00
|
|
|
return add_object (platform, build_rtnl_addr (AF_INET, ifindex, &addr,
|
|
|
|
|
peer_addr ? &peer_addr : NULL,
|
2014-02-19 16:10:59 -05:00
|
|
|
plen, lifetime, preferred, 0,
|
|
|
|
|
label));
|
2013-03-27 22:23:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
2013-12-02 10:20:26 -05:00
|
|
|
ip6_address_add (NMPlatform *platform,
|
|
|
|
|
int ifindex,
|
|
|
|
|
struct in6_addr addr,
|
|
|
|
|
struct in6_addr peer_addr,
|
|
|
|
|
int plen,
|
|
|
|
|
guint32 lifetime,
|
|
|
|
|
guint32 preferred,
|
|
|
|
|
guint flags)
|
2013-03-27 22:23:24 +01:00
|
|
|
{
|
2013-12-02 10:20:26 -05:00
|
|
|
return add_object (platform, build_rtnl_addr (AF_INET6, ifindex, &addr,
|
|
|
|
|
IN6_IS_ADDR_UNSPECIFIED (&peer_addr) ? NULL : &peer_addr,
|
2014-02-19 16:10:59 -05:00
|
|
|
plen, lifetime, preferred, flags,
|
|
|
|
|
NULL));
|
2013-03-27 22:23:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
2014-08-12 01:31:00 +02:00
|
|
|
ip4_address_delete (NMPlatform *platform, int ifindex, in_addr_t addr, int plen, in_addr_t peer_address)
|
2013-03-27 22:23:24 +01:00
|
|
|
{
|
2014-08-12 01:31:00 +02:00
|
|
|
return delete_object (platform, build_rtnl_addr (AF_INET, ifindex, &addr, peer_address ? &peer_address : NULL, plen, 0, 0, 0, NULL), TRUE);
|
2013-03-27 22:23:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
ip6_address_delete (NMPlatform *platform, int ifindex, struct in6_addr addr, int plen)
|
|
|
|
|
{
|
platform: fix lookup of routes and deletion of IPv4 routes
When doing a lookup for an libnl route, the cache comparison function
for routes takes into account 'family', 'tos', 'table', 'dst', and 'prio'.
In NetworkManager we don't use all of these properties for a route, so
at several places when doing a cache lookup we don't have all identifying
properties. Usually we only have 'family' and 'dst' ('table' is
implicit 0, because NM does currently not care about any other tables).
The problem is that NM sees routes with different 'tos', 'prio', but it
cannot look them up in the cache. Add a hack to search the cache
fuzzy.
This is similar to the hack for link, where the identifying properties
are 'family' and 'ifindex', but we only have 'ifindex' at hand. However,
contrary to this hack, we coerce the 'family' to AF_UNSPEC for every link cache
operation. This is not viable in this case, because we internally need
the 'tos' field.
We need the 'tos' field because when deleting an IPv4 route, the 'tos' field must
match. See fib_table_delete(). This was already partially fixed by commit
f0daf90298d1bd9cafac7b9c02dc905327e0b85a, but before the lookup to the
cached object would fail for any non-zero 'tos'.
Signed-off-by: Thomas Haller <thaller@redhat.com>
2014-05-28 18:46:12 +02:00
|
|
|
return delete_object (platform, build_rtnl_addr (AF_INET6, ifindex, &addr, NULL, plen, 0, 0, 0, NULL), TRUE);
|
2013-03-27 22:23:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
ip_address_exists (NMPlatform *platform, int family, int ifindex, gconstpointer addr, int plen)
|
|
|
|
|
{
|
2014-02-19 16:10:59 -05:00
|
|
|
auto_nl_object struct nl_object *object = build_rtnl_addr (family, ifindex, addr, NULL, plen, 0, 0, 0, NULL);
|
2013-03-27 22:23:24 +01:00
|
|
|
auto_nl_object struct nl_object *cached_object = nl_cache_search (choose_cache (platform, object), object);
|
|
|
|
|
|
|
|
|
|
return !!cached_object;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
ip4_address_exists (NMPlatform *platform, int ifindex, in_addr_t addr, int plen)
|
|
|
|
|
{
|
|
|
|
|
return ip_address_exists (platform, AF_INET, ifindex, &addr, plen);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
ip6_address_exists (NMPlatform *platform, int ifindex, struct in6_addr addr, int plen)
|
|
|
|
|
{
|
|
|
|
|
return ip_address_exists (platform, AF_INET6, ifindex, &addr, plen);
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-26 00:10:04 +01:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-27 22:23:24 +01:00
|
|
|
/******************************************************************/
|
|
|
|
|
|
2014-03-07 16:44:19 +01:00
|
|
|
static gboolean
|
2014-11-26 00:10:04 +01:00
|
|
|
_route_match (struct rtnl_route *rtnlroute, int family, int ifindex, gboolean include_proto_kernel)
|
2013-03-27 22:23:24 +01:00
|
|
|
{
|
2014-03-07 16:44:19 +01:00
|
|
|
struct rtnl_nexthop *nexthop;
|
2013-03-27 22:23:24 +01:00
|
|
|
|
2014-03-07 16:44:19 +01:00
|
|
|
g_return_val_if_fail (rtnlroute, FALSE);
|
|
|
|
|
|
|
|
|
|
if (rtnl_route_get_type (rtnlroute) != RTN_UNICAST ||
|
|
|
|
|
rtnl_route_get_table (rtnlroute) != RT_TABLE_MAIN ||
|
2014-11-26 00:10:04 +01:00
|
|
|
(!include_proto_kernel && rtnl_route_get_protocol (rtnlroute) == RTPROT_KERNEL) ||
|
2014-03-07 16:44:19 +01:00
|
|
|
rtnl_route_get_family (rtnlroute) != family ||
|
2014-07-11 14:51:12 -05:00
|
|
|
rtnl_route_get_nnexthops (rtnlroute) != 1 ||
|
|
|
|
|
rtnl_route_get_flags (rtnlroute) & RTM_F_CLONED)
|
2014-03-07 16:44:19 +01:00
|
|
|
return FALSE;
|
|
|
|
|
|
2014-06-24 13:47:25 +02:00
|
|
|
if (ifindex == 0)
|
|
|
|
|
return TRUE;
|
|
|
|
|
|
2014-03-07 16:44:19 +01:00
|
|
|
nexthop = rtnl_route_nexthop_n (rtnlroute, 0);
|
|
|
|
|
return rtnl_route_nh_get_ifindex (nexthop) == ifindex;
|
2013-03-27 22:23:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static GArray *
|
2014-10-14 19:02:50 +02:00
|
|
|
ip4_route_get_all (NMPlatform *platform, int ifindex, NMPlatformGetRouteMode mode)
|
2013-03-27 22:23:24 +01:00
|
|
|
{
|
|
|
|
|
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
|
|
|
|
|
GArray *routes;
|
|
|
|
|
NMPlatformIP4Route route;
|
|
|
|
|
struct nl_object *object;
|
|
|
|
|
|
2014-10-14 19:02:50 +02:00
|
|
|
g_return_val_if_fail (NM_IN_SET (mode, NM_PLATFORM_GET_ROUTE_MODE_ALL, NM_PLATFORM_GET_ROUTE_MODE_NO_DEFAULT, NM_PLATFORM_GET_ROUTE_MODE_ONLY_DEFAULT), NULL);
|
|
|
|
|
|
2014-03-12 10:30:03 +01:00
|
|
|
routes = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP4Route));
|
2013-03-27 22:23:24 +01:00
|
|
|
|
|
|
|
|
for (object = nl_cache_get_first (priv->route_cache); object; object = nl_cache_get_next (object)) {
|
2014-11-26 00:10:04 +01:00
|
|
|
if (_route_match ((struct rtnl_route *) object, AF_INET, ifindex, FALSE)) {
|
2014-10-14 19:02:50 +02:00
|
|
|
if (_rtnl_route_is_default ((struct rtnl_route *) object)) {
|
|
|
|
|
if (mode == NM_PLATFORM_GET_ROUTE_MODE_NO_DEFAULT)
|
|
|
|
|
continue;
|
|
|
|
|
} else {
|
|
|
|
|
if (mode == NM_PLATFORM_GET_ROUTE_MODE_ONLY_DEFAULT)
|
|
|
|
|
continue;
|
2013-08-12 17:25:29 +02:00
|
|
|
}
|
2014-10-14 19:02:50 +02:00
|
|
|
if (init_ip4_route (&route, (struct rtnl_route *) object))
|
|
|
|
|
g_array_append_val (routes, route);
|
2013-03-27 22:23:24 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return routes;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static GArray *
|
2014-10-14 19:02:50 +02:00
|
|
|
ip6_route_get_all (NMPlatform *platform, int ifindex, NMPlatformGetRouteMode mode)
|
2013-03-27 22:23:24 +01:00
|
|
|
{
|
|
|
|
|
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
|
|
|
|
|
GArray *routes;
|
|
|
|
|
NMPlatformIP6Route route;
|
|
|
|
|
struct nl_object *object;
|
|
|
|
|
|
2014-10-14 19:02:50 +02:00
|
|
|
g_return_val_if_fail (NM_IN_SET (mode, NM_PLATFORM_GET_ROUTE_MODE_ALL, NM_PLATFORM_GET_ROUTE_MODE_NO_DEFAULT, NM_PLATFORM_GET_ROUTE_MODE_ONLY_DEFAULT), NULL);
|
|
|
|
|
|
2014-03-12 10:30:03 +01:00
|
|
|
routes = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP6Route));
|
2013-03-27 22:23:24 +01:00
|
|
|
|
|
|
|
|
for (object = nl_cache_get_first (priv->route_cache); object; object = nl_cache_get_next (object)) {
|
2014-11-26 00:10:04 +01:00
|
|
|
if (_route_match ((struct rtnl_route *) object, AF_INET6, ifindex, FALSE)) {
|
2014-10-14 19:02:50 +02:00
|
|
|
if (_rtnl_route_is_default ((struct rtnl_route *) object)) {
|
|
|
|
|
if (mode == NM_PLATFORM_GET_ROUTE_MODE_NO_DEFAULT)
|
|
|
|
|
continue;
|
|
|
|
|
} else {
|
|
|
|
|
if (mode == NM_PLATFORM_GET_ROUTE_MODE_ONLY_DEFAULT)
|
|
|
|
|
continue;
|
2013-08-12 17:25:29 +02:00
|
|
|
}
|
2014-10-14 19:02:50 +02:00
|
|
|
if (init_ip6_route (&route, (struct rtnl_route *) object))
|
|
|
|
|
g_array_append_val (routes, route);
|
2013-03-27 22:23:24 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return routes;
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-13 19:47:04 +01:00
|
|
|
static void
|
|
|
|
|
clear_host_address (int family, const void *network, int plen, void *dst)
|
|
|
|
|
{
|
|
|
|
|
g_return_if_fail (plen == (guint8)plen);
|
|
|
|
|
g_return_if_fail (network);
|
|
|
|
|
|
|
|
|
|
switch (family) {
|
|
|
|
|
case AF_INET:
|
|
|
|
|
*((in_addr_t *) dst) = nm_utils_ip4_address_clear_host_address (*((in_addr_t *) network), plen);
|
|
|
|
|
break;
|
|
|
|
|
case AF_INET6:
|
|
|
|
|
nm_utils_ip6_address_clear_host_address ((struct in6_addr *) dst, (const struct in6_addr *) network, plen);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
g_assert_not_reached ();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-27 22:23:24 +01:00
|
|
|
static struct nl_object *
|
2014-10-13 11:52:29 +02:00
|
|
|
build_rtnl_route (int family, int ifindex, NMIPConfigSource source,
|
2014-05-15 15:19:59 -04:00
|
|
|
gconstpointer network, int plen, gconstpointer gateway,
|
2014-11-11 14:19:12 +01:00
|
|
|
gconstpointer pref_src,
|
2014-08-28 17:25:36 +02:00
|
|
|
guint32 metric, guint32 mss)
|
2013-03-27 22:23:24 +01:00
|
|
|
{
|
2014-02-13 19:47:04 +01:00
|
|
|
guint32 network_clean[4];
|
2014-04-29 16:00:48 +02:00
|
|
|
struct rtnl_route *rtnlroute;
|
|
|
|
|
struct rtnl_nexthop *nexthop;
|
2013-03-27 22:23:24 +01:00
|
|
|
int addrlen = (family == AF_INET) ? sizeof (in_addr_t) : sizeof (struct in6_addr);
|
2013-06-19 15:35:53 +02:00
|
|
|
/* Workaround a libnl bug by using zero destination address length for default routes */
|
2014-02-13 19:47:04 +01:00
|
|
|
auto_nl_addr struct nl_addr *dst = NULL;
|
2014-04-29 16:00:48 +02:00
|
|
|
auto_nl_addr struct nl_addr *gw = gateway ? _nm_nl_addr_build (family, gateway, addrlen) : NULL;
|
2014-11-11 14:19:12 +01:00
|
|
|
auto_nl_addr struct nl_addr *pref_src_nl = pref_src ? _nm_nl_addr_build (family, pref_src, addrlen) : NULL;
|
2013-03-27 22:23:24 +01:00
|
|
|
|
2014-02-13 19:47:04 +01:00
|
|
|
/* There seem to be problems adding a route with non-zero host identifier.
|
|
|
|
|
* Adding IPv6 routes is simply ignored, without error message.
|
|
|
|
|
* In the IPv4 case, we got an error. Thus, we have to make sure, that
|
|
|
|
|
* the address is sane. */
|
|
|
|
|
clear_host_address (family, network, plen, network_clean);
|
2014-04-29 16:00:48 +02:00
|
|
|
dst = _nm_nl_addr_build (family, network_clean, plen ? addrlen : 0);
|
2013-03-27 22:23:24 +01:00
|
|
|
nl_addr_set_prefixlen (dst, plen);
|
|
|
|
|
|
2014-04-29 16:00:48 +02:00
|
|
|
rtnlroute = _nm_rtnl_route_alloc ();
|
2013-03-27 22:23:24 +01:00
|
|
|
rtnl_route_set_table (rtnlroute, RT_TABLE_MAIN);
|
|
|
|
|
rtnl_route_set_tos (rtnlroute, 0);
|
|
|
|
|
rtnl_route_set_dst (rtnlroute, dst);
|
|
|
|
|
rtnl_route_set_priority (rtnlroute, metric);
|
2014-02-13 15:11:05 +01:00
|
|
|
rtnl_route_set_family (rtnlroute, family);
|
2014-05-08 14:50:02 -04:00
|
|
|
rtnl_route_set_protocol (rtnlroute, source_to_rtprot (source));
|
2013-03-27 22:23:24 +01:00
|
|
|
|
2014-04-29 16:00:48 +02:00
|
|
|
nexthop = _nm_rtnl_route_nh_alloc ();
|
2013-03-27 22:23:24 +01:00
|
|
|
rtnl_route_nh_set_ifindex (nexthop, ifindex);
|
2013-05-02 08:06:08 +02:00
|
|
|
if (gw && !nl_addr_iszero (gw))
|
2013-03-27 22:23:24 +01:00
|
|
|
rtnl_route_nh_set_gateway (nexthop, gw);
|
2014-11-11 14:19:12 +01:00
|
|
|
if (pref_src_nl)
|
|
|
|
|
rtnl_route_set_pref_src (rtnlroute, pref_src_nl);
|
2013-03-27 22:23:24 +01:00
|
|
|
rtnl_route_add_nexthop (rtnlroute, nexthop);
|
|
|
|
|
|
|
|
|
|
if (mss > 0)
|
|
|
|
|
rtnl_route_set_metric (rtnlroute, RTAX_ADVMSS, mss);
|
|
|
|
|
|
|
|
|
|
return (struct nl_object *) rtnlroute;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
2014-10-13 11:52:29 +02:00
|
|
|
ip4_route_add (NMPlatform *platform, int ifindex, NMIPConfigSource source,
|
2014-05-15 15:19:59 -04:00
|
|
|
in_addr_t network, int plen, in_addr_t gateway,
|
2014-11-11 14:19:12 +01:00
|
|
|
guint32 pref_src, guint32 metric, guint32 mss)
|
2013-03-27 22:23:24 +01:00
|
|
|
{
|
2014-11-11 14:19:12 +01:00
|
|
|
return add_object (platform, build_rtnl_route (AF_INET, ifindex, source, &network, plen, &gateway, pref_src ? &pref_src : NULL, metric, mss));
|
2013-03-27 22:23:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
2014-10-13 11:52:29 +02:00
|
|
|
ip6_route_add (NMPlatform *platform, int ifindex, NMIPConfigSource source,
|
2014-05-15 15:19:59 -04:00
|
|
|
struct in6_addr network, int plen, struct in6_addr gateway,
|
2014-08-28 17:25:36 +02:00
|
|
|
guint32 metric, guint32 mss)
|
2013-03-27 22:23:24 +01:00
|
|
|
{
|
2014-12-22 17:06:13 +01:00
|
|
|
metric = nm_utils_ip6_route_metric_normalize (metric);
|
|
|
|
|
|
2014-11-11 14:19:12 +01:00
|
|
|
return add_object (platform, build_rtnl_route (AF_INET6, ifindex, source, &network, plen, &gateway, NULL, metric, mss));
|
2013-03-27 22:23:24 +01:00
|
|
|
}
|
|
|
|
|
|
platform: fix lookup of routes and deletion of IPv4 routes
When doing a lookup for an libnl route, the cache comparison function
for routes takes into account 'family', 'tos', 'table', 'dst', and 'prio'.
In NetworkManager we don't use all of these properties for a route, so
at several places when doing a cache lookup we don't have all identifying
properties. Usually we only have 'family' and 'dst' ('table' is
implicit 0, because NM does currently not care about any other tables).
The problem is that NM sees routes with different 'tos', 'prio', but it
cannot look them up in the cache. Add a hack to search the cache
fuzzy.
This is similar to the hack for link, where the identifying properties
are 'family' and 'ifindex', but we only have 'ifindex' at hand. However,
contrary to this hack, we coerce the 'family' to AF_UNSPEC for every link cache
operation. This is not viable in this case, because we internally need
the 'tos' field.
We need the 'tos' field because when deleting an IPv4 route, the 'tos' field must
match. See fib_table_delete(). This was already partially fixed by commit
f0daf90298d1bd9cafac7b9c02dc905327e0b85a, but before the lookup to the
cached object would fail for any non-zero 'tos'.
Signed-off-by: Thomas Haller <thaller@redhat.com>
2014-05-28 18:46:12 +02:00
|
|
|
static struct rtnl_route *
|
2014-08-28 17:25:36 +02:00
|
|
|
route_search_cache (struct nl_cache *cache, int family, int ifindex, const void *network, int plen, guint32 metric)
|
platform: fix lookup of routes and deletion of IPv4 routes
When doing a lookup for an libnl route, the cache comparison function
for routes takes into account 'family', 'tos', 'table', 'dst', and 'prio'.
In NetworkManager we don't use all of these properties for a route, so
at several places when doing a cache lookup we don't have all identifying
properties. Usually we only have 'family' and 'dst' ('table' is
implicit 0, because NM does currently not care about any other tables).
The problem is that NM sees routes with different 'tos', 'prio', but it
cannot look them up in the cache. Add a hack to search the cache
fuzzy.
This is similar to the hack for link, where the identifying properties
are 'family' and 'ifindex', but we only have 'ifindex' at hand. However,
contrary to this hack, we coerce the 'family' to AF_UNSPEC for every link cache
operation. This is not viable in this case, because we internally need
the 'tos' field.
We need the 'tos' field because when deleting an IPv4 route, the 'tos' field must
match. See fib_table_delete(). This was already partially fixed by commit
f0daf90298d1bd9cafac7b9c02dc905327e0b85a, but before the lookup to the
cached object would fail for any non-zero 'tos'.
Signed-off-by: Thomas Haller <thaller@redhat.com>
2014-05-28 18:46:12 +02:00
|
|
|
{
|
|
|
|
|
guint32 network_clean[4], dst_clean[4];
|
|
|
|
|
struct nl_object *object;
|
|
|
|
|
|
|
|
|
|
clear_host_address (family, network, plen, network_clean);
|
|
|
|
|
|
|
|
|
|
for (object = nl_cache_get_first (cache); object; object = nl_cache_get_next (object)) {
|
|
|
|
|
struct nl_addr *dst;
|
|
|
|
|
struct rtnl_route *rtnlroute = (struct rtnl_route *) object;
|
|
|
|
|
|
2014-11-26 00:10:04 +01:00
|
|
|
if (!_route_match (rtnlroute, family, ifindex, FALSE))
|
platform: fix lookup of routes and deletion of IPv4 routes
When doing a lookup for an libnl route, the cache comparison function
for routes takes into account 'family', 'tos', 'table', 'dst', and 'prio'.
In NetworkManager we don't use all of these properties for a route, so
at several places when doing a cache lookup we don't have all identifying
properties. Usually we only have 'family' and 'dst' ('table' is
implicit 0, because NM does currently not care about any other tables).
The problem is that NM sees routes with different 'tos', 'prio', but it
cannot look them up in the cache. Add a hack to search the cache
fuzzy.
This is similar to the hack for link, where the identifying properties
are 'family' and 'ifindex', but we only have 'ifindex' at hand. However,
contrary to this hack, we coerce the 'family' to AF_UNSPEC for every link cache
operation. This is not viable in this case, because we internally need
the 'tos' field.
We need the 'tos' field because when deleting an IPv4 route, the 'tos' field must
match. See fib_table_delete(). This was already partially fixed by commit
f0daf90298d1bd9cafac7b9c02dc905327e0b85a, but before the lookup to the
cached object would fail for any non-zero 'tos'.
Signed-off-by: Thomas Haller <thaller@redhat.com>
2014-05-28 18:46:12 +02:00
|
|
|
continue;
|
|
|
|
|
|
2014-12-22 18:01:12 +01:00
|
|
|
if (metric != rtnl_route_get_priority (rtnlroute))
|
platform: fix lookup of routes and deletion of IPv4 routes
When doing a lookup for an libnl route, the cache comparison function
for routes takes into account 'family', 'tos', 'table', 'dst', and 'prio'.
In NetworkManager we don't use all of these properties for a route, so
at several places when doing a cache lookup we don't have all identifying
properties. Usually we only have 'family' and 'dst' ('table' is
implicit 0, because NM does currently not care about any other tables).
The problem is that NM sees routes with different 'tos', 'prio', but it
cannot look them up in the cache. Add a hack to search the cache
fuzzy.
This is similar to the hack for link, where the identifying properties
are 'family' and 'ifindex', but we only have 'ifindex' at hand. However,
contrary to this hack, we coerce the 'family' to AF_UNSPEC for every link cache
operation. This is not viable in this case, because we internally need
the 'tos' field.
We need the 'tos' field because when deleting an IPv4 route, the 'tos' field must
match. See fib_table_delete(). This was already partially fixed by commit
f0daf90298d1bd9cafac7b9c02dc905327e0b85a, but before the lookup to the
cached object would fail for any non-zero 'tos'.
Signed-off-by: Thomas Haller <thaller@redhat.com>
2014-05-28 18:46:12 +02:00
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
dst = rtnl_route_get_dst (rtnlroute);
|
|
|
|
|
if ( !dst
|
|
|
|
|
|| nl_addr_get_family (dst) != family
|
|
|
|
|
|| nl_addr_get_prefixlen (dst) != plen)
|
|
|
|
|
continue;
|
|
|
|
|
|
2015-01-14 13:57:50 -06:00
|
|
|
/* plen = 0 means all host bits, so all bits should be cleared.
|
|
|
|
|
* Likewise if the binary address is not present or all zeros.
|
|
|
|
|
*/
|
|
|
|
|
if (plen == 0 || nl_addr_iszero (dst))
|
|
|
|
|
memset (dst_clean, 0, sizeof (dst_clean));
|
|
|
|
|
else
|
|
|
|
|
clear_host_address (family, nl_addr_get_binary_addr (dst), plen, dst_clean);
|
|
|
|
|
|
platform: fix lookup of routes and deletion of IPv4 routes
When doing a lookup for an libnl route, the cache comparison function
for routes takes into account 'family', 'tos', 'table', 'dst', and 'prio'.
In NetworkManager we don't use all of these properties for a route, so
at several places when doing a cache lookup we don't have all identifying
properties. Usually we only have 'family' and 'dst' ('table' is
implicit 0, because NM does currently not care about any other tables).
The problem is that NM sees routes with different 'tos', 'prio', but it
cannot look them up in the cache. Add a hack to search the cache
fuzzy.
This is similar to the hack for link, where the identifying properties
are 'family' and 'ifindex', but we only have 'ifindex' at hand. However,
contrary to this hack, we coerce the 'family' to AF_UNSPEC for every link cache
operation. This is not viable in this case, because we internally need
the 'tos' field.
We need the 'tos' field because when deleting an IPv4 route, the 'tos' field must
match. See fib_table_delete(). This was already partially fixed by commit
f0daf90298d1bd9cafac7b9c02dc905327e0b85a, but before the lookup to the
cached object would fail for any non-zero 'tos'.
Signed-off-by: Thomas Haller <thaller@redhat.com>
2014-05-28 18:46:12 +02:00
|
|
|
if (memcmp (dst_clean, network_clean,
|
|
|
|
|
family == AF_INET ? sizeof (guint32) : sizeof (struct in6_addr)) != 0)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
rtnl_route_get (rtnlroute);
|
|
|
|
|
return rtnlroute;
|
|
|
|
|
}
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
2014-12-22 18:05:17 +01:00
|
|
|
refresh_route (NMPlatform *platform, int family, int ifindex, const void *network, int plen, guint32 metric)
|
platform: fix lookup of routes and deletion of IPv4 routes
When doing a lookup for an libnl route, the cache comparison function
for routes takes into account 'family', 'tos', 'table', 'dst', and 'prio'.
In NetworkManager we don't use all of these properties for a route, so
at several places when doing a cache lookup we don't have all identifying
properties. Usually we only have 'family' and 'dst' ('table' is
implicit 0, because NM does currently not care about any other tables).
The problem is that NM sees routes with different 'tos', 'prio', but it
cannot look them up in the cache. Add a hack to search the cache
fuzzy.
This is similar to the hack for link, where the identifying properties
are 'family' and 'ifindex', but we only have 'ifindex' at hand. However,
contrary to this hack, we coerce the 'family' to AF_UNSPEC for every link cache
operation. This is not viable in this case, because we internally need
the 'tos' field.
We need the 'tos' field because when deleting an IPv4 route, the 'tos' field must
match. See fib_table_delete(). This was already partially fixed by commit
f0daf90298d1bd9cafac7b9c02dc905327e0b85a, but before the lookup to the
cached object would fail for any non-zero 'tos'.
Signed-off-by: Thomas Haller <thaller@redhat.com>
2014-05-28 18:46:12 +02:00
|
|
|
{
|
|
|
|
|
struct nl_cache *cache;
|
|
|
|
|
auto_nl_object struct rtnl_route *cached_object = NULL;
|
|
|
|
|
|
|
|
|
|
cache = choose_cache_by_type (platform, family == AF_INET ? OBJECT_TYPE_IP4_ROUTE : OBJECT_TYPE_IP6_ROUTE);
|
|
|
|
|
cached_object = route_search_cache (cache, family, ifindex, network, plen, metric);
|
|
|
|
|
|
|
|
|
|
if (cached_object)
|
|
|
|
|
return refresh_object (platform, (struct nl_object *) cached_object, TRUE, NM_PLATFORM_REASON_INTERNAL);
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-27 22:23:24 +01:00
|
|
|
static gboolean
|
2014-08-28 17:25:36 +02:00
|
|
|
ip4_route_delete (NMPlatform *platform, int ifindex, in_addr_t network, int plen, guint32 metric)
|
2013-03-27 22:23:24 +01:00
|
|
|
{
|
2013-05-02 08:06:08 +02:00
|
|
|
in_addr_t gateway = 0;
|
2014-05-27 21:03:41 +02:00
|
|
|
struct rtnl_route *cached_object;
|
2014-11-11 14:19:12 +01:00
|
|
|
struct nl_object *route = build_rtnl_route (AF_INET, ifindex, NM_IP_CONFIG_SOURCE_UNKNOWN, &network, plen, &gateway, NULL, metric, 0);
|
2014-02-23 14:57:50 +01:00
|
|
|
uint8_t scope = RT_SCOPE_NOWHERE;
|
platform: fix lookup of routes and deletion of IPv4 routes
When doing a lookup for an libnl route, the cache comparison function
for routes takes into account 'family', 'tos', 'table', 'dst', and 'prio'.
In NetworkManager we don't use all of these properties for a route, so
at several places when doing a cache lookup we don't have all identifying
properties. Usually we only have 'family' and 'dst' ('table' is
implicit 0, because NM does currently not care about any other tables).
The problem is that NM sees routes with different 'tos', 'prio', but it
cannot look them up in the cache. Add a hack to search the cache
fuzzy.
This is similar to the hack for link, where the identifying properties
are 'family' and 'ifindex', but we only have 'ifindex' at hand. However,
contrary to this hack, we coerce the 'family' to AF_UNSPEC for every link cache
operation. This is not viable in this case, because we internally need
the 'tos' field.
We need the 'tos' field because when deleting an IPv4 route, the 'tos' field must
match. See fib_table_delete(). This was already partially fixed by commit
f0daf90298d1bd9cafac7b9c02dc905327e0b85a, but before the lookup to the
cached object would fail for any non-zero 'tos'.
Signed-off-by: Thomas Haller <thaller@redhat.com>
2014-05-28 18:46:12 +02:00
|
|
|
struct nl_cache *cache;
|
2014-02-13 15:11:05 +01:00
|
|
|
|
|
|
|
|
g_return_val_if_fail (route, FALSE);
|
|
|
|
|
|
platform: fix lookup of routes and deletion of IPv4 routes
When doing a lookup for an libnl route, the cache comparison function
for routes takes into account 'family', 'tos', 'table', 'dst', and 'prio'.
In NetworkManager we don't use all of these properties for a route, so
at several places when doing a cache lookup we don't have all identifying
properties. Usually we only have 'family' and 'dst' ('table' is
implicit 0, because NM does currently not care about any other tables).
The problem is that NM sees routes with different 'tos', 'prio', but it
cannot look them up in the cache. Add a hack to search the cache
fuzzy.
This is similar to the hack for link, where the identifying properties
are 'family' and 'ifindex', but we only have 'ifindex' at hand. However,
contrary to this hack, we coerce the 'family' to AF_UNSPEC for every link cache
operation. This is not viable in this case, because we internally need
the 'tos' field.
We need the 'tos' field because when deleting an IPv4 route, the 'tos' field must
match. See fib_table_delete(). This was already partially fixed by commit
f0daf90298d1bd9cafac7b9c02dc905327e0b85a, but before the lookup to the
cached object would fail for any non-zero 'tos'.
Signed-off-by: Thomas Haller <thaller@redhat.com>
2014-05-28 18:46:12 +02:00
|
|
|
cache = choose_cache_by_type (platform, OBJECT_TYPE_IP4_ROUTE);
|
|
|
|
|
|
2014-12-22 17:59:16 +01:00
|
|
|
if (metric == 0) {
|
|
|
|
|
/* Deleting an IPv4 route with metric 0 does not only delete an exectly matching route.
|
|
|
|
|
* If no route with metric 0 exists, it might delete another route to the same destination.
|
|
|
|
|
* For nm_platform_ip4_route_delete() we don't want this semantic.
|
|
|
|
|
*
|
|
|
|
|
* Instead, re-fetch the route from kernel, and if that fails, there is nothing to do.
|
|
|
|
|
* On success, there is still a race that we might end up deleting the wrong route. */
|
2015-01-11 17:42:46 +01:00
|
|
|
if (!refresh_object (platform, (struct nl_object *) route, FALSE, _NM_PLATFORM_REASON_CACHE_CHECK_INTERNAL)) {
|
2014-12-22 17:59:16 +01:00
|
|
|
rtnl_route_put ((struct rtnl_route *) route);
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-05-27 21:03:41 +02:00
|
|
|
/* when deleting an IPv4 route, several fields of the provided route must match.
|
|
|
|
|
* Lookup in the cache so that we hopefully get the right values. */
|
platform: fix lookup of routes and deletion of IPv4 routes
When doing a lookup for an libnl route, the cache comparison function
for routes takes into account 'family', 'tos', 'table', 'dst', and 'prio'.
In NetworkManager we don't use all of these properties for a route, so
at several places when doing a cache lookup we don't have all identifying
properties. Usually we only have 'family' and 'dst' ('table' is
implicit 0, because NM does currently not care about any other tables).
The problem is that NM sees routes with different 'tos', 'prio', but it
cannot look them up in the cache. Add a hack to search the cache
fuzzy.
This is similar to the hack for link, where the identifying properties
are 'family' and 'ifindex', but we only have 'ifindex' at hand. However,
contrary to this hack, we coerce the 'family' to AF_UNSPEC for every link cache
operation. This is not viable in this case, because we internally need
the 'tos' field.
We need the 'tos' field because when deleting an IPv4 route, the 'tos' field must
match. See fib_table_delete(). This was already partially fixed by commit
f0daf90298d1bd9cafac7b9c02dc905327e0b85a, but before the lookup to the
cached object would fail for any non-zero 'tos'.
Signed-off-by: Thomas Haller <thaller@redhat.com>
2014-05-28 18:46:12 +02:00
|
|
|
cached_object = (struct rtnl_route *) nl_cache_search (cache, route);
|
|
|
|
|
if (!cached_object)
|
|
|
|
|
cached_object = route_search_cache (cache, AF_INET, ifindex, &network, plen, metric);
|
2014-05-27 21:03:41 +02:00
|
|
|
|
2014-02-23 14:57:50 +01:00
|
|
|
if (!_nl_has_capability (1 /* NL_CAPABILITY_ROUTE_BUILD_MSG_SET_SCOPE */)) {
|
|
|
|
|
/* When searching for a matching IPv4 route to delete, the kernel
|
|
|
|
|
* searches for a matching scope, unless the RTM_DELROUTE message
|
|
|
|
|
* specifies RT_SCOPE_NOWHERE (see fib_table_delete()).
|
|
|
|
|
*
|
|
|
|
|
* However, if we set the scope of @rtnlroute to RT_SCOPE_NOWHERE (or
|
|
|
|
|
* leave it unset), rtnl_route_build_msg() will reset the scope to
|
|
|
|
|
* rtnl_route_guess_scope() -- which probably guesses wrong.
|
|
|
|
|
*
|
|
|
|
|
* As a workaround, we look at the cached route and use that scope.
|
|
|
|
|
*
|
|
|
|
|
* Newer versions of libnl, no longer reset the scope if explicitly set to RT_SCOPE_NOWHERE.
|
|
|
|
|
* So, this workaround is only needed unless we have NL_CAPABILITY_ROUTE_BUILD_MSG_SET_SCOPE.
|
|
|
|
|
**/
|
|
|
|
|
|
2014-05-27 21:03:41 +02:00
|
|
|
if (cached_object)
|
|
|
|
|
scope = rtnl_route_get_scope (cached_object);
|
2014-02-23 14:57:50 +01:00
|
|
|
|
|
|
|
|
if (scope == RT_SCOPE_NOWHERE) {
|
|
|
|
|
/* If we would set the scope to RT_SCOPE_NOWHERE, libnl would guess the scope.
|
2014-05-27 21:03:41 +02:00
|
|
|
* But probably it will guess 'link' because we set the next hop of the route
|
|
|
|
|
* to zero (0.0.0.0). A better guess is 'global'. */
|
2014-02-23 14:57:50 +01:00
|
|
|
scope = RT_SCOPE_UNIVERSE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
rtnl_route_set_scope ((struct rtnl_route *) route, scope);
|
2013-05-02 08:06:08 +02:00
|
|
|
|
2014-05-27 21:03:41 +02:00
|
|
|
if (cached_object)
|
|
|
|
|
rtnl_route_set_tos ((struct rtnl_route *) route, rtnl_route_get_tos (cached_object));
|
|
|
|
|
|
|
|
|
|
/* The following fields are also relevant when comparing the route, but the default values
|
|
|
|
|
* are already as we want them:
|
|
|
|
|
*
|
|
|
|
|
* type: RTN_UNICAST (setting to zero would ignore the type, but we only want to delete RTN_UNICAST)
|
|
|
|
|
* pref_src: NULL
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
rtnl_route_put (cached_object);
|
platform: fix lookup of routes and deletion of IPv4 routes
When doing a lookup for an libnl route, the cache comparison function
for routes takes into account 'family', 'tos', 'table', 'dst', and 'prio'.
In NetworkManager we don't use all of these properties for a route, so
at several places when doing a cache lookup we don't have all identifying
properties. Usually we only have 'family' and 'dst' ('table' is
implicit 0, because NM does currently not care about any other tables).
The problem is that NM sees routes with different 'tos', 'prio', but it
cannot look them up in the cache. Add a hack to search the cache
fuzzy.
This is similar to the hack for link, where the identifying properties
are 'family' and 'ifindex', but we only have 'ifindex' at hand. However,
contrary to this hack, we coerce the 'family' to AF_UNSPEC for every link cache
operation. This is not viable in this case, because we internally need
the 'tos' field.
We need the 'tos' field because when deleting an IPv4 route, the 'tos' field must
match. See fib_table_delete(). This was already partially fixed by commit
f0daf90298d1bd9cafac7b9c02dc905327e0b85a, but before the lookup to the
cached object would fail for any non-zero 'tos'.
Signed-off-by: Thomas Haller <thaller@redhat.com>
2014-05-28 18:46:12 +02:00
|
|
|
return delete_object (platform, route, FALSE) && refresh_route (platform, AF_INET, ifindex, &network, plen, metric);
|
2013-03-27 22:23:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
2014-08-28 17:25:36 +02:00
|
|
|
ip6_route_delete (NMPlatform *platform, int ifindex, struct in6_addr network, int plen, guint32 metric)
|
2013-03-27 22:23:24 +01:00
|
|
|
{
|
2014-02-13 19:47:04 +01:00
|
|
|
struct in6_addr gateway = IN6ADDR_ANY_INIT;
|
2013-05-02 08:06:08 +02:00
|
|
|
|
2014-12-22 17:06:13 +01:00
|
|
|
metric = nm_utils_ip6_route_metric_normalize (metric);
|
|
|
|
|
|
2014-11-11 14:19:12 +01:00
|
|
|
return delete_object (platform, build_rtnl_route (AF_INET6, ifindex, NM_IP_CONFIG_SOURCE_UNKNOWN ,&network, plen, &gateway, NULL, metric, 0), FALSE) &&
|
platform: fix lookup of routes and deletion of IPv4 routes
When doing a lookup for an libnl route, the cache comparison function
for routes takes into account 'family', 'tos', 'table', 'dst', and 'prio'.
In NetworkManager we don't use all of these properties for a route, so
at several places when doing a cache lookup we don't have all identifying
properties. Usually we only have 'family' and 'dst' ('table' is
implicit 0, because NM does currently not care about any other tables).
The problem is that NM sees routes with different 'tos', 'prio', but it
cannot look them up in the cache. Add a hack to search the cache
fuzzy.
This is similar to the hack for link, where the identifying properties
are 'family' and 'ifindex', but we only have 'ifindex' at hand. However,
contrary to this hack, we coerce the 'family' to AF_UNSPEC for every link cache
operation. This is not viable in this case, because we internally need
the 'tos' field.
We need the 'tos' field because when deleting an IPv4 route, the 'tos' field must
match. See fib_table_delete(). This was already partially fixed by commit
f0daf90298d1bd9cafac7b9c02dc905327e0b85a, but before the lookup to the
cached object would fail for any non-zero 'tos'.
Signed-off-by: Thomas Haller <thaller@redhat.com>
2014-05-28 18:46:12 +02:00
|
|
|
refresh_route (platform, AF_INET6, ifindex, &network, plen, metric);
|
2013-03-27 22:23:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
2014-08-28 17:25:36 +02:00
|
|
|
ip_route_exists (NMPlatform *platform, int family, int ifindex, gpointer network, int plen, guint32 metric)
|
2013-03-27 22:23:24 +01:00
|
|
|
{
|
2014-05-08 14:50:02 -04:00
|
|
|
auto_nl_object struct nl_object *object = build_rtnl_route (family, ifindex,
|
2014-10-13 11:52:29 +02:00
|
|
|
NM_IP_CONFIG_SOURCE_UNKNOWN,
|
2014-11-11 14:19:12 +01:00
|
|
|
network, plen, NULL, NULL, metric, 0);
|
platform: fix lookup of routes and deletion of IPv4 routes
When doing a lookup for an libnl route, the cache comparison function
for routes takes into account 'family', 'tos', 'table', 'dst', and 'prio'.
In NetworkManager we don't use all of these properties for a route, so
at several places when doing a cache lookup we don't have all identifying
properties. Usually we only have 'family' and 'dst' ('table' is
implicit 0, because NM does currently not care about any other tables).
The problem is that NM sees routes with different 'tos', 'prio', but it
cannot look them up in the cache. Add a hack to search the cache
fuzzy.
This is similar to the hack for link, where the identifying properties
are 'family' and 'ifindex', but we only have 'ifindex' at hand. However,
contrary to this hack, we coerce the 'family' to AF_UNSPEC for every link cache
operation. This is not viable in this case, because we internally need
the 'tos' field.
We need the 'tos' field because when deleting an IPv4 route, the 'tos' field must
match. See fib_table_delete(). This was already partially fixed by commit
f0daf90298d1bd9cafac7b9c02dc905327e0b85a, but before the lookup to the
cached object would fail for any non-zero 'tos'.
Signed-off-by: Thomas Haller <thaller@redhat.com>
2014-05-28 18:46:12 +02:00
|
|
|
struct nl_cache *cache = choose_cache (platform, object);
|
|
|
|
|
auto_nl_object struct nl_object *cached_object = nl_cache_search (cache, object);
|
2013-03-27 22:23:24 +01:00
|
|
|
|
platform: fix lookup of routes and deletion of IPv4 routes
When doing a lookup for an libnl route, the cache comparison function
for routes takes into account 'family', 'tos', 'table', 'dst', and 'prio'.
In NetworkManager we don't use all of these properties for a route, so
at several places when doing a cache lookup we don't have all identifying
properties. Usually we only have 'family' and 'dst' ('table' is
implicit 0, because NM does currently not care about any other tables).
The problem is that NM sees routes with different 'tos', 'prio', but it
cannot look them up in the cache. Add a hack to search the cache
fuzzy.
This is similar to the hack for link, where the identifying properties
are 'family' and 'ifindex', but we only have 'ifindex' at hand. However,
contrary to this hack, we coerce the 'family' to AF_UNSPEC for every link cache
operation. This is not viable in this case, because we internally need
the 'tos' field.
We need the 'tos' field because when deleting an IPv4 route, the 'tos' field must
match. See fib_table_delete(). This was already partially fixed by commit
f0daf90298d1bd9cafac7b9c02dc905327e0b85a, but before the lookup to the
cached object would fail for any non-zero 'tos'.
Signed-off-by: Thomas Haller <thaller@redhat.com>
2014-05-28 18:46:12 +02:00
|
|
|
if (!cached_object)
|
|
|
|
|
cached_object = (struct nl_object *) route_search_cache (cache, family, ifindex, network, plen, metric);
|
2013-03-27 22:23:24 +01:00
|
|
|
return !!cached_object;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
2014-08-28 17:25:36 +02:00
|
|
|
ip4_route_exists (NMPlatform *platform, int ifindex, in_addr_t network, int plen, guint32 metric)
|
2013-03-27 22:23:24 +01:00
|
|
|
{
|
2013-05-02 08:06:08 +02:00
|
|
|
return ip_route_exists (platform, AF_INET, ifindex, &network, plen, metric);
|
2013-03-27 22:23:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
2014-08-28 17:25:36 +02:00
|
|
|
ip6_route_exists (NMPlatform *platform, int ifindex, struct in6_addr network, int plen, guint32 metric)
|
2013-03-27 22:23:24 +01:00
|
|
|
{
|
2014-12-22 17:06:13 +01:00
|
|
|
metric = nm_utils_ip6_route_metric_normalize (metric);
|
|
|
|
|
|
2013-05-02 08:06:08 +02:00
|
|
|
return ip_route_exists (platform, AF_INET6, ifindex, &network, plen, metric);
|
2013-03-27 22:23:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
|
|
|
|
2014-12-04 16:22:39 +01:00
|
|
|
/* Initialize the link cache while ensuring all links are of AF_UNSPEC,
|
|
|
|
|
* family (even though the kernel might set AF_BRIDGE for bridges).
|
|
|
|
|
* See also: _nl_link_family_unset() */
|
|
|
|
|
static void
|
|
|
|
|
init_link_cache (NMPlatform *platform)
|
|
|
|
|
{
|
|
|
|
|
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
|
|
|
|
|
struct nl_object *object = NULL;
|
|
|
|
|
|
|
|
|
|
rtnl_link_alloc_cache (priv->nlh, AF_UNSPEC, &priv->link_cache);
|
|
|
|
|
|
|
|
|
|
do {
|
|
|
|
|
for (object = nl_cache_get_first (priv->link_cache); object; object = nl_cache_get_next (object)) {
|
|
|
|
|
if (rtnl_link_get_family ((struct rtnl_link *)object) != AF_UNSPEC)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (object) {
|
|
|
|
|
/* A non-AF_UNSPEC object encoutnered */
|
|
|
|
|
struct nl_object *existing;
|
|
|
|
|
|
|
|
|
|
nl_object_get (object);
|
|
|
|
|
nl_cache_remove (object);
|
|
|
|
|
rtnl_link_set_family ((struct rtnl_link *)object, AF_UNSPEC);
|
|
|
|
|
existing = nl_cache_search (priv->link_cache, object);
|
|
|
|
|
if (existing)
|
|
|
|
|
nl_object_put (existing);
|
|
|
|
|
else
|
|
|
|
|
nl_cache_add (priv->link_cache, object);
|
|
|
|
|
nl_object_put (object);
|
|
|
|
|
}
|
|
|
|
|
} while (object);
|
|
|
|
|
}
|
|
|
|
|
|
2014-12-08 18:08:42 +01:00
|
|
|
/* Calls announce_object with appropriate arguments for all objects
|
|
|
|
|
* which are not coherent between old and new caches and deallocates
|
|
|
|
|
* the old cache. */
|
|
|
|
|
static void
|
|
|
|
|
cache_announce_changes (NMPlatform *platform, struct nl_cache *new, struct nl_cache *old)
|
|
|
|
|
{
|
|
|
|
|
struct nl_object *object;
|
|
|
|
|
|
|
|
|
|
if (!old)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
for (object = nl_cache_get_first (new); object; object = nl_cache_get_next (object)) {
|
|
|
|
|
struct nl_object *cached_object = nm_nl_cache_search (old, object);
|
|
|
|
|
|
|
|
|
|
if (cached_object) {
|
|
|
|
|
ObjectType type = object_type_from_nl_object (object);
|
|
|
|
|
if (nm_nl_object_diff (type, object, cached_object))
|
|
|
|
|
announce_object (platform, object, NM_PLATFORM_SIGNAL_CHANGED, NM_PLATFORM_REASON_EXTERNAL);
|
|
|
|
|
nl_object_put (cached_object);
|
|
|
|
|
} else
|
|
|
|
|
announce_object (platform, object, NM_PLATFORM_SIGNAL_ADDED, NM_PLATFORM_REASON_EXTERNAL);
|
|
|
|
|
}
|
|
|
|
|
for (object = nl_cache_get_first (old); object; object = nl_cache_get_next (object)) {
|
|
|
|
|
struct nl_object *cached_object = nm_nl_cache_search (new, object);
|
|
|
|
|
if (cached_object)
|
|
|
|
|
nl_object_put (cached_object);
|
|
|
|
|
else
|
|
|
|
|
announce_object (platform, object, NM_PLATFORM_SIGNAL_REMOVED, NM_PLATFORM_REASON_EXTERNAL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nl_cache_free (old);
|
|
|
|
|
}
|
|
|
|
|
|
platform: avoid storing unknown netlink object types (bgo #742928)
Testing WWAN connections through a Nokia Series 40 phone, addresses of family
AF_PHONET end up triggering an assert() in object_has_ifindex(), just because
object_type_from_nl_object() only handles AF_INET and AF_INET6 address.
In order to avoid this kind of problems, we'll try to make sure that the object
caches kept by NM only store known object types.
(fixup by dcbw to use cached passed to cache_remove_unknown())
https://bugzilla.gnome.org/show_bug.cgi?id=742928
Connect: ppp0 <--> /dev/ttyACM0
nm-pppd-plugin-Message: nm-ppp-plugin: (nm_phasechange): status 5 / phase 'establish'
NetworkManager[27434]: <info> (ppp0): new Generic device (driver: 'unknown' ifindex: 12)
NetworkManager[27434]: <info> (ppp0): exported as /org/freedesktop/NetworkManager/Devices/4
[Thread 0x7ffff1ecf700 (LWP 27439) exited]
NetworkManager[27434]: <info> (ttyACM0): device state change: ip-config -> deactivating (reason 'user-requested') [70 110 39]
Terminating on signal 15
nm-pppd-plugin-Message: nm-ppp-plugin: (nm_phasechange): status 10 / phase 'terminate'
**
NetworkManager:ERROR:platform/nm-linux-platform.c:1534:object_has_ifindex: code should not be reached
Program received signal SIGABRT, Aborted.
0x00007ffff4692a97 in raise () from /usr/lib/libc.so.6
(gdb) bt
#0 0x00007ffff4692a97 in raise () from /usr/lib/libc.so.6
#1 0x00007ffff4693e6a in abort () from /usr/lib/libc.so.6
#2 0x00007ffff4c8d7f5 in g_assertion_message () from /usr/lib/libglib-2.0.so.0
#3 0x00007ffff4c8d88a in g_assertion_message_expr () from /usr/lib/libglib-2.0.so.0
#4 0x0000000000472b91 in object_has_ifindex (object=0x8a8320, ifindex=12) at platform/nm-linux-platform.c:1534
#5 0x0000000000472bec in check_cache_items (platform=0x7fe8a0, cache=0x7fda30, ifindex=12) at platform/nm-linux-platform.c:1549
#6 0x0000000000472de3 in announce_object (platform=0x7fe8a0, object=0x8a8c30, change_type=NM_PLATFORM_SIGNAL_REMOVED, reason=NM_PLATFORM_REASON_EXTERNAL) at platform/nm-linux-platform.c:1617
#7 0x0000000000473dd2 in event_notification (msg=0x8a7970, user_data=0x7fe8a0) at platform/nm-linux-platform.c:1992
#8 0x00007ffff5ee14de in nl_recvmsgs_report () from /usr/lib/libnl-3.so.200
#9 0x00007ffff5ee1849 in nl_recvmsgs () from /usr/lib/libnl-3.so.200
#10 0x00000000004794df in event_handler (channel=0x7fc930, io_condition=G_IO_IN, user_data=0x7fe8a0) at platform/nm-linux-platform.c:4152
#11 0x00007ffff4c6791d in g_main_context_dispatch () from /usr/lib/libglib-2.0.so.0
#12 0x00007ffff4c67cf8 in ?? () from /usr/lib/libglib-2.0.so.0
#13 0x00007ffff4c68022 in g_main_loop_run () from /usr/lib/libglib-2.0.so.0
#14 0x00000000004477ee in main (argc=1, argv=0x7fffffffeaa8) at main.c:447
(gdb) fr 4
#4 0x0000000000472b91 in object_has_ifindex (object=0x8a8320, ifindex=12) at platform/nm-linux-platform.c:1534
1534 g_assert_not_reached ();
2015-01-15 09:18:07 +01:00
|
|
|
/* The cache should always avoid containing objects not handled by NM, like
|
|
|
|
|
* e.g. addresses of the AF_PHONET family. */
|
|
|
|
|
static void
|
|
|
|
|
cache_remove_unknown (struct nl_cache *cache)
|
|
|
|
|
{
|
|
|
|
|
GPtrArray *objects_to_remove = NULL;
|
|
|
|
|
struct nl_object *object;
|
|
|
|
|
|
|
|
|
|
for (object = nl_cache_get_first (cache); object; object = nl_cache_get_next (object)) {
|
|
|
|
|
if (object_type_from_nl_object (object) == OBJECT_TYPE_UNKNOWN) {
|
|
|
|
|
if (!objects_to_remove)
|
|
|
|
|
objects_to_remove = g_ptr_array_new_with_free_func ((GDestroyNotify) nl_object_put);
|
|
|
|
|
nl_object_get (object);
|
|
|
|
|
g_ptr_array_add (objects_to_remove, object);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (objects_to_remove) {
|
|
|
|
|
guint i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < objects_to_remove->len; i++)
|
|
|
|
|
nl_cache_remove (g_ptr_array_index (objects_to_remove, i));
|
|
|
|
|
|
|
|
|
|
g_ptr_array_free (objects_to_remove, TRUE);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-12-08 18:08:42 +01:00
|
|
|
/* Creates and populates the netlink object caches. Called upon platform init and
|
|
|
|
|
* when we run out of sync (out of buffer space, netlink congestion control). In case
|
|
|
|
|
* the caches already exist, it finds changed, added and removed objects, announces
|
|
|
|
|
* them and destroys the old caches. */
|
|
|
|
|
static void
|
|
|
|
|
cache_repopulate_all (NMPlatform *platform)
|
|
|
|
|
{
|
|
|
|
|
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
|
|
|
|
|
struct nl_cache *old_link_cache = priv->link_cache;
|
|
|
|
|
struct nl_cache *old_address_cache = priv->address_cache;
|
|
|
|
|
struct nl_cache *old_route_cache = priv->route_cache;
|
|
|
|
|
struct nl_object *object;
|
|
|
|
|
|
|
|
|
|
debug ("platform: %spopulate platform cache", old_link_cache ? "re" : "");
|
|
|
|
|
|
|
|
|
|
/* Allocate new netlink caches */
|
|
|
|
|
init_link_cache (platform);
|
|
|
|
|
rtnl_addr_alloc_cache (priv->nlh, &priv->address_cache);
|
|
|
|
|
rtnl_route_alloc_cache (priv->nlh, AF_UNSPEC, 0, &priv->route_cache);
|
|
|
|
|
g_assert (priv->link_cache && priv->address_cache && priv->route_cache);
|
|
|
|
|
|
platform: avoid storing unknown netlink object types (bgo #742928)
Testing WWAN connections through a Nokia Series 40 phone, addresses of family
AF_PHONET end up triggering an assert() in object_has_ifindex(), just because
object_type_from_nl_object() only handles AF_INET and AF_INET6 address.
In order to avoid this kind of problems, we'll try to make sure that the object
caches kept by NM only store known object types.
(fixup by dcbw to use cached passed to cache_remove_unknown())
https://bugzilla.gnome.org/show_bug.cgi?id=742928
Connect: ppp0 <--> /dev/ttyACM0
nm-pppd-plugin-Message: nm-ppp-plugin: (nm_phasechange): status 5 / phase 'establish'
NetworkManager[27434]: <info> (ppp0): new Generic device (driver: 'unknown' ifindex: 12)
NetworkManager[27434]: <info> (ppp0): exported as /org/freedesktop/NetworkManager/Devices/4
[Thread 0x7ffff1ecf700 (LWP 27439) exited]
NetworkManager[27434]: <info> (ttyACM0): device state change: ip-config -> deactivating (reason 'user-requested') [70 110 39]
Terminating on signal 15
nm-pppd-plugin-Message: nm-ppp-plugin: (nm_phasechange): status 10 / phase 'terminate'
**
NetworkManager:ERROR:platform/nm-linux-platform.c:1534:object_has_ifindex: code should not be reached
Program received signal SIGABRT, Aborted.
0x00007ffff4692a97 in raise () from /usr/lib/libc.so.6
(gdb) bt
#0 0x00007ffff4692a97 in raise () from /usr/lib/libc.so.6
#1 0x00007ffff4693e6a in abort () from /usr/lib/libc.so.6
#2 0x00007ffff4c8d7f5 in g_assertion_message () from /usr/lib/libglib-2.0.so.0
#3 0x00007ffff4c8d88a in g_assertion_message_expr () from /usr/lib/libglib-2.0.so.0
#4 0x0000000000472b91 in object_has_ifindex (object=0x8a8320, ifindex=12) at platform/nm-linux-platform.c:1534
#5 0x0000000000472bec in check_cache_items (platform=0x7fe8a0, cache=0x7fda30, ifindex=12) at platform/nm-linux-platform.c:1549
#6 0x0000000000472de3 in announce_object (platform=0x7fe8a0, object=0x8a8c30, change_type=NM_PLATFORM_SIGNAL_REMOVED, reason=NM_PLATFORM_REASON_EXTERNAL) at platform/nm-linux-platform.c:1617
#7 0x0000000000473dd2 in event_notification (msg=0x8a7970, user_data=0x7fe8a0) at platform/nm-linux-platform.c:1992
#8 0x00007ffff5ee14de in nl_recvmsgs_report () from /usr/lib/libnl-3.so.200
#9 0x00007ffff5ee1849 in nl_recvmsgs () from /usr/lib/libnl-3.so.200
#10 0x00000000004794df in event_handler (channel=0x7fc930, io_condition=G_IO_IN, user_data=0x7fe8a0) at platform/nm-linux-platform.c:4152
#11 0x00007ffff4c6791d in g_main_context_dispatch () from /usr/lib/libglib-2.0.so.0
#12 0x00007ffff4c67cf8 in ?? () from /usr/lib/libglib-2.0.so.0
#13 0x00007ffff4c68022 in g_main_loop_run () from /usr/lib/libglib-2.0.so.0
#14 0x00000000004477ee in main (argc=1, argv=0x7fffffffeaa8) at main.c:447
(gdb) fr 4
#4 0x0000000000472b91 in object_has_ifindex (object=0x8a8320, ifindex=12) at platform/nm-linux-platform.c:1534
1534 g_assert_not_reached ();
2015-01-15 09:18:07 +01:00
|
|
|
/* Remove all unknown objects from the caches */
|
|
|
|
|
cache_remove_unknown (priv->link_cache);
|
|
|
|
|
cache_remove_unknown (priv->address_cache);
|
|
|
|
|
cache_remove_unknown (priv->route_cache);
|
|
|
|
|
|
2014-12-08 18:08:42 +01:00
|
|
|
for (object = nl_cache_get_first (priv->address_cache); object; object = nl_cache_get_next (object)) {
|
|
|
|
|
_rtnl_addr_hack_lifetimes_rel_to_abs ((struct rtnl_addr *) object);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Make sure all changes we've missed are announced. */
|
|
|
|
|
cache_announce_changes (platform, priv->link_cache, old_link_cache);
|
|
|
|
|
cache_announce_changes (platform, priv->address_cache, old_address_cache);
|
|
|
|
|
cache_announce_changes (platform, priv->route_cache, old_route_cache);
|
|
|
|
|
}
|
|
|
|
|
|
2014-12-04 16:22:39 +01:00
|
|
|
/******************************************************************/
|
|
|
|
|
|
2013-03-27 22:23:24 +01:00
|
|
|
#define EVENT_CONDITIONS ((GIOCondition) (G_IO_IN | G_IO_PRI))
|
|
|
|
|
#define ERROR_CONDITIONS ((GIOCondition) (G_IO_ERR | G_IO_NVAL))
|
|
|
|
|
#define DISCONNECT_CONDITIONS ((GIOCondition) (G_IO_HUP))
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
verify_source (struct nl_msg *msg, gpointer user_data)
|
|
|
|
|
{
|
|
|
|
|
struct ucred *creds = nlmsg_get_creds (msg);
|
|
|
|
|
|
|
|
|
|
if (!creds || creds->pid || creds->uid || creds->gid) {
|
|
|
|
|
if (creds)
|
|
|
|
|
warning ("netlink: received non-kernel message (pid %d uid %d gid %d)",
|
|
|
|
|
creds->pid, creds->uid, creds->gid);
|
|
|
|
|
else
|
|
|
|
|
warning ("netlink: received message without credentials");
|
|
|
|
|
return NL_STOP;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return NL_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
event_handler (GIOChannel *channel,
|
2014-03-04 18:07:05 -05:00
|
|
|
GIOCondition io_condition,
|
|
|
|
|
gpointer user_data)
|
2013-03-27 22:23:24 +01:00
|
|
|
{
|
2014-12-08 18:08:42 +01:00
|
|
|
NMPlatform *platform = NM_PLATFORM (user_data);
|
|
|
|
|
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
|
2013-03-27 22:23:24 +01:00
|
|
|
int nle;
|
|
|
|
|
|
|
|
|
|
nle = nl_recvmsgs_default (priv->nlh_event);
|
2014-02-25 16:19:43 +01:00
|
|
|
if (nle < 0)
|
|
|
|
|
switch (nle) {
|
2014-03-04 18:07:05 -05:00
|
|
|
case -NLE_DUMP_INTR:
|
2014-02-25 16:19:43 +01:00
|
|
|
/* this most likely happens due to our request (RTM_GETADDR, AF_INET6, NLM_F_DUMP)
|
|
|
|
|
* to detect support for support_kernel_extended_ifa_flags. This is not critical
|
|
|
|
|
* and can happen easily. */
|
|
|
|
|
debug ("Uncritical failure to retrieve incoming events: %s (%d)", nl_geterror (nle), nle);
|
|
|
|
|
break;
|
2014-12-08 18:08:42 +01:00
|
|
|
case -NLE_NOMEM:
|
|
|
|
|
warning ("Too many netlink events. Need to resynchronize platform cache");
|
|
|
|
|
/* Drain the event queue, we've lost events and are out of sync anyway and we'd
|
|
|
|
|
* like to free up some space. We'll read in the status synchronously. */
|
|
|
|
|
nl_socket_modify_cb (priv->nlh_event, NL_CB_VALID, NL_CB_DEFAULT, NULL, NULL);
|
|
|
|
|
do {
|
2015-01-12 17:03:59 +01:00
|
|
|
errno = 0;
|
|
|
|
|
|
2014-12-08 18:08:42 +01:00
|
|
|
nle = nl_recvmsgs_default (priv->nlh_event);
|
2015-01-12 17:03:59 +01:00
|
|
|
|
|
|
|
|
/* Work around a libnl bug fixed in 3.2.22 (375a6294) */
|
|
|
|
|
if (nle == 0 && (errno == EAGAIN || errno == EWOULDBLOCK))
|
|
|
|
|
nle = -NLE_AGAIN;
|
2014-12-08 18:08:42 +01:00
|
|
|
} while (nle != -NLE_AGAIN);
|
|
|
|
|
nl_socket_modify_cb (priv->nlh_event, NL_CB_VALID, NL_CB_CUSTOM, event_notification, user_data);
|
|
|
|
|
cache_repopulate_all (platform);
|
|
|
|
|
break;
|
2014-02-25 16:19:43 +01:00
|
|
|
default:
|
|
|
|
|
error ("Failed to retrieve incoming events: %s (%d)", nl_geterror (nle), nle);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2013-03-27 22:23:24 +01:00
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct nl_sock *
|
|
|
|
|
setup_socket (gboolean event, gpointer user_data)
|
|
|
|
|
{
|
|
|
|
|
struct nl_sock *sock;
|
|
|
|
|
int nle;
|
|
|
|
|
|
|
|
|
|
sock = nl_socket_alloc ();
|
|
|
|
|
g_return_val_if_fail (sock, NULL);
|
|
|
|
|
|
|
|
|
|
/* Only ever accept messages from kernel */
|
|
|
|
|
nle = nl_socket_modify_cb (sock, NL_CB_MSG_IN, NL_CB_CUSTOM, verify_source, user_data);
|
|
|
|
|
g_assert (!nle);
|
|
|
|
|
|
|
|
|
|
/* Dispatch event messages (event socket only) */
|
|
|
|
|
if (event) {
|
|
|
|
|
nl_socket_modify_cb (sock, NL_CB_VALID, NL_CB_CUSTOM, event_notification, user_data);
|
|
|
|
|
nl_socket_disable_seq_check (sock);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nle = nl_connect (sock, NETLINK_ROUTE);
|
|
|
|
|
g_assert (!nle);
|
|
|
|
|
nle = nl_socket_set_passcred (sock, 1);
|
|
|
|
|
g_assert (!nle);
|
|
|
|
|
|
2014-12-08 18:08:42 +01:00
|
|
|
/* No blocking for event socket, so that we can drain it safely. */
|
|
|
|
|
if (event) {
|
|
|
|
|
nle = nl_socket_set_nonblocking (sock);
|
|
|
|
|
g_assert (!nle);
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-27 22:23:24 +01:00
|
|
|
return sock;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
|
|
|
|
2013-05-29 12:00:50 -03:00
|
|
|
static void
|
|
|
|
|
udev_device_added (NMPlatform *platform,
|
|
|
|
|
GUdevDevice *udev_device)
|
|
|
|
|
{
|
|
|
|
|
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
|
|
|
|
|
auto_nl_object struct rtnl_link *rtnllink = NULL;
|
2013-08-07 12:35:05 -05:00
|
|
|
const char *ifname;
|
2013-05-29 12:00:50 -03:00
|
|
|
int ifindex;
|
2014-04-17 14:57:55 +02:00
|
|
|
gboolean was_announceable = FALSE;
|
2013-05-29 12:00:50 -03:00
|
|
|
|
|
|
|
|
ifname = g_udev_device_get_name (udev_device);
|
|
|
|
|
if (!ifname) {
|
2014-04-17 14:57:55 +02:00
|
|
|
debug ("udev-add: failed to get device's interface");
|
2013-05-29 12:00:50 -03:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2013-10-16 18:24:59 +02:00
|
|
|
if (g_udev_device_get_property (udev_device, "IFINDEX"))
|
|
|
|
|
ifindex = g_udev_device_get_property_as_int (udev_device, "IFINDEX");
|
2013-05-29 12:00:50 -03:00
|
|
|
else {
|
2014-04-17 14:57:55 +02:00
|
|
|
warning ("(%s): udev-add: failed to get device's ifindex", ifname);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (ifindex <= 0) {
|
|
|
|
|
warning ("(%s): udev-add: retrieved invalid IFINDEX=%d", ifname, ifindex);
|
2013-05-29 12:00:50 -03:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!g_udev_device_get_sysfs_path (udev_device)) {
|
2014-04-17 14:57:55 +02:00
|
|
|
debug ("(%s): udev-add: couldn't determine device path; ignoring...", ifname);
|
2013-05-29 12:00:50 -03:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2014-04-17 14:57:55 +02:00
|
|
|
rtnllink = rtnl_link_get (priv->link_cache, ifindex);
|
|
|
|
|
if (rtnllink)
|
|
|
|
|
was_announceable = link_is_announceable (platform, rtnllink);
|
|
|
|
|
|
2013-07-26 17:03:39 +02:00
|
|
|
g_hash_table_insert (priv->udev_devices, GINT_TO_POINTER (ifindex),
|
|
|
|
|
g_object_ref (udev_device));
|
|
|
|
|
|
2014-04-17 14:57:55 +02:00
|
|
|
/* Announce devices only if they also have been discovered via Netlink. */
|
|
|
|
|
if (rtnllink && link_is_announceable (platform, rtnllink))
|
|
|
|
|
announce_object (platform, (struct nl_object *) rtnllink, was_announceable ? NM_PLATFORM_SIGNAL_CHANGED : NM_PLATFORM_SIGNAL_ADDED, NM_PLATFORM_REASON_EXTERNAL);
|
2013-05-29 12:00:50 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
udev_device_removed (NMPlatform *platform,
|
|
|
|
|
GUdevDevice *udev_device)
|
|
|
|
|
{
|
|
|
|
|
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
|
2014-04-17 14:57:55 +02:00
|
|
|
auto_nl_object struct rtnl_link *rtnllink = NULL;
|
2013-07-26 17:03:39 +02:00
|
|
|
int ifindex = 0;
|
2014-04-17 14:57:55 +02:00
|
|
|
gboolean was_announceable = FALSE;
|
2013-05-29 12:00:50 -03:00
|
|
|
|
2014-04-17 14:57:55 +02:00
|
|
|
if (g_udev_device_get_property (udev_device, "IFINDEX"))
|
2013-10-16 18:24:59 +02:00
|
|
|
ifindex = g_udev_device_get_property_as_int (udev_device, "IFINDEX");
|
2014-04-17 14:57:55 +02:00
|
|
|
else {
|
2013-05-29 12:00:50 -03:00
|
|
|
GHashTableIter iter;
|
|
|
|
|
gpointer key, value;
|
|
|
|
|
|
2013-10-16 18:24:59 +02:00
|
|
|
/* This should not happen, but just to be sure.
|
|
|
|
|
* If we can't get IFINDEX, go through the devices and
|
|
|
|
|
* compare the pointers.
|
2013-05-29 12:00:50 -03:00
|
|
|
*/
|
|
|
|
|
g_hash_table_iter_init (&iter, priv->udev_devices);
|
|
|
|
|
while (g_hash_table_iter_next (&iter, &key, &value)) {
|
|
|
|
|
if ((GUdevDevice *)value == udev_device) {
|
2013-10-15 20:09:50 +02:00
|
|
|
ifindex = GPOINTER_TO_INT (key);
|
2013-05-29 12:00:50 -03:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-07-26 17:03:39 +02:00
|
|
|
|
2014-04-17 14:57:55 +02:00
|
|
|
debug ("udev-remove: IFINDEX=%d", ifindex);
|
|
|
|
|
if (ifindex <= 0)
|
|
|
|
|
return;
|
2013-07-26 17:03:39 +02:00
|
|
|
|
2014-04-17 14:57:55 +02:00
|
|
|
rtnllink = rtnl_link_get (priv->link_cache, ifindex);
|
|
|
|
|
if (rtnllink)
|
|
|
|
|
was_announceable = link_is_announceable (platform, rtnllink);
|
|
|
|
|
|
|
|
|
|
g_hash_table_remove (priv->udev_devices, GINT_TO_POINTER (ifindex));
|
|
|
|
|
|
|
|
|
|
/* Announce device removal if it is no longer announceable. */
|
|
|
|
|
if (was_announceable && !link_is_announceable (platform, rtnllink))
|
|
|
|
|
announce_object (platform, (struct nl_object *) rtnllink, NM_PLATFORM_SIGNAL_REMOVED, NM_PLATFORM_REASON_EXTERNAL);
|
2013-05-29 12:00:50 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
handle_udev_event (GUdevClient *client,
|
|
|
|
|
const char *action,
|
|
|
|
|
GUdevDevice *udev_device,
|
|
|
|
|
gpointer user_data)
|
|
|
|
|
{
|
|
|
|
|
NMPlatform *platform = NM_PLATFORM (user_data);
|
|
|
|
|
const char *subsys;
|
2013-10-15 19:45:42 +02:00
|
|
|
const char *ifindex;
|
2013-10-16 18:24:59 +02:00
|
|
|
guint64 seqnum;
|
2013-05-29 12:00:50 -03:00
|
|
|
|
|
|
|
|
g_return_if_fail (action != NULL);
|
|
|
|
|
|
|
|
|
|
/* A bit paranoid */
|
|
|
|
|
subsys = g_udev_device_get_subsystem (udev_device);
|
|
|
|
|
g_return_if_fail (!g_strcmp0 (subsys, "net"));
|
|
|
|
|
|
2013-10-16 18:24:59 +02:00
|
|
|
ifindex = g_udev_device_get_property (udev_device, "IFINDEX");
|
|
|
|
|
seqnum = g_udev_device_get_seqnum (udev_device);
|
|
|
|
|
debug ("UDEV event: action '%s' subsys '%s' device '%s' (%s); seqnum=%" G_GUINT64_FORMAT,
|
2013-10-15 19:45:42 +02:00
|
|
|
action, subsys, g_udev_device_get_name (udev_device),
|
2013-10-16 18:24:59 +02:00
|
|
|
ifindex ? ifindex : "unknown", seqnum);
|
2013-05-29 12:00:50 -03:00
|
|
|
|
2014-03-07 13:30:30 +01:00
|
|
|
if (!strcmp (action, "add") || !strcmp (action, "move"))
|
2013-05-29 12:00:50 -03:00
|
|
|
udev_device_added (platform, udev_device);
|
|
|
|
|
if (!strcmp (action, "remove"))
|
|
|
|
|
udev_device_removed (platform, udev_device);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
|
|
|
|
2013-03-27 22:23:24 +01:00
|
|
|
static void
|
|
|
|
|
nm_linux_platform_init (NMLinuxPlatform *platform)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
setup (NMPlatform *platform)
|
|
|
|
|
{
|
|
|
|
|
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
|
2013-05-29 12:00:50 -03:00
|
|
|
const char *udev_subsys[] = { "net", NULL };
|
|
|
|
|
GUdevEnumerator *enumerator;
|
|
|
|
|
GList *devices, *iter;
|
2013-03-27 22:23:24 +01:00
|
|
|
int channel_flags;
|
|
|
|
|
gboolean status;
|
|
|
|
|
int nle;
|
2014-12-11 15:20:08 +01:00
|
|
|
#if HAVE_LIBNL_INET6_ADDR_GEN_MODE
|
|
|
|
|
struct nl_object *object;
|
|
|
|
|
#endif
|
2013-03-27 22:23:24 +01:00
|
|
|
|
|
|
|
|
/* Initialize netlink socket for requests */
|
|
|
|
|
priv->nlh = setup_socket (FALSE, platform);
|
|
|
|
|
g_assert (priv->nlh);
|
|
|
|
|
debug ("Netlink socket for requests established: %d", nl_socket_get_local_port (priv->nlh));
|
|
|
|
|
|
|
|
|
|
/* Initialize netlink socket for events */
|
|
|
|
|
priv->nlh_event = setup_socket (TRUE, platform);
|
|
|
|
|
g_assert (priv->nlh_event);
|
|
|
|
|
/* The default buffer size wasn't enough for the testsuites. It might just
|
|
|
|
|
* as well happen with NetworkManager itself. For now let's hope 128KB is
|
|
|
|
|
* good enough.
|
|
|
|
|
*/
|
|
|
|
|
nle = nl_socket_set_buffer_size (priv->nlh_event, 131072, 0);
|
|
|
|
|
g_assert (!nle);
|
|
|
|
|
nle = nl_socket_add_memberships (priv->nlh_event,
|
2014-01-28 14:23:31 +01:00
|
|
|
RTNLGRP_LINK,
|
|
|
|
|
RTNLGRP_IPV4_IFADDR, RTNLGRP_IPV6_IFADDR,
|
|
|
|
|
RTNLGRP_IPV4_ROUTE, RTNLGRP_IPV6_ROUTE,
|
|
|
|
|
0);
|
2013-03-27 22:23:24 +01:00
|
|
|
g_assert (!nle);
|
|
|
|
|
debug ("Netlink socket for events established: %d", nl_socket_get_local_port (priv->nlh_event));
|
|
|
|
|
|
|
|
|
|
priv->event_channel = g_io_channel_unix_new (nl_socket_get_fd (priv->nlh_event));
|
|
|
|
|
g_io_channel_set_encoding (priv->event_channel, NULL, NULL);
|
|
|
|
|
g_io_channel_set_close_on_unref (priv->event_channel, TRUE);
|
|
|
|
|
|
|
|
|
|
channel_flags = g_io_channel_get_flags (priv->event_channel);
|
|
|
|
|
status = g_io_channel_set_flags (priv->event_channel,
|
|
|
|
|
channel_flags | G_IO_FLAG_NONBLOCK, NULL);
|
|
|
|
|
g_assert (status);
|
|
|
|
|
priv->event_id = g_io_add_watch (priv->event_channel,
|
|
|
|
|
(EVENT_CONDITIONS | ERROR_CONDITIONS | DISCONNECT_CONDITIONS),
|
|
|
|
|
event_handler, platform);
|
|
|
|
|
|
2014-12-08 18:08:42 +01:00
|
|
|
cache_repopulate_all (platform);
|
platform: fix invalid address lifetimes on startup
When starting, the initally loaded addresses were not fixed to have
absolute lifetimes.
This also leads to a crash due to failed assertion[1], when having
already expired addresses during NetworkManager start.
[1] backtrace:
#0 0x00007f39db91ec39 in __GI_raise (sig=sig@entry=6) at ../nptl/sysdeps/unix/sysv/linux/raise.c:56
#1 0x00007f39db920348 in __GI_abort () at abort.c:89
#2 0x00007f39dc35a0ed in _g_log_abort () at gmessages.c:255
#3 0x00007f39dc377c97 in g_assertion_message (domain=domain@entry=0x7f39e0085c67 "NetworkManager", file=file@entry=0x7f39e007f070 "platform/nm-linux-platform.c", line=line@entry=1146,
func=func@entry=0x7f39e0081430 <__FUNCTION__.23775> "_init_ip_address_lifetime", message=message@entry=0x7f39e13828b0 "assertion failed: (a_preferred <= a_valid && a_valid > 0 && a_preferred > 0)") at gtestutils.c:2278
#4 0x00007f39dc377cfa in g_assertion_message_expr (domain=domain@entry=0x7f39e0085c67 "NetworkManager", file=file@entry=0x7f39e007f070 "platform/nm-linux-platform.c", line=line@entry=1146,
func=func@entry=0x7f39e0081430 <__FUNCTION__.23775> "_init_ip_address_lifetime", expr=expr@entry=0x7f39e007f4e8 "a_preferred <= a_valid && a_valid > 0 && a_preferred > 0") at gtestutils.c:2293
#5 0x00007f39e0004608 in _init_ip_address_lifetime (address=address@entry=0x7fff7f8a2580, rtnladdr=rtnladdr@entry=0x7f39e12cdb20) at platform/nm-linux-platform.c:1144
#6 0x00007f39e0004ae4 in init_ip6_address (address=address@entry=0x7fff7f8a2580, rtnladdr=rtnladdr@entry=0x7f39e12cdb20) at platform/nm-linux-platform.c:1216
#7 0x00007f39e000a75e in ip6_address_get_all (platform=<optimized out>, ifindex=3) at platform/nm-linux-platform.c:3342
#8 0x00007f39e0032729 in nm_ip6_config_capture (ifindex=ifindex@entry=3, capture_resolv_conf=capture_resolv_conf@entry=0, use_temporary=use_temporary@entry=NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN) at nm-ip6-config.c:303
#9 0x00007f39dffee8fa in update_ip_config (self=0x7f39e1380300, initial=<optimized out>) at devices/nm-device.c:5841
#10 0x00007f39dffef52a in nm_device_capture_initial_config (dev=<optimized out>) at devices/nm-device.c:5869
#11 0x00007f39e003e74e in get_existing_connection (manager=manager@entry=0x7f39e132b150, device=device@entry=0x7f39e1380300) at nm-manager.c:1576
#12 0x00007f39e003edb2 in add_device (self=self@entry=0x7f39e132b150, device=device@entry=0x7f39e1380300, generate_con=<optimized out>) at nm-manager.c:1826
#13 0x00007f39e003f503 in platform_link_added (reason=<optimized out>, plink=0x7f39e1372340, ifindex=3, self=0x7f39e132b150) at nm-manager.c:2205
#14 platform_link_cb (platform=<optimized out>, ifindex=3, plink=0x7f39e1372340, change_type=<optimized out>, reason=<optimized out>, user_data=<optimized out>) at nm-manager.c:2220
#15 0x00007f39da868d8c in ffi_call_unix64 () at ../src/x86/unix64.S:76
#16 0x00007f39da8686bc in ffi_call (cif=cif@entry=0x7fff7f8a2a40, fn=0x7f39e003f360 <platform_link_cb>, rvalue=0x7fff7f8a29b0, avalue=avalue@entry=0x7fff7f8a2930) at ../src/x86/ffi64.c:522
#17 0x00007f39dc84aad8 in g_cclosure_marshal_generic (closure=0x7f39e1368080, return_gvalue=0x0, n_param_values=<optimized out>, param_values=<optimized out>, invocation_hint=<optimized out>, marshal_data=0x0) at gclosure.c:1454
#18 0x00007f39dc84a298 in g_closure_invoke (closure=0x7f39e1368080, return_value=return_value@entry=0x0, n_param_values=5, param_values=param_values@entry=0x7fff7f8a2c40, invocation_hint=invocation_hint@entry=0x7fff7f8a2be0)
at gclosure.c:777
#19 0x00007f39dc85c35d in signal_emit_unlocked_R (node=node@entry=0x7f39e12c16c0, detail=detail@entry=0, instance=instance@entry=0x7f39e12c30a0, emission_return=emission_return@entry=0x0,
instance_and_params=instance_and_params@entry=0x7fff7f8a2c40) at gsignal.c:3586
#20 0x00007f39dc8640f2 in g_signal_emit_valist (instance=<optimized out>, signal_id=<optimized out>, detail=<optimized out>, var_args=var_args@entry=0x7fff7f8a2e20) at gsignal.c:3330
#21 0x00007f39dc8643af in g_signal_emit (instance=<optimized out>, signal_id=<optimized out>, detail=detail@entry=0) at gsignal.c:3386
#22 0x00007f39e000e24c in nm_platform_query_devices () at platform/nm-platform.c:330
#23 0x00007f39e0040f30 in nm_manager_start (self=0x7f39e132b150) at nm-manager.c:4207
#24 0x00007f39dffe53ab in main (argc=1, argv=0x7fff7f8a3468) at main.c:657
Regression introduced by commit 8310a039d81e3a316cf657aa9f28edabb9be125c.
https://bugzilla.gnome.org/show_bug.cgi?id=732472
Signed-off-by: Thomas Haller <thaller@redhat.com>
2014-06-30 14:19:27 +02:00
|
|
|
|
2014-07-24 15:57:08 -05:00
|
|
|
#if HAVE_LIBNL_INET6_ADDR_GEN_MODE
|
|
|
|
|
/* Initial check for user IPv6LL support once the link cache is allocated
|
|
|
|
|
* and filled. If there are no links in the cache yet then we'll check
|
|
|
|
|
* when a new link shows up in announce_object().
|
|
|
|
|
*/
|
|
|
|
|
object = nl_cache_get_first (priv->link_cache);
|
|
|
|
|
if (object) {
|
|
|
|
|
uint8_t mode;
|
|
|
|
|
|
|
|
|
|
if (rtnl_link_inet6_get_addr_gen_mode ((struct rtnl_link *) object, &mode) == 0)
|
|
|
|
|
priv->support_user_ipv6ll = 1;
|
|
|
|
|
else
|
|
|
|
|
priv->support_user_ipv6ll = -1;
|
|
|
|
|
}
|
|
|
|
|
#else
|
|
|
|
|
priv->support_user_ipv6ll = -1;
|
|
|
|
|
#endif
|
|
|
|
|
|
2013-05-29 12:00:50 -03:00
|
|
|
/* Set up udev monitoring */
|
|
|
|
|
priv->udev_client = g_udev_client_new (udev_subsys);
|
|
|
|
|
g_signal_connect (priv->udev_client, "uevent", G_CALLBACK (handle_udev_event), platform);
|
|
|
|
|
priv->udev_devices = g_hash_table_new_full (NULL, NULL, NULL, g_object_unref);
|
|
|
|
|
|
|
|
|
|
/* And read initial device list */
|
|
|
|
|
enumerator = g_udev_enumerator_new (priv->udev_client);
|
|
|
|
|
g_udev_enumerator_add_match_subsystem (enumerator, "net");
|
|
|
|
|
g_udev_enumerator_add_match_is_initialized (enumerator);
|
|
|
|
|
|
|
|
|
|
devices = g_udev_enumerator_execute (enumerator);
|
|
|
|
|
for (iter = devices; iter; iter = g_list_next (iter)) {
|
2013-06-06 11:41:30 -05:00
|
|
|
udev_device_added (platform, G_UDEV_DEVICE (iter->data));
|
2013-05-29 12:00:50 -03:00
|
|
|
g_object_unref (G_UDEV_DEVICE (iter->data));
|
|
|
|
|
}
|
|
|
|
|
g_list_free (devices);
|
|
|
|
|
g_object_unref (enumerator);
|
|
|
|
|
|
2014-01-07 17:21:12 +01:00
|
|
|
/* request all IPv6 addresses (hopeing that there is at least one), to check for
|
|
|
|
|
* the IFA_FLAGS attribute. */
|
|
|
|
|
nle = nl_rtgen_request (priv->nlh_event, RTM_GETADDR, AF_INET6, NLM_F_DUMP);
|
2014-01-31 14:54:31 +01:00
|
|
|
if (nle < 0)
|
2014-01-07 17:21:12 +01:00
|
|
|
nm_log_warn (LOGD_PLATFORM, "Netlink error: requesting RTM_GETADDR failed with %s", nl_geterror (nle));
|
|
|
|
|
|
2014-02-04 14:27:03 +01:00
|
|
|
priv->wifi_data = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) wifi_utils_deinit);
|
|
|
|
|
|
2013-03-27 22:23:24 +01:00
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
nm_linux_platform_finalize (GObject *object)
|
|
|
|
|
{
|
|
|
|
|
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (object);
|
|
|
|
|
|
|
|
|
|
/* Free netlink resources */
|
|
|
|
|
g_source_remove (priv->event_id);
|
|
|
|
|
g_io_channel_unref (priv->event_channel);
|
|
|
|
|
nl_socket_free (priv->nlh);
|
|
|
|
|
nl_socket_free (priv->nlh_event);
|
|
|
|
|
nl_cache_free (priv->link_cache);
|
2013-03-27 22:23:24 +01:00
|
|
|
nl_cache_free (priv->address_cache);
|
2013-03-27 22:23:24 +01:00
|
|
|
nl_cache_free (priv->route_cache);
|
2013-03-27 22:23:24 +01:00
|
|
|
|
2013-05-29 12:00:50 -03:00
|
|
|
g_object_unref (priv->udev_client);
|
|
|
|
|
g_hash_table_unref (priv->udev_devices);
|
2014-02-04 14:27:03 +01:00
|
|
|
g_hash_table_unref (priv->wifi_data);
|
2013-05-29 12:00:50 -03:00
|
|
|
|
2013-03-27 22:23:24 +01:00
|
|
|
G_OBJECT_CLASS (nm_linux_platform_parent_class)->finalize (object);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define OVERRIDE(function) platform_class->function = function
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
nm_linux_platform_class_init (NMLinuxPlatformClass *klass)
|
|
|
|
|
{
|
|
|
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
|
NMPlatformClass *platform_class = NM_PLATFORM_CLASS (klass);
|
|
|
|
|
|
|
|
|
|
g_type_class_add_private (klass, sizeof (NMLinuxPlatformPrivate));
|
|
|
|
|
|
|
|
|
|
/* virtual methods */
|
|
|
|
|
object_class->finalize = nm_linux_platform_finalize;
|
|
|
|
|
|
|
|
|
|
platform_class->setup = setup;
|
|
|
|
|
|
2013-04-03 16:10:38 +02:00
|
|
|
platform_class->sysctl_set = sysctl_set;
|
|
|
|
|
platform_class->sysctl_get = sysctl_get;
|
|
|
|
|
|
2014-04-22 16:02:15 +02:00
|
|
|
platform_class->link_get = _nm_platform_link_get;
|
2013-03-27 22:23:24 +01:00
|
|
|
platform_class->link_get_all = link_get_all;
|
|
|
|
|
platform_class->link_add = link_add;
|
|
|
|
|
platform_class->link_delete = link_delete;
|
|
|
|
|
platform_class->link_get_ifindex = link_get_ifindex;
|
|
|
|
|
platform_class->link_get_name = link_get_name;
|
|
|
|
|
platform_class->link_get_type = link_get_type;
|
2013-04-26 11:43:08 -04:00
|
|
|
platform_class->link_get_type_name = link_get_type_name;
|
2013-03-27 22:23:24 +01:00
|
|
|
|
2014-02-11 13:58:00 +01:00
|
|
|
platform_class->link_refresh = link_refresh;
|
|
|
|
|
|
2013-03-27 22:23:24 +01:00
|
|
|
platform_class->link_set_up = link_set_up;
|
|
|
|
|
platform_class->link_set_down = link_set_down;
|
|
|
|
|
platform_class->link_set_arp = link_set_arp;
|
|
|
|
|
platform_class->link_set_noarp = link_set_noarp;
|
|
|
|
|
platform_class->link_is_up = link_is_up;
|
|
|
|
|
platform_class->link_is_connected = link_is_connected;
|
|
|
|
|
platform_class->link_uses_arp = link_uses_arp;
|
2013-03-27 22:23:24 +01:00
|
|
|
|
2014-10-13 08:26:52 +02:00
|
|
|
platform_class->link_get_ipv6_token = link_get_ipv6_token;
|
|
|
|
|
|
2014-07-24 15:57:08 -05:00
|
|
|
platform_class->link_get_user_ipv6ll_enabled = link_get_user_ipv6ll_enabled;
|
|
|
|
|
platform_class->link_set_user_ipv6ll_enabled = link_set_user_ipv6ll_enabled;
|
|
|
|
|
|
2013-03-27 22:53:55 +01:00
|
|
|
platform_class->link_get_address = link_get_address;
|
|
|
|
|
platform_class->link_set_address = link_set_address;
|
2013-04-15 21:48:12 +02:00
|
|
|
platform_class->link_get_mtu = link_get_mtu;
|
|
|
|
|
platform_class->link_set_mtu = link_set_mtu;
|
2013-03-27 22:53:55 +01:00
|
|
|
|
2013-10-11 14:59:26 -04:00
|
|
|
platform_class->link_get_physical_port_id = link_get_physical_port_id;
|
2014-02-05 11:56:44 +01:00
|
|
|
platform_class->link_get_wake_on_lan = link_get_wake_on_lan;
|
2013-10-11 14:59:26 -04:00
|
|
|
|
2013-03-27 22:53:55 +01:00
|
|
|
platform_class->link_supports_carrier_detect = link_supports_carrier_detect;
|
|
|
|
|
platform_class->link_supports_vlans = link_supports_vlans;
|
|
|
|
|
|
2013-03-27 22:53:55 +01:00
|
|
|
platform_class->link_enslave = link_enslave;
|
|
|
|
|
platform_class->link_release = link_release;
|
|
|
|
|
platform_class->link_get_master = link_get_master;
|
2013-04-04 17:07:47 +02:00
|
|
|
platform_class->master_set_option = master_set_option;
|
|
|
|
|
platform_class->master_get_option = master_get_option;
|
|
|
|
|
platform_class->slave_set_option = slave_set_option;
|
|
|
|
|
platform_class->slave_get_option = slave_get_option;
|
2013-03-27 22:53:55 +01:00
|
|
|
|
2013-03-27 22:53:55 +01:00
|
|
|
platform_class->vlan_add = vlan_add;
|
|
|
|
|
platform_class->vlan_get_info = vlan_get_info;
|
|
|
|
|
platform_class->vlan_set_ingress_map = vlan_set_ingress_map;
|
|
|
|
|
platform_class->vlan_set_egress_map = vlan_set_egress_map;
|
|
|
|
|
|
2013-06-10 16:21:08 -03:00
|
|
|
platform_class->infiniband_partition_add = infiniband_partition_add;
|
|
|
|
|
|
2013-05-03 13:55:51 -04:00
|
|
|
platform_class->veth_get_properties = veth_get_properties;
|
2013-04-25 15:46:39 -04:00
|
|
|
platform_class->tun_get_properties = tun_get_properties;
|
2013-05-06 09:16:17 -04:00
|
|
|
platform_class->macvlan_get_properties = macvlan_get_properties;
|
2013-06-04 10:31:22 -03:00
|
|
|
platform_class->vxlan_get_properties = vxlan_get_properties;
|
2013-05-21 12:49:24 -03:00
|
|
|
platform_class->gre_get_properties = gre_get_properties;
|
2013-05-03 13:55:51 -04:00
|
|
|
|
2014-02-04 14:27:03 +01:00
|
|
|
platform_class->wifi_get_capabilities = wifi_get_capabilities;
|
|
|
|
|
platform_class->wifi_get_bssid = wifi_get_bssid;
|
|
|
|
|
platform_class->wifi_get_ssid = wifi_get_ssid;
|
|
|
|
|
platform_class->wifi_get_frequency = wifi_get_frequency;
|
|
|
|
|
platform_class->wifi_get_quality = wifi_get_quality;
|
|
|
|
|
platform_class->wifi_get_rate = wifi_get_rate;
|
|
|
|
|
platform_class->wifi_get_mode = wifi_get_mode;
|
|
|
|
|
platform_class->wifi_set_mode = wifi_set_mode;
|
2014-10-23 14:19:59 -04:00
|
|
|
platform_class->wifi_set_powersave = wifi_set_powersave;
|
2014-02-04 14:27:03 +01:00
|
|
|
platform_class->wifi_find_frequency = wifi_find_frequency;
|
|
|
|
|
platform_class->wifi_indicate_addressing_running = wifi_indicate_addressing_running;
|
|
|
|
|
|
|
|
|
|
platform_class->mesh_get_channel = mesh_get_channel;
|
|
|
|
|
platform_class->mesh_set_channel = mesh_set_channel;
|
|
|
|
|
platform_class->mesh_set_ssid = mesh_set_ssid;
|
|
|
|
|
|
2013-03-27 22:23:24 +01:00
|
|
|
platform_class->ip4_address_get_all = ip4_address_get_all;
|
|
|
|
|
platform_class->ip6_address_get_all = ip6_address_get_all;
|
|
|
|
|
platform_class->ip4_address_add = ip4_address_add;
|
|
|
|
|
platform_class->ip6_address_add = ip6_address_add;
|
|
|
|
|
platform_class->ip4_address_delete = ip4_address_delete;
|
|
|
|
|
platform_class->ip6_address_delete = ip6_address_delete;
|
|
|
|
|
platform_class->ip4_address_exists = ip4_address_exists;
|
|
|
|
|
platform_class->ip6_address_exists = ip6_address_exists;
|
2013-03-27 22:23:24 +01:00
|
|
|
|
2014-11-26 00:10:04 +01:00
|
|
|
platform_class->ip4_check_reinstall_device_route = ip4_check_reinstall_device_route;
|
|
|
|
|
|
2013-03-27 22:23:24 +01:00
|
|
|
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;
|
|
|
|
|
platform_class->ip6_route_add = ip6_route_add;
|
|
|
|
|
platform_class->ip4_route_delete = ip4_route_delete;
|
|
|
|
|
platform_class->ip6_route_delete = ip6_route_delete;
|
|
|
|
|
platform_class->ip4_route_exists = ip4_route_exists;
|
|
|
|
|
platform_class->ip6_route_exists = ip6_route_exists;
|
2014-01-07 17:21:12 +01:00
|
|
|
|
|
|
|
|
platform_class->check_support_kernel_extended_ifa_flags = check_support_kernel_extended_ifa_flags;
|
2014-07-24 15:57:08 -05:00
|
|
|
platform_class->check_support_user_ipv6ll = check_support_user_ipv6ll;
|
2013-03-27 22:23:24 +01:00
|
|
|
}
|