ibft: add settings plugin for reading iBFT configuration (bgo #734009)
authorDan Williams <dcbw@redhat.com>
Wed, 30 Jul 2014 20:37:31 +0000 (15:37 -0500)
committerDan Williams <dcbw@redhat.com>
Fri, 29 Aug 2014 23:46:11 +0000 (18:46 -0500)
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

25 files changed:
.gitignore
configure.ac
include/nm-test-utils.h
po/POTFILES.in
src/settings/plugins/Makefile.am
src/settings/plugins/ibft/Makefile.am [new file with mode: 0644]
src/settings/plugins/ibft/errors.c [new file with mode: 0644]
src/settings/plugins/ibft/errors.h [new file with mode: 0644]
src/settings/plugins/ibft/nm-ibft-connection.c [new file with mode: 0644]
src/settings/plugins/ibft/nm-ibft-connection.h [new file with mode: 0644]
src/settings/plugins/ibft/plugin.c [new file with mode: 0644]
src/settings/plugins/ibft/plugin.h [new file with mode: 0644]
src/settings/plugins/ibft/reader.c [new file with mode: 0644]
src/settings/plugins/ibft/reader.h [new file with mode: 0644]
src/settings/plugins/ibft/tests/Makefile.am [new file with mode: 0644]
src/settings/plugins/ibft/tests/iscsiadm-test-bad-dns1 [new file with mode: 0755]
src/settings/plugins/ibft/tests/iscsiadm-test-bad-dns2 [new file with mode: 0755]
src/settings/plugins/ibft/tests/iscsiadm-test-bad-entry [new file with mode: 0755]
src/settings/plugins/ibft/tests/iscsiadm-test-bad-gateway [new file with mode: 0755]
src/settings/plugins/ibft/tests/iscsiadm-test-bad-ipaddr [new file with mode: 0755]
src/settings/plugins/ibft/tests/iscsiadm-test-bad-record [new file with mode: 0755]
src/settings/plugins/ibft/tests/iscsiadm-test-dhcp [new file with mode: 0755]
src/settings/plugins/ibft/tests/iscsiadm-test-static [new file with mode: 0755]
src/settings/plugins/ibft/tests/iscsiadm-test-vlan [new file with mode: 0755]
src/settings/plugins/ibft/tests/test-ibft.c [new file with mode: 0644]

index ca36160..6df1270 100644 (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
index 6bc94b9..1bc73f6 100644 (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
index 9c80093..0bff1c2 100644 (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__ */
index 2c6c0e3..a7e75c4 100644 (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
index 41694e7..22f4171 100644 (file)
@@ -1,4 +1,4 @@
-SUBDIRS=keyfile example
+SUBDIRS=keyfile example ibft
 
 @GNOME_CODE_COVERAGE_RULES@
 
diff --git a/src/settings/plugins/ibft/Makefile.am b/src/settings/plugins/ibft/Makefile.am
new file mode 100644 (file)
index 0000000..c687cf3
--- /dev/null
@@ -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)
+
diff --git a/src/settings/plugins/ibft/errors.c b/src/settings/plugins/ibft/errors.c
new file mode 100644 (file)
index 0000000..ead2686
--- /dev/null
@@ -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;
+}
+
+
diff --git a/src/settings/plugins/ibft/errors.h b/src/settings/plugins/ibft/errors.h
new file mode 100644 (file)
index 0000000..cb37567
--- /dev/null
@@ -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__ */
+
diff --git a/src/settings/plugins/ibft/nm-ibft-connection.c b/src/settings/plugins/ibft/nm-ibft-connection.c
new file mode 100644 (file)
index 0000000..2ed4679
--- /dev/null
@@ -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)
+{
+}
+
diff --git a/src/settings/plugins/ibft/nm-ibft-connection.h b/src/settings/plugins/ibft/nm-ibft-connection.h
new file mode 100644 (file)
index 0000000..4ccebc3
--- /dev/null
@@ -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 */
diff --git a/src/settings/plugins/ibft/plugin.c b/src/settings/plugins/ibft/plugin.c
new file mode 100644 (file)
index 0000000..c8f9b58
--- /dev/null
@@ -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);
+}
diff --git a/src/settings/plugins/ibft/plugin.h b/src/settings/plugins/ibft/plugin.h
new file mode 100644 (file)
index 0000000..2c81135
--- /dev/null
@@ -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_ */
+
diff --git a/src/settings/plugins/ibft/reader.c b/src/settings/plugins/ibft/reader.c
new file mode 100644 (file)
index 0000000..908ba69
--- /dev/null
@@ -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;
+}
+
diff --git a/src/settings/plugins/ibft/reader.h b/src/settings/plugins/ibft/reader.h
new file mode 100644 (file)
index 0000000..0b2f22b
--- /dev/null
@@ -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__ */
diff --git a/src/settings/plugins/ibft/tests/Makefile.am b/src/settings/plugins/ibft/tests/Makefile.am
new file mode 100644 (file)
index 0000000..6201175
--- /dev/null
@@ -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
+
diff --git a/src/settings/plugins/ibft/tests/iscsiadm-test-bad-dns1 b/src/settings/plugins/ibft/tests/iscsiadm-test-bad-dns1
new file mode 100755 (executable)
index 0000000..54f02da
--- /dev/null
@@ -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
+
diff --git a/src/settings/plugins/ibft/tests/iscsiadm-test-bad-dns2 b/src/settings/plugins/ibft/tests/iscsiadm-test-bad-dns2
new file mode 100755 (executable)
index 0000000..ebd7a9c
--- /dev/null
@@ -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
+
diff --git a/src/settings/plugins/ibft/tests/iscsiadm-test-bad-entry b/src/settings/plugins/ibft/tests/iscsiadm-test-bad-entry
new file mode 100755 (executable)
index 0000000..4e32604
--- /dev/null
@@ -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
+
diff --git a/src/settings/plugins/ibft/tests/iscsiadm-test-bad-gateway b/src/settings/plugins/ibft/tests/iscsiadm-test-bad-gateway
new file mode 100755 (executable)
index 0000000..5390a6c
--- /dev/null
@@ -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
+
diff --git a/src/settings/plugins/ibft/tests/iscsiadm-test-bad-ipaddr b/src/settings/plugins/ibft/tests/iscsiadm-test-bad-ipaddr
new file mode 100755 (executable)
index 0000000..b41cd1f
--- /dev/null
@@ -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
+
diff --git a/src/settings/plugins/ibft/tests/iscsiadm-test-bad-record b/src/settings/plugins/ibft/tests/iscsiadm-test-bad-record
new file mode 100755 (executable)
index 0000000..22b34e6
--- /dev/null
@@ -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
+
diff --git a/src/settings/plugins/ibft/tests/iscsiadm-test-dhcp b/src/settings/plugins/ibft/tests/iscsiadm-test-dhcp
new file mode 100755 (executable)
index 0000000..556b058
--- /dev/null
@@ -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
+
diff --git a/src/settings/plugins/ibft/tests/iscsiadm-test-static b/src/settings/plugins/ibft/tests/iscsiadm-test-static
new file mode 100755 (executable)
index 0000000..5171148
--- /dev/null
@@ -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
+
diff --git a/src/settings/plugins/ibft/tests/iscsiadm-test-vlan b/src/settings/plugins/ibft/tests/iscsiadm-test-vlan
new file mode 100755 (executable)
index 0000000..59b80bd
--- /dev/null
@@ -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
+
diff --git a/src/settings/plugins/ibft/tests/test-ibft.c b/src/settings/plugins/ibft/tests/test-ibft.c
new file mode 100644 (file)
index 0000000..45c8d1f
--- /dev/null
@@ -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 ();
+}
+