ibft: add settings plugin for reading iBFT configuration (bgo #734009)

Instead of handling iBFT (iSCSI Boot Firmware Table) in the ifcfg-rh plugin,
create a new plugin for it.  This allows all distributions to use iBFT
configuration, and makes both iBFT handling and ifcfg-rh less complicated.

The plugin (like the old ifcfg-rh code) creates read-only connections backed
by the data exported by iscsiadm.  The plugin does not support adding new
connections or modifying existing connections (since the iBFT data is
read-only anyway).  Instead, users should change their iBFT data through
the normal firmware interfaces.

Unmanaged devices can be configured through NetworkManager.conf and the
normal 'keyfile' mechanisms.

(In the future, we'll read this data directly from the kernel's
/sys/firmware/ibft/ethernetX directory instead of iscsiadm, since the
kernel has all the information we need and that's where iscsiadm gets
it from anyway.)

https://bugzilla.gnome.org/show_bug.cgi?id=734009
This commit is contained in:
Dan Williams 2014-07-30 15:37:31 -05:00
parent a27c7e3721
commit 448254f761
25 changed files with 1682 additions and 1 deletions

1
.gitignore vendored
View file

@ -222,6 +222,7 @@ valgrind-*.log
/src/dhcp-manager/tests/test-dhcp-dhclient
/src/dhcp-manager/tests/test-dhcp-options
/src/dnsmasq-manager/tests/test-dnsmasq-utils
/src/settings/plugins/ibft/tests/test-ibft
/src/settings/plugins/ifcfg-rh/tests/network-scripts/*-Test_Write_*
/src/settings/plugins/ifcfg-rh/tests/network-scripts/Test_Write_*
/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh

View file

@ -843,6 +843,8 @@ src/settings/plugins/ifnet/tests/Makefile
src/settings/plugins/ifcfg-rh/Makefile
src/settings/plugins/ifcfg-rh/tests/Makefile
src/settings/plugins/ifcfg-rh/tests/network-scripts/Makefile
src/settings/plugins/ibft/Makefile
src/settings/plugins/ibft/tests/Makefile
src/settings/plugins/ifcfg-suse/Makefile
src/settings/plugins/keyfile/Makefile
src/settings/plugins/keyfile/tests/Makefile

View file

@ -958,5 +958,51 @@ nmtst_assert_connection_unnormalizable (NMConnection *con,
#endif
static inline void
nmtst_assert_ip4_address_equals (guint32 addr, const char *expected, const char *loc)
{
guint32 addr2 = nmtst_inet4_from_string (expected);
if (addr != addr2)
g_error ("assert: %s: ip4 address '%s' expected, but got %s",
loc, expected ? expected : "any", nm_utils_inet4_ntop (addr, NULL));
}
#define nmtst_assert_ip4_address_equals(addr, expected) \
nmtst_assert_ip4_address_equals (addr, expected, G_STRLOC)
#ifdef __NM_UTILS_H__
static inline void
nmtst_assert_hwaddr_equals (gconstpointer hwaddr1, gssize hwaddr1_len, const char *expected, const char *loc)
{
guint8 buf2[NM_UTILS_HWADDR_LEN_MAX];
gsize hwaddr2_len = 1;
const char *p;
gboolean success;
g_assert (hwaddr1_len > 0 && hwaddr1_len <= NM_UTILS_HWADDR_LEN_MAX);
g_assert (expected);
for (p = expected; *p; p++) {
if (*p == ':' || *p == '-')
hwaddr2_len++;
}
g_assert (hwaddr2_len <= NM_UTILS_HWADDR_LEN_MAX);
g_assert (nm_utils_hwaddr_aton (expected, buf2, hwaddr2_len));
/* Manually check the entire hardware address instead of using
* nm_utils_hwaddr_matches() because that function doesn't compare
* entire InfiniBand addresses for various (legitimate) reasons.
*/
success = (hwaddr1_len == hwaddr2_len);
if (success)
success = !memcmp (hwaddr1, buf2, hwaddr1_len);
if (!success) {
g_error ("assert: %s: hwaddr '%s' (%zd) expected, but got %s (%zd)",
loc, expected, hwaddr2_len, nm_utils_hwaddr_ntoa (hwaddr1, hwaddr1_len), hwaddr1_len);
}
}
#define nmtst_assert_hwaddr_equals(hwaddr1, hwaddr1_len, expected) \
nmtst_assert_hwaddr_equals (hwaddr1, hwaddr1_len, expected, G_STRLOC)
#endif
#endif /* __NM_TEST_UTILS_H__ */

View file

@ -123,5 +123,6 @@ src/nm-config.c
src/nm-logging.c
src/nm-manager.c
src/nm-sleep-monitor-systemd.c
src/settings/plugins/ibft/plugin.c
src/settings/plugins/ifcfg-rh/reader.c
src/settings/nm-settings-utils.c

View file

@ -1,4 +1,4 @@
SUBDIRS=keyfile example
SUBDIRS=keyfile example ibft
@GNOME_CODE_COVERAGE_RULES@

View file

@ -0,0 +1,40 @@
SUBDIRS = . tests
@GNOME_CODE_COVERAGE_RULES@
pkglib_LTLIBRARIES = libnm-settings-plugin-ibft.la
noinst_LTLIBRARIES = libibft-io.la
libibft_io_la_SOURCES = \
reader.c \
reader.h \
errors.c \
errors.h
AM_CPPFLAGS = \
-I$(top_srcdir)/src/ \
-I$(top_srcdir)/src/platform \
-I$(top_srcdir)/src/settings \
-I$(top_srcdir)/include \
-I$(top_srcdir)/libnm-core \
-I$(top_builddir)/libnm-core \
-DNETWORKMANAGER_COMPILATION \
-DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE \
$(GLIB_CFLAGS) \
$(DBUS_CFLAGS) \
-DG_LOG_DOMAIN=\""NetworkManager-ibft"\" \
-DSYSCONFDIR=\"$(sysconfdir)\" \
-DSBINDIR=\"$(sbindir)\"
libnm_settings_plugin_ibft_la_SOURCES = \
plugin.c \
plugin.h \
nm-ibft-connection.c \
nm-ibft-connection.h
libnm_settings_plugin_ibft_la_LDFLAGS = -module -avoid-version
libnm_settings_plugin_ibft_la_LIBADD = libibft-io.la
CLEANFILES = $(BUILT_SOURCES)

View file

@ -0,0 +1,34 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
/* NetworkManager system settings service
*
* 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 of the License, 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 2014 Red Hat, Inc.
*/
#include <glib.h>
#include "errors.h"
GQuark
ibft_plugin_error_quark (void)
{
static GQuark error_quark = 0;
if (G_UNLIKELY (error_quark == 0))
error_quark = g_quark_from_static_string ("ibft-plugin-error-quark");
return error_quark;
}

View file

@ -0,0 +1,29 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
/* NetworkManager system settings service
*
* 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 of the License, 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 2014 Red Hat, Inc.
*/
#ifndef __ERRORS_H__
#define __ERRORS_H__
#define IBFT_PLUGIN_ERROR (ibft_plugin_error_quark ())
GQuark ibft_plugin_error_quark (void);
#endif /* __ERRORS_H__ */

View file

@ -0,0 +1,65 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
/* NetworkManager system settings service
*
* 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 of the License, 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 2014 Red Hat, Inc.
*/
#include <string.h>
#include <net/ethernet.h>
#include <netinet/ether.h>
#include <glib/gstdio.h>
#include "nm-ibft-connection.h"
#include "errors.h"
#include "reader.h"
G_DEFINE_TYPE (NMIbftConnection, nm_ibft_connection, NM_TYPE_SETTINGS_CONNECTION)
NMIbftConnection *
nm_ibft_connection_new (const GPtrArray *block, GError **error)
{
NMConnection *source;
GObject *object;
source = connection_from_block (block, error);
if (!source)
return NULL;
object = g_object_new (NM_TYPE_IBFT_CONNECTION, NULL);
/* Update settings with what was read from iscsiadm */
if (!nm_settings_connection_replace_settings (NM_SETTINGS_CONNECTION (object),
source,
FALSE,
error))
g_clear_object (&object);
return (NMIbftConnection *) object;
}
/* GObject */
static void
nm_ibft_connection_init (NMIbftConnection *connection)
{
}
static void
nm_ibft_connection_class_init (NMIbftConnectionClass *ibft_connection_class)
{
}

View file

@ -0,0 +1,50 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
/* NetworkManager system settings service
*
* 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 of the License, 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 2014 Red Hat, Inc.
*/
#ifndef NM_IBFT_CONNECTION_H
#define NM_IBFT_CONNECTION_H
G_BEGIN_DECLS
#include <nm-settings-connection.h>
#define NM_TYPE_IBFT_CONNECTION (nm_ibft_connection_get_type ())
#define NM_IBFT_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_IBFT_CONNECTION, NMIbftConnection))
#define NM_IBFT_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_IBFT_CONNECTION, NMIbftConnectionClass))
#define NM_IS_IBFT_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_IBFT_CONNECTION))
#define NM_IS_IBFT_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_IBFT_CONNECTION))
#define NM_IBFT_CONNECTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_IBFT_CONNECTION, NMIbftConnectionClass))
typedef struct {
NMSettingsConnection parent;
} NMIbftConnection;
typedef struct {
NMSettingsConnectionClass parent;
} NMIbftConnectionClass;
GType nm_ibft_connection_get_type (void);
NMIbftConnection *nm_ibft_connection_new (const GPtrArray *block,
GError **error);
G_END_DECLS
#endif /* NM_IBFT_CONNECTION_H */

View file

@ -0,0 +1,211 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
/* NetworkManager system settings service
*
* 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 of the License, 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 2014 Red Hat, Inc.
*/
#include <config.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <gmodule.h>
#include <glib-object.h>
#include <glib/gi18n.h>
#include <gio/gio.h>
#include <nm-setting-connection.h>
#include "nm-dbus-glib-types.h"
#include "nm-system-config-interface.h"
#include "nm-settings-error.h"
#include "nm-logging.h"
#include "NetworkManagerUtils.h"
#include "plugin.h"
#include "errors.h"
#include "reader.h"
#include "nm-ibft-connection.h"
static void system_config_interface_init (NMSystemConfigInterface *system_config_interface_class);
G_DEFINE_TYPE_EXTENDED (SCPluginIbft, sc_plugin_ibft, G_TYPE_OBJECT, 0,
G_IMPLEMENT_INTERFACE (NM_TYPE_SYSTEM_CONFIG_INTERFACE,
system_config_interface_init))
#define SC_PLUGIN_IBFT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SC_TYPE_PLUGIN_IBFT, SCPluginIbftPrivate))
typedef struct {
GHashTable *connections; /* uuid::connection */
gboolean initialized;
} SCPluginIbftPrivate;
static void
read_connections (SCPluginIbft *self)
{
SCPluginIbftPrivate *priv = SC_PLUGIN_IBFT_GET_PRIVATE (self);
GSList *blocks, *iter;
GError *error = NULL;
NMIbftConnection *connection;
if (!read_ibft_blocks ("/sbin/iscsiadm", &blocks, &error)) {
nm_log_dbg (LOGD_SETTINGS, _("ibft: failed to read iscsiadm records: %s"), error->message);
g_error_free (error);
return;
}
for (iter = blocks; iter; iter = iter->next) {
connection = nm_ibft_connection_new (iter->data, &error);
if (connection) {
nm_log_info (LOGD_SETTINGS, _("ibft: read connection '%s'"),
nm_connection_get_id (NM_CONNECTION (connection)));
g_hash_table_insert (priv->connections,
g_strdup (nm_connection_get_uuid (NM_CONNECTION (connection))),
connection);
} else {
nm_log_warn (LOGD_SETTINGS, _("ibft: failed to read iscsiadm record: %s"), error->message);
g_clear_error (&error);
}
}
g_slist_free_full (blocks, (GDestroyNotify) g_ptr_array_unref);
}
static GSList *
get_connections (NMSystemConfigInterface *config)
{
SCPluginIbft *self = SC_PLUGIN_IBFT (config);
SCPluginIbftPrivate *priv = SC_PLUGIN_IBFT_GET_PRIVATE (self);
GSList *list = NULL;
GHashTableIter iter;
NMIbftConnection *connection;
if (!priv->initialized) {
read_connections (self);
priv->initialized = TRUE;
}
g_hash_table_iter_init (&iter, priv->connections);
while (g_hash_table_iter_next (&iter, NULL, (gpointer) &connection))
list = g_slist_prepend (list, connection);
return list;
}
static void
init (NMSystemConfigInterface *config)
{
}
static void
sc_plugin_ibft_init (SCPluginIbft *self)
{
SCPluginIbftPrivate *priv = SC_PLUGIN_IBFT_GET_PRIVATE (self);
priv->connections = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
}
static void
dispose (GObject *object)
{
SCPluginIbft *self = SC_PLUGIN_IBFT (object);
SCPluginIbftPrivate *priv = SC_PLUGIN_IBFT_GET_PRIVATE (self);
if (priv->connections) {
g_hash_table_destroy (priv->connections);
priv->connections = NULL;
}
G_OBJECT_CLASS (sc_plugin_ibft_parent_class)->dispose (object);
}
static void
get_property (GObject *object, guint prop_id,
GValue *value, GParamSpec *pspec)
{
switch (prop_id) {
case NM_SYSTEM_CONFIG_INTERFACE_PROP_NAME:
g_value_set_string (value, "iBFT");
break;
case NM_SYSTEM_CONFIG_INTERFACE_PROP_INFO:
g_value_set_string (value, "(c) 2014 Red Hat, Inc. To report bugs please use the NetworkManager mailing list.");
break;
case NM_SYSTEM_CONFIG_INTERFACE_PROP_CAPABILITIES:
g_value_set_uint (value, NM_SYSTEM_CONFIG_INTERFACE_CAP_NONE);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
set_property (GObject *object, guint prop_id,
const GValue *value, GParamSpec *pspec)
{
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
static void
sc_plugin_ibft_class_init (SCPluginIbftClass *req_class)
{
GObjectClass *object_class = G_OBJECT_CLASS (req_class);
g_type_class_add_private (req_class, sizeof (SCPluginIbftPrivate));
object_class->dispose = dispose;
object_class->get_property = get_property;
object_class->set_property = set_property;
g_object_class_override_property (object_class,
NM_SYSTEM_CONFIG_INTERFACE_PROP_NAME,
NM_SYSTEM_CONFIG_INTERFACE_NAME);
g_object_class_override_property (object_class,
NM_SYSTEM_CONFIG_INTERFACE_PROP_INFO,
NM_SYSTEM_CONFIG_INTERFACE_INFO);
g_object_class_override_property (object_class,
NM_SYSTEM_CONFIG_INTERFACE_PROP_CAPABILITIES,
NM_SYSTEM_CONFIG_INTERFACE_CAPABILITIES);
g_object_class_override_property (object_class,
NM_SYSTEM_CONFIG_INTERFACE_PROP_HOSTNAME,
NM_SYSTEM_CONFIG_INTERFACE_HOSTNAME);
}
static void
system_config_interface_init (NMSystemConfigInterface *system_config_interface_class)
{
/* interface implementation */
system_config_interface_class->get_connections = get_connections;
system_config_interface_class->init = init;
}
G_MODULE_EXPORT GObject *
nm_system_config_factory (void)
{
static SCPluginIbft *singleton = NULL;
if (!singleton)
singleton = SC_PLUGIN_IBFT (g_object_new (SC_TYPE_PLUGIN_IBFT, NULL));
else
g_object_ref (singleton);
return G_OBJECT (singleton);
}

View file

@ -0,0 +1,47 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
/* NetworkManager system settings service
*
* 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 of the License, 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 2014 Red Hat, Inc.
*/
#ifndef _PLUGIN_H_
#define _PLUGIN_H_
#include <glib-object.h>
#define SC_TYPE_PLUGIN_IBFT (sc_plugin_ibft_get_type ())
#define SC_PLUGIN_IBFT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SC_TYPE_PLUGIN_IBFT, SCPluginIbft))
#define SC_PLUGIN_IBFT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SC_TYPE_PLUGIN_IBFT, SCPluginIbftClass))
#define SC_IS_PLUGIN_IBFT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SC_TYPE_PLUGIN_IBFT))
#define SC_IS_PLUGIN_IBFT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SC_TYPE_PLUGIN_IBFT))
#define SC_PLUGIN_IBFT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SC_TYPE_PLUGIN_IBFT, SCPluginIbftClass))
typedef struct _SCPluginIbft SCPluginIbft;
typedef struct _SCPluginIbftClass SCPluginIbftClass;
struct _SCPluginIbft {
GObject parent;
};
struct _SCPluginIbftClass {
GObjectClass parent;
};
GType sc_plugin_ibft_get_type (void);
#endif /* _PLUGIN_H_ */

View file

@ -0,0 +1,565 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
/* NetworkManager system settings service
*
* 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 of the License, 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 2014 Red Hat, Inc.
*/
#include <config.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <sys/inotify.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <glib.h>
#include <glib/gi18n.h>
#include <nm-connection.h>
#include <nm-setting-connection.h>
#include <nm-setting-ip4-config.h>
#include <nm-setting-ip6-config.h>
#include <nm-setting-vlan.h>
#include <nm-setting-wired.h>
#include <nm-utils.h>
#include "nm-platform.h"
#include "nm-posix-signals.h"
#include "NetworkManagerUtils.h"
#include "nm-logging.h"
#include "errors.h"
#include "reader.h"
#define PARSE_WARNING(msg...) nm_log_warn (LOGD_SETTINGS, " " msg)
static void
iscsiadm_child_setup (gpointer user_data G_GNUC_UNUSED)
{
/* We are in the child process here; set a different process group to
* ensure signal isolation between child and parent.
*/
pid_t pid = getpid ();
setpgid (pid, pid);
/*
* We blocked signals in main(). We need to restore original signal
* mask for iscsiadm here so that it can receive signals.
*/
nm_unblock_posix_signals (NULL);
}
/* Removes trailing whitespace and whitespace before and immediately after the '=' */
static char *
remove_most_whitespace (const char *src)
{
char *s_new, *s2;
const char *svalue;
while (*src && g_ascii_isspace (*src))
src++;
svalue = strchr (src, '=');
if (!svalue || svalue == src)
return NULL;
s_new = g_new (char, strlen (src) + 1);
memcpy (s_new, src, svalue - src);
s_new[svalue - src] = '\0';
g_strchomp (s_new);
svalue++;
while (*svalue && g_ascii_isspace (*svalue))
svalue++;
s2 = strchr (s_new, '\0');
s2[0] = '=';
strcpy (++s2, svalue);
g_strchomp (s2);
return s_new;
}
#define TAG_BEGIN "# BEGIN RECORD"
#define TAG_END "# END RECORD"
/**
* read_ibft_blocks:
* @iscsiadm_path: path to iscsiadm program
* @out_blocks: on return if successful, a #GSList of #GPtrArray, or %NULL on
* failure
* @error: location for an error on failure
*
* Parses iscsiadm output and returns a #GSList of #GPtrArray in the @out_blocks
* argument on success, otherwise @out_blocks is set to %NULL. Each #GPtrArray
* in @out_blocks contains the lines from an iscsiadm interface block.
*
* Returns: %TRUE on success, %FALSE on errors
*/
gboolean
read_ibft_blocks (const char *iscsiadm_path,
GSList **out_blocks,
GError **error)
{
const char *argv[4] = { iscsiadm_path, "-m", "fw", NULL };
const char *envp[1] = { NULL };
GSList *blocks = NULL;
char *out = NULL, *err = NULL;
gint status = 0;
char **lines = NULL, **iter;
GPtrArray *block_lines = NULL;
gboolean success = FALSE;
g_return_val_if_fail (iscsiadm_path != NULL, FALSE);
g_return_val_if_fail (out_blocks != NULL && *out_blocks == NULL, FALSE);
if (!g_spawn_sync ("/", (char **) argv, (char **) envp, 0,
iscsiadm_child_setup, NULL, &out, &err, &status, error))
goto done;
if (!WIFEXITED (status)) {
g_set_error (error, IBFT_PLUGIN_ERROR, 0,
"iBFT: %s exited abnormally.", iscsiadm_path);
goto done;
}
if (WEXITSTATUS (status) != 0) {
g_set_error (error, IBFT_PLUGIN_ERROR, 0,
"iBFT: %s exited with error %d. Message: '%s'",
iscsiadm_path, WEXITSTATUS (status), err ? err : "(none)");
goto done;
}
nm_log_dbg (LOGD_SETTINGS, "iBFT records:\n%s", out);
lines = g_strsplit_set (out, "\n\r", -1);
for (iter = lines; iter && *iter; iter++) {
if (!*iter[0])
continue;
if (!g_ascii_strncasecmp (*iter, TAG_BEGIN, STRLEN (TAG_BEGIN))) {
if (block_lines) {
PARSE_WARNING ("malformed iscsiadm record: missing END RECORD.");
g_ptr_array_unref (block_lines);
}
/* Start new record */
block_lines = g_ptr_array_new_full (15, g_free);
} else if (!g_ascii_strncasecmp (*iter, TAG_END, STRLEN (TAG_END))) {
if (block_lines) {
if (block_lines->len)
blocks = g_slist_prepend (blocks, block_lines);
else
g_ptr_array_unref (block_lines);
block_lines = NULL;
}
} else if (block_lines) {
char *s = remove_most_whitespace (*iter);
if (s)
g_ptr_array_add (block_lines, s);
else {
PARSE_WARNING ("malformed iscsiadm record: no = in '%s'.", *iter);
g_clear_pointer (&block_lines, g_ptr_array_unref);
}
}
}
if (block_lines) {
PARSE_WARNING ("malformed iscsiadm record: missing # END RECORD.");
g_clear_pointer (&block_lines, g_ptr_array_unref);
}
success = TRUE;
done:
if (lines)
g_strfreev (lines);
g_free (out);
if (success)
*out_blocks = blocks;
else
g_slist_free_full (blocks, (GDestroyNotify) g_ptr_array_unref);
return success;
}
#define ISCSI_HWADDR_TAG "iface.hwaddress"
#define ISCSI_BOOTPROTO_TAG "iface.bootproto"
#define ISCSI_IPADDR_TAG "iface.ipaddress"
#define ISCSI_SUBNET_TAG "iface.subnet_mask"
#define ISCSI_GATEWAY_TAG "iface.gateway"
#define ISCSI_DNS1_TAG "iface.primary_dns"
#define ISCSI_DNS2_TAG "iface.secondary_dns"
#define ISCSI_VLAN_ID_TAG "iface.vlan_id"
#define ISCSI_IFACE_TAG "iface.net_ifacename"
static const char *
match_iscsiadm_tag (const char *line, const char *tag)
{
gsize taglen = strlen (tag);
if (g_ascii_strncasecmp (line, tag, taglen) != 0)
return NULL;
if (line[taglen] != '=')
return NULL;
return line + taglen + 1;
}
/**
* parse_ibft_config:
* @data: an array of iscsiadm interface block lines
* @error: return location for errors
* @...: pairs of key (const char *) : location (const char **) indicating the
* key to look for and the location to store the retrieved value in
*
* Parses an iscsiadm interface block into variables requested by the caller.
* Callers should verify the returned data is complete and valid. Returned
* strings are owned by @data and should not be used after @data is freed.
*
* Returns: %TRUE if at least , %FALSE on failure
*/
gboolean
parse_ibft_config (const GPtrArray *data, GError **error, ...)
{
gboolean success = FALSE;
const char **out_value, *p;
va_list ap;
const char *key;
guint i;
g_return_val_if_fail (data != NULL, FALSE);
g_return_val_if_fail (data->len > 0, FALSE);
/* Find requested keys and populate return values */
va_start (ap, error);
while ((key = va_arg (ap, const char *))) {
out_value = va_arg (ap, const char **);
*out_value = NULL;
for (i = 0; i < data->len; i++) {
p = match_iscsiadm_tag (g_ptr_array_index (data, i), key);
if (p) {
*out_value = p;
success = TRUE;
break;
}
}
}
va_end (ap);
if (!success) {
g_set_error_literal (error, IBFT_PLUGIN_ERROR, 0,
"iBFT: failed to match at least one iscsiadm block field");
}
return success;
}
static gboolean
ip4_setting_add_from_block (const GPtrArray *block,
NMConnection *connection,
GError **error)
{
NMSettingIP4Config *s_ip4 = NULL;
NMIP4Address *addr;
const char *s_method = NULL;
const char *s_ipaddr = NULL;
const char *s_gateway = NULL;
const char *s_dns1 = NULL;
const char *s_dns2 = NULL;
const char *s_netmask = NULL;
guint32 ipaddr = 0;
guint32 netmask = 0;
guint32 gateway = 0;
guint32 dns1 = 0;
guint32 dns2 = 0;
guint32 prefix;
g_assert (block);
if (!parse_ibft_config (block, error,
ISCSI_BOOTPROTO_TAG, &s_method,
ISCSI_IPADDR_TAG, &s_ipaddr,
ISCSI_SUBNET_TAG, &s_netmask,
ISCSI_GATEWAY_TAG, &s_gateway,
ISCSI_DNS1_TAG, &s_dns1,
ISCSI_DNS2_TAG, &s_dns2,
NULL))
goto error;
if (!s_method) {
g_set_error_literal (error, IBFT_PLUGIN_ERROR, 0,
"iBFT: malformed iscsiadm record: missing " ISCSI_BOOTPROTO_TAG);
goto error;
}
s_ip4 = (NMSettingIP4Config *) nm_setting_ip4_config_new ();
if (!g_ascii_strcasecmp (s_method, "dhcp")) {
g_object_set (s_ip4, NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL);
goto success;
} else if (g_ascii_strcasecmp (s_method, "static") != 0) {
g_set_error (error, IBFT_PLUGIN_ERROR, 0,
"iBFT: malformed iscsiadm record: unknown " ISCSI_BOOTPROTO_TAG " '%s'.",
s_method);
goto error;
}
/* Static configuration stuff */
g_object_set (s_ip4, NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_MANUAL, NULL);
/* IP address */
if (!s_ipaddr || inet_pton (AF_INET, s_ipaddr, &ipaddr) != 1) {
g_set_error (error, IBFT_PLUGIN_ERROR, 0,
"iBFT: malformed iscsiadm record: invalid IP address '%s'.",
s_ipaddr);
goto error;
}
/* Subnet/prefix */
if (!s_netmask || inet_pton (AF_INET, s_netmask, &netmask) != 1) {
g_set_error (error, IBFT_PLUGIN_ERROR, 0,
"iBFT: malformed iscsiadm record: invalid subnet mask '%s'.",
s_netmask);
goto error;
}
prefix = nm_utils_ip4_netmask_to_prefix (netmask);
if (s_gateway && inet_pton (AF_INET, s_gateway, &gateway) != 1) {
g_set_error (error, IBFT_PLUGIN_ERROR, 0,
"iBFT: malformed iscsiadm record: invalid IP gateway '%s'.",
s_gateway);
goto error;
}
if (s_dns1 && inet_pton (AF_INET, s_dns1, &dns1) != 1) {
g_set_error (error, IBFT_PLUGIN_ERROR, 0,
"iBFT: malformed iscsiadm record: invalid DNS1 address '%s'.",
s_dns1);
goto error;
}
if (s_dns2 && inet_pton (AF_INET, s_dns2, &dns2) != 1) {
g_set_error (error, IBFT_PLUGIN_ERROR, 0,
"iBFT: malformed iscsiadm record: invalid DNS2 address '%s'.",
s_dns2);
goto error;
}
addr = nm_ip4_address_new ();
nm_ip4_address_set_address (addr, ipaddr);
nm_ip4_address_set_prefix (addr, prefix);
nm_ip4_address_set_gateway (addr, gateway);
nm_setting_ip4_config_add_address (s_ip4, addr);
nm_ip4_address_unref (addr);
if (dns1)
nm_setting_ip4_config_add_dns (s_ip4, dns1);
if (dns2)
nm_setting_ip4_config_add_dns (s_ip4, dns2);
success:
nm_connection_add_setting (connection, NM_SETTING (s_ip4));
return TRUE;
error:
g_clear_object (&s_ip4);
return FALSE;
}
static gboolean
connection_setting_add (const GPtrArray *block,
NMConnection *connection,
const char *type,
const char *prefix,
const char *iface,
GError **error)
{
NMSetting *s_con;
char *id, *uuid, *uuid_data;
const char *s_hwaddr = NULL, *s_ip4addr = NULL, *s_vlanid;
if (!parse_ibft_config (block, error,
ISCSI_VLAN_ID_TAG, &s_vlanid,
ISCSI_HWADDR_TAG, &s_hwaddr,
ISCSI_IPADDR_TAG, &s_ip4addr,
NULL))
return FALSE;
if (!s_hwaddr) {
g_set_error_literal (error, IBFT_PLUGIN_ERROR, 0,
"iBFT: malformed iscsiadm record: missing " ISCSI_HWADDR_TAG);
return FALSE;
}
id = g_strdup_printf ("iBFT%s%s %s",
prefix ? " " : "",
prefix ? prefix : "",
iface);
uuid_data = g_strdup_printf ("%s%s%s",
s_vlanid ? s_vlanid : "0",
s_hwaddr,
s_ip4addr ? s_ip4addr : "DHCP");
uuid = nm_utils_uuid_generate_from_string (uuid_data);
g_free (uuid_data);
s_con = nm_setting_connection_new ();
g_object_set (s_con,
NM_SETTING_CONNECTION_TYPE, type,
NM_SETTING_CONNECTION_UUID, uuid,
NM_SETTING_CONNECTION_ID, id,
NM_SETTING_CONNECTION_READ_ONLY, TRUE,
NULL);
g_free (uuid);
g_free (id);
nm_connection_add_setting (connection, NM_SETTING (s_con));
return TRUE;
}
static gboolean
is_ibft_vlan_device (const GPtrArray *block)
{
char *s_vlan_id = NULL;
if (parse_ibft_config (block, NULL, ISCSI_VLAN_ID_TAG, &s_vlan_id, NULL)) {
g_assert (s_vlan_id);
/* VLAN 0 is normally a valid VLAN ID, but in the iBFT case it
* means "no VLAN".
*/
if (nm_utils_ascii_str_to_int64 (s_vlan_id, 10, 1, 4095, -1) != -1)
return TRUE;
}
return FALSE;
}
static gboolean
vlan_setting_add_from_block (const GPtrArray *block,
NMConnection *connection,
GError **error)
{
NMSetting *s_vlan = NULL;
const char *vlan_id_str = NULL;
gint64 vlan_id = -1;
gboolean success;
g_assert (block);
g_assert (connection);
/* This won't fail since this function shouldn't be called unless the
* iBFT VLAN ID exists and is > 0.
*/
success = parse_ibft_config (block, NULL, ISCSI_VLAN_ID_TAG, &vlan_id_str, NULL);
g_assert (success);
g_assert (vlan_id_str);
/* VLAN 0 is normally a valid VLAN ID, but in the iBFT case it means "no VLAN" */
vlan_id = nm_utils_ascii_str_to_int64 (vlan_id_str, 10, 1, 4095, -1);
if (vlan_id == -1) {
g_set_error (error, IBFT_PLUGIN_ERROR, 0, "Invalid VLAN_ID '%s'", vlan_id_str);
return FALSE;
}
s_vlan = nm_setting_vlan_new ();
g_object_set (s_vlan, NM_SETTING_VLAN_ID, (guint32) vlan_id, NULL);
nm_connection_add_setting (connection, NM_SETTING (s_vlan));
return TRUE;
}
static gboolean
wired_setting_add_from_block (const GPtrArray *block,
NMConnection *connection,
GError **error)
{
NMSetting *s_wired = NULL;
const char *hwaddr_str = NULL;
GByteArray *hwaddr;
g_assert (block);
g_assert (connection);
if (!parse_ibft_config (block, NULL, ISCSI_HWADDR_TAG, &hwaddr_str, NULL)) {
g_set_error_literal (error, IBFT_PLUGIN_ERROR, 0,
"iBFT: malformed iscsiadm record: missing " ISCSI_HWADDR_TAG);
return FALSE;
}
hwaddr = nm_utils_hwaddr_atoba (hwaddr_str, ETH_ALEN);
if (!hwaddr) {
g_set_error (error, IBFT_PLUGIN_ERROR, 0,
"iBFT: malformed iscsiadm record: invalid " ISCSI_HWADDR_TAG " '%s'.",
hwaddr_str);
return FALSE;
}
s_wired = nm_setting_wired_new ();
g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, hwaddr, NULL);
g_byte_array_unref (hwaddr);
nm_connection_add_setting (connection, s_wired);
return TRUE;
}
NMConnection *
connection_from_block (const GPtrArray *block, GError **error)
{
NMConnection *connection = NULL;
gboolean is_vlan = FALSE;
const char *iface = NULL;
g_assert (block);
if (!parse_ibft_config (block, error, ISCSI_IFACE_TAG, &iface, NULL)) {
g_set_error_literal (error, IBFT_PLUGIN_ERROR, 0,
"iBFT: malformed iscsiadm record: missing " ISCSI_IFACE_TAG);
return NULL;
}
connection = nm_simple_connection_new ();
is_vlan = is_ibft_vlan_device (block);
if (is_vlan && !vlan_setting_add_from_block (block, connection, error))
goto error;
/* Always have a wired setting; for VLAN it defines the parent */
if (!wired_setting_add_from_block (block, connection, error))
goto error;
if (!ip4_setting_add_from_block (block, connection, error))
goto error;
if (!connection_setting_add (block,
connection,
is_vlan ? NM_SETTING_VLAN_SETTING_NAME : NM_SETTING_WIRED_SETTING_NAME,
is_vlan ? "VLAN" : NULL,
iface,
error))
goto error;
if (!nm_connection_normalize (connection, NULL, NULL, error))
goto error;
return connection;
error:
g_object_unref (connection);
return NULL;
}

View file

@ -0,0 +1,36 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
/* NetworkManager system settings service
*
* 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 of the License, 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 2014 Red Hat, Inc.
*/
#ifndef __READER_H__
#define __READER_H__
#include <glib.h>
#include <nm-connection.h>
gboolean read_ibft_blocks (const char *iscsiadm_path,
GSList **out_blocks,
GError **error);
NMConnection *connection_from_block (const GPtrArray *block, GError **error);
/* For testcases */
gboolean parse_ibft_config (const GPtrArray *data, GError **error, ...) G_GNUC_NULL_TERMINATED;
#endif /* __READER_H__ */

View file

@ -0,0 +1,50 @@
if ENABLE_TESTS
@GNOME_CODE_COVERAGE_RULES@
AM_CPPFLAGS = \
$(GLIB_CFLAGS) \
$(CODE_COVERAGE_CFLAGS) \
-I$(top_srcdir)/include \
-I$(top_srcdir)/libnm-core \
-I$(top_builddir)/libnm-core \
-I$(top_srcdir)/src/ \
-I$(top_srcdir)/src/platform \
-I$(top_srcdir)/src/settings \
-I$(srcdir)/../ \
-DG_LOG_DOMAIN=\""NetworkManager-ibft"\" \
-DNETWORKMANAGER_COMPILATION \
-DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE \
-DTEST_IBFT_DIR=\"$(abs_srcdir)\" \
-DTEST_SCRATCH_DIR=\"$(abs_builddir)/\"
AM_LDFLAGS = \
$(GLIB_LIBS) \
$(DBUS_LIBS) \
$(CODE_COVERAGE_LDFLAGS)
noinst_PROGRAMS = test-ibft
test_ibft_SOURCES = \
test-ibft.c \
../errors.c \
../reader.c
test_ibft_LDADD = \
$(top_builddir)/src/libNetworkManager.la
TESTS = test-ibft
EXTRA_DIST = \
iscsiadm-test-dhcp \
iscsiadm-test-static \
iscsiadm-test-bad-ipaddr \
iscsiadm-test-bad-gateway \
iscsiadm-test-bad-dns1 \
iscsiadm-test-bad-dns2 \
iscsiadm-test-bad-entry \
iscsiadm-test-bad-record \
iscsiadm-test-vlan
endif

View file

@ -0,0 +1,21 @@
#!/bin/bash
cat << EOF
# BEGIN RECORD
iface.initiatorname = iqn.pjones6
iface.hwaddress = 00:33:21:98:b9:f0
iface.bootproto = STATIC
iface.ipaddress = 192.168.32.72
iface.subnet_mask = 255.255.252.0
iface.gateway = 192.168.35.254
iface.primary_dns = 10000.500.250.1
iface.secondary_dns = 10.16.255.3
iface.vlan_id = 0
iface.net_ifacename = eth0
node.name = iqn.0.2008-11.com.blahblah:iscsi0
node.conn[0].address = 10.16.52.16
node.conn[0].port = 3260
node.boot_lun = 00000000
# END RECORD
EOF

View file

@ -0,0 +1,21 @@
#!/bin/bash
cat << EOF
# BEGIN RECORD
iface.initiatorname = iqn.pjones6
iface.hwaddress = 00:33:21:98:b9:f0
iface.bootproto = STATIC
iface.ipaddress = 192.168.32.72
iface.subnet_mask = 255.255.252.0
iface.gateway = 192.168.35.254
iface.primary_dns = 10.16.255.2
iface.secondary_dns = blah.foo.bar.baz
iface.vlan_id = 0
iface.net_ifacename = eth0
node.name = iqn.0.2008-11.com.blahblah:iscsi0
node.conn[0].address = 10.16.52.16
node.conn[0].port = 3260
node.boot_lun = 00000000
# END RECORD
EOF

View file

@ -0,0 +1,20 @@
#!/bin/bash
cat << EOF
# BEGIN RECORD
iface.initiatorname = iqn.pjones6
iface.hwaddress = 00:33:21:98:b9:f0
iface.bootproto = STATIC
iface.ipaddress 192.168.32.72
iface.subnet_mask = 255.255.252.0
iface.gateway = 192.168.35.254
iface.primary_dns = 10.16.255.2
iface.secondary_dns = 10.16.255.3
iface.vlan_id = 0
iface.net_ifacename = eth0
node.name = iqn.0.2008-11.com.blahblah:iscsi0
node.conn[0].address = 10.16.52.16
node.conn[0].port = 3260
node.boot_lun = 00000000
# END RECORD

View file

@ -0,0 +1,21 @@
#!/bin/bash
cat << EOF
# BEGIN RECORD
iface.initiatorname = iqn.pjones6
iface.hwaddress = 00:33:21:98:b9:f0
iface.bootproto = STATIC
iface.ipaddress = 192.168.32.72
iface.subnet_mask = 255.255.252.0
iface.gateway = bb.cc.dd.ee
iface.primary_dns = 10.16.255.2
iface.secondary_dns = 10.16.255.3
iface.vlan_id = 0
iface.net_ifacename = eth0
node.name = iqn.0.2008-11.com.blahblah:iscsi0
node.conn[0].address = 10.16.52.16
node.conn[0].port = 3260
node.boot_lun = 00000000
# END RECORD
EOF

View file

@ -0,0 +1,21 @@
#!/bin/bash
cat << EOF
# BEGIN RECORD
iface.initiatorname = iqn.pjones6
iface.hwaddress = 00:33:21:98:b9:f0
iface.bootproto = STATIC
iface.ipaddress = aa.bb.cc.dd
iface.subnet_mask = 255.255.252.0
iface.gateway = 192.168.35.254
iface.primary_dns = 10.16.255.2
iface.secondary_dns = 10.16.255.3
iface.vlan_id = 0
iface.net_ifacename = eth0
node.name = iqn.0.2008-11.com.blahblah:iscsi0
node.conn[0].address = 10.16.52.16
node.conn[0].port = 3260
node.boot_lun = 00000000
# END RECORD
EOF

View file

@ -0,0 +1,18 @@
#!/bin/bash
cat << EOF
# BEGIN RECORD
iface.initiatorname = iqn.pjones6
iface.hwaddress = 00:33:21:98:b9:f0
iface.bootproto = DHCP
iface.gateway = 10.16.52.254
iface.primary_dns = 10.16.255.2
iface.secondary_dns = 10.16.255.3
iface.vlan_id = 0
iface.net_ifacename = eth0
node.name = iqn.0.2008-11.com.blahblah:iscsi0
node.conn[0].address = 10.16.52.16
node.conn[0].port = 3260
node.boot_lun = 00000000
EOF

View file

@ -0,0 +1,33 @@
#!/bin/bash
cat << EOF
# BEGIN RECORD
iface.initiatorname = iqn.pjones6
iface.hwaddress = 00:33:21:98:b9:f0
iface.bootproto = DHCP
iface.gateway = 10.16.52.254
iface.primary_dns = 10.16.255.2
iface.secondary_dns = 10.16.255.3
iface.vlan_id = 0
iface.net_ifacename = eth0
node.name = iqn.0.2008-11.com.blahblah:iscsi0
node.conn[0].address = 10.16.52.16
node.conn[0].port = 3260
node.boot_lun = 00000000
# END RECORD
# BEGIN RECORD
iface.initiatorname = iqn.pjones6
iface.hwaddress = 00:33:21:98:b9:f1
iface.bootproto = DHCP
iface.gateway = 10.16.52.254
iface.primary_dns = 10.16.255.2
iface.secondary_dns = 10.16.255.3
iface.vlan_id = 0
iface.net_ifacename = eth1
node.name = iqn.1.2008-11.com.blahblah:iscsi1
node.conn[0].address = 10.16.52.16
node.conn[0].port = 3260
node.boot_lun = 00000000
# END RECORD
EOF

View file

@ -0,0 +1,35 @@
#!/bin/bash
cat << EOF
# BEGIN RECORD
iface.initiatorname = iqn.pjones6
iface.hwaddress = 00:33:21:98:b9:f0
iface.bootproto = STATIC
iface.ipaddress = 192.168.32.72
iface.subnet_mask = 255.255.252.0
iface.gateway = 192.168.35.254
iface.primary_dns = 10.16.255.2
iface.secondary_dns = 10.16.255.3
iface.vlan_id = 0
iface.net_ifacename = eth0
node.name = iqn.0.2008-11.com.blahblah:iscsi0
node.conn[0].address = 10.16.52.16
node.conn[0].port = 3260
node.boot_lun = 00000000
# END RECORD
# BEGIN RECORD
iface.initiatorname = iqn.pjones6
iface.hwaddress = 00:33:21:98:b9:f1
iface.bootproto = DHCP
iface.gateway = 10.16.52.254
iface.primary_dns = 10.16.255.2
iface.secondary_dns = 10.16.255.3
iface.vlan_id = 0
iface.net_ifacename = eth1
node.name = iqn.1.2008-11.com.blahblah:iscsi1
node.conn[0].address = 10.16.52.16
node.conn[0].port = 3260
node.boot_lun = 00000000
# END RECORD
EOF

View file

@ -0,0 +1,19 @@
#!/bin/bash
cat << EOF
# BEGIN RECORD 6.2.0.873-21
iface.initiatorname = iqn.2010-04.org.ipxe:d05faa97-c4be-44f6-a723-efde9aa399a0
iface.transport_name = tcp
iface.hwaddress = 00:33:21:98:b9:f0
iface.bootproto = STATIC
iface.ipaddress = 192.168.6.200
iface.subnet_mask = 255.255.255.0
iface.vlan_id = 123
iface.net_ifacename = eth0
node.name = iqn.2003-01.org.x:disk1
node.conn[0].address = 192.168.6.32
node.conn[0].port = 3260
node.boot_lun = 01000000
# END RECORD
EOF

View file

@ -0,0 +1,295 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
/* NetworkManager system settings service
*
* 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 of the License, 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 2014 Red Hat, Inc.
*/
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
#include <string.h>
#include <netinet/ether.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <nm-utils.h>
#include <nm-setting-connection.h>
#include <nm-setting-wired.h>
#include <nm-setting-ip4-config.h>
#include <nm-setting-vlan.h>
#include "NetworkManagerUtils.h"
#include "errors.h"
#include "reader.h"
#include "nm-logging.h"
#include "nm-test-utils.h"
static GPtrArray *
read_block (const char *iscsiadm_path, const char *expected_mac)
{
GSList *blocks = NULL, *iter;
GPtrArray *block = NULL;
GError *error = NULL;
gboolean success;
success = read_ibft_blocks (iscsiadm_path, &blocks, &error);
g_assert_no_error (error);
g_assert (success);
g_assert (blocks);
for (iter = blocks; iter; iter = iter->next) {
const char *s_hwaddr = NULL;
if (!parse_ibft_config (iter->data, NULL, "iface.hwaddress", &s_hwaddr, NULL))
continue;
g_assert (s_hwaddr);
if (nm_utils_hwaddr_matches (s_hwaddr, -1, expected_mac, -1)) {
block = g_ptr_array_ref (iter->data);
break;
}
}
g_assert (block);
g_slist_foreach (blocks, (GFunc) g_ptr_array_unref, NULL);
return block;
}
static void
test_read_ibft_dhcp (void)
{
NMConnection *connection;
NMSettingConnection *s_con;
NMSettingWired *s_wired;
NMSettingIP4Config *s_ip4;
GError *error = NULL;
const GByteArray *array;
const char *expected_mac_address = "00:33:21:98:b9:f1";
GPtrArray *block;
block = read_block (TEST_IBFT_DIR "/iscsiadm-test-dhcp", expected_mac_address);
connection = connection_from_block (block, &error);
g_assert_no_error (error);
nmtst_assert_connection_verifies_without_normalization (connection);
g_assert (!nm_connection_get_setting_vlan (connection));
/* ===== CONNECTION SETTING ===== */
s_con = nm_connection_get_setting_connection (connection);
g_assert (s_con);
g_assert_cmpstr (nm_setting_connection_get_connection_type (s_con), ==, NM_SETTING_WIRED_SETTING_NAME);
g_assert_cmpstr (nm_setting_connection_get_id (s_con), ==, "iBFT eth1");
g_assert_cmpint (nm_setting_connection_get_timestamp (s_con), ==, 0);
g_assert (nm_setting_connection_get_autoconnect (s_con));
g_assert (nm_setting_connection_get_read_only (s_con));
/* ===== WIRED SETTING ===== */
s_wired = nm_connection_get_setting_wired (connection);
g_assert (s_wired);
array = nm_setting_wired_get_mac_address (s_wired);
g_assert (array);
nmtst_assert_hwaddr_equals (array->data, array->len, expected_mac_address);
g_assert_cmpint (nm_setting_wired_get_mtu (s_wired), ==, 0);
/* ===== IPv4 SETTING ===== */
s_ip4 = nm_connection_get_setting_ip4_config (connection);
g_assert (s_ip4);
g_assert_cmpstr (nm_setting_ip4_config_get_method (s_ip4), ==, NM_SETTING_IP4_CONFIG_METHOD_AUTO);
g_object_unref (connection);
}
static void
test_read_ibft_static (void)
{
NMConnection *connection;
NMSettingConnection *s_con;
NMSettingWired *s_wired;
NMSettingIP4Config *s_ip4;
GError *error = NULL;
const GByteArray *array;
const char *expected_mac_address = "00:33:21:98:b9:f0";
NMIP4Address *ip4_addr;
GPtrArray *block;
block = read_block (TEST_IBFT_DIR "/iscsiadm-test-static", expected_mac_address);
connection = connection_from_block (block, &error);
g_assert_no_error (error);
nmtst_assert_connection_verifies_without_normalization (connection);
g_assert (!nm_connection_get_setting_vlan (connection));
/* ===== CONNECTION SETTING ===== */
s_con = nm_connection_get_setting_connection (connection);
g_assert (s_con);
g_assert_cmpstr (nm_setting_connection_get_connection_type (s_con), ==, NM_SETTING_WIRED_SETTING_NAME);
g_assert_cmpstr (nm_setting_connection_get_id (s_con), ==, "iBFT eth0");
g_assert_cmpint (nm_setting_connection_get_timestamp (s_con), ==, 0);
g_assert (nm_setting_connection_get_autoconnect (s_con));
g_assert (nm_setting_connection_get_read_only (s_con));
/* ===== WIRED SETTING ===== */
s_wired = nm_connection_get_setting_wired (connection);
g_assert (s_wired);
array = nm_setting_wired_get_mac_address (s_wired);
g_assert (array);
nmtst_assert_hwaddr_equals (array->data, array->len, expected_mac_address);
g_assert_cmpint (nm_setting_wired_get_mtu (s_wired), ==, 0);
/* ===== IPv4 SETTING ===== */
s_ip4 = nm_connection_get_setting_ip4_config (connection);
g_assert (s_ip4);
g_assert_cmpstr (nm_setting_ip4_config_get_method (s_ip4), ==, NM_SETTING_IP4_CONFIG_METHOD_MANUAL);
g_assert_cmpint (nm_setting_ip4_config_get_num_dns (s_ip4), ==, 2);
nmtst_assert_ip4_address_equals (nm_setting_ip4_config_get_dns (s_ip4, 0), "10.16.255.2");
nmtst_assert_ip4_address_equals (nm_setting_ip4_config_get_dns (s_ip4, 1), "10.16.255.3");
g_assert_cmpint (nm_setting_ip4_config_get_num_addresses (s_ip4), ==, 1);
ip4_addr = nm_setting_ip4_config_get_address (s_ip4, 0);
g_assert (ip4_addr);
nmtst_assert_ip4_address_equals (nm_ip4_address_get_address (ip4_addr), "192.168.32.72");
g_assert_cmpint (nm_ip4_address_get_prefix (ip4_addr), ==, 22);
nmtst_assert_ip4_address_equals (nm_ip4_address_get_gateway (ip4_addr), "192.168.35.254");
g_object_unref (connection);
g_ptr_array_unref (block);
}
static void
test_read_ibft_malformed (gconstpointer user_data)
{
const char *iscsiadm_path = user_data;
GSList *blocks = NULL;
GError *error = NULL;
gboolean success;
g_assert (g_file_test (iscsiadm_path, G_FILE_TEST_EXISTS));
g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING, "*malformed iscsiadm record*");
success = read_ibft_blocks (iscsiadm_path, &blocks, &error);
g_assert_no_error (error);
g_assert (success);
g_assert (blocks == NULL);
g_test_assert_expected_messages ();
}
static void
test_read_ibft_bad_address (gconstpointer user_data)
{
const char *iscsiadm_path = user_data;
NMConnection *connection;
const char *expected_mac_address = "00:33:21:98:b9:f0";
GPtrArray *block;
GError *error = NULL;
g_assert (g_file_test (iscsiadm_path, G_FILE_TEST_EXISTS));
block = read_block (iscsiadm_path, expected_mac_address);
connection = connection_from_block (block, &error);
g_assert_error (error, IBFT_PLUGIN_ERROR, 0);
g_assert (strstr (error->message, "iBFT: malformed iscsiadm record: invalid"));
g_clear_error (&error);
g_assert (connection == NULL);
g_ptr_array_unref (block);
}
static void
test_read_ibft_vlan (void)
{
NMConnection *connection;
NMSettingConnection *s_con;
NMSettingWired *s_wired;
NMSettingVlan *s_vlan;
NMSettingIP4Config *s_ip4;
const GByteArray *array;
const char *expected_mac_address = "00:33:21:98:b9:f0";
NMIP4Address *ip4_addr;
GError *error = NULL;
GPtrArray *block;
block = read_block (TEST_IBFT_DIR "/iscsiadm-test-vlan", expected_mac_address);
connection = connection_from_block (block, &error);
g_assert_no_error (error);
nmtst_assert_connection_verifies_without_normalization (connection);
s_con = nm_connection_get_setting_connection (connection);
g_assert (s_con);
g_assert_cmpstr (nm_setting_connection_get_connection_type (s_con), ==, NM_SETTING_VLAN_SETTING_NAME);
/* ===== WIRED SETTING ===== */
s_wired = nm_connection_get_setting_wired (connection);
g_assert (s_wired);
array = nm_setting_wired_get_mac_address (s_wired);
g_assert (array);
nmtst_assert_hwaddr_equals (array->data, array->len, expected_mac_address);
/* ===== VLAN SETTING ===== */
s_vlan = nm_connection_get_setting_vlan (connection);
g_assert (s_vlan);
g_assert_cmpint (nm_setting_vlan_get_id (s_vlan), ==, 123);
g_assert_cmpstr (nm_setting_vlan_get_parent (s_vlan), ==, NULL);
g_assert_cmpstr (nm_setting_vlan_get_interface_name (s_vlan), ==, NULL);
/* ===== IPv4 SETTING ===== */
s_ip4 = nm_connection_get_setting_ip4_config (connection);
g_assert (s_ip4);
g_assert_cmpstr (nm_setting_ip4_config_get_method (s_ip4), ==, NM_SETTING_IP4_CONFIG_METHOD_MANUAL);
g_assert_cmpint (nm_setting_ip4_config_get_num_dns (s_ip4), ==, 0);
g_assert_cmpint (nm_setting_ip4_config_get_num_addresses (s_ip4), ==, 1);
ip4_addr = nm_setting_ip4_config_get_address (s_ip4, 0);
g_assert (ip4_addr);
nmtst_assert_ip4_address_equals (nm_ip4_address_get_address (ip4_addr), "192.168.6.200");
g_assert_cmpint (nm_ip4_address_get_prefix (ip4_addr), ==, 24);
nmtst_assert_ip4_address_equals (nm_ip4_address_get_gateway (ip4_addr), "0.0.0.0");
g_object_unref (connection);
g_ptr_array_ref (block);
}
NMTST_DEFINE ();
#define TPATH "/settings/plugins/ibft/"
int main (int argc, char **argv)
{
nmtst_init_assert_logging (&argc, &argv);
g_test_add_func (TPATH "ibft/dhcp", test_read_ibft_dhcp);
g_test_add_func (TPATH "ibft/static", test_read_ibft_static);
g_test_add_func (TPATH "ibft/vlan", test_read_ibft_vlan);
g_test_add_data_func (TPATH "ibft/bad-record-read", TEST_IBFT_DIR "/iscsiadm-test-bad-record", test_read_ibft_malformed);
g_test_add_data_func (TPATH "ibft/bad-entry-read", TEST_IBFT_DIR "/iscsiadm-test-bad-entry", test_read_ibft_malformed);
g_test_add_data_func (TPATH "ibft/bad-ipaddr-read", TEST_IBFT_DIR "/iscsiadm-test-bad-ipaddr", test_read_ibft_bad_address);
g_test_add_data_func (TPATH "ibft/bad-gateway-read", TEST_IBFT_DIR "/iscsiadm-test-bad-gateway", test_read_ibft_bad_address);
g_test_add_data_func (TPATH "ibft/bad-dns1-read", TEST_IBFT_DIR "/iscsiadm-test-bad-dns1", test_read_ibft_bad_address);
g_test_add_data_func (TPATH "ibft/bad-dns2-read", TEST_IBFT_DIR "/iscsiadm-test-bad-dns2", test_read_ibft_bad_address);
return g_test_run ();
}