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"
|
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
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
|
|
|
|
|
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] = {
|
|
|
|
|
[IFLA_LINKINFO] = { .type = NLA_NESTED },
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static struct nla_policy info_data_link_info_policy[IFLA_INFO_MAX + 1] = {
|
|
|
|
|
[IFLA_INFO_DATA] = { .type = NLA_NESTED },
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
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) {
|
|
|
|
|
g_warn_if_reached ();
|
|
|
|
|
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-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
|
2013-08-07 12:35:05 -05:00
|
|
|
link_type_from_udev (NMPlatform *platform, int ifindex, 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;
|
2013-10-16 12:29:13 -05:00
|
|
|
const char *prop, *name, *sysfs_path;
|
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
|
|
|
name = g_udev_device_get_name (udev_device);
|
|
|
|
|
sysfs_path = g_udev_device_get_sysfs_path (udev_device);
|
|
|
|
|
if (g_strcmp0 (prop, "wlan") == 0 || wifi_utils_is_wifi (name, 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") ||
|
|
|
|
|
!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;
|
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");
|
2013-05-29 12:00:50 -03:00
|
|
|
else 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.
|
|
|
|
|
*/
|
|
|
|
|
if (g_str_has_prefix (rtnl_link_get_name (rtnllink), "ctc"))
|
|
|
|
|
return_type (NM_LINK_TYPE_ETHERNET, "ethernet");
|
2013-09-06 19:39:11 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
driver = ethtool_get_driver (rtnl_link_get_name (rtnllink));
|
|
|
|
|
if (!g_strcmp0 (driver, "openvswitch"))
|
|
|
|
|
return_type (NM_LINK_TYPE_OPENVSWITCH, "openvswitch");
|
|
|
|
|
|
|
|
|
|
return link_type_from_udev (platform,
|
|
|
|
|
rtnl_link_get_ifindex (rtnllink),
|
|
|
|
|
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-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
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2013-05-29 12:00:50 -03:00
|
|
|
link_init (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;
|
|
|
|
|
|
2013-03-27 22:23:24 +01:00
|
|
|
memset (info, 0, sizeof (*info));
|
|
|
|
|
|
|
|
|
|
g_assert (rtnllink);
|
|
|
|
|
|
|
|
|
|
info->ifindex = rtnl_link_get_ifindex (rtnllink);
|
2014-01-20 22:27:23 +01:00
|
|
|
g_strlcpy (info->name, rtnl_link_get_name (rtnllink), sizeof (info->name));
|
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);
|
|
|
|
|
}
|
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;
|
|
|
|
|
|
|
|
|
|
if (!object)
|
|
|
|
|
return;
|
|
|
|
|
if (strcmp (nl_object_get_type (object), "route/link"))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
rtnllink = (struct rtnl_link *) object;
|
|
|
|
|
|
|
|
|
|
ifindex = rtnl_link_get_ifindex (rtnllink);
|
|
|
|
|
|
2013-05-29 12:00:50 -03:00
|
|
|
switch (link_extract_type (platform, rtnllink, NULL)) {
|
2013-03-27 22:53:55 +01:00
|
|
|
case NM_LINK_TYPE_BRIDGE:
|
|
|
|
|
case NM_LINK_TYPE_BOND:
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rtnl_link_unset_flags (rtnllink, IFF_LOWER_UP);
|
2013-03-27 22:23:24 +01:00
|
|
|
}
|
|
|
|
|
|
2013-03-27 22:23:24 +01:00
|
|
|
static void
|
|
|
|
|
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);
|
2013-03-27 22:23:24 +01:00
|
|
|
|
|
|
|
|
g_assert (nladdr);
|
|
|
|
|
|
|
|
|
|
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-03-27 22:23:24 +01:00
|
|
|
g_assert (nl_addr_get_len (nladdr) == sizeof (address->address));
|
|
|
|
|
memcpy (&address->address, nl_addr_get_binary_addr (nladdr), sizeof (address->address));
|
2013-12-02 10:20:26 -05:00
|
|
|
if (nlpeer) {
|
|
|
|
|
g_assert (nl_addr_get_len (nlpeer) == sizeof (address->peer_address));
|
|
|
|
|
memcpy (&address->peer_address, nl_addr_get_binary_addr (nlpeer), sizeof (address->peer_address));
|
|
|
|
|
}
|
2013-03-27 22:23:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
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);
|
2013-03-27 22:23:24 +01:00
|
|
|
g_assert (nl_addr_get_len (nladdr) == sizeof (address->address));
|
|
|
|
|
memcpy (&address->address, nl_addr_get_binary_addr (nladdr), sizeof (address->address));
|
2013-12-02 10:20:26 -05:00
|
|
|
if (nlpeer) {
|
|
|
|
|
g_assert (nl_addr_get_len (nlpeer) == sizeof (address->peer_address));
|
|
|
|
|
memcpy (&address->peer_address, nl_addr_get_binary_addr (nlpeer), sizeof (address->peer_address));
|
|
|
|
|
}
|
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)) {
|
|
|
|
|
g_assert (nl_addr_get_len (dst) == sizeof (route->network));
|
|
|
|
|
memcpy (&route->network, nl_addr_get_binary_addr (dst), sizeof (route->network));
|
|
|
|
|
}
|
|
|
|
|
if (gw) {
|
|
|
|
|
g_assert (nl_addr_get_len (gw) == sizeof (route->network));
|
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)) {
|
|
|
|
|
g_assert (nl_addr_get_len (dst) == sizeof (route->network));
|
|
|
|
|
memcpy (&route->network, nl_addr_get_binary_addr (dst), sizeof (route->network));
|
|
|
|
|
}
|
|
|
|
|
if (gw) {
|
|
|
|
|
g_assert (nl_addr_get_len (gw) == sizeof (route->network));
|
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-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)) {
|
|
|
|
|
debug ("cache %p object %p", cloned_cache, 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
|
|
|
|
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
|
|
|
link_init (platform, &device, rtnl_link);
|
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);
|
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;
|
|
|
|
|
|
|
|
|
|
init_ip4_address (&address, (struct rtnl_addr *) object);
|
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;
|
|
|
|
|
|
|
|
|
|
init_ip6_address (&address, (struct rtnl_addr *) object);
|
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:
|
|
|
|
|
error ("Netlink error: %s", 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:
|
|
|
|
|
error ("Netlink error: %s", 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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
fd = open (path, O_WRONLY | O_TRUNC);
|
|
|
|
|
if (fd == -1) {
|
|
|
|
|
error ("sysctl: failed to open '%s': (%d) %s",
|
|
|
|
|
path, errno, strerror (errno));
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
debug ("sysctl: setting '%s' to '%s'", path, value);
|
|
|
|
|
|
|
|
|
|
/* 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++) {
|
|
|
|
|
errno = 0;
|
|
|
|
|
nwrote = write (fd, actual, len);
|
|
|
|
|
if (nwrote == -1) {
|
|
|
|
|
if (errno == EINTR) {
|
|
|
|
|
error ("sysctl: interrupted, will try again");
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (nwrote != len && errno != EEXIST) {
|
|
|
|
|
error ("sysctl: failed to set '%s' to '%s': (%d) %s",
|
|
|
|
|
path, value, errno, strerror (errno));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
g_free (actual);
|
|
|
|
|
close (fd);
|
|
|
|
|
return (nwrote == len);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char *
|
2014-01-21 11:23:16 +01:00
|
|
|
sysctl_get (NMPlatform *platform, const char *path, gboolean silent_on_error)
|
2013-04-03 16:10:38 +02:00
|
|
|
{
|
|
|
|
|
GError *error = NULL;
|
|
|
|
|
char *contents;
|
|
|
|
|
|
|
|
|
|
if (!g_file_get_contents (path, &contents, NULL, &error)) {
|
2014-01-21 11:23:16 +01:00
|
|
|
if (!silent_on_error)
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return g_strstrip (contents);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
|
|
|
|
2013-03-27 22:23:24 +01:00
|
|
|
static GArray *
|
|
|
|
|
link_get_all (NMPlatform *platform)
|
|
|
|
|
{
|
|
|
|
|
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
|
|
|
|
|
GArray *links = g_array_sized_new (TRUE, TRUE, sizeof (NMPlatformLink), nl_cache_nitems (priv->link_cache));
|
|
|
|
|
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)) {
|
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
|
|
|
link_init (platform, &device, rtnl_link);
|
2013-06-06 11:41:30 -05:00
|
|
|
g_array_append_val (links, device);
|
|
|
|
|
}
|
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
|
|
|
}
|
|
|
|
|
|
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:
|
|
|
|
|
error ("Firmware not found; Netlink error: %s)", 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:
|
|
|
|
|
error ("Netlink error: %s", nl_geterror (nle));
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
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-01-22 13:20:18 -05:00
|
|
|
path = g_strdup_printf ("/sys/class/net/%s/phys_port_id", ifname);
|
2014-01-30 16:33:55 +01:00
|
|
|
id = sysctl_get (platform, path, TRUE);
|
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
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
return g_strdup_printf ("/sys/class/net/%s/%s/%s", name, category, option);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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-01-21 11:23:16 +01:00
|
|
|
return path ? nm_platform_sysctl_get (path, FALSE) : 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);
|
|
|
|
|
|
|
|
|
|
path = g_strdup_printf ("/sys/class/net/%s/create_child", parent_name);
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
path = g_strdup_printf ("/sys/class/net/%s/owner", ifname);
|
2014-01-21 11:04:26 +01:00
|
|
|
val = nm_platform_sysctl_get (path, TRUE);
|
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-01-21 11:04:26 +01:00
|
|
|
val = nm_platform_sysctl_get (path, TRUE);
|
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-01-21 11:04:26 +01:00
|
|
|
val = nm_platform_sysctl_get (path, TRUE);
|
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 },
|
2013-06-04 12:01:38 -03:00
|
|
|
#ifdef IFLA_MACVLAN_FLAGS
|
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);
|
|
|
|
|
return (err == 0);
|
|
|
|
|
}
|
|
|
|
|
|
2013-05-21 12:49:24 -03:00
|
|
|
static const struct nla_policy gre_info_policy[IFLA_GRE_MAX + 1] = {
|
|
|
|
|
[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 },
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
return (err == 0);
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-27 22:23:24 +01:00
|
|
|
/******************************************************************/
|
|
|
|
|
|
2013-03-27 22:23:24 +01:00
|
|
|
static int
|
|
|
|
|
ip_address_mark_all (NMPlatform *platform, int family, int ifindex)
|
|
|
|
|
{
|
|
|
|
|
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
|
|
|
|
|
struct nl_object *object;
|
|
|
|
|
int count = 0;
|
|
|
|
|
|
|
|
|
|
for (object = nl_cache_get_first (priv->address_cache); object; object = nl_cache_get_next (object)) {
|
|
|
|
|
nl_object_unmark (object);
|
|
|
|
|
if (rtnl_addr_get_family ((struct rtnl_addr *) object) != family)
|
|
|
|
|
continue;
|
|
|
|
|
if (rtnl_addr_get_ifindex ((struct rtnl_addr *) object) != ifindex)
|
|
|
|
|
continue;
|
|
|
|
|
nl_object_mark (object);
|
|
|
|
|
count++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return count;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
int count;
|
|
|
|
|
|
|
|
|
|
count = ip_address_mark_all (platform, AF_INET, ifindex);
|
|
|
|
|
addresses = g_array_sized_new (TRUE, TRUE, sizeof (NMPlatformIP4Address), count);
|
|
|
|
|
|
|
|
|
|
for (object = nl_cache_get_first (priv->address_cache); object; object = nl_cache_get_next (object)) {
|
|
|
|
|
if (nl_object_is_marked (object)) {
|
|
|
|
|
init_ip4_address (&address, (struct rtnl_addr *) object);
|
2014-01-06 14:14:14 -06:00
|
|
|
address.source = NM_PLATFORM_SOURCE_KERNEL;
|
2013-03-27 22:23:24 +01:00
|
|
|
g_array_append_val (addresses, address);
|
|
|
|
|
nl_object_unmark (object);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
int count;
|
|
|
|
|
|
|
|
|
|
count = ip_address_mark_all (platform, AF_INET6, ifindex);
|
|
|
|
|
addresses = g_array_sized_new (TRUE, TRUE, sizeof (NMPlatformIP6Address), count);
|
|
|
|
|
|
|
|
|
|
for (object = nl_cache_get_first (priv->address_cache); object; object = nl_cache_get_next (object)) {
|
|
|
|
|
if (nl_object_is_marked (object)) {
|
|
|
|
|
init_ip6_address (&address, (struct rtnl_addr *) object);
|
2014-01-06 14:14:14 -06:00
|
|
|
address.source = NM_PLATFORM_SOURCE_KERNEL;
|
2013-03-27 22:23:24 +01:00
|
|
|
g_array_append_val (addresses, address);
|
|
|
|
|
nl_object_unmark (object);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return addresses;
|
|
|
|
|
}
|
|
|
|
|
|
2013-12-03 11:42:28 -06:00
|
|
|
static void
|
|
|
|
|
addr4_to_broadcast (struct in_addr *dst, const struct in_addr *src, guint8 plen)
|
|
|
|
|
{
|
|
|
|
|
guint nbytes = plen / 8;
|
|
|
|
|
guint nbits = plen % 8;
|
|
|
|
|
|
|
|
|
|
g_return_if_fail (plen <= 32);
|
|
|
|
|
g_assert (src);
|
|
|
|
|
g_assert (dst);
|
|
|
|
|
|
|
|
|
|
if (plen >= 32)
|
|
|
|
|
*dst = *src;
|
|
|
|
|
else {
|
|
|
|
|
dst->s_addr = 0xFFFFFFFF;
|
|
|
|
|
memcpy (dst, src, nbytes);
|
|
|
|
|
((guint8 *) dst)[nbytes] = (((const guint8 *) src)[nbytes] | (0xFF >> nbits));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
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,
|
|
|
|
|
guint flags)
|
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
|
|
|
|
2013-12-03 11:42:28 -06:00
|
|
|
/* IPv4 Broadcast address */
|
|
|
|
|
if (family == AF_INET) {
|
|
|
|
|
struct in_addr 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
|
|
|
|
|
|
|
|
addr4_to_broadcast (&bcast, addr, plen);
|
|
|
|
|
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);
|
|
|
|
|
}
|
2013-10-15 20:44:59 +02:00
|
|
|
if (flags)
|
|
|
|
|
rtnl_addr_set_flags (rtnladdr, flags);
|
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,
|
|
|
|
|
guint32 preferred)
|
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,
|
|
|
|
|
plen, lifetime, preferred, 0));
|
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,
|
|
|
|
|
plen, lifetime, preferred, flags));
|
2013-03-27 22:23:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
ip4_address_delete (NMPlatform *platform, int ifindex, in_addr_t addr, int plen)
|
|
|
|
|
{
|
2013-12-02 10:20:26 -05:00
|
|
|
return delete_object (platform, build_rtnl_addr (AF_INET, ifindex, &addr, NULL, plen, 0, 0, 0));
|
2013-03-27 22:23:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
ip6_address_delete (NMPlatform *platform, int ifindex, struct in6_addr addr, int plen)
|
|
|
|
|
{
|
2013-12-02 10:20:26 -05:00
|
|
|
return delete_object (platform, build_rtnl_addr (AF_INET6, ifindex, &addr, NULL, plen, 0, 0, 0));
|
2013-03-27 22:23:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
ip_address_exists (NMPlatform *platform, int family, int ifindex, gconstpointer addr, int plen)
|
|
|
|
|
{
|
2013-12-02 10:20:26 -05:00
|
|
|
auto_nl_object struct nl_object *object = build_rtnl_addr (family, ifindex, addr, NULL, plen, 0, 0, 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
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
|
|
|
|
2013-03-27 22:23:24 +01:00
|
|
|
static int
|
|
|
|
|
ip_route_mark_all (NMPlatform *platform, int family, int ifindex)
|
|
|
|
|
{
|
|
|
|
|
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
|
|
|
|
|
struct nl_object *object;
|
|
|
|
|
int count = 0;
|
|
|
|
|
|
|
|
|
|
for (object = nl_cache_get_first (priv->route_cache); object; object = nl_cache_get_next (object)) {
|
|
|
|
|
struct rtnl_route *rtnlroute = (struct rtnl_route *) object;
|
|
|
|
|
struct rtnl_nexthop *nexthop;
|
|
|
|
|
|
|
|
|
|
nl_object_unmark (object);
|
|
|
|
|
if (rtnl_route_get_type (rtnlroute) != RTN_UNICAST)
|
|
|
|
|
continue;
|
|
|
|
|
if (rtnl_route_get_table (rtnlroute) != RT_TABLE_MAIN)
|
|
|
|
|
continue;
|
2013-07-30 00:24:58 +02:00
|
|
|
if (rtnl_route_get_protocol (rtnlroute) == RTPROT_KERNEL)
|
|
|
|
|
continue;
|
2013-03-27 22:23:24 +01:00
|
|
|
if (rtnl_route_get_family (rtnlroute) != family)
|
|
|
|
|
continue;
|
|
|
|
|
if (rtnl_route_get_nnexthops (rtnlroute) != 1)
|
|
|
|
|
continue;
|
|
|
|
|
nexthop = rtnl_route_nexthop_n (rtnlroute, 0);
|
|
|
|
|
if (rtnl_route_nh_get_ifindex (nexthop) != ifindex)
|
|
|
|
|
continue;
|
|
|
|
|
nl_object_mark (object);
|
|
|
|
|
count++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return count;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
int count = 0;
|
|
|
|
|
|
|
|
|
|
count = ip_route_mark_all (platform, AF_INET, ifindex);
|
|
|
|
|
routes = g_array_sized_new (TRUE, TRUE, sizeof (NMPlatformIP4Route), count);
|
|
|
|
|
|
|
|
|
|
for (object = nl_cache_get_first (priv->route_cache); object; object = nl_cache_get_next (object)) {
|
|
|
|
|
if (nl_object_is_marked (object)) {
|
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
|
|
|
nl_object_unmark (object);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
int count;
|
|
|
|
|
|
|
|
|
|
count = ip_route_mark_all (platform, AF_INET6, ifindex);
|
|
|
|
|
routes = g_array_sized_new (TRUE, TRUE, sizeof (NMPlatformIP6Route), count);
|
|
|
|
|
|
|
|
|
|
for (object = nl_cache_get_first (priv->route_cache); object; object = nl_cache_get_next (object)) {
|
|
|
|
|
if (nl_object_is_marked (object)) {
|
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
|
|
|
nl_object_unmark (object);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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,
|
|
|
|
|
GIOCondition io_condition,
|
|
|
|
|
gpointer user_data)
|
|
|
|
|
{
|
|
|
|
|
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (user_data);
|
|
|
|
|
int nle;
|
|
|
|
|
|
|
|
|
|
nle = nl_recvmsgs_default (priv->nlh_event);
|
|
|
|
|
if (nle)
|
|
|
|
|
error ("Failed to retrieve incoming events: %s", nl_geterror (nle));
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2013-08-02 18:51:06 +02:00
|
|
|
announce_object (platform, (struct nl_object *) rtnllink, 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
|
|
|
|
|
|
|
|
if (!strcmp (action, "add"))
|
|
|
|
|
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));
|
|
|
|
|
|
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);
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
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-05-21 12:49:24 -03:00
|
|
|
platform_class->gre_get_properties = gre_get_properties;
|
2013-05-03 13:55:51 -04:00
|
|
|
|
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
|
|
|
}
|