mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager
synced 2024-10-04 15:21:12 +00:00
platform: link management
Create the new nm-platform framework and implement link (or interface) management. The nm-platform serves as the point of contact between the rest of NetworkManager and the operating system. There are two backends for nm-platform: * NMFakePlatform: Fake kernel backend for testing purposes * NMLinuxPlatform: Linux kernel backend for actual use A comprehensive testsuite is included and will be extended with new feature additions. To enable the Linux part of the testsuite, use --enable-tests=root configure options and run 'make check' as root. Use --enable-code-coverage for code coverage support. ./autogen.sh --enable-tests=root --enable-code-coverage make make -C src/platform check-code-coverage Link features: * Retrieve the list of links * Translate between indexes and names * Discover device type * Add/remove dummy interfaces (for testing) Thanks to Thomas Graf for helping with libnl3 synchronization issues.
This commit is contained in:
parent
b530603019
commit
15fd7cd75e
|
@ -679,6 +679,8 @@ src/settings/plugins/keyfile/tests/Makefile
|
|||
src/settings/plugins/keyfile/tests/keyfiles/Makefile
|
||||
src/settings/plugins/example/Makefile
|
||||
src/settings/tests/Makefile
|
||||
src/platform/Makefile
|
||||
src/platform/tests/Makefile
|
||||
src/wimax/Makefile
|
||||
libnm-util/libnm-util.pc
|
||||
libnm-util/Makefile
|
||||
|
|
|
@ -3,6 +3,7 @@ SUBDIRS= \
|
|||
logging \
|
||||
config \
|
||||
posix-signals \
|
||||
platform \
|
||||
dns-manager \
|
||||
vpn-manager \
|
||||
dhcp-manager \
|
||||
|
@ -37,6 +38,7 @@ INCLUDES = -I${top_srcdir} \
|
|||
-I${top_srcdir}/src/dhcp-manager \
|
||||
-I${top_srcdir}/src/ip6-manager \
|
||||
-I${top_srcdir}/src/supplicant-manager \
|
||||
-I${top_srcdir}/src/platform \
|
||||
-I${top_srcdir}/src/dnsmasq-manager \
|
||||
-I${top_srcdir}/src/modem-manager \
|
||||
-I$(top_srcdir)/src/bluez-manager \
|
||||
|
@ -326,6 +328,7 @@ NetworkManager_LDADD = \
|
|||
./logging/libnm-logging.la \
|
||||
./config/libnm-config.la \
|
||||
./posix-signals/libnm-posix-signals.la \
|
||||
./platform/libnm-platform.la \
|
||||
./dns-manager/libdns-manager.la \
|
||||
./vpn-manager/libvpn-manager.la \
|
||||
./dhcp-manager/libdhcp-manager.la \
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
#include "NetworkManagerUtils.h"
|
||||
#include "nm-manager.h"
|
||||
#include "nm-policy.h"
|
||||
#include "nm-linux-platform.h"
|
||||
#include "nm-dns-manager.h"
|
||||
#include "nm-dbus-manager.h"
|
||||
#include "nm-supplicant-manager.h"
|
||||
|
@ -484,6 +485,9 @@ main (int argc, char *argv[])
|
|||
|
||||
main_loop = g_main_loop_new (NULL, FALSE);
|
||||
|
||||
/* Set up platform interaction layer */
|
||||
nm_linux_platform_setup ();
|
||||
|
||||
/* Initialize our DBus service & connection */
|
||||
dbus_mgr = nm_dbus_manager_get ();
|
||||
g_assert (dbus_mgr != NULL);
|
||||
|
|
30
src/platform/Makefile.am
Normal file
30
src/platform/Makefile.am
Normal file
|
@ -0,0 +1,30 @@
|
|||
SUBDIRS = . tests
|
||||
|
||||
AM_CPPFLAGS = \
|
||||
-I${top_srcdir} \
|
||||
-I${top_srcdir}/src \
|
||||
-I${top_srcdir}/src/logging \
|
||||
-I${top_srcdir}/libnm-util \
|
||||
-DKERNEL_HACKS=1
|
||||
|
||||
@GNOME_CODE_COVERAGE_RULES@
|
||||
|
||||
noinst_LTLIBRARIES = libnm-platform.la
|
||||
|
||||
libnm_platform_la_SOURCES = \
|
||||
nm-platform.h \
|
||||
nm-platform.c \
|
||||
nm-fake-platform.h \
|
||||
nm-fake-platform.c \
|
||||
nm-linux-platform.h \
|
||||
nm-linux-platform.c
|
||||
|
||||
libnm_platform_la_CPPFLAGS = \
|
||||
$(AM_CPPFLAGS) \
|
||||
$(GLIB_CFLAGS) \
|
||||
$(LIBNL_CFLAGS)
|
||||
|
||||
libnm_platform_la_LIBADD = \
|
||||
$(top_builddir)/src/logging/libnm-logging.la \
|
||||
$(GLIB_LIBS) \
|
||||
$(LIBNL_LIBS)
|
216
src/platform/nm-fake-platform.c
Normal file
216
src/platform/nm-fake-platform.c
Normal file
|
@ -0,0 +1,216 @@
|
|||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
|
||||
/* nm-platform-fake.c - Fake platform interaction code for testing NetworkManager
|
||||
*
|
||||
* 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 <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <netinet/icmp6.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include "nm-fake-platform.h"
|
||||
#include "nm-logging.h"
|
||||
|
||||
#define debug(format, ...) nm_log_dbg (LOGD_PLATFORM, format, __VA_ARGS__)
|
||||
|
||||
typedef struct {
|
||||
GArray *links;
|
||||
} NMFakePlatformPrivate;
|
||||
|
||||
#define NM_FAKE_PLATFORM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_FAKE_PLATFORM, NMFakePlatformPrivate))
|
||||
|
||||
G_DEFINE_TYPE (NMFakePlatform, nm_fake_platform, NM_TYPE_PLATFORM)
|
||||
|
||||
/******************************************************************/
|
||||
|
||||
void
|
||||
nm_fake_platform_setup (void)
|
||||
{
|
||||
nm_platform_setup (NM_TYPE_FAKE_PLATFORM);
|
||||
}
|
||||
|
||||
/******************************************************************/
|
||||
|
||||
static void
|
||||
link_init (NMPlatformLink *device, int ifindex, int type, const char *name)
|
||||
{
|
||||
g_assert (!name || strlen (name) < sizeof(device->name));
|
||||
|
||||
memset (device, 0, sizeof (*device));
|
||||
|
||||
device->ifindex = name ? ifindex : 0;
|
||||
device->type = type;
|
||||
if (name)
|
||||
strcpy (device->name, name);
|
||||
}
|
||||
|
||||
static NMPlatformLink *
|
||||
link_get (NMPlatform *platform, int ifindex)
|
||||
{
|
||||
NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform);
|
||||
NMPlatformLink *device;
|
||||
|
||||
if (ifindex >= priv->links->len)
|
||||
goto not_found;
|
||||
device = &g_array_index (priv->links, NMPlatformLink, ifindex);
|
||||
if (!device->ifindex)
|
||||
goto not_found;
|
||||
|
||||
return device;
|
||||
not_found:
|
||||
debug ("link not found: %d", ifindex);
|
||||
platform->error = NM_PLATFORM_ERROR_NOT_FOUND;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static GArray *
|
||||
link_get_all (NMPlatform *platform)
|
||||
{
|
||||
NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform);
|
||||
GArray *links = g_array_sized_new (TRUE, TRUE, sizeof (NMPlatformLink), priv->links->len);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < priv->links->len; i++)
|
||||
if (g_array_index (priv->links, NMPlatformLink, i).ifindex)
|
||||
g_array_append_val (links, g_array_index (priv->links, NMPlatformLink, i));
|
||||
|
||||
return links;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
link_add (NMPlatform *platform, const char *name, NMLinkType type)
|
||||
{
|
||||
NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform);
|
||||
NMPlatformLink device;
|
||||
|
||||
link_init (&device, priv->links->len, type, name);
|
||||
|
||||
g_array_append_val (priv->links, device);
|
||||
|
||||
if (device.ifindex)
|
||||
g_signal_emit_by_name (platform, NM_PLATFORM_LINK_ADDED, &device);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
link_delete (NMPlatform *platform, int ifindex)
|
||||
{
|
||||
NMPlatformLink *device = link_get (platform, ifindex);
|
||||
NMPlatformLink deleted_device;
|
||||
|
||||
if (!device)
|
||||
return FALSE;
|
||||
|
||||
memcpy (&deleted_device, device, sizeof (deleted_device));
|
||||
memset (device, 0, sizeof (*device));
|
||||
|
||||
g_signal_emit_by_name (platform, NM_PLATFORM_LINK_REMOVED, &deleted_device);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static int
|
||||
link_get_ifindex (NMPlatform *platform, const char *name)
|
||||
{
|
||||
NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < priv->links->len; i++) {
|
||||
NMPlatformLink *device = &g_array_index (priv->links, NMPlatformLink, i);
|
||||
|
||||
if (device && !g_strcmp0 (device->name, name))
|
||||
return device->ifindex;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *
|
||||
link_get_name (NMPlatform *platform, int ifindex)
|
||||
{
|
||||
NMPlatformLink *device = link_get (platform, ifindex);
|
||||
|
||||
return device ? device->name : NULL;
|
||||
}
|
||||
|
||||
static NMLinkType
|
||||
link_get_type (NMPlatform *platform, int ifindex)
|
||||
{
|
||||
NMPlatformLink *device = link_get (platform, ifindex);
|
||||
|
||||
return device ? device->type : NM_LINK_TYPE_NONE;
|
||||
}
|
||||
|
||||
/******************************************************************/
|
||||
|
||||
static void
|
||||
nm_fake_platform_init (NMFakePlatform *fake_platform)
|
||||
{
|
||||
NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (fake_platform);
|
||||
|
||||
priv->links = g_array_new (TRUE, TRUE, sizeof (NMPlatformLink));
|
||||
}
|
||||
|
||||
static gboolean
|
||||
setup (NMPlatform *platform)
|
||||
{
|
||||
/* skip zero element */
|
||||
link_add (platform, NULL, NM_LINK_TYPE_NONE);
|
||||
|
||||
/* add loopback interface */
|
||||
link_add (platform, "lo", NM_LINK_TYPE_LOOPBACK);
|
||||
|
||||
/* add some ethernets */
|
||||
link_add (platform, "eth0", NM_LINK_TYPE_ETHERNET);
|
||||
link_add (platform, "eth1", NM_LINK_TYPE_ETHERNET);
|
||||
link_add (platform, "eth2", NM_LINK_TYPE_ETHERNET);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
nm_fake_platform_finalize (GObject *object)
|
||||
{
|
||||
NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (object);
|
||||
|
||||
g_array_unref (priv->links);
|
||||
|
||||
G_OBJECT_CLASS (nm_fake_platform_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
nm_fake_platform_class_init (NMFakePlatformClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
NMPlatformClass *platform_class = NM_PLATFORM_CLASS (klass);
|
||||
|
||||
g_type_class_add_private (klass, sizeof (NMFakePlatformPrivate));
|
||||
|
||||
/* virtual methods */
|
||||
object_class->finalize = nm_fake_platform_finalize;
|
||||
|
||||
platform_class->setup = setup;
|
||||
|
||||
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;
|
||||
}
|
49
src/platform/nm-fake-platform.h
Normal file
49
src/platform/nm-fake-platform.h
Normal file
|
@ -0,0 +1,49 @@
|
|||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
|
||||
/* nm-fake-platform.h - Fake platform interaction code for testing NetworkManager
|
||||
*
|
||||
* 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 Red Hat, Inc.
|
||||
*/
|
||||
|
||||
#ifndef NM_FAKE_PLATFORM_H
|
||||
#define NM_FAKE_PLATFORM_H
|
||||
|
||||
#include "nm-platform.h"
|
||||
|
||||
#define NM_TYPE_FAKE_PLATFORM (nm_fake_platform_get_type ())
|
||||
#define NM_FAKE_PLATFORM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_FAKE_PLATFORM, NMFakePlatform))
|
||||
#define NM_FAKE_PLATFORM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_FAKE_PLATFORM, NMFakePlatformClass))
|
||||
#define NM_IS_FAKE_PLATFORM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_FAKE_PLATFORM))
|
||||
#define NM_IS_FAKE_PLATFORM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_FAKE_PLATFORM))
|
||||
#define NM_FAKE_PLATFORM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_FAKE_PLATFORM, NMFakePlatformClass))
|
||||
|
||||
/******************************************************************/
|
||||
|
||||
typedef struct {
|
||||
NMPlatform parent;
|
||||
} NMFakePlatform;
|
||||
|
||||
typedef struct {
|
||||
NMPlatformClass parent;
|
||||
} NMFakePlatformClass;
|
||||
|
||||
/******************************************************************/
|
||||
|
||||
GType nm_fake_platform_get_type (void);
|
||||
|
||||
void nm_fake_platform_setup (void);
|
||||
|
||||
#endif /* NM_FAKE_PLATFORM_H */
|
701
src/platform/nm-linux-platform.c
Normal file
701
src/platform/nm-linux-platform.c
Normal file
|
@ -0,0 +1,701 @@
|
|||
/* -*- 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>
|
||||
#include <netinet/icmp6.h>
|
||||
#include <netinet/in.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <netlink/netlink.h>
|
||||
#include <netlink/object.h>
|
||||
#include <netlink/cache.h>
|
||||
#include <netlink/route/link.h>
|
||||
#include <netlink/route/link/vlan.h>
|
||||
|
||||
#include "nm-linux-platform.h"
|
||||
#include "nm-logging.h"
|
||||
|
||||
/* 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;
|
||||
GIOChannel *event_channel;
|
||||
guint event_id;
|
||||
} 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 */
|
||||
#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;
|
||||
}
|
||||
}
|
||||
|
||||
/* libnl doesn't use const where due */
|
||||
#define nl_addr_build(family, addr, addrlen) nl_addr_build (family, (gpointer) addr, addrlen)
|
||||
|
||||
typedef enum {
|
||||
LINK,
|
||||
N_TYPES
|
||||
} ObjectType;
|
||||
|
||||
typedef enum {
|
||||
ADDED,
|
||||
CHANGED,
|
||||
REMOVED,
|
||||
N_STATUSES
|
||||
} ObjectStatus;
|
||||
|
||||
static ObjectType
|
||||
object_type_from_nl_object (const struct nl_object *object)
|
||||
{
|
||||
g_assert (object);
|
||||
|
||||
if (!strcmp (nl_object_get_type (object), "route/link"))
|
||||
return LINK;
|
||||
else
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
/* libnl inclues LINK_ATTR_FAMILY in oo_id_attrs of link_obj_ops and thus
|
||||
* refuses to search for items that lack this attribute. I believe this is a
|
||||
* bug or a bad design at the least. Address family is not an identifying
|
||||
* attribute of a network interface and IMO is not an attribute of a network
|
||||
* interface at all.
|
||||
*/
|
||||
static struct nl_object *
|
||||
nm_nl_cache_search (struct nl_cache *cache, struct nl_object *needle)
|
||||
{
|
||||
if (object_type_from_nl_object (needle) == LINK)
|
||||
rtnl_link_set_family ((struct rtnl_link *) needle, AF_UNSPEC);
|
||||
|
||||
return nl_cache_search (cache, needle);
|
||||
}
|
||||
#define nl_cache_search nm_nl_cache_search
|
||||
|
||||
/* 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.
|
||||
*
|
||||
* libnl 3.2 doesn't seem to provide such functionality.
|
||||
*/
|
||||
static struct nl_object *
|
||||
get_kernel_object (struct nl_sock *sock, struct nl_object *needle)
|
||||
{
|
||||
|
||||
switch (object_type_from_nl_object (needle)) {
|
||||
case LINK:
|
||||
{
|
||||
struct nl_object *kernel_object;
|
||||
int ifindex = rtnl_link_get_ifindex ((struct rtnl_link *) needle);
|
||||
const char *name = rtnl_link_get_name ((struct rtnl_link *) needle);
|
||||
int nle;
|
||||
|
||||
nle = rtnl_link_get_kernel (sock, ifindex, name, (struct rtnl_link **) &kernel_object);
|
||||
switch (nle) {
|
||||
case -NLE_SUCCESS:
|
||||
return kernel_object;
|
||||
case -NLE_NODEV:
|
||||
return NULL;
|
||||
default:
|
||||
error ("Netlink error: %s", nl_geterror (nle));
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
default:
|
||||
/* Fallback to a one-time cache allocation. */
|
||||
{
|
||||
struct nl_cache *cache;
|
||||
struct nl_object *object;
|
||||
int nle;
|
||||
|
||||
nle = nl_cache_alloc_and_fill (
|
||||
nl_cache_ops_lookup (nl_object_get_type (needle)),
|
||||
sock, &cache);
|
||||
g_return_val_if_fail (!nle, NULL);
|
||||
object = nl_cache_search (cache, needle);
|
||||
|
||||
nl_cache_put (cache);
|
||||
return object;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* libnl 3.2 doesn't seem to provide such a generic way to add libnl-route objects. */
|
||||
static gboolean
|
||||
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);
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
}
|
||||
|
||||
/* libnl 3.2 doesn't seem to provide such a generic way to delete libnl-route objects. */
|
||||
static int
|
||||
delete_kernel_object (struct nl_sock *sock, struct nl_object *object)
|
||||
{
|
||||
switch (object_type_from_nl_object (object)) {
|
||||
case LINK:
|
||||
return rtnl_link_delete (sock, (struct rtnl_link *) object);
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************/
|
||||
|
||||
/* Object type specific utilities */
|
||||
|
||||
static const char *
|
||||
type_to_string (NMLinkType type)
|
||||
{
|
||||
switch (type) {
|
||||
case NM_LINK_TYPE_DUMMY:
|
||||
return "dummy";
|
||||
default:
|
||||
g_warning ("Wrong type: %d", type);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static NMLinkType
|
||||
link_extract_type (struct rtnl_link *rtnllink)
|
||||
{
|
||||
const char *type;
|
||||
|
||||
if (!rtnllink)
|
||||
return NM_LINK_TYPE_NONE;
|
||||
|
||||
type = rtnl_link_get_type (rtnllink);
|
||||
|
||||
if (!type)
|
||||
switch (rtnl_link_get_arptype (rtnllink)) {
|
||||
case ARPHRD_LOOPBACK:
|
||||
return NM_LINK_TYPE_LOOPBACK;
|
||||
case ARPHRD_ETHER:
|
||||
return NM_LINK_TYPE_ETHERNET;
|
||||
default:
|
||||
return NM_LINK_TYPE_GENERIC;
|
||||
}
|
||||
else if (!g_strcmp0 (type, "dummy"))
|
||||
return NM_LINK_TYPE_DUMMY;
|
||||
else
|
||||
return NM_LINK_TYPE_UNKNOWN;
|
||||
}
|
||||
|
||||
static void
|
||||
link_init (NMPlatformLink *info, struct rtnl_link *rtnllink)
|
||||
{
|
||||
memset (info, 0, sizeof (*info));
|
||||
|
||||
g_assert (rtnllink);
|
||||
|
||||
info->ifindex = rtnl_link_get_ifindex (rtnllink);
|
||||
strcpy (info->name, rtnl_link_get_name (rtnllink));
|
||||
info->type = link_extract_type (rtnllink);
|
||||
}
|
||||
|
||||
/******************************************************************/
|
||||
|
||||
/* Object and cache manipulation */
|
||||
|
||||
static const char *signal_by_type_and_status[N_TYPES][N_STATUSES] = {
|
||||
{ NM_PLATFORM_LINK_ADDED, NM_PLATFORM_LINK_CHANGED, NM_PLATFORM_LINK_REMOVED },
|
||||
};
|
||||
|
||||
static struct nl_cache *
|
||||
choose_cache (NMPlatform *platform, struct nl_object *object)
|
||||
{
|
||||
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
|
||||
|
||||
switch (object_type_from_nl_object (object)) {
|
||||
case LINK:
|
||||
return priv->link_cache;
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
announce_object (NMPlatform *platform, const struct nl_object *object, ObjectStatus status)
|
||||
{
|
||||
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;
|
||||
|
||||
link_init (&device, (struct rtnl_link *) object);
|
||||
g_signal_emit_by_name (platform, sig, &device);
|
||||
}
|
||||
return;
|
||||
default:
|
||||
error ("Announcing object: object type unknown: %d", object_type);
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
process_nl_error (NMPlatform *platform, int nle)
|
||||
{
|
||||
/* NLE_EXIST is considered equivalent to success to avoid race conditions. You
|
||||
* never know when something sends an identical object just before
|
||||
* NetworkManager, e.g. from a dispatcher script.
|
||||
*/
|
||||
switch (nle) {
|
||||
case -NLE_SUCCESS:
|
||||
case -NLE_EXIST:
|
||||
return FALSE;
|
||||
default:
|
||||
error ("Netlink error: %s", nl_geterror (nle));
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
refresh_object (NMPlatform *platform, struct nl_object *object, int nle)
|
||||
{
|
||||
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;
|
||||
|
||||
if (process_nl_error (platform, nle))
|
||||
return FALSE;
|
||||
|
||||
cache = choose_cache (platform, object);
|
||||
cached_object = nl_cache_search (choose_cache (platform, object), object);
|
||||
kernel_object = get_kernel_object (priv->nlh, object);
|
||||
|
||||
g_return_val_if_fail (kernel_object, FALSE);
|
||||
|
||||
if (cached_object) {
|
||||
nl_cache_remove (cached_object);
|
||||
nle = nl_cache_add (cache, kernel_object);
|
||||
g_return_val_if_fail (!nle, 0);
|
||||
} else {
|
||||
nle = nl_cache_add (cache, kernel_object);
|
||||
g_return_val_if_fail (!nle, FALSE);
|
||||
}
|
||||
|
||||
announce_object (platform, kernel_object, cached_object ? CHANGED : ADDED);
|
||||
|
||||
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);
|
||||
|
||||
return refresh_object (platform, object, add_kernel_object (priv->nlh, object));
|
||||
}
|
||||
|
||||
/* 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);
|
||||
auto_nl_object struct nl_object *object = obj;
|
||||
auto_nl_object struct nl_object *cached_object;
|
||||
|
||||
cached_object = nl_cache_search (choose_cache (platform, object), object);
|
||||
g_assert (cached_object);
|
||||
|
||||
if (process_nl_error (platform, delete_kernel_object (priv->nlh, cached_object)))
|
||||
return FALSE;
|
||||
|
||||
nl_cache_remove (cached_object);
|
||||
announce_object (platform, cached_object, REMOVED);
|
||||
|
||||
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;
|
||||
nl_msg_parse (msg, ref_object, &object);
|
||||
g_return_val_if_fail (object, NL_OK);
|
||||
|
||||
cache = choose_cache (platform, object);
|
||||
cached_object = nl_cache_search (cache, object);
|
||||
kernel_object = get_kernel_object (priv->nlh, object);
|
||||
|
||||
debug ("netlink event (type %d)", event);
|
||||
|
||||
/* Removed object */
|
||||
switch (event) {
|
||||
case RTM_DELLINK:
|
||||
/* 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);
|
||||
announce_object (platform, cached_object, REMOVED);
|
||||
|
||||
return NL_OK;
|
||||
case RTM_NEWLINK:
|
||||
/* 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;
|
||||
}
|
||||
announce_object (platform, kernel_object, ADDED);
|
||||
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;
|
||||
}
|
||||
announce_object (platform, kernel_object, CHANGED);
|
||||
|
||||
return NL_OK;
|
||||
default:
|
||||
error ("Unknown netlink event: %d", event);
|
||||
return NL_OK;
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************/
|
||||
|
||||
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)) {
|
||||
link_init (&device, (struct rtnl_link *) object);
|
||||
g_array_append_val (links, device);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
return add_object (platform, build_rtnl_link (0, name, type));
|
||||
}
|
||||
|
||||
static gboolean
|
||||
link_delete (NMPlatform *platform, int ifindex)
|
||||
{
|
||||
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 struct rtnl_link *
|
||||
link_get (NMPlatform *platform, int ifindex)
|
||||
{
|
||||
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
|
||||
struct rtnl_link *rtnllink = rtnl_link_get (priv->link_cache, ifindex);
|
||||
|
||||
if (!rtnllink)
|
||||
platform->error = NM_PLATFORM_ERROR_NOT_FOUND;
|
||||
|
||||
return rtnllink;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return link_extract_type (rtnllink);
|
||||
}
|
||||
|
||||
/******************************************************************/
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
/******************************************************************/
|
||||
|
||||
static void
|
||||
nm_linux_platform_init (NMLinuxPlatform *platform)
|
||||
{
|
||||
}
|
||||
|
||||
static gboolean
|
||||
setup (NMPlatform *platform)
|
||||
{
|
||||
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
|
||||
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,
|
||||
RTNLGRP_LINK,
|
||||
NULL);
|
||||
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);
|
||||
g_assert (priv->link_cache);
|
||||
|
||||
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);
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
49
src/platform/nm-linux-platform.h
Normal file
49
src/platform/nm-linux-platform.h
Normal file
|
@ -0,0 +1,49 @@
|
|||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
|
||||
/* nm-linux-platform.h - 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 Red Hat, Inc.
|
||||
*/
|
||||
|
||||
#ifndef NM_LINUX_PLATFORM_H
|
||||
#define NM_LINUX_PLATFORM_H
|
||||
|
||||
#include "nm-platform.h"
|
||||
|
||||
#define NM_TYPE_LINUX_PLATFORM (nm_linux_platform_get_type ())
|
||||
#define NM_LINUX_PLATFORM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_LINUX_PLATFORM, NMLinuxPlatform))
|
||||
#define NM_LINUX_PLATFORM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_LINUX_PLATFORM, NMLinuxPlatformClass))
|
||||
#define NM_IS_LINUX_PLATFORM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_LINUX_PLATFORM))
|
||||
#define NM_IS_LINUX_PLATFORM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_LINUX_PLATFORM))
|
||||
#define NM_LINUX_PLATFORM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_LINUX_PLATFORM, NMLinuxPlatformClass))
|
||||
|
||||
/******************************************************************/
|
||||
|
||||
typedef struct {
|
||||
NMPlatform parent;
|
||||
} NMLinuxPlatform;
|
||||
|
||||
typedef struct {
|
||||
NMPlatformClass parent;
|
||||
} NMLinuxPlatformClass;
|
||||
|
||||
/******************************************************************/
|
||||
|
||||
GType nm_linux_platform_get_type (void);
|
||||
|
||||
void nm_linux_platform_setup (void);
|
||||
|
||||
#endif /* NM_LINUX_PLATFORM_H */
|
396
src/platform/nm-platform.c
Normal file
396
src/platform/nm-platform.c
Normal file
|
@ -0,0 +1,396 @@
|
|||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
|
||||
/* nm-platform.c - Handle runtime kernel networking configuration
|
||||
*
|
||||
* 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 Red Hat, Inc.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include "nm-platform.h"
|
||||
#include "nm-logging.h"
|
||||
|
||||
#define debug(...) nm_log_dbg (LOGD_PLATFORM, __VA_ARGS__)
|
||||
|
||||
#define NM_PLATFORM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_PLATFORM, NMPlatformPrivate))
|
||||
|
||||
G_DEFINE_TYPE (NMPlatform, nm_platform, G_TYPE_OBJECT)
|
||||
|
||||
/* NMPlatform signals */
|
||||
enum {
|
||||
LINK_ADDED,
|
||||
LINK_CHANGED,
|
||||
LINK_REMOVED,
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
static guint signals[LAST_SIGNAL] = { 0 };
|
||||
|
||||
/******************************************************************/
|
||||
|
||||
/* Singleton NMPlatform subclass instance and cached class object */
|
||||
static NMPlatform *platform = NULL;
|
||||
static NMPlatformClass *klass = NULL;
|
||||
|
||||
/**
|
||||
* nm_platform_setup:
|
||||
* @type: The #GType for a subclass of #NMPlatform
|
||||
*
|
||||
* Do not use this function directly, it is intended to be called by
|
||||
* NMPlatform subclasses. For the linux platform initialization use
|
||||
* nm_linux_platform_setup() instead.
|
||||
*
|
||||
* Failing to set up #NMPlatform singleton results in a fatal error,
|
||||
* as well as trying to initialize it multiple times without freeing
|
||||
* it.
|
||||
*
|
||||
* NetworkManager will typically use only one platform object during
|
||||
* its run. Test programs might want to switch platform implementations,
|
||||
* though. This is done with a combination of nm_platform_free() and
|
||||
* nm_*_platform_setup().
|
||||
*/
|
||||
void
|
||||
nm_platform_setup (GType type)
|
||||
{
|
||||
gboolean status;
|
||||
|
||||
g_assert (platform == NULL);
|
||||
|
||||
platform = g_object_new (type, NULL);
|
||||
g_assert (NM_IS_PLATFORM (platform));
|
||||
|
||||
klass = NM_PLATFORM_GET_CLASS (platform);
|
||||
g_assert (klass->setup);
|
||||
|
||||
status = klass->setup (platform);
|
||||
g_assert (status);
|
||||
}
|
||||
|
||||
/**
|
||||
* nm_platform_free:
|
||||
*
|
||||
* Free #NMPlatform singleton created by nm_*_platform_setup().
|
||||
*/
|
||||
void
|
||||
nm_platform_free (void)
|
||||
{
|
||||
g_assert (platform);
|
||||
|
||||
g_object_unref (platform);
|
||||
platform = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* nm_platform_get:
|
||||
*
|
||||
* Retrieve #NMPlatform singleton. Use this whenever you want to connect to
|
||||
* #NMPlatform signals. It is an error to call it before nm_*_platform_setup()
|
||||
* or after nm_platform_free().
|
||||
*
|
||||
* Returns: (transfer none): The #NMPlatform singleton reference.
|
||||
*/
|
||||
NMPlatform *
|
||||
nm_platform_get (void)
|
||||
{
|
||||
g_assert (platform);
|
||||
|
||||
return platform;
|
||||
}
|
||||
|
||||
/******************************************************************/
|
||||
|
||||
/**
|
||||
* nm_platform_get_error:
|
||||
*
|
||||
* Convenience function to quickly retrieve the error code of the last
|
||||
* operation.
|
||||
*
|
||||
* Returns: Integer error code.
|
||||
*/
|
||||
int
|
||||
nm_platform_get_error (void)
|
||||
{
|
||||
g_assert (platform);
|
||||
|
||||
return platform->error;
|
||||
}
|
||||
|
||||
/**
|
||||
* nm_platform_get_error_message:
|
||||
*
|
||||
* Returns: Static human-readable string for the error. Don't free.
|
||||
*/
|
||||
const char *
|
||||
nm_platform_get_error_msg (void)
|
||||
{
|
||||
g_assert (platform);
|
||||
|
||||
switch (platform->error) {
|
||||
case NM_PLATFORM_ERROR_NONE:
|
||||
return "unknown error";
|
||||
case NM_PLATFORM_ERROR_NOT_FOUND:
|
||||
return "object not found";
|
||||
case NM_PLATFORM_ERROR_EXISTS:
|
||||
return "object already exists";
|
||||
default:
|
||||
return "invalid error number";
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
reset_error (void)
|
||||
{
|
||||
g_assert (platform);
|
||||
platform->error = NM_PLATFORM_ERROR_NONE;
|
||||
}
|
||||
|
||||
/******************************************************************/
|
||||
|
||||
/**
|
||||
* nm_platform_link_get_all:
|
||||
*
|
||||
* Retrieve a snapshot of configuration for all links at once. The result is
|
||||
* owned by the caller and should be freed with g_array_unref().
|
||||
*/
|
||||
GArray *
|
||||
nm_platform_link_get_all ()
|
||||
{
|
||||
reset_error ();
|
||||
|
||||
g_return_val_if_fail (klass->link_get_all, NULL);
|
||||
|
||||
return klass->link_get_all (platform);
|
||||
}
|
||||
|
||||
/**
|
||||
* nm_platform_link_add:
|
||||
* @name: Interface name
|
||||
* @type: Interface type
|
||||
*
|
||||
* Add a software interface. Sets platform->error to NM_PLATFORM_ERROR_EXISTS
|
||||
* if interface is already already exists.
|
||||
*/
|
||||
static gboolean
|
||||
nm_platform_link_add (const char *name, NMLinkType type)
|
||||
{
|
||||
reset_error ();
|
||||
|
||||
g_return_val_if_fail (name, FALSE);
|
||||
g_return_val_if_fail (klass->link_add, FALSE);
|
||||
|
||||
if (nm_platform_link_exists (name)) {
|
||||
debug ("link: already exists");
|
||||
platform->error = NM_PLATFORM_ERROR_EXISTS;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return klass->link_add (platform, name, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* nm_platform_dummy_add:
|
||||
* @name: New interface name
|
||||
*
|
||||
* Create a software ethernet-like interface
|
||||
*/
|
||||
gboolean
|
||||
nm_platform_dummy_add (const char *name)
|
||||
{
|
||||
g_return_val_if_fail (name, FALSE);
|
||||
|
||||
debug ("link: adding dummy '%s'", name);
|
||||
return nm_platform_link_add (name, NM_LINK_TYPE_DUMMY);
|
||||
}
|
||||
|
||||
/**
|
||||
* nm_platform_link_exists:
|
||||
* @name: Interface name
|
||||
*
|
||||
* Returns: %TRUE if an interface of this name exists, %FALSE otherwise.
|
||||
*/
|
||||
gboolean
|
||||
nm_platform_link_exists (const char *name)
|
||||
{
|
||||
int ifindex = nm_platform_link_get_ifindex (name);
|
||||
|
||||
reset_error();
|
||||
return ifindex > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* nm_platform_link_delete:
|
||||
* @ifindex: Interface index
|
||||
*
|
||||
* Delete a software interface. Sets platform->error to
|
||||
* NM_PLATFORM_ERROR_NOT_FOUND if ifindex not available.
|
||||
*/
|
||||
gboolean
|
||||
nm_platform_link_delete (int ifindex)
|
||||
{
|
||||
const char *name;
|
||||
|
||||
reset_error ();
|
||||
|
||||
g_return_val_if_fail (ifindex > 0, FALSE);
|
||||
g_return_val_if_fail (klass->link_delete, FALSE);
|
||||
|
||||
name = nm_platform_link_get_name (ifindex);
|
||||
|
||||
if (!name)
|
||||
return FALSE;
|
||||
|
||||
debug ("link: deleting '%s' (%d)", name, ifindex);
|
||||
return klass->link_delete (platform, ifindex);
|
||||
}
|
||||
|
||||
/**
|
||||
* nm_platform_link_delete_by_name:
|
||||
* @name: Interface name
|
||||
*
|
||||
* Delete a software interface.
|
||||
*/
|
||||
gboolean
|
||||
nm_platform_link_delete_by_name (const char *name)
|
||||
{
|
||||
int ifindex = nm_platform_link_get_ifindex (name);
|
||||
|
||||
if (!ifindex)
|
||||
return FALSE;
|
||||
|
||||
return nm_platform_link_delete (ifindex);
|
||||
}
|
||||
|
||||
/**
|
||||
* nm_platform_link_get_index:
|
||||
* @name: Interface name
|
||||
*
|
||||
* Returns: The interface index corresponding to the given interface name
|
||||
* or 0. Inteface name is owned by #NMPlatform, don't free it.
|
||||
*/
|
||||
int
|
||||
nm_platform_link_get_ifindex (const char *name)
|
||||
{
|
||||
int ifindex;
|
||||
|
||||
reset_error ();
|
||||
|
||||
g_return_val_if_fail (name, 0);
|
||||
g_return_val_if_fail (klass->link_get_ifindex, 0);
|
||||
|
||||
ifindex = klass->link_get_ifindex (platform, name);
|
||||
|
||||
if (!ifindex) {
|
||||
debug ("link not found: %s", name);
|
||||
platform->error = NM_PLATFORM_ERROR_NOT_FOUND;
|
||||
}
|
||||
|
||||
return ifindex;
|
||||
}
|
||||
|
||||
/**
|
||||
* nm_platform_link_get_name:
|
||||
* @name: Interface name
|
||||
*
|
||||
* Returns: The interface name corresponding to the given interface index
|
||||
* or NULL.
|
||||
*/
|
||||
const char *
|
||||
nm_platform_link_get_name (int ifindex)
|
||||
{
|
||||
const char *name;
|
||||
|
||||
reset_error ();
|
||||
|
||||
g_return_val_if_fail (ifindex > 0, NULL);
|
||||
g_return_val_if_fail (klass->link_get_name, NULL);
|
||||
|
||||
name = klass->link_get_name (platform, ifindex);
|
||||
|
||||
if (!name) {
|
||||
debug ("link not found: %d", ifindex);
|
||||
platform->error = NM_PLATFORM_ERROR_NOT_FOUND;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* nm_platform_link_get_type:
|
||||
* @ifindex: Interface index.
|
||||
*
|
||||
* Returns: Link type constant as defined in nm-platform.h. On error,
|
||||
* NM_LINK_TYPE_NONE is returned.
|
||||
*/
|
||||
NMLinkType
|
||||
nm_platform_link_get_type (int ifindex)
|
||||
{
|
||||
reset_error ();
|
||||
|
||||
g_return_val_if_fail (klass->link_get_type, NM_LINK_TYPE_NONE);
|
||||
|
||||
return klass->link_get_type (platform, ifindex);
|
||||
}
|
||||
|
||||
/******************************************************************/
|
||||
|
||||
static void
|
||||
log_link_added (NMPlatform *p, NMPlatformLink *info, gpointer user_data)
|
||||
{
|
||||
debug ("signal: link address: '%s' (%d)", info->name, info->ifindex);
|
||||
}
|
||||
|
||||
static void
|
||||
log_link_changed (NMPlatform *p, NMPlatformLink *info, gpointer user_data)
|
||||
{
|
||||
debug ("signal: link changed: '%s' (%d)", info->name, info->ifindex);
|
||||
}
|
||||
|
||||
static void
|
||||
log_link_removed (NMPlatform *p, NMPlatformLink *info, gpointer user_data)
|
||||
{
|
||||
debug ("signal: link removed: '%s' (%d)", info->name, info->ifindex);
|
||||
}
|
||||
|
||||
/******************************************************************/
|
||||
|
||||
static void
|
||||
nm_platform_init (NMPlatform *object)
|
||||
{
|
||||
}
|
||||
|
||||
#define SIGNAL(signal_id, method) signals[signal_id] = \
|
||||
g_signal_new_class_handler (NM_PLATFORM_ ## signal_id, \
|
||||
G_OBJECT_CLASS_TYPE (object_class), \
|
||||
G_SIGNAL_RUN_FIRST, \
|
||||
G_CALLBACK (method), \
|
||||
NULL, NULL, \
|
||||
g_cclosure_marshal_VOID__POINTER, \
|
||||
G_TYPE_NONE, 1, G_TYPE_POINTER); \
|
||||
|
||||
static void
|
||||
nm_platform_class_init (NMPlatformClass *platform_class)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (platform_class);
|
||||
|
||||
/* Signals */
|
||||
SIGNAL (LINK_ADDED, log_link_added)
|
||||
SIGNAL (LINK_CHANGED, log_link_changed)
|
||||
SIGNAL (LINK_REMOVED, log_link_removed)
|
||||
}
|
148
src/platform/nm-platform.h
Normal file
148
src/platform/nm-platform.h
Normal file
|
@ -0,0 +1,148 @@
|
|||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
|
||||
/* nm-platform.c - Handle runtime kernel networking configuration
|
||||
*
|
||||
* 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) 2009 - 2010 Red Hat, Inc.
|
||||
*/
|
||||
|
||||
#ifndef NM_PLATFORM_H
|
||||
#define NM_PLATFORM_H
|
||||
|
||||
#include <glib-object.h>
|
||||
#include <netinet/in.h>
|
||||
#include <linux/if.h>
|
||||
|
||||
#define NM_TYPE_PLATFORM (nm_platform_get_type ())
|
||||
#define NM_PLATFORM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_PLATFORM, NMPlatform))
|
||||
#define NM_PLATFORM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_PLATFORM, NMPlatformClass))
|
||||
#define NM_IS_PLATFORM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_PLATFORM))
|
||||
#define NM_IS_PLATFORM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_PLATFORM))
|
||||
#define NM_PLATFORM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_PLATFORM, NMPlatformClass))
|
||||
|
||||
/******************************************************************/
|
||||
|
||||
typedef enum {
|
||||
NM_LINK_TYPE_NONE,
|
||||
NM_LINK_TYPE_UNKNOWN,
|
||||
NM_LINK_TYPE_GENERIC,
|
||||
NM_LINK_TYPE_LOOPBACK,
|
||||
NM_LINK_TYPE_ETHERNET,
|
||||
NM_LINK_TYPE_DUMMY,
|
||||
} NMLinkType;
|
||||
|
||||
typedef struct {
|
||||
int ifindex;
|
||||
char name[IFNAMSIZ];
|
||||
NMLinkType type;
|
||||
} NMPlatformLink;
|
||||
|
||||
/******************************************************************/
|
||||
|
||||
/* NMPlatform abstract class and its implementations provide a layer between
|
||||
* networkmanager's device management classes and the operating system kernel.
|
||||
*
|
||||
* How it works, is best seen in tests/nm-platform-test.c source file.
|
||||
*
|
||||
* NMPlatform provides interface to configure kernel interfaces and receive
|
||||
* notifications about both internal and external configuration changes. It
|
||||
* respects the following rules:
|
||||
*
|
||||
* 1) Every change made through NMPlatform is readily available and the respective
|
||||
* signals are called synchronously.
|
||||
*
|
||||
* 2) State of an object retrieved from NMPlatform (through functions or events)
|
||||
* is at least as recent than the state retrieved before.
|
||||
*
|
||||
* Any failure of the above rules should be fixed in NMPlatform implementations
|
||||
* and tested in nm-platform-test. Synchronization hacks should never be put
|
||||
* to any other code. That's why NMPlatform was created and that's why the
|
||||
* testing code was written for it.
|
||||
*
|
||||
* In future, parts of linux platform implementation may be moved to the libnl
|
||||
* library.
|
||||
*
|
||||
* If you have any problems related to NMPlatform on your system, you should
|
||||
* always first run tests/nm-linux-platform-test as root and with all
|
||||
* network configuration daemons stopped. Look at the code first.
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
GObject parent;
|
||||
|
||||
int error;
|
||||
} NMPlatform;
|
||||
|
||||
typedef struct {
|
||||
GObjectClass parent;
|
||||
|
||||
gboolean (*setup) (NMPlatform *);
|
||||
|
||||
GArray *(*link_get_all) (NMPlatform *);
|
||||
gboolean (*link_add) (NMPlatform *, const char *name, NMLinkType type);
|
||||
gboolean (*link_delete) (NMPlatform *, int ifindex);
|
||||
int (*link_get_ifindex) (NMPlatform *, const char *name);
|
||||
const char *(*link_get_name) (NMPlatform *, int ifindex);
|
||||
NMLinkType (*link_get_type) (NMPlatform *, int ifindex);
|
||||
} NMPlatformClass;
|
||||
|
||||
/* NMPlatform signals
|
||||
*
|
||||
* Each signal handler is called with a type-specific object that provides
|
||||
* key attributes that constitute identity of the object. They may also
|
||||
* provide additional attributes for convenience.
|
||||
*
|
||||
* The object only intended to be used by the signal handler to determine
|
||||
* the current values. It is no longer valid after the signal handler exits
|
||||
* but you are free to copy the provided information and use it for later
|
||||
* reference.
|
||||
*/
|
||||
#define NM_PLATFORM_LINK_ADDED "link-added"
|
||||
#define NM_PLATFORM_LINK_CHANGED "link-changed"
|
||||
#define NM_PLATFORM_LINK_REMOVED "link-removed"
|
||||
|
||||
/* NMPlatform error codes */
|
||||
enum {
|
||||
/* no error specified, sometimes this means the arguments were wrong */
|
||||
NM_PLATFORM_ERROR_NONE,
|
||||
/* object was not found */
|
||||
NM_PLATFORM_ERROR_NOT_FOUND,
|
||||
/* object already exists */
|
||||
NM_PLATFORM_ERROR_EXISTS,
|
||||
};
|
||||
|
||||
/******************************************************************/
|
||||
|
||||
GType nm_platform_get_type (void);
|
||||
|
||||
void nm_platform_setup (GType type);
|
||||
NMPlatform *nm_platform_get (void);
|
||||
void nm_platform_free (void);
|
||||
|
||||
/******************************************************************/
|
||||
|
||||
int nm_platform_get_error (void);
|
||||
const char *nm_platform_get_error_msg (void);
|
||||
|
||||
GArray *nm_platform_link_get_all (void);
|
||||
gboolean nm_platform_dummy_add (const char *name);
|
||||
gboolean nm_platform_link_exists (const char *name);
|
||||
gboolean nm_platform_link_delete (int ifindex);
|
||||
gboolean nm_platform_link_delete_by_name (const char *ifindex);
|
||||
int nm_platform_link_get_ifindex (const char *name);
|
||||
const char *nm_platform_link_get_name (int ifindex);
|
||||
NMLinkType nm_platform_link_get_type (int ifindex);
|
||||
|
||||
#endif /* NM_PLATFORM_H */
|
4
src/platform/tests/.gitignore
vendored
Normal file
4
src/platform/tests/.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
/dump
|
||||
/monitor
|
||||
/test-link-fake
|
||||
/test-link-linux
|
72
src/platform/tests/Makefile.am
Normal file
72
src/platform/tests/Makefile.am
Normal file
|
@ -0,0 +1,72 @@
|
|||
if ENABLE_TESTS
|
||||
|
||||
AM_CPPFLAGS = \
|
||||
-I${top_srcdir} \
|
||||
-I${top_srcdir}/src \
|
||||
-I${top_srcdir}/src/logging \
|
||||
-I${top_srcdir}/libnm-util \
|
||||
-I${srcdir}/.. \
|
||||
$(GLIB_CFLAGS) \
|
||||
$(LIBNL_CFLAGS)
|
||||
|
||||
AM_CFLAGS = $(CODE_COVERAGE_CFLAGS)
|
||||
AM_LDFLAGS = $(GLIB_LIBS) $(LIBNL_LIBS) $(CODE_COVERAGE_LDFLAGS)
|
||||
COMMON_LDADD = $(top_builddir)/src/logging/libnm-logging.la
|
||||
PLATFORM_LDADD = $(COMMON_LDADD) $(top_builddir)/src/platform/libnm-platform.la
|
||||
|
||||
@GNOME_CODE_COVERAGE_RULES@
|
||||
|
||||
noinst_PROGRAMS = \
|
||||
dump \
|
||||
monitor \
|
||||
test-link-fake \
|
||||
test-link-linux
|
||||
|
||||
EXTRA_DIST = test-common.h
|
||||
|
||||
monitor_SOURCES = monitor.c
|
||||
monitor_CPPFLAGS = $(AM_CPPFLAGS)
|
||||
monitor_LDADD = $(PLATFORM_LDADD)
|
||||
|
||||
dump_SOURCES = dump.c
|
||||
dump_CPPFLAGS = $(AM_CPPFLAGS)
|
||||
dump_LDADD = $(PLATFORM_LDADD)
|
||||
|
||||
test_link_fake_SOURCES = \
|
||||
test-link.c \
|
||||
test-common.c \
|
||||
${srcdir}/../nm-platform.c \
|
||||
${srcdir}/../nm-fake-platform.c
|
||||
test_link_fake_CPPFLAGS = \
|
||||
$(AM_CPPFLAGS) \
|
||||
-DSETUP=nm_fake_platform_setup \
|
||||
-DKERNEL_HACKS=0
|
||||
test_link_fake_LDADD = $(COMMON_LDADD)
|
||||
|
||||
test_link_linux_SOURCES = \
|
||||
test-link.c \
|
||||
test-common.c \
|
||||
${srcdir}/../nm-platform.c \
|
||||
${srcdir}/../nm-linux-platform.c
|
||||
test_link_linux_CPPFLAGS = \
|
||||
$(AM_CPPFLAGS) \
|
||||
-DSETUP=nm_linux_platform_setup \
|
||||
-DKERNEL_HACKS=1
|
||||
test_link_linux_LDADD = $(COMMON_LDADD)
|
||||
|
||||
# Unfortunately, we cannot run nm-linux-platform-test as an automatic test
|
||||
# program by default, as it requires root access and modifies kernel
|
||||
# configuration.
|
||||
#
|
||||
# However, we can check whether the fake platform fakes platform behavior
|
||||
# correctly.
|
||||
@VALGRIND_RULES@
|
||||
TESTS = ./test-link-fake
|
||||
ROOTTESTS = ./test-link-linux
|
||||
|
||||
# If explicitly enabled, we can run the root tests
|
||||
if RUN_ROOT_TESTS
|
||||
TESTS += $(ROOTTESTS)
|
||||
endif
|
||||
|
||||
endif
|
54
src/platform/tests/dump.c
Normal file
54
src/platform/tests/dump.c
Normal file
|
@ -0,0 +1,54 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "nm-platform.h"
|
||||
#include "nm-linux-platform.h"
|
||||
#include "nm-fake-platform.h"
|
||||
|
||||
static const char *
|
||||
type_to_string (NMLinkType type)
|
||||
{
|
||||
switch (type) {
|
||||
case NM_LINK_TYPE_LOOPBACK:
|
||||
return "loopback";
|
||||
case NM_LINK_TYPE_ETHERNET:
|
||||
return "ethernet";
|
||||
case NM_LINK_TYPE_DUMMY:
|
||||
return "dummy";
|
||||
default:
|
||||
return "unknown-type";
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
dump_interface (NMPlatformLink *link)
|
||||
{
|
||||
printf ("%d: %s: %s", link->ifindex, link->name, type_to_string (link->type));
|
||||
printf ("\n");
|
||||
}
|
||||
|
||||
static void
|
||||
dump_all (void)
|
||||
{
|
||||
GArray *links = nm_platform_link_get_all ();
|
||||
int i;
|
||||
|
||||
for (i = 0; i < links->len; i++)
|
||||
dump_interface (&g_array_index (links, NMPlatformLink, i));
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
g_type_init ();
|
||||
|
||||
g_assert (argc <= 2);
|
||||
if (argc > 1 && !g_strcmp0 (argv[1], "--fake"))
|
||||
nm_fake_platform_setup ();
|
||||
else
|
||||
nm_linux_platform_setup ();
|
||||
|
||||
dump_all ();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
27
src/platform/tests/monitor.c
Normal file
27
src/platform/tests/monitor.c
Normal file
|
@ -0,0 +1,27 @@
|
|||
#include <stdlib.h>
|
||||
#include <syslog.h>
|
||||
|
||||
#include "nm-fake-platform.h"
|
||||
#include "nm-linux-platform.h"
|
||||
#include "nm-logging.h"
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
GMainLoop *loop;
|
||||
|
||||
g_type_init ();
|
||||
loop = g_main_loop_new (NULL, FALSE);
|
||||
nm_logging_setup ("debug", NULL, NULL);
|
||||
openlog (G_LOG_DOMAIN, LOG_CONS | LOG_PERROR, LOG_DAEMON);
|
||||
|
||||
g_assert (argc <= 2);
|
||||
if (argc > 1 && !g_strcmp0 (argv[1], "--fake"))
|
||||
nm_fake_platform_setup ();
|
||||
else
|
||||
nm_linux_platform_setup ();
|
||||
|
||||
g_main_loop_run (loop);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
60
src/platform/tests/test-common.c
Normal file
60
src/platform/tests/test-common.c
Normal file
|
@ -0,0 +1,60 @@
|
|||
#include "test-common.h"
|
||||
|
||||
SignalData *
|
||||
add_signal_full (const char *name, GCallback callback, int ifindex)
|
||||
{
|
||||
SignalData *data = g_new0 (SignalData, 1);
|
||||
|
||||
data->name = name;
|
||||
data->received = FALSE;
|
||||
data->handler_id = g_signal_connect (nm_platform_get (), name, callback, data);
|
||||
data->ifindex = ifindex;
|
||||
|
||||
g_assert (data->handler_id >= 0);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
void
|
||||
accept_signal (SignalData *data)
|
||||
{
|
||||
if (!data->received)
|
||||
g_error ("Attemted to accept a non-received signal '%s'.", data->name);
|
||||
|
||||
data->received = FALSE;
|
||||
}
|
||||
|
||||
void
|
||||
wait_signal (SignalData *data)
|
||||
{
|
||||
data->loop = g_main_loop_new (NULL, FALSE);
|
||||
g_main_loop_run (data->loop);
|
||||
g_main_loop_unref (data->loop);
|
||||
data->loop = NULL;
|
||||
|
||||
accept_signal (data);
|
||||
}
|
||||
|
||||
void
|
||||
free_signal (SignalData *data)
|
||||
{
|
||||
if (data->received)
|
||||
g_error ("Attempted to free received but not accepted signal '%s'.", data->name);
|
||||
|
||||
g_signal_handler_disconnect (nm_platform_get (), data->handler_id);
|
||||
g_free (data);
|
||||
}
|
||||
|
||||
void
|
||||
run_command (const char *format, ...)
|
||||
{
|
||||
char *command;
|
||||
va_list ap;
|
||||
|
||||
va_start (ap, format);
|
||||
|
||||
command = g_strdup_vprintf (format, ap);
|
||||
g_assert (!system (command));
|
||||
g_free (command);
|
||||
}
|
||||
|
28
src/platform/tests/test-common.h
Normal file
28
src/platform/tests/test-common.h
Normal file
|
@ -0,0 +1,28 @@
|
|||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <syslog.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "nm-logging.h"
|
||||
#include "nm-platform.h"
|
||||
#include "nm-fake-platform.h"
|
||||
#include "nm-linux-platform.h"
|
||||
|
||||
#define error(err) g_assert (nm_platform_get_error () == err)
|
||||
#define no_error() error (NM_PLATFORM_ERROR_NONE)
|
||||
|
||||
typedef struct {
|
||||
int handler_id;
|
||||
const char *name;
|
||||
gboolean received;
|
||||
GMainLoop *loop;
|
||||
int ifindex;
|
||||
} SignalData;
|
||||
|
||||
SignalData *add_signal_full (const char *name, GCallback callback, int ifindex);
|
||||
#define add_signal(name, callback) add_signal_full (name, (GCallback) callback, 0)
|
||||
void accept_signal (SignalData *data);
|
||||
void wait_signal (SignalData *data);
|
||||
void free_signal (SignalData *data);
|
||||
|
||||
void run_command (const char *format, ...);
|
190
src/platform/tests/test-link.c
Normal file
190
src/platform/tests/test-link.c
Normal file
|
@ -0,0 +1,190 @@
|
|||
#include "test-common.h"
|
||||
|
||||
#define LO_INDEX 1
|
||||
#define LO_NAME "lo"
|
||||
|
||||
#define DEVICE_NAME "nm-test-device"
|
||||
#define BOGUS_NAME "nm-bogus-device"
|
||||
#define BOGUS_IFINDEX INT_MAX
|
||||
|
||||
static void
|
||||
link_callback (NMPlatform *platform, NMPlatformLink *received, SignalData *data)
|
||||
{
|
||||
|
||||
GArray *links;
|
||||
NMPlatformLink *cached;
|
||||
int i;
|
||||
|
||||
g_assert (received);
|
||||
|
||||
if (data->ifindex && data->ifindex != received->ifindex)
|
||||
return;
|
||||
|
||||
if (data->loop)
|
||||
g_main_loop_quit (data->loop);
|
||||
|
||||
if (data->received)
|
||||
g_error ("Received signal '%s' a second time.", data->name);
|
||||
|
||||
data->received = TRUE;
|
||||
|
||||
/* Check the data */
|
||||
g_assert (received->ifindex > 0);
|
||||
links = nm_platform_link_get_all ();
|
||||
for (i = 0; i < links->len; i++) {
|
||||
cached = &g_array_index (links, NMPlatformLink, i);
|
||||
if (cached->ifindex == received->ifindex) {
|
||||
g_assert (!memcmp (cached, received, sizeof (*cached)));
|
||||
if (!g_strcmp0 (data->name, NM_PLATFORM_LINK_REMOVED)) {
|
||||
g_error ("Deleted link still found in the local cache.");
|
||||
}
|
||||
g_array_unref (links);
|
||||
return;
|
||||
}
|
||||
}
|
||||
g_array_unref (links);
|
||||
|
||||
if (g_strcmp0 (data->name, NM_PLATFORM_LINK_REMOVED))
|
||||
g_error ("Added/changed link not found in the local cache.");
|
||||
}
|
||||
|
||||
static void
|
||||
test_bogus(void)
|
||||
{
|
||||
g_assert (!nm_platform_link_exists (BOGUS_NAME));
|
||||
no_error ();
|
||||
g_assert (!nm_platform_link_delete (BOGUS_IFINDEX));
|
||||
error (NM_PLATFORM_ERROR_NOT_FOUND);
|
||||
g_assert (!nm_platform_link_delete_by_name (BOGUS_NAME));
|
||||
error (NM_PLATFORM_ERROR_NOT_FOUND);
|
||||
g_assert (!nm_platform_link_get_ifindex (BOGUS_NAME));
|
||||
error (NM_PLATFORM_ERROR_NOT_FOUND);
|
||||
g_assert (!nm_platform_link_get_name (BOGUS_IFINDEX));
|
||||
error (NM_PLATFORM_ERROR_NOT_FOUND);
|
||||
g_assert (!nm_platform_link_get_type (BOGUS_IFINDEX));
|
||||
error (NM_PLATFORM_ERROR_NOT_FOUND);
|
||||
}
|
||||
|
||||
static void
|
||||
test_loopback (void)
|
||||
{
|
||||
g_assert (nm_platform_link_exists (LO_NAME));
|
||||
g_assert (nm_platform_link_get_type (LO_INDEX) == NM_LINK_TYPE_LOOPBACK);
|
||||
g_assert (nm_platform_link_get_ifindex (LO_NAME) == LO_INDEX);
|
||||
g_assert (!g_strcmp0 (nm_platform_link_get_name (LO_INDEX), LO_NAME));
|
||||
}
|
||||
|
||||
static void
|
||||
test_internal (void)
|
||||
{
|
||||
SignalData *link_added = add_signal (NM_PLATFORM_LINK_ADDED, link_callback);
|
||||
SignalData *link_changed = add_signal (NM_PLATFORM_LINK_CHANGED, link_callback);
|
||||
SignalData *link_removed = add_signal (NM_PLATFORM_LINK_REMOVED, link_callback);
|
||||
int ifindex;
|
||||
|
||||
/* Check the functions for non-existent devices */
|
||||
g_assert (!nm_platform_link_exists (DEVICE_NAME)); no_error ();
|
||||
g_assert (!nm_platform_link_get_ifindex (DEVICE_NAME));
|
||||
error (NM_PLATFORM_ERROR_NOT_FOUND);
|
||||
|
||||
/* Add device */
|
||||
g_assert (nm_platform_dummy_add (DEVICE_NAME));
|
||||
no_error ();
|
||||
accept_signal (link_added);
|
||||
|
||||
/* Try to add again */
|
||||
g_assert (!nm_platform_dummy_add (DEVICE_NAME));
|
||||
error (NM_PLATFORM_ERROR_EXISTS);
|
||||
|
||||
/* Check device index, name and type */
|
||||
ifindex = nm_platform_link_get_ifindex (DEVICE_NAME);
|
||||
g_assert (ifindex > 0);
|
||||
g_assert (!g_strcmp0 (nm_platform_link_get_name (ifindex), DEVICE_NAME));
|
||||
g_assert (nm_platform_link_get_type (ifindex) == NM_LINK_TYPE_DUMMY);
|
||||
|
||||
/* Delete device */
|
||||
g_assert (nm_platform_link_delete (ifindex));
|
||||
no_error ();
|
||||
accept_signal (link_removed);
|
||||
|
||||
/* Try to delete again */
|
||||
g_assert (!nm_platform_link_delete (ifindex));
|
||||
error (NM_PLATFORM_ERROR_NOT_FOUND);
|
||||
|
||||
/* Add back */
|
||||
g_assert (nm_platform_dummy_add (DEVICE_NAME));
|
||||
no_error ();
|
||||
accept_signal (link_added);
|
||||
|
||||
/* Delete device by name */
|
||||
g_assert (nm_platform_link_delete_by_name (DEVICE_NAME));
|
||||
no_error ();
|
||||
accept_signal (link_removed);
|
||||
|
||||
/* Try to delete again */
|
||||
g_assert (!nm_platform_link_delete_by_name (DEVICE_NAME));
|
||||
error (NM_PLATFORM_ERROR_NOT_FOUND);
|
||||
|
||||
free_signal (link_added);
|
||||
free_signal (link_changed);
|
||||
free_signal (link_removed);
|
||||
}
|
||||
|
||||
static void
|
||||
test_external (void)
|
||||
{
|
||||
SignalData *link_added = add_signal (NM_PLATFORM_LINK_ADDED, link_callback);
|
||||
SignalData *link_changed = add_signal (NM_PLATFORM_LINK_CHANGED, link_callback);
|
||||
SignalData *link_removed = add_signal (NM_PLATFORM_LINK_REMOVED, link_callback);
|
||||
int ifindex;
|
||||
|
||||
run_command ("ip link add %s type %s", DEVICE_NAME, "dummy");
|
||||
wait_signal (link_added);
|
||||
g_assert (nm_platform_link_exists (DEVICE_NAME));
|
||||
ifindex = nm_platform_link_get_ifindex (DEVICE_NAME);
|
||||
g_assert (ifindex > 0);
|
||||
g_assert (!g_strcmp0 (nm_platform_link_get_name (ifindex), DEVICE_NAME));
|
||||
g_assert (nm_platform_link_get_type (ifindex) == NM_LINK_TYPE_DUMMY);
|
||||
|
||||
run_command ("ip link del %s", DEVICE_NAME);
|
||||
wait_signal (link_removed);
|
||||
g_assert (!nm_platform_link_exists (DEVICE_NAME));
|
||||
|
||||
free_signal (link_added);
|
||||
free_signal (link_changed);
|
||||
free_signal (link_removed);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
int result;
|
||||
|
||||
openlog (G_LOG_DOMAIN, LOG_CONS | LOG_PERROR, LOG_DAEMON);
|
||||
g_type_init ();
|
||||
g_test_init (&argc, &argv, NULL);
|
||||
/* Enable debug messages if called with --debug */
|
||||
for (; *argv; argv++) {
|
||||
if (!g_strcmp0 (*argv, "--debug")) {
|
||||
nm_logging_setup ("debug", NULL, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
SETUP ();
|
||||
|
||||
/* Clean up */
|
||||
nm_platform_link_delete_by_name (DEVICE_NAME);
|
||||
g_assert (!nm_platform_link_exists (DEVICE_NAME));
|
||||
|
||||
g_test_add_func ("/link/bogus", test_bogus);
|
||||
g_test_add_func ("/link/loopback", test_loopback);
|
||||
g_test_add_func ("/link/internal", test_internal);
|
||||
|
||||
if (strcmp (g_type_name (G_TYPE_FROM_INSTANCE (nm_platform_get ())), "NMFakePlatform"))
|
||||
g_test_add_func ("/link/external", test_external);
|
||||
|
||||
result = g_test_run ();
|
||||
|
||||
nm_platform_free ();
|
||||
return result;
|
||||
}
|
|
@ -74,3 +74,13 @@
|
|||
fun:g_object_constructor
|
||||
...
|
||||
}
|
||||
{
|
||||
g_signal_new_class_handler
|
||||
Memcheck:Leak
|
||||
...
|
||||
fun:g_closure_new_simple
|
||||
fun:g_cclosure_new
|
||||
fun:g_signal_new_class_handler
|
||||
...
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue