Commit 15fd7cd7 authored by Pavel Šimerda's avatar Pavel Šimerda

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.
parent b5306030
......@@ -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);
......
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)
/* -*- 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;
}
/* -*- 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 */
This diff is collapsed.
/* -*- 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 */
<
/* -*- 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);
}