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.
|
|
|
|
|
*/
|
|
|
|
|
#include <config.h>
|
|
|
|
|
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
#include <sys/socket.h>
|
2013-04-03 16:10:38 +02:00
|
|
|
#include <fcntl.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
|
|
|
|
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__)
|
|
|
|
|
|
|
|
|
|
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;
|
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);
|
|
|
|
|
|
2013-03-27 22:23:24 +01:00
|
|
|
void
|
|
|
|
|
nm_linux_platform_setup (void)
|
|
|
|
|
{
|
|
|
|
|
nm_platform_setup (NM_TYPE_LINUX_PLATFORM);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-27 22:23:24 +01:00
|
|
|
/* libnl doesn't use const where due */
|
|
|
|
|
#define nl_addr_build(family, addr, addrlen) nl_addr_build (family, (gpointer) addr, addrlen)
|
|
|
|
|
|
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-02-11 22:10:05 +01:00
|
|
|
UNKNOWN_OBJECT_TYPE,
|
2013-03-27 22:23:24 +01:00
|
|
|
LINK,
|
2013-03-27 22:23:24 +01:00
|
|
|
IP4_ADDRESS,
|
|
|
|
|
IP6_ADDRESS,
|
2013-03-27 22:23:24 +01:00
|
|
|
IP4_ROUTE,
|
|
|
|
|
IP6_ROUTE,
|
2014-02-11 22:10:05 +01:00
|
|
|
N_TYPES,
|
2013-03-27 22:23:24 +01:00
|
|
|
} ObjectType;
|
|
|
|
|
|
|
|
|
|
typedef enum {
|
|
|
|
|
ADDED,
|
|
|
|
|
CHANGED,
|
|
|
|
|
REMOVED,
|
|
|
|
|
N_STATUSES
|
|
|
|
|
} ObjectStatus;
|
|
|
|
|
|
|
|
|
|
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)))
|
|
|
|
|
return UNKNOWN_OBJECT_TYPE;
|
2013-03-27 22:23:24 +01:00
|
|
|
|
2013-07-27 00:42:10 +02:00
|
|
|
if (!strcmp (type_str, "route/link"))
|
2013-03-27 22:23:24 +01:00
|
|
|
return 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:
|
|
|
|
|
return IP4_ADDRESS;
|
|
|
|
|
case AF_INET6:
|
|
|
|
|
return IP6_ADDRESS;
|
|
|
|
|
default:
|
2014-02-11 22:10:05 +01:00
|
|
|
return UNKNOWN_OBJECT_TYPE;
|
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:
|
|
|
|
|
return IP4_ROUTE;
|
|
|
|
|
case AF_INET6:
|
|
|
|
|
return IP6_ROUTE;
|
|
|
|
|
default:
|
2014-02-11 22:10:05 +01:00
|
|
|
return UNKNOWN_OBJECT_TYPE;
|
2013-03-27 22:23:24 +01:00
|
|
|
}
|
2013-03-27 22:23:24 +01:00
|
|
|
} else
|
2014-02-11 22:10:05 +01:00
|
|
|
return UNKNOWN_OBJECT_TYPE;
|
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)
|
|
|
|
|
{
|
|
|
|
|
if (!obj || object_type_from_nl_object (obj) != LINK)
|
|
|
|
|
*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) {
|
2013-03-27 22:23:24 +01:00
|
|
|
case LINK:
|
|
|
|
|
{
|
|
|
|
|
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-02-11 22:10:05 +01:00
|
|
|
case IP4_ADDRESS:
|
|
|
|
|
case IP6_ADDRESS:
|
|
|
|
|
case IP4_ROUTE:
|
|
|
|
|
case IP6_ROUTE:
|
2013-03-27 22:23:24 +01:00
|
|
|
/* Fallback to a one-time cache allocation. */
|
|
|
|
|
{
|
|
|
|
|
struct nl_cache *cache;
|
|
|
|
|
int nle;
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
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)) {
|
|
|
|
|
case LINK:
|
|
|
|
|
return rtnl_link_add (sock, (struct rtnl_link *) object, NLM_F_CREATE);
|
2013-03-27 22:23:24 +01:00
|
|
|
case IP4_ADDRESS:
|
|
|
|
|
case 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);
|
2013-03-27 22:23:24 +01:00
|
|
|
case IP4_ROUTE:
|
|
|
|
|
case 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)
|
|
|
|
|
{
|
|
|
|
|
auto_g_free struct ethtool_sset_info *info = NULL;
|
|
|
|
|
auto_g_free struct ethtool_gstrings *strings = NULL;
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
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");
|
|
|
|
|
|
|
|
|
|
prop = g_udev_device_get_property (udev_device, "ID_NM_OLPC_MESH");
|
|
|
|
|
if (prop)
|
|
|
|
|
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);
|
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_strcmp0 (prop, "wlan") == 0 || wifi_utils_is_wifi (ifname, sysfs_path))
|
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
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
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)
|
|
|
|
|
info->driver = 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-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));
|
|
|
|
|
|
|
|
|
|
address->ifindex = rtnl_addr_get_ifindex (rtnladdr);
|
|
|
|
|
address->plen = rtnl_addr_get_prefixlen (rtnladdr);
|
2013-12-10 19:04:22 +01:00
|
|
|
address->timestamp = nm_utils_get_monotonic_timestamp_s ();
|
2013-06-29 11:30:11 +02:00
|
|
|
address->lifetime = rtnl_addr_get_valid_lifetime (rtnladdr);
|
|
|
|
|
address->preferred = rtnl_addr_get_preferred_lifetime (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));
|
|
|
|
|
|
|
|
|
|
address->ifindex = rtnl_addr_get_ifindex (rtnladdr);
|
|
|
|
|
address->plen = rtnl_addr_get_prefixlen (rtnladdr);
|
2013-12-10 19:04:22 +01:00
|
|
|
address->timestamp = nm_utils_get_monotonic_timestamp_s ();
|
2013-06-29 11:30:11 +02:00
|
|
|
address->lifetime = rtnl_addr_get_valid_lifetime (rtnladdr);
|
|
|
|
|
address->preferred = rtnl_addr_get_preferred_lifetime (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
|
|
|
}
|
|
|
|
|
|
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);
|
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);
|
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) {
|
|
|
|
|
case LINK:
|
|
|
|
|
return to_string_link (platform, (struct rtnl_link *) obj);
|
|
|
|
|
case IP4_ADDRESS:
|
|
|
|
|
return to_string_ip4_address ((struct rtnl_addr *) obj);
|
|
|
|
|
case IP6_ADDRESS:
|
|
|
|
|
return to_string_ip6_address ((struct rtnl_addr *) obj);
|
|
|
|
|
case IP4_ROUTE:
|
|
|
|
|
return to_string_ip4_route ((struct rtnl_route *) obj);
|
|
|
|
|
case IP6_ROUTE:
|
|
|
|
|
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 */
|
|
|
|
|
|
|
|
|
|
static const char *signal_by_type_and_status[N_TYPES][N_STATUSES] = {
|
2014-02-11 22:10:05 +01:00
|
|
|
[LINK] = { NM_PLATFORM_LINK_ADDED, NM_PLATFORM_LINK_CHANGED, NM_PLATFORM_LINK_REMOVED },
|
|
|
|
|
[IP4_ADDRESS] = { NM_PLATFORM_IP4_ADDRESS_ADDED, NM_PLATFORM_IP4_ADDRESS_CHANGED, NM_PLATFORM_IP4_ADDRESS_REMOVED },
|
|
|
|
|
[IP6_ADDRESS] = { NM_PLATFORM_IP6_ADDRESS_ADDED, NM_PLATFORM_IP6_ADDRESS_CHANGED, NM_PLATFORM_IP6_ADDRESS_REMOVED },
|
|
|
|
|
[IP4_ROUTE] = { NM_PLATFORM_IP4_ROUTE_ADDED, NM_PLATFORM_IP4_ROUTE_CHANGED, NM_PLATFORM_IP4_ROUTE_REMOVED },
|
|
|
|
|
[IP6_ROUTE] = { NM_PLATFORM_IP6_ROUTE_ADDED, NM_PLATFORM_IP6_ROUTE_CHANGED, NM_PLATFORM_IP6_ROUTE_REMOVED }
|
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) {
|
2013-03-27 22:23:24 +01:00
|
|
|
case LINK:
|
|
|
|
|
return priv->link_cache;
|
2013-03-27 22:23:24 +01:00
|
|
|
case IP4_ADDRESS:
|
|
|
|
|
case IP6_ADDRESS:
|
|
|
|
|
return priv->address_cache;
|
2013-03-27 22:23:24 +01:00
|
|
|
case IP4_ROUTE:
|
|
|
|
|
case IP6_ROUTE:
|
|
|
|
|
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)) {
|
|
|
|
|
case IP4_ADDRESS:
|
|
|
|
|
case IP6_ADDRESS:
|
2013-08-02 14:25:07 +02:00
|
|
|
return ifindex == rtnl_addr_get_ifindex ((struct rtnl_addr *) object);
|
2013-07-24 10:38:43 +02:00
|
|
|
case IP4_ROUTE:
|
|
|
|
|
case IP6_ROUTE:
|
|
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
for (object = nl_cache_get_first (cloned_cache); object; object = nl_cache_get_next (object)) {
|
|
|
|
|
g_assert (nl_object_get_cache (object) == cloned_cache);
|
|
|
|
|
if (object_has_ifindex (object, ifindex))
|
2013-08-02 18:51:06 +02:00
|
|
|
refresh_object (platform, object, TRUE, NM_PLATFORM_REASON_CACHE_CHECK);
|
2013-07-24 10:38:43 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-27 22:23:24 +01:00
|
|
|
static void
|
2013-08-02 18:51:06 +02:00
|
|
|
announce_object (NMPlatform *platform, const struct nl_object *object, ObjectStatus status, 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);
|
|
|
|
|
const char *sig = signal_by_type_and_status[object_type][status];
|
|
|
|
|
|
|
|
|
|
switch (object_type) {
|
|
|
|
|
case LINK:
|
|
|
|
|
{
|
|
|
|
|
NMPlatformLink device;
|
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-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
|
|
|
*/
|
|
|
|
|
switch (status) {
|
|
|
|
|
case ADDED:
|
|
|
|
|
case 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
|
|
|
*/
|
|
|
|
|
switch (status) {
|
|
|
|
|
case CHANGED:
|
|
|
|
|
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;
|
|
|
|
|
case 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
|
|
|
|
2013-08-02 18:51:06 +02:00
|
|
|
g_signal_emit_by_name (platform, sig, device.ifindex, &device, reason);
|
2013-03-27 22:23:24 +01:00
|
|
|
}
|
|
|
|
|
return;
|
2013-03-27 22:23:24 +01:00
|
|
|
case IP4_ADDRESS:
|
|
|
|
|
{
|
|
|
|
|
NMPlatformIP4Address address;
|
|
|
|
|
|
2014-02-17 12:48:05 +01:00
|
|
|
if (!init_ip4_address (&address, (struct rtnl_addr *) object))
|
|
|
|
|
return;
|
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.
|
|
|
|
|
*/
|
|
|
|
|
switch (status) {
|
|
|
|
|
case REMOVED:
|
|
|
|
|
check_cache_items (platform, priv->route_cache, address.ifindex);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2013-08-02 18:51:06 +02:00
|
|
|
g_signal_emit_by_name (platform, sig, address.ifindex, &address, reason);
|
2013-03-27 22:23:24 +01:00
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
case IP6_ADDRESS:
|
|
|
|
|
{
|
|
|
|
|
NMPlatformIP6Address address;
|
|
|
|
|
|
2014-02-17 12:48:05 +01:00
|
|
|
if (!init_ip6_address (&address, (struct rtnl_addr *) object))
|
|
|
|
|
return;
|
2013-08-02 18:51:06 +02:00
|
|
|
g_signal_emit_by_name (platform, sig, address.ifindex, &address, reason);
|
2013-03-27 22:23:24 +01:00
|
|
|
}
|
|
|
|
|
return;
|
2013-03-27 22:23:24 +01:00
|
|
|
case IP4_ROUTE:
|
|
|
|
|
{
|
|
|
|
|
NMPlatformIP4Route route;
|
|
|
|
|
|
2013-08-12 17:25:29 +02:00
|
|
|
if (init_ip4_route (&route, (struct rtnl_route *) object))
|
|
|
|
|
g_signal_emit_by_name (platform, sig, route.ifindex, &route, reason);
|
2013-03-27 22:23:24 +01:00
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
case IP6_ROUTE:
|
|
|
|
|
{
|
|
|
|
|
NMPlatformIP6Route route;
|
|
|
|
|
|
2013-08-12 17:25:29 +02:00
|
|
|
if (init_ip6_route (&route, (struct rtnl_route *) object))
|
|
|
|
|
g_signal_emit_by_name (platform, sig, route.ifindex, &route, 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
|
|
|
|
2013-08-02 18:51:06 +02:00
|
|
|
announce_object (platform, cached_object, REMOVED, reason);
|
2013-08-02 00:43:12 +02:00
|
|
|
}
|
|
|
|
|
} else {
|
2013-08-23 19:08:27 +02:00
|
|
|
if (!kernel_object)
|
|
|
|
|
return FALSE;
|
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
|
|
|
|
2013-08-02 18:51:06 +02:00
|
|
|
announce_object (platform, kernel_object, cached_object ? CHANGED : 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) */
|
|
|
|
|
if (object_type_from_nl_object (kernel_object) == LINK) {
|
|
|
|
|
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
|
|
|
|
|
delete_object (NMPlatform *platform, struct nl_object *obj)
|
|
|
|
|
{
|
|
|
|
|
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
|
2014-02-13 15:11:05 +01:00
|
|
|
auto_nl_object struct nl_object *obj_cleanup = obj;
|
2013-08-07 15:47:47 -05:00
|
|
|
auto_nl_object struct nl_object *cached_object = NULL;
|
2014-02-13 15:11:05 +01:00
|
|
|
struct nl_object *object;
|
|
|
|
|
int object_type;
|
2013-06-21 02:32:34 +02:00
|
|
|
int nle;
|
2013-03-27 22:23:24 +01:00
|
|
|
|
2014-02-13 15:11:05 +01:00
|
|
|
object_type = object_type_from_nl_object (obj);
|
|
|
|
|
g_return_val_if_fail (object_type != UNKNOWN_OBJECT_TYPE, FALSE);
|
2013-03-27 22:23:24 +01:00
|
|
|
|
2014-02-13 15:11:05 +01:00
|
|
|
cached_object = nm_nl_cache_search (choose_cache_by_type (platform, object_type), obj);
|
|
|
|
|
object = cached_object ? cached_object : obj;
|
|
|
|
|
|
|
|
|
|
switch (object_type) {
|
|
|
|
|
case LINK:
|
|
|
|
|
nle = rtnl_link_delete (priv->nlh, (struct rtnl_link *) object);
|
|
|
|
|
break;
|
|
|
|
|
case IP4_ADDRESS:
|
|
|
|
|
case IP6_ADDRESS:
|
|
|
|
|
nle = rtnl_addr_delete (priv->nlh, (struct rtnl_addr *) object, 0);
|
|
|
|
|
break;
|
|
|
|
|
case IP4_ROUTE:
|
|
|
|
|
case IP6_ROUTE:
|
|
|
|
|
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-02-13 15:11:05 +01:00
|
|
|
case -NLE_NOADDR:
|
|
|
|
|
if (object_type == IP4_ADDRESS || object_type == IP6_ADDRESS) {
|
|
|
|
|
debug("delete_object for address failed with \"%s\" (%d), meaning the address was already removed",
|
|
|
|
|
nl_geterror (nle), nle);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
/* fall-through to error, because we only expect this for addresses. */
|
2013-06-21 02:32:34 +02:00
|
|
|
default:
|
2014-02-17 14:20:44 +01:00
|
|
|
error ("Netlink error deleting %s: %s", to_string_object (platform, obj), nl_geterror (nle));
|
2013-03-27 22:23:24 +01:00
|
|
|
return FALSE;
|
2013-06-21 02:32:34 +02:00
|
|
|
}
|
2013-03-27 22:23:24 +01:00
|
|
|
|
2013-08-02 18:51:06 +02:00
|
|
|
refresh_object (platform, object, TRUE, NM_PLATFORM_REASON_INTERNAL);
|
2013-03-27 22:23:24 +01:00
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
ref_object (struct nl_object *obj, void *data)
|
|
|
|
|
{
|
|
|
|
|
struct nl_object **out = data;
|
|
|
|
|
|
|
|
|
|
nl_object_get (obj);
|
|
|
|
|
*out = obj;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
|
|
|
|
|
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-02-11 20:12:12 +01:00
|
|
|
if (nm_logging_enabled (LOGL_DEBUG, LOGD_PLATFORM)) {
|
|
|
|
|
if (object_type_from_nl_object (object) == LINK) {
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
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-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);
|
2013-07-26 17:03:39 +02:00
|
|
|
/* Don't announced removed interfaces that are not recognized by
|
|
|
|
|
* 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;
|
|
|
|
|
}
|
2013-08-02 18:51:06 +02:00
|
|
|
announce_object (platform, cached_object, 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;
|
|
|
|
|
/* 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;
|
|
|
|
|
}
|
2013-08-02 18:51:06 +02:00
|
|
|
announce_object (platform, kernel_object, 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.
|
|
|
|
|
*/
|
|
|
|
|
if (!nl_object_diff (kernel_object, cached_object))
|
|
|
|
|
return NL_OK;
|
|
|
|
|
/* 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;
|
|
|
|
|
}
|
2013-08-02 18:51:06 +02:00
|
|
|
announce_object (platform, kernel_object, 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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct nl_object *
|
|
|
|
|
build_rtnl_link (int ifindex, const char *name, NMLinkType type)
|
|
|
|
|
{
|
|
|
|
|
struct rtnl_link *rtnllink;
|
|
|
|
|
int nle;
|
|
|
|
|
|
|
|
|
|
rtnllink = rtnl_link_alloc ();
|
|
|
|
|
g_assert (rtnllink);
|
|
|
|
|
if (ifindex)
|
|
|
|
|
rtnl_link_set_ifindex (rtnllink, ifindex);
|
|
|
|
|
if (name)
|
|
|
|
|
rtnl_link_set_name (rtnllink, name);
|
|
|
|
|
if (type) {
|
|
|
|
|
nle = rtnl_link_set_type (rtnllink, type_to_string (type));
|
|
|
|
|
g_assert (!nle);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (struct nl_object *) rtnllink;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
link_add (NMPlatform *platform, const char *name, NMLinkType type)
|
|
|
|
|
{
|
2013-05-09 10:51:27 -05:00
|
|
|
int r;
|
|
|
|
|
|
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))
|
2013-05-09 10:51:27 -05:00
|
|
|
/* Ignore return value to shut up the compiler */
|
|
|
|
|
r = system ("modprobe bonding max_bonds=0");
|
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);
|
|
|
|
|
|
2013-03-27 22:23:24 +01:00
|
|
|
return add_object (platform, build_rtnl_link (0, name, type));
|
|
|
|
|
}
|
|
|
|
|
|
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;
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-27 22:23:24 +01:00
|
|
|
return delete_object (platform, build_rtnl_link (ifindex, NULL, NM_LINK_TYPE_NONE));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
auto_nl_object struct rtnl_link *rtnllink = rtnl_link_alloc ();
|
|
|
|
|
|
|
|
|
|
if (rtnllink) {
|
|
|
|
|
rtnl_link_set_ifindex (rtnllink, ifindex);
|
|
|
|
|
return refresh_object (platform, (struct nl_object *) rtnllink, FALSE, NM_PLATFORM_REASON_EXTERNAL);
|
|
|
|
|
} else
|
|
|
|
|
error ("link_refresh failed with out of memory");
|
|
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
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)
|
|
|
|
|
{
|
2013-08-07 15:47:47 -05:00
|
|
|
auto_nl_object struct rtnl_link *change = rtnl_link_alloc ();
|
2013-03-27 22:23:24 +01:00
|
|
|
|
|
|
|
|
g_return_val_if_fail (change != NULL, FALSE);
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
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);
|
2013-05-08 11:20:43 -04:00
|
|
|
auto_g_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)
|
|
|
|
|
{
|
|
|
|
|
auto_nl_object struct rtnl_link *change = NULL;
|
|
|
|
|
auto_nl_addr struct nl_addr *nladdr = NULL;
|
|
|
|
|
|
|
|
|
|
change = rtnl_link_alloc ();
|
|
|
|
|
g_return_val_if_fail (change, FALSE);
|
|
|
|
|
|
|
|
|
|
nladdr = nl_addr_build (AF_LLC, address, length);
|
|
|
|
|
g_return_val_if_fail (nladdr, FALSE);
|
|
|
|
|
|
|
|
|
|
rtnl_link_set_addr (change, nladdr);
|
|
|
|
|
|
2014-03-05 10:56:16 +01:00
|
|
|
if (nm_logging_enabled (LOGL_DEBUG, LOGD_PLATFORM)) {
|
|
|
|
|
char *mac = nm_utils_hwaddr_ntoa_len (address, length);
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
nladdr = rtnllink ? rtnl_link_get_addr (rtnllink) : NULL;
|
|
|
|
|
|
|
|
|
|
if (length)
|
|
|
|
|
*length = nladdr ? nl_addr_get_len (nladdr) : 0;
|
|
|
|
|
|
|
|
|
|
return nladdr ? nl_addr_get_binary_addr (nladdr) : NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-15 21:48:12 +02:00
|
|
|
static gboolean
|
|
|
|
|
link_set_mtu (NMPlatform *platform, int ifindex, guint32 mtu)
|
|
|
|
|
{
|
2013-08-07 15:47:47 -05:00
|
|
|
auto_nl_object struct rtnl_link *change = rtnl_link_alloc ();
|
2013-04-15 21:48:12 +02:00
|
|
|
|
|
|
|
|
g_return_val_if_fail (change != NULL, FALSE);
|
|
|
|
|
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
|
|
|
{
|
2013-08-07 15:47:47 -05:00
|
|
|
auto_nl_object struct rtnl_link *change = rtnl_link_alloc ();
|
2013-03-27 22:53:55 +01:00
|
|
|
|
2013-08-07 15:47:47 -05:00
|
|
|
g_return_val_if_fail (change != NULL, FALSE);
|
2013-03-27 22:53:55 +01:00
|
|
|
|
2013-08-02 00:43:12 +02:00
|
|
|
rtnl_link_set_master (change, master);
|
2013-03-27 22:53:55 +01:00
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
auto_g_free char *path = link_option_path (master, category, option);
|
|
|
|
|
|
|
|
|
|
return path && nm_platform_sysctl_set (path, value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char *
|
|
|
|
|
link_get_option (int master, const char *category, const char *option)
|
|
|
|
|
{
|
|
|
|
|
auto_g_free char *path = link_option_path (master, category, option);
|
|
|
|
|
|
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) {
|
|
|
|
|
auto_nl_object struct rtnl_link *rtnllink = rtnl_link_alloc ();
|
|
|
|
|
auto_g_free char *ifname = g_strdup_printf ("%s.%04x", parent_name, p_key);
|
|
|
|
|
|
|
|
|
|
rtnl_link_set_name (rtnllink, ifname);
|
|
|
|
|
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;
|
|
|
|
|
auto_g_free struct ethtool_stats *stats = NULL;
|
|
|
|
|
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) {
|
|
|
|
|
props->owner = nm_utils_ascii_str_to_int64 (val, 10, -1, G_MAXINT64, -1);
|
|
|
|
|
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) {
|
|
|
|
|
props->group = nm_utils_ascii_str_to_int64 (val, 10, -1, G_MAXINT64, -1);
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
flags = nm_utils_ascii_str_to_int64 (val, 16, 0, G_MAXINT64, 0);
|
|
|
|
|
if (!errno) {
|
|
|
|
|
#ifndef IFF_MULTI_QUEUE
|
|
|
|
|
const int IFF_MULTI_QUEUE = 0x0100;
|
2013-06-04 12:01:38 -03:00
|
|
|
#endif
|
2014-01-21 11:04:26 +01:00
|
|
|
props->mode = ((flags & TUN_TYPE_MASK) == TUN_TUN_DEV) ? "tun" : "tap";
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
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,
|
|
|
|
|
.minlen = sizeof (struct ifla_vxlan_port_range) },
|
|
|
|
|
[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];
|
|
|
|
|
struct ifla_vxlan_port_range *range;
|
|
|
|
|
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));
|
|
|
|
|
|
|
|
|
|
props->parent_ifindex = tb[IFLA_VXLAN_LINK] ? nla_get_u32 (tb[IFLA_VXLAN_LINK]) : 0;
|
|
|
|
|
props->id = nla_get_u32 (tb[IFLA_VXLAN_ID]);
|
|
|
|
|
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));
|
|
|
|
|
|
|
|
|
|
props->ageing = nla_get_u32 (tb[IFLA_VXLAN_AGEING]);
|
|
|
|
|
props->limit = nla_get_u32 (tb[IFLA_VXLAN_LIMIT]);
|
|
|
|
|
props->tos = nla_get_u8 (tb[IFLA_VXLAN_TOS]);
|
|
|
|
|
props->ttl = nla_get_u8 (tb[IFLA_VXLAN_TTL]);
|
|
|
|
|
|
|
|
|
|
props->dst_port = nla_get_u16 (tb[IFLA_VXLAN_PORT]);
|
|
|
|
|
range = nla_data (tb[IFLA_VXLAN_PORT_RANGE]);
|
|
|
|
|
props->src_port_min = range->low;
|
|
|
|
|
props->src_port_max = range->high;
|
|
|
|
|
|
|
|
|
|
props->learning = !!nla_get_u8 (tb[IFLA_VXLAN_LEARNING]);
|
|
|
|
|
props->proxy = !!nla_get_u8 (tb[IFLA_VXLAN_PROXY]);
|
|
|
|
|
props->rsc = !!nla_get_u8 (tb[IFLA_VXLAN_RSC]);
|
|
|
|
|
props->l2miss = !!nla_get_u8 (tb[IFLA_VXLAN_L2MISS]);
|
|
|
|
|
props->l3miss = !!nla_get_u8 (tb[IFLA_VXLAN_L3MISS]);
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
wifi_get_bssid (NMPlatform *platform, int ifindex, struct ether_addr *bssid)
|
|
|
|
|
{
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
mesh_set_ssid (NMPlatform *platform, int ifindex, const GByteArray *ssid)
|
|
|
|
|
{
|
|
|
|
|
WifiData *wifi_data = wifi_get_wifi_data (platform, ifindex);
|
|
|
|
|
|
|
|
|
|
if (!wifi_data)
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
return wifi_utils_set_mesh_ssid (wifi_data, ssid);
|
|
|
|
|
}
|
|
|
|
|
|
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 &&
|
|
|
|
|
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-02-17 12:48:05 +01:00
|
|
|
if (init_ip4_address (&address, (struct rtnl_addr *) object)) {
|
|
|
|
|
address.source = NM_PLATFORM_SOURCE_KERNEL;
|
|
|
|
|
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-02-17 12:48:05 +01:00
|
|
|
if (init_ip6_address (&address, (struct rtnl_addr *) object)) {
|
|
|
|
|
address.source = NM_PLATFORM_SOURCE_KERNEL;
|
|
|
|
|
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
|
|
|
{
|
|
|
|
|
struct rtnl_addr *rtnladdr = rtnl_addr_alloc ();
|
|
|
|
|
int addrlen = family == AF_INET ? sizeof (in_addr_t) : sizeof (struct in6_addr);
|
|
|
|
|
auto_nl_addr struct nl_addr *nladdr = nl_addr_build (family, addr, addrlen);
|
|
|
|
|
int nle;
|
|
|
|
|
|
|
|
|
|
g_assert (rtnladdr && nladdr);
|
|
|
|
|
|
2013-12-03 11:42:28 -06:00
|
|
|
/* IP address */
|
2013-03-27 22:23:24 +01:00
|
|
|
rtnl_addr_set_ifindex (rtnladdr, ifindex);
|
|
|
|
|
nle = rtnl_addr_set_local (rtnladdr, nladdr);
|
|
|
|
|
g_assert (!nle);
|
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);
|
2013-12-03 11:42:28 -06:00
|
|
|
bcaddr = nl_addr_build (family, &bcast, addrlen);
|
|
|
|
|
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) {
|
|
|
|
|
auto_nl_addr struct nl_addr *nlpeer = nl_addr_build (family, peer_addr, addrlen);
|
|
|
|
|
|
|
|
|
|
nle = rtnl_addr_set_peer (rtnladdr, nlpeer);
|
2014-01-06 17:21:11 -06:00
|
|
|
/* IPv6 doesn't support peer addresses yet */
|
|
|
|
|
g_assert (!nle || (nle == -NLE_AF_NOSUPPORT));
|
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) {
|
|
|
|
|
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
|
|
|
|
|
|
|
|
return (struct nl_object *) rtnladdr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
ip4_address_delete (NMPlatform *platform, int ifindex, in_addr_t addr, int plen)
|
|
|
|
|
{
|
2014-02-19 16:10:59 -05:00
|
|
|
return delete_object (platform, build_rtnl_addr (AF_INET, ifindex, &addr, NULL, plen, 0, 0, 0, NULL));
|
2013-03-27 22:23:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
ip6_address_delete (NMPlatform *platform, int ifindex, struct in6_addr addr, int plen)
|
|
|
|
|
{
|
2014-02-19 16:10:59 -05:00
|
|
|
return delete_object (platform, build_rtnl_addr (AF_INET6, ifindex, &addr, NULL, plen, 0, 0, 0, NULL));
|
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-03-07 16:44:19 +01:00
|
|
|
static gboolean
|
|
|
|
|
_route_match (struct rtnl_route *rtnlroute, int family, int ifindex)
|
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 ||
|
|
|
|
|
rtnl_route_get_protocol (rtnlroute) == RTPROT_KERNEL ||
|
|
|
|
|
rtnl_route_get_family (rtnlroute) != family ||
|
|
|
|
|
rtnl_route_get_nnexthops (rtnlroute) != 1)
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
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 *
|
2013-08-02 23:49:34 -05:00
|
|
|
ip4_route_get_all (NMPlatform *platform, int ifindex, gboolean include_default)
|
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-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-03-07 16:44:19 +01:00
|
|
|
if (_route_match ((struct rtnl_route *) object, AF_INET, ifindex)) {
|
2013-08-12 17:25:29 +02:00
|
|
|
if (init_ip4_route (&route, (struct rtnl_route *) object)) {
|
2014-01-06 14:14:14 -06:00
|
|
|
route.source = NM_PLATFORM_SOURCE_KERNEL;
|
2013-08-12 17:25:29 +02:00
|
|
|
if (route.plen != 0 || include_default)
|
|
|
|
|
g_array_append_val (routes, route);
|
|
|
|
|
}
|
2013-03-27 22:23:24 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return routes;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static GArray *
|
2013-08-02 23:49:34 -05:00
|
|
|
ip6_route_get_all (NMPlatform *platform, int ifindex, gboolean include_default)
|
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-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-03-07 16:44:19 +01:00
|
|
|
if (_route_match ((struct rtnl_route *) object, AF_INET6, ifindex)) {
|
2013-08-12 17:25:29 +02:00
|
|
|
if (init_ip6_route (&route, (struct rtnl_route *) object)) {
|
2014-01-06 14:14:14 -06:00
|
|
|
route.source = NM_PLATFORM_SOURCE_KERNEL;
|
2013-08-12 17:25:29 +02:00
|
|
|
if (route.plen != 0 || include_default)
|
|
|
|
|
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 *
|
|
|
|
|
build_rtnl_route (int family, int ifindex, gconstpointer network, int plen, gconstpointer gateway, int metric, int mss)
|
|
|
|
|
{
|
2014-02-13 19:47:04 +01:00
|
|
|
guint32 network_clean[4];
|
2013-03-27 22:23:24 +01:00
|
|
|
struct rtnl_route *rtnlroute = rtnl_route_alloc ();
|
|
|
|
|
struct rtnl_nexthop *nexthop = rtnl_route_nh_alloc ();
|
|
|
|
|
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;
|
2013-05-02 08:06:08 +02:00
|
|
|
auto_nl_addr struct nl_addr *gw = gateway ? nl_addr_build (family, gateway, 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);
|
|
|
|
|
dst = nl_addr_build (family, network_clean, plen ? addrlen : 0);
|
|
|
|
|
|
2013-05-02 08:06:08 +02:00
|
|
|
g_assert (rtnlroute && dst && nexthop);
|
2013-03-27 22:23:24 +01:00
|
|
|
|
|
|
|
|
nl_addr_set_prefixlen (dst, plen);
|
|
|
|
|
|
|
|
|
|
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);
|
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);
|
|
|
|
|
rtnl_route_add_nexthop (rtnlroute, nexthop);
|
|
|
|
|
|
|
|
|
|
if (mss > 0)
|
|
|
|
|
rtnl_route_set_metric (rtnlroute, RTAX_ADVMSS, mss);
|
|
|
|
|
|
|
|
|
|
return (struct nl_object *) rtnlroute;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
ip4_route_add (NMPlatform *platform, int ifindex, in_addr_t network, int plen, in_addr_t gateway, int metric, int mss)
|
|
|
|
|
{
|
|
|
|
|
return add_object (platform, build_rtnl_route (AF_INET, ifindex, &network, plen, &gateway, metric, mss));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
ip6_route_add (NMPlatform *platform, int ifindex, struct in6_addr network, int plen, struct in6_addr gateway, int metric, int mss)
|
|
|
|
|
{
|
|
|
|
|
return add_object (platform, build_rtnl_route (AF_INET6, ifindex, &network, plen, &gateway, metric, mss));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
2013-05-02 08:06:08 +02:00
|
|
|
ip4_route_delete (NMPlatform *platform, int ifindex, in_addr_t network, int plen, int metric)
|
2013-03-27 22:23:24 +01:00
|
|
|
{
|
2013-05-02 08:06:08 +02:00
|
|
|
in_addr_t gateway = 0;
|
2014-02-13 15:11:05 +01:00
|
|
|
struct nl_object *route = build_rtnl_route (AF_INET, ifindex, &network, plen, &gateway, metric, 0);
|
|
|
|
|
|
|
|
|
|
g_return_val_if_fail (route, FALSE);
|
|
|
|
|
|
|
|
|
|
/* 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 might be the wrong scope.
|
|
|
|
|
*
|
|
|
|
|
* As a workaround, we set the scope to RT_SCOPE_UNIVERSE, so libnl
|
|
|
|
|
* will not overwrite it. But this only works if we guess correctly.
|
|
|
|
|
*
|
|
|
|
|
* As a better workaround, we don't use @rtnlroute as argument for
|
|
|
|
|
* rtnl_route_delete(), but we look into our cache, if we already have
|
|
|
|
|
* this route ready.
|
|
|
|
|
**/
|
|
|
|
|
rtnl_route_set_scope ((struct rtnl_route *) route, RT_SCOPE_UNIVERSE);
|
2013-05-02 08:06:08 +02:00
|
|
|
|
2014-02-13 15:11:05 +01:00
|
|
|
return delete_object (platform, route);
|
2013-03-27 22:23:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
2013-05-02 08:06:08 +02:00
|
|
|
ip6_route_delete (NMPlatform *platform, int ifindex, struct in6_addr network, int plen, int 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
|
|
|
|
2013-03-27 22:23:24 +01:00
|
|
|
return delete_object (platform, build_rtnl_route (AF_INET6, ifindex, &network, plen, &gateway, metric, 0));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
2013-05-02 08:06:08 +02:00
|
|
|
ip_route_exists (NMPlatform *platform, int family, int ifindex, gpointer network, int plen, int metric)
|
2013-03-27 22:23:24 +01:00
|
|
|
{
|
|
|
|
|
auto_nl_object struct nl_object *object = build_rtnl_route (
|
2013-05-02 08:06:08 +02:00
|
|
|
family, ifindex, network, plen, INADDR_ANY, metric, 0);
|
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
|
2013-05-02 08:06:08 +02:00
|
|
|
ip4_route_exists (NMPlatform *platform, int ifindex, in_addr_t network, int plen, int 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
|
2013-05-02 08:06:08 +02:00
|
|
|
ip6_route_exists (NMPlatform *platform, int ifindex, struct in6_addr network, int plen, int metric)
|
2013-03-27 22:23:24 +01:00
|
|
|
{
|
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
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
|
|
|
|
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
|
|
|
{
|
|
|
|
|
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (user_data);
|
|
|
|
|
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;
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
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-03-07 13:30:30 +01:00
|
|
|
gboolean is_changed;
|
2013-05-29 12:00:50 -03:00
|
|
|
|
|
|
|
|
ifname = g_udev_device_get_name (udev_device);
|
|
|
|
|
if (!ifname) {
|
|
|
|
|
debug ("failed to get device's interface");
|
|
|
|
|
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 {
|
|
|
|
|
warning ("(%s): failed to get device's ifindex", ifname);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!g_udev_device_get_sysfs_path (udev_device)) {
|
|
|
|
|
debug ("(%s): couldn't determine device path; ignoring...", ifname);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-07 13:30:30 +01:00
|
|
|
is_changed = g_hash_table_lookup_extended (priv->udev_devices, GINT_TO_POINTER (ifindex), NULL, NULL);
|
2013-07-26 17:03:39 +02:00
|
|
|
g_hash_table_insert (priv->udev_devices, GINT_TO_POINTER (ifindex),
|
|
|
|
|
g_object_ref (udev_device));
|
|
|
|
|
|
|
|
|
|
/* Don't announce devices that have not yet been discovered via Netlink. */
|
|
|
|
|
rtnllink = rtnl_link_get (priv->link_cache, ifindex);
|
2013-05-29 12:00:50 -03:00
|
|
|
if (!rtnllink) {
|
|
|
|
|
debug ("%s: not found in link cache, ignoring...", ifname);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-07 13:30:30 +01:00
|
|
|
announce_object (platform, (struct nl_object *) rtnllink, is_changed ? CHANGED : 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);
|
2013-07-26 17:03:39 +02:00
|
|
|
int ifindex = 0;
|
2013-05-29 12:00:50 -03:00
|
|
|
|
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
|
|
|
g_hash_table_remove (priv->udev_devices, GINT_TO_POINTER (ifindex));
|
|
|
|
|
} else {
|
|
|
|
|
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
|
|
|
g_hash_table_iter_remove (&iter);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-07-26 17:03:39 +02:00
|
|
|
|
2013-10-16 18:24:59 +02:00
|
|
|
/* Announce device removal if it's still in the Netlink cache. */
|
2013-07-26 17:03:39 +02:00
|
|
|
if (ifindex) {
|
2013-07-27 00:35:51 +02:00
|
|
|
auto_nl_object struct rtnl_link *device = rtnl_link_get (priv->link_cache, ifindex);
|
2013-07-26 17:03:39 +02:00
|
|
|
|
|
|
|
|
if (device)
|
2013-08-02 18:51:06 +02:00
|
|
|
announce_object (platform, (struct nl_object *) device, REMOVED, NM_PLATFORM_REASON_EXTERNAL);
|
2013-07-26 17:03:39 +02:00
|
|
|
}
|
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;
|
|
|
|
|
|
|
|
|
|
/* 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);
|
|
|
|
|
|
|
|
|
|
/* Allocate netlink caches */
|
|
|
|
|
rtnl_link_alloc_cache (priv->nlh, AF_UNSPEC, &priv->link_cache);
|
2013-03-27 22:23:24 +01:00
|
|
|
rtnl_addr_alloc_cache (priv->nlh, &priv->address_cache);
|
2013-03-27 22:23:24 +01:00
|
|
|
rtnl_route_alloc_cache (priv->nlh, AF_UNSPEC, 0, &priv->route_cache);
|
|
|
|
|
g_assert (priv->link_cache && priv->address_cache && priv->route_cache);
|
2013-03-27 22:23:24 +01:00
|
|
|
|
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;
|
|
|
|
|
|
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
|
|
|
|
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;
|
|
|
|
|
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
|
|
|
|
|
|
|
|
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;
|
2013-03-27 22:23:24 +01:00
|
|
|
}
|