diff options
author | Dan Winship <danw@gnome.org> | 2014-03-26 10:57:20 -0400 |
---|---|---|
committer | Dan Winship <danw@gnome.org> | 2014-03-26 10:57:20 -0400 |
commit | cfba0f8693de9a13bd68f4dd8c6bed191302521a (patch) | |
tree | 6893d94e0e17ebcb8c16a3f9ed62de5cf9b20a6b | |
parent | f27c01453af8b66eecd658b1664168f8bf0d14b8 (diff) | |
parent | 12d3a7908e1eff6f4b4b185a165c62fea9a0e86e (diff) | |
download | NetworkManager-cfba0f8693de9a13bd68f4dd8c6bed191302521a.tar.gz |
ifcfg-rh: add support for ifcfg alias files (rh #1067170)
30 files changed, 1298 insertions, 78 deletions
diff --git a/libnm-util/Makefile.am b/libnm-util/Makefile.am index 0e99da28d7..84f4aa303d 100644 --- a/libnm-util/Makefile.am +++ b/libnm-util/Makefile.am @@ -56,6 +56,7 @@ libnm_util_include_HEADERS = \ libnm_util_la_private_headers = \ crypto.h \ nm-param-spec-specialized.h \ + nm-util-private.h \ nm-utils-private.h \ nm-setting-private.h @@ -90,6 +91,7 @@ libnm_util_la_csources = \ nm-setting-wireless.c \ nm-setting-wireless-security.c \ nm-setting-vpn.c \ + nm-util-private.c \ nm-utils-enum-types.c \ nm-utils.c \ nm-value-transforms.c diff --git a/libnm-util/libnm-util.ver b/libnm-util/libnm-util.ver index ca9b393b07..ab7c2846ca 100644 --- a/libnm-util/libnm-util.ver +++ b/libnm-util/libnm-util.ver @@ -600,6 +600,7 @@ global: nm_setting_wireless_security_remove_proto; nm_setting_wireless_security_remove_proto_by_value; nm_setting_wireless_security_set_wep_key; + nm_util_get_private; nm_utils_ap_mode_security_valid; nm_utils_bin2hexstr; nm_utils_check_virtual_device_compatibility; diff --git a/libnm-util/nm-setting-ip4-config.c b/libnm-util/nm-setting-ip4-config.c index be819b1cac..d6574ca659 100644 --- a/libnm-util/nm-setting-ip4-config.c +++ b/libnm-util/nm-setting-ip4-config.c @@ -78,6 +78,7 @@ typedef struct { GArray *dns; /* array of guint32; elements in network byte order */ GSList *dns_search; /* list of strings */ GSList *addresses; /* array of NMIP4Address */ + GSList *address_labels; /* list of strings */ GSList *routes; /* array of NMIP4Route */ gboolean ignore_auto_routes; gboolean ignore_auto_dns; @@ -94,6 +95,7 @@ enum { PROP_DNS, PROP_DNS_SEARCH, PROP_ADDRESSES, + PROP_ADDRESS_LABELS, PROP_ROUTES, PROP_IGNORE_AUTO_ROUTES, PROP_IGNORE_AUTO_DNS, @@ -440,6 +442,19 @@ nm_setting_ip4_config_get_address (NMSettingIP4Config *setting, guint32 i) return (NMIP4Address *) g_slist_nth_data (priv->addresses, i); } +const char * +nm_setting_ip4_config_get_address_label (NMSettingIP4Config *setting, guint32 i) +{ + NMSettingIP4ConfigPrivate *priv; + + g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), NULL); + + priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting); + g_return_val_if_fail (i <= g_slist_length (priv->address_labels), NULL); + + return (const char *) g_slist_nth_data (priv->address_labels, i); +} + /** * nm_setting_ip4_config_add_address: * @setting: the #NMSettingIP4Config @@ -455,6 +470,14 @@ gboolean nm_setting_ip4_config_add_address (NMSettingIP4Config *setting, NMIP4Address *address) { + return nm_setting_ip4_config_add_address_with_label (setting, address, NULL); +} + +gboolean +nm_setting_ip4_config_add_address_with_label (NMSettingIP4Config *setting, + NMIP4Address *address, + const char *label) +{ NMSettingIP4ConfigPrivate *priv; NMIP4Address *copy; GSList *iter; @@ -472,6 +495,8 @@ nm_setting_ip4_config_add_address (NMSettingIP4Config *setting, g_return_val_if_fail (copy != NULL, FALSE); priv->addresses = g_slist_append (priv->addresses, copy); + priv->address_labels = g_slist_append (priv->address_labels, g_strdup (label)); + g_object_notify (G_OBJECT (setting), NM_SETTING_IP4_CONFIG_ADDRESSES); return TRUE; } @@ -487,16 +512,21 @@ void nm_setting_ip4_config_remove_address (NMSettingIP4Config *setting, guint32 i) { NMSettingIP4ConfigPrivate *priv; - GSList *elt; + GSList *addr, *label; g_return_if_fail (NM_IS_SETTING_IP4_CONFIG (setting)); priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting); - elt = g_slist_nth (priv->addresses, i); - g_return_if_fail (elt != NULL); + addr = g_slist_nth (priv->addresses, i); + label = g_slist_nth (priv->address_labels, i); + g_return_if_fail (addr != NULL && label != NULL); + + nm_ip4_address_unref ((NMIP4Address *) addr->data); + priv->addresses = g_slist_delete_link (priv->addresses, addr); + if (label->data) + g_free (label->data); + priv->address_labels = g_slist_delete_link (priv->address_labels, label); - nm_ip4_address_unref ((NMIP4Address *) elt->data); - priv->addresses = g_slist_delete_link (priv->addresses, elt); g_object_notify (G_OBJECT (setting), NM_SETTING_IP4_CONFIG_ADDRESSES); } @@ -548,6 +578,8 @@ nm_setting_ip4_config_clear_addresses (NMSettingIP4Config *setting) g_slist_free_full (priv->addresses, (GDestroyNotify) nm_ip4_address_unref); priv->addresses = NULL; + g_slist_free_full (priv->address_labels, g_free); + priv->address_labels = NULL; g_object_notify (G_OBJECT (setting), NM_SETTING_IP4_CONFIG_ADDRESSES); } @@ -822,10 +854,34 @@ nm_setting_ip4_config_get_may_fail (NMSettingIP4Config *setting) } static gboolean +verify_label (const char *label) +{ + const char *p; + char *iface; + + p = strchr (label, ':'); + if (!p) + return FALSE; + iface = g_strndup (label, p - label); + if (!nm_utils_iface_valid_name (iface)) { + g_free (iface); + return FALSE; + } + g_free (iface); + + for (p++; *p; p++) { + if (!g_ascii_isalnum (*p) && *p != '_') + return FALSE; + } + + return TRUE; +} + +static gboolean verify (NMSetting *setting, GSList *all_settings, GError **error) { NMSettingIP4ConfigPrivate *priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting); - GSList *iter; + GSList *iter, *l_iter; int i; if (!priv->method) { @@ -912,8 +968,11 @@ verify (NMSetting *setting, GSList *all_settings, GError **error) } /* Validate addresses */ - for (iter = priv->addresses, i = 0; iter; iter = g_slist_next (iter), i++) { + for (iter = priv->addresses, l_iter = priv->address_labels, i = 0; + iter && l_iter; + iter = g_slist_next (iter), l_iter = g_slist_next (l_iter), i++) { NMIP4Address *addr = (NMIP4Address *) iter->data; + const char *label = (const char *) l_iter->data; guint32 prefix = nm_ip4_address_get_prefix (addr); if (!nm_ip4_address_get_address (addr)) { @@ -935,6 +994,27 @@ verify (NMSetting *setting, GSList *all_settings, GError **error) g_prefix_error (error, "%s.%s: ", NM_SETTING_IP4_CONFIG_SETTING_NAME, NM_SETTING_IP4_CONFIG_ADDRESSES); return FALSE; } + + if (label && !verify_label (label)) { + g_set_error (error, + NM_SETTING_IP4_CONFIG_ERROR, + NM_SETTING_IP4_CONFIG_ERROR_INVALID_PROPERTY, + _("%d. IPv4 address has invalid label '%s'"), + i+1, label); + g_prefix_error (error, "%s.%s: ", NM_SETTING_IP4_CONFIG_SETTING_NAME, "address-labels"); + return FALSE; + } + } + + if (iter || l_iter) { + g_set_error (error, + NM_SETTING_IP4_CONFIG_ERROR, + NM_SETTING_IP4_CONFIG_ERROR_INVALID_PROPERTY, + _("IPv4 address / label count mismatch (%d vs %d)"), + g_slist_length (priv->addresses), + g_slist_length (priv->address_labels)); + g_prefix_error (error, "%s.%s: ", NM_SETTING_IP4_CONFIG_SETTING_NAME, "address-labels"); + return FALSE; } /* Validate routes */ @@ -990,6 +1070,7 @@ finalize (GObject *object) g_slist_free_full (priv->dns_search, g_free); g_slist_free_full (priv->addresses, (GDestroyNotify) nm_ip4_address_unref); + g_slist_free_full (priv->address_labels, g_free); g_slist_free_full (priv->routes, (GDestroyNotify) nm_ip4_route_unref); G_OBJECT_CLASS (nm_setting_ip4_config_parent_class)->finalize (object); @@ -1001,6 +1082,7 @@ set_property (GObject *object, guint prop_id, { NMSettingIP4Config *setting = NM_SETTING_IP4_CONFIG (object); NMSettingIP4ConfigPrivate *priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting); + GSList *iter; switch (prop_id) { case PROP_METHOD: @@ -1020,6 +1102,24 @@ set_property (GObject *object, guint prop_id, case PROP_ADDRESSES: g_slist_free_full (priv->addresses, (GDestroyNotify) nm_ip4_address_unref); priv->addresses = nm_utils_ip4_addresses_from_gvalue (value); + + if (g_slist_length (priv->addresses) != g_slist_length (priv->address_labels)) { + g_slist_free_full (priv->address_labels, g_free); + priv->address_labels = NULL; + for (iter = priv->addresses; iter; iter = iter->next) + priv->address_labels = g_slist_prepend (priv->address_labels, NULL); + } + break; + case PROP_ADDRESS_LABELS: + g_slist_free_full (priv->address_labels, g_free); + priv->address_labels = g_value_dup_boxed (value); + /* NULLs get converted to "" when this is sent over D-Bus. */ + for (iter = priv->address_labels; iter; iter = iter->next) { + if (!g_strcmp0 (iter->data, "")) { + g_free (iter->data); + iter->data = NULL; + } + } break; case PROP_ROUTES: g_slist_free_full (priv->routes, (GDestroyNotify) nm_ip4_route_unref); @@ -1074,6 +1174,9 @@ get_property (GObject *object, guint prop_id, case PROP_ADDRESSES: nm_utils_ip4_addresses_to_gvalue (priv->addresses, value); break; + case PROP_ADDRESS_LABELS: + g_value_set_boxed (value, priv->address_labels); + break; case PROP_ROUTES: nm_utils_ip4_routes_to_gvalue (priv->routes, value); break; @@ -1239,6 +1342,14 @@ nm_setting_ip4_config_class_init (NMSettingIP4ConfigClass *setting_class) DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UINT, G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE)); + g_object_class_install_property + (object_class, PROP_ADDRESS_LABELS, + _nm_param_spec_specialized ("address-labels", + "Address labels", + "Internal use only", + DBUS_TYPE_G_LIST_OF_STRING, + G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE)); + /** * NMSettingIP4Config:routes: * diff --git a/libnm-util/nm-setting-private.h b/libnm-util/nm-setting-private.h index b3a0947123..c3ec9d1993 100644 --- a/libnm-util/nm-setting-private.h +++ b/libnm-util/nm-setting-private.h @@ -21,6 +21,7 @@ #ifndef NM_SETTING_PRIVATE_H #define NM_SETTING_PRIVATE_H +#include "nm-setting.h" #include "nm-glib-compat.h" #define NM_SETTING_SECRET_FLAGS_ALL \ @@ -86,5 +87,10 @@ static void __attribute__((constructor)) register_setting (void) \ NMSetting *nm_setting_find_in_list (GSList *settings_list, const char *setting_name); +/* Private NMSettingIP4Config methods */ +#include "nm-setting-ip4-config.h" +const char *nm_setting_ip4_config_get_address_label (NMSettingIP4Config *setting, guint32 i); +gboolean nm_setting_ip4_config_add_address_with_label (NMSettingIP4Config *setting, NMIP4Address *address, const char *label); + #endif /* NM_SETTING_PRIVATE_H */ diff --git a/libnm-util/nm-util-private.c b/libnm-util/nm-util-private.c new file mode 100644 index 0000000000..545113e619 --- /dev/null +++ b/libnm-util/nm-util-private.c @@ -0,0 +1,44 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * (C) Copyright 2014 Red Hat, Inc. + */ + +#include "nm-util-private.h" + +static const NMUtilPrivateData data = { + .nm_setting_ip4_config_get_address_label = nm_setting_ip4_config_get_address_label, + .nm_setting_ip4_config_add_address_with_label = nm_setting_ip4_config_add_address_with_label, +}; + +/** + * nm_util_get_private: + * + * Entry point for NetworkManager-internal API. Although this symbol is exported, + * it is only useful if you have access to "nm-util-private.h", which is only + * available inside the NetworkManager tree. + * + * Return value: Who knows? It's a mystery. + * + * Since: 0.9.10 + */ +const NMUtilPrivateData * +nm_util_get_private (void) +{ + return &data; +} diff --git a/libnm-util/nm-util-private.h b/libnm-util/nm-util-private.h new file mode 100644 index 0000000000..f59fc887ab --- /dev/null +++ b/libnm-util/nm-util-private.h @@ -0,0 +1,60 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * (C) Copyright 2014 Red Hat, Inc. + */ + +#ifndef __NM_UTIL_PRIVATE_H__ +#define __NM_UTIL_PRIVATE_H__ + +#include <glib.h> + +#include "nm-setting-private.h" + +G_BEGIN_DECLS + +typedef struct NMUtilPrivateData { + const char * (*nm_setting_ip4_config_get_address_label) (NMSettingIP4Config *setting, + guint32 i); + gboolean (*nm_setting_ip4_config_add_address_with_label) (NMSettingIP4Config *setting, + NMIP4Address *address, + const char *label); +} NMUtilPrivateData; + +const NMUtilPrivateData *nm_util_get_private (void); + + +/** + * NM_UTIL_PRIVATE_CALL: + * @call: a call to a private libnm-util function + * + * Used to call private libnm-util functions. Eg, if there was a + * private function called nm_foo_get_bar(), you could call it like: + * + * bar = NM_UTIL_PRIVATE_CALL (nm_foo_get_bar (foo, x, y, z)); + * + * This macro only exists inside the NetworkManager source tree and + * is not part of the public API. + * + * Since: 0.9.10 + */ +#define NM_UTIL_PRIVATE_CALL(call) (nm_util_get_private ()->call) + +G_END_DECLS + +#endif diff --git a/libnm-util/tests/test-general.c b/libnm-util/tests/test-general.c index 90aa05bfbd..0af42150ac 100644 --- a/libnm-util/tests/test-general.c +++ b/libnm-util/tests/test-general.c @@ -42,6 +42,7 @@ #include "nm-setting-vlan.h" #include "nm-setting-bond.h" #include "nm-utils.h" +#include "nm-util-private.h" #include "nm-dbus-glib-types.h" #include "nm-glib-compat.h" @@ -272,6 +273,153 @@ test_setting_vpn_modify_during_foreach (void) g_object_unref (s_vpn); } +static void +test_setting_ip4_config_labels (void) +{ + NMSettingIP4Config *s_ip4; + NMIP4Address *addr; + const char *label; + GPtrArray *addrs; + GSList *labels; + GError *error = NULL; + + s_ip4 = (NMSettingIP4Config *) nm_setting_ip4_config_new (); + g_object_set (G_OBJECT (s_ip4), + NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_MANUAL, + NULL); + + /* addr 1 */ + addr = nm_ip4_address_new (); + nm_ip4_address_set_address (addr, 0x01010101); + nm_ip4_address_set_prefix (addr, 24); + + nm_setting_ip4_config_add_address (s_ip4, addr); + nm_setting_verify (NM_SETTING (s_ip4), NULL, &error); + g_assert_no_error (error); + + label = NM_UTIL_PRIVATE_CALL (nm_setting_ip4_config_get_address_label (s_ip4, 0)); + g_assert_cmpstr (label, ==, NULL); + + /* addr 2 */ + addr = nm_ip4_address_new (); + nm_ip4_address_set_address (addr, 0x02020202); + nm_ip4_address_set_prefix (addr, 24); + + NM_UTIL_PRIVATE_CALL (nm_setting_ip4_config_add_address_with_label (s_ip4, addr, "eth0:1")); + nm_setting_verify (NM_SETTING (s_ip4), NULL, &error); + g_assert_no_error (error); + + label = NM_UTIL_PRIVATE_CALL (nm_setting_ip4_config_get_address_label (s_ip4, 1)); + g_assert_cmpstr (label, ==, "eth0:1"); + + /* addr 3 */ + addr = nm_ip4_address_new (); + nm_ip4_address_set_address (addr, 0x03030303); + nm_ip4_address_set_prefix (addr, 24); + + NM_UTIL_PRIVATE_CALL (nm_setting_ip4_config_add_address_with_label (s_ip4, addr, NULL)); + nm_setting_verify (NM_SETTING (s_ip4), NULL, &error); + g_assert_no_error (error); + + label = NM_UTIL_PRIVATE_CALL (nm_setting_ip4_config_get_address_label (s_ip4, 2)); + g_assert_cmpstr (label, ==, NULL); + + /* Remove addr 1 and re-verify remaining addresses */ + nm_setting_ip4_config_remove_address (s_ip4, 0); + nm_setting_verify (NM_SETTING (s_ip4), NULL, &error); + g_assert_no_error (error); + + addr = nm_setting_ip4_config_get_address (s_ip4, 0); + g_assert_cmpint (nm_ip4_address_get_address (addr), ==, 0x02020202); + label = NM_UTIL_PRIVATE_CALL (nm_setting_ip4_config_get_address_label (s_ip4, 0)); + g_assert_cmpstr (label, ==, "eth0:1"); + + addr = nm_setting_ip4_config_get_address (s_ip4, 1); + g_assert_cmpint (nm_ip4_address_get_address (addr), ==, 0x03030303); + label = NM_UTIL_PRIVATE_CALL (nm_setting_ip4_config_get_address_label (s_ip4, 1)); + g_assert_cmpstr (label, ==, NULL); + + + /* Test explicit property assignment */ + g_object_get (G_OBJECT (s_ip4), + NM_SETTING_IP4_CONFIG_ADDRESSES, &addrs, + "address-labels", &labels, + NULL); + + nm_setting_ip4_config_clear_addresses (s_ip4); + g_assert_cmpint (nm_setting_ip4_config_get_num_addresses (s_ip4), ==, 0); + + /* Setting addrs but not labels will result in empty labels */ + g_object_set (G_OBJECT (s_ip4), + NM_SETTING_IP4_CONFIG_ADDRESSES, addrs, + NULL); + g_boxed_free (DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UINT, addrs); + nm_setting_verify (NM_SETTING (s_ip4), NULL, &error); + g_assert_no_error (error); + g_assert_cmpint (nm_setting_ip4_config_get_num_addresses (s_ip4), ==, 2); + + addr = nm_setting_ip4_config_get_address (s_ip4, 0); + g_assert_cmpint (nm_ip4_address_get_address (addr), ==, 0x02020202); + label = NM_UTIL_PRIVATE_CALL (nm_setting_ip4_config_get_address_label (s_ip4, 0)); + g_assert_cmpstr (label, ==, NULL); + + addr = nm_setting_ip4_config_get_address (s_ip4, 1); + g_assert_cmpint (nm_ip4_address_get_address (addr), ==, 0x03030303); + label = NM_UTIL_PRIVATE_CALL (nm_setting_ip4_config_get_address_label (s_ip4, 1)); + g_assert_cmpstr (label, ==, NULL); + + /* Setting labels now will leave addresses untouched */ + g_object_set (G_OBJECT (s_ip4), + "address-labels", labels, + NULL); + g_boxed_free (DBUS_TYPE_G_LIST_OF_STRING, labels); + nm_setting_verify (NM_SETTING (s_ip4), NULL, &error); + g_assert_no_error (error); + g_assert_cmpint (nm_setting_ip4_config_get_num_addresses (s_ip4), ==, 2); + + addr = nm_setting_ip4_config_get_address (s_ip4, 0); + g_assert_cmpint (nm_ip4_address_get_address (addr), ==, 0x02020202); + label = NM_UTIL_PRIVATE_CALL (nm_setting_ip4_config_get_address_label (s_ip4, 0)); + g_assert_cmpstr (label, ==, "eth0:1"); + + addr = nm_setting_ip4_config_get_address (s_ip4, 1); + g_assert_cmpint (nm_ip4_address_get_address (addr), ==, 0x03030303); + label = NM_UTIL_PRIVATE_CALL (nm_setting_ip4_config_get_address_label (s_ip4, 1)); + g_assert_cmpstr (label, ==, NULL); + + /* Setting labels to a value that's too short or too long will result in + * the setting not verifying. + */ + labels = g_slist_append (NULL, "eth0:2"); + g_object_set (G_OBJECT (s_ip4), + "address-labels", labels, + NULL); + + nm_setting_verify (NM_SETTING (s_ip4), NULL, &error); + g_assert_error (error, NM_SETTING_IP4_CONFIG_ERROR, NM_SETTING_IP4_CONFIG_ERROR_INVALID_PROPERTY); + g_assert (g_str_has_prefix (error->message, "ipv4.address-labels:")); + g_clear_error (&error); + + labels = g_slist_append (labels, "eth0:3"); + g_object_set (G_OBJECT (s_ip4), + "address-labels", labels, + NULL); + nm_setting_verify (NM_SETTING (s_ip4), NULL, &error); + g_assert_no_error (error); + + labels = g_slist_append (labels, "eth0:4"); + g_object_set (G_OBJECT (s_ip4), + "address-labels", labels, + NULL); + nm_setting_verify (NM_SETTING (s_ip4), NULL, &error); + g_assert_error (error, NM_SETTING_IP4_CONFIG_ERROR, NM_SETTING_IP4_CONFIG_ERROR_INVALID_PROPERTY); + g_assert (g_str_has_prefix (error->message, "ipv4.address-labels:")); + g_clear_error (&error); + + + g_object_unref (s_ip4); +} + #define OLD_DBUS_TYPE_G_IP6_ADDRESS (dbus_g_type_get_struct ("GValueArray", DBUS_TYPE_G_UCHAR_ARRAY, G_TYPE_UINT, G_TYPE_INVALID)) #define OLD_DBUS_TYPE_G_ARRAY_OF_IP6_ADDRESS (dbus_g_type_get_collection ("GPtrArray", OLD_DBUS_TYPE_G_IP6_ADDRESS)) @@ -1197,6 +1345,7 @@ test_connection_diff_a_only (void) { NM_SETTING_IP4_CONFIG_DNS, NM_SETTING_DIFF_RESULT_IN_A }, { NM_SETTING_IP4_CONFIG_DNS_SEARCH, NM_SETTING_DIFF_RESULT_IN_A }, { NM_SETTING_IP4_CONFIG_ADDRESSES, NM_SETTING_DIFF_RESULT_IN_A }, + { "address-labels", NM_SETTING_DIFF_RESULT_IN_A }, { NM_SETTING_IP4_CONFIG_ROUTES, NM_SETTING_DIFF_RESULT_IN_A }, { NM_SETTING_IP4_CONFIG_IGNORE_AUTO_ROUTES, NM_SETTING_DIFF_RESULT_IN_A }, { NM_SETTING_IP4_CONFIG_IGNORE_AUTO_DNS, NM_SETTING_DIFF_RESULT_IN_A }, @@ -2232,6 +2381,7 @@ int main (int argc, char **argv) test_setting_vpn_items (); test_setting_vpn_update_secrets (); test_setting_vpn_modify_during_foreach (); + test_setting_ip4_config_labels (); test_setting_ip6_config_old_address_array (); test_setting_gsm_apn_spaces (); test_setting_gsm_apn_bad_chars (); diff --git a/src/nm-ip4-config.c b/src/nm-ip4-config.c index 38227b082a..034d2e9127 100644 --- a/src/nm-ip4-config.c +++ b/src/nm-ip4-config.c @@ -30,7 +30,7 @@ #include "nm-dbus-glib-types.h" #include "nm-ip4-config-glue.h" #include "NetworkManagerUtils.h" - +#include "nm-util-private.h" G_DEFINE_TYPE (NMIP4Config, nm_ip4_config, G_TYPE_OBJECT) @@ -333,6 +333,7 @@ nm_ip4_config_merge_setting (NMIP4Config *config, NMSettingIP4Config *setting) /* Addresses */ for (i = 0; i < naddresses; i++) { NMIP4Address *s_addr = nm_setting_ip4_config_get_address (setting, i); + const char *label = NM_UTIL_PRIVATE_CALL (nm_setting_ip4_config_get_address_label (setting, i)); NMPlatformIP4Address address; memset (&address, 0, sizeof (address)); @@ -341,6 +342,8 @@ nm_ip4_config_merge_setting (NMIP4Config *config, NMSettingIP4Config *setting) address.lifetime = NM_PLATFORM_LIFETIME_PERMANENT; address.preferred = NM_PLATFORM_LIFETIME_PERMANENT; address.source = NM_PLATFORM_SOURCE_USER; + if (label) + g_strlcpy (address.label, label, sizeof (address.label)); nm_ip4_config_add_address (config, &address); } @@ -418,7 +421,10 @@ nm_ip4_config_update_setting (const NMIP4Config *config, NMSettingIP4Config *set if (same_prefix (address->address, gateway, address->plen)) nm_ip4_address_set_gateway (s_addr, gateway); - nm_setting_ip4_config_add_address (setting, s_addr); + if (*address->label) + NM_UTIL_PRIVATE_CALL (nm_setting_ip4_config_add_address_with_label (setting, s_addr, address->label)); + else + nm_setting_ip4_config_add_address (setting, s_addr); nm_ip4_address_unref (s_addr); } diff --git a/src/platform/nm-fake-platform.c b/src/platform/nm-fake-platform.c index 90d181bb8f..3eec6497f0 100644 --- a/src/platform/nm-fake-platform.c +++ b/src/platform/nm-fake-platform.c @@ -738,7 +738,8 @@ ip6_address_get_all (NMPlatform *platform, int ifindex) static gboolean ip4_address_add (NMPlatform *platform, int ifindex, in_addr_t addr, in_addr_t peer_addr, - int plen, guint32 lifetime, guint32 preferred) + int plen, guint32 lifetime, guint32 preferred, + const char *label) { NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform); NMPlatformIP4Address address; @@ -752,6 +753,8 @@ ip4_address_add (NMPlatform *platform, int ifindex, address.timestamp = nm_utils_get_monotonic_timestamp_s (); address.lifetime = lifetime; address.preferred = preferred; + if (label) + g_strlcpy (address.label, label, sizeof (address.label)); for (i = 0; i < priv->ip4_addresses->len; i++) { NMPlatformIP4Address *item = &g_array_index (priv->ip4_addresses, NMPlatformIP4Address, i); diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index 6311b3b9b9..252d30d197 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -868,6 +868,7 @@ init_ip4_address (NMPlatformIP4Address *address, struct rtnl_addr *rtnladdr) { struct nl_addr *nladdr = rtnl_addr_get_local (rtnladdr); struct nl_addr *nlpeer = rtnl_addr_get_peer (rtnladdr); + const char *label; g_return_val_if_fail (nladdr, FALSE); @@ -890,6 +891,9 @@ init_ip4_address (NMPlatformIP4Address *address, struct rtnl_addr *rtnladdr) } memcpy (&address->peer_address, nl_addr_get_binary_addr (nlpeer), sizeof (address->peer_address)); } + label = rtnl_addr_get_label (rtnladdr); + if (label && *label) + g_strlcpy (address->label, label, sizeof (address->label)); return TRUE; } @@ -2783,7 +2787,8 @@ build_rtnl_addr (int family, int plen, guint32 lifetime, guint32 preferred, - guint flags) + guint flags, + const char *label) { struct rtnl_addr *rtnladdr = rtnl_addr_alloc (); int addrlen = family == AF_INET ? sizeof (in_addr_t) : sizeof (struct in6_addr); @@ -2824,6 +2829,8 @@ build_rtnl_addr (int family, } if (flags) rtnl_addr_set_flags (rtnladdr, flags); + if (label && *label) + rtnl_addr_set_label (rtnladdr, label); return (struct nl_object *) rtnladdr; } @@ -2835,11 +2842,13 @@ ip4_address_add (NMPlatform *platform, in_addr_t peer_addr, int plen, guint32 lifetime, - guint32 preferred) + guint32 preferred, + const char *label) { return add_object (platform, build_rtnl_addr (AF_INET, ifindex, &addr, peer_addr ? &peer_addr : NULL, - plen, lifetime, preferred, 0)); + plen, lifetime, preferred, 0, + label)); } static gboolean @@ -2854,25 +2863,26 @@ ip6_address_add (NMPlatform *platform, { return add_object (platform, build_rtnl_addr (AF_INET6, ifindex, &addr, IN6_IS_ADDR_UNSPECIFIED (&peer_addr) ? NULL : &peer_addr, - plen, lifetime, preferred, flags)); + plen, lifetime, preferred, flags, + NULL)); } static gboolean ip4_address_delete (NMPlatform *platform, int ifindex, in_addr_t addr, int plen) { - return delete_object (platform, build_rtnl_addr (AF_INET, ifindex, &addr, NULL, plen, 0, 0, 0)); + return delete_object (platform, build_rtnl_addr (AF_INET, ifindex, &addr, NULL, plen, 0, 0, 0, NULL)); } static gboolean ip6_address_delete (NMPlatform *platform, int ifindex, struct in6_addr addr, int plen) { - return delete_object (platform, build_rtnl_addr (AF_INET6, ifindex, &addr, NULL, plen, 0, 0, 0)); + return delete_object (platform, build_rtnl_addr (AF_INET6, ifindex, &addr, NULL, plen, 0, 0, 0, NULL)); } static gboolean ip_address_exists (NMPlatform *platform, int family, int ifindex, gconstpointer addr, int plen) { - auto_nl_object struct nl_object *object = build_rtnl_addr (family, ifindex, addr, NULL, plen, 0, 0, 0); + auto_nl_object struct nl_object *object = build_rtnl_addr (family, ifindex, addr, NULL, plen, 0, 0, 0, NULL); auto_nl_object struct nl_object *cached_object = nl_cache_search (choose_cache (platform, object), object); return !!cached_object; diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index a42910f2c1..ad39084c69 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -1230,7 +1230,8 @@ nm_platform_ip4_address_add (int ifindex, in_addr_t peer_address, int plen, guint32 lifetime, - guint32 preferred) + guint32 preferred, + const char *label) { reset_error (); @@ -1238,6 +1239,7 @@ nm_platform_ip4_address_add (int ifindex, g_return_val_if_fail (plen > 0, FALSE); g_return_val_if_fail (lifetime > 0, FALSE); g_return_val_if_fail (klass->ip4_address_add, FALSE); + g_return_val_if_fail (!label || strlen (label) < sizeof (((NMPlatformIP4Address *) NULL)->label), FALSE); if (nm_logging_enabled (LOGL_DEBUG, LOGD_PLATFORM)) { NMPlatformIP4Address addr = { 0 }; @@ -1248,10 +1250,12 @@ nm_platform_ip4_address_add (int ifindex, addr.plen = plen; addr.lifetime = lifetime; addr.preferred = preferred; + if (label) + g_strlcpy (addr.label, label, sizeof (addr.label)); debug ("address: adding or updating IPv4 address: %s", nm_platform_ip4_address_to_string (&addr)); } - return klass->ip4_address_add (platform, ifindex, address, peer_address, plen, lifetime, preferred); + return klass->ip4_address_add (platform, ifindex, address, peer_address, plen, lifetime, preferred, label); } gboolean @@ -1422,7 +1426,7 @@ nm_platform_ip4_address_sync (int ifindex, const GArray *known_addresses) } else lifetime = preferred = NM_PLATFORM_LIFETIME_PERMANENT; - if (!nm_platform_ip4_address_add (ifindex, known_address->address, known_address->peer_address, known_address->plen, lifetime, preferred)) + if (!nm_platform_ip4_address_add (ifindex, known_address->address, known_address->peer_address, known_address->plen, lifetime, preferred, known_address->label)) return FALSE; } @@ -1914,6 +1918,7 @@ nm_platform_ip4_address_to_string (const NMPlatformIP4Address *address) char s_address[INET_ADDRSTRLEN]; char s_peer[INET_ADDRSTRLEN]; char str_dev[TO_STRING_DEV_BUF_SIZE]; + char str_label[32]; char *str_peer = NULL; g_return_val_if_fail (address, "(unknown)"); @@ -1927,11 +1932,17 @@ nm_platform_ip4_address_to_string (const NMPlatformIP4Address *address) _to_string_dev (address->ifindex, str_dev, sizeof (str_dev)); - g_snprintf (to_string_buffer, sizeof (to_string_buffer), "%s/%d lft %u pref %u time %u%s%s src %s", + if (*address->label) + g_snprintf (str_label, sizeof (str_label), " label %s", address->label); + else + str_label[0] = 0; + + g_snprintf (to_string_buffer, sizeof (to_string_buffer), "%s/%d lft %u pref %u time %u%s%s%s src %s", s_address, address->plen, (guint)address->lifetime, (guint)address->preferred, (guint)address->timestamp, str_peer ? str_peer : "", str_dev, + str_label, source_to_string (address->source)); g_free (str_peer); return to_string_buffer; @@ -2092,6 +2103,13 @@ nm_platform_ip6_route_to_string (const NMPlatformIP6Route *route) return (((a)->field) < ((b)->field)) ? -1 : 1; \ } G_STMT_END +#define _CMP_FIELD_STR(a, b, field) \ + G_STMT_START { \ + int c = strcmp ((a)->field, (b)->field); \ + if (c != 0) \ + return c < 0 ? -1 : 1; \ + } G_STMT_END + #define _CMP_FIELD_MEMCMP(a, b, field) \ G_STMT_START { \ int c = memcmp (&((a)->field), &((b)->field), \ @@ -2112,6 +2130,7 @@ nm_platform_ip4_address_cmp (const NMPlatformIP4Address *a, const NMPlatformIP4A _CMP_FIELD (a, b, timestamp); _CMP_FIELD (a, b, lifetime); _CMP_FIELD (a, b, preferred); + _CMP_FIELD_STR (a, b, label); return 0; } diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index cf41ad85fd..356af1f940 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -156,6 +156,7 @@ typedef struct { guint32 timestamp; guint32 lifetime; /* seconds */ guint32 preferred; /* seconds */ + char label[IFNAMSIZ]; } NMPlatformIP4Address; /** @@ -346,7 +347,8 @@ typedef struct { GArray * (*ip6_address_get_all) (NMPlatform *, int ifindex); gboolean (*ip4_address_add) (NMPlatform *, int ifindex, in_addr_t address, in_addr_t peer_address, int plen, - guint32 lifetime, guint32 preferred_lft); + guint32 lifetime, guint32 preferred_lft, + const char *label); gboolean (*ip6_address_add) (NMPlatform *, int ifindex, struct in6_addr address, struct in6_addr peer_address, int plen, guint32 lifetime, guint32 preferred_lft, guint flags); @@ -475,7 +477,8 @@ GArray *nm_platform_ip4_address_get_all (int ifindex); GArray *nm_platform_ip6_address_get_all (int ifindex); gboolean nm_platform_ip4_address_add (int ifindex, in_addr_t address, in_addr_t peer_address, int plen, - guint32 lifetime, guint32 preferred_lft); + guint32 lifetime, guint32 preferred_lft, + const char *label); gboolean nm_platform_ip6_address_add (int ifindex, struct in6_addr address, struct in6_addr peer_address, int plen, guint32 lifetime, guint32 preferred_lft, guint flags); diff --git a/src/platform/tests/platform.c b/src/platform/tests/platform.c index d599096fea..baa7cd5a69 100644 --- a/src/platform/tests/platform.c +++ b/src/platform/tests/platform.c @@ -570,7 +570,7 @@ do_ip4_address_add (char **argv) guint32 lifetime = strtol (*argv++, NULL, 10); guint32 preferred = strtol (*argv++, NULL, 10); - gboolean value = nm_platform_ip4_address_add (ifindex, address, 0, plen, lifetime, preferred); + gboolean value = nm_platform_ip4_address_add (ifindex, address, 0, plen, lifetime, preferred, NULL); return value; } else return FALSE; diff --git a/src/platform/tests/test-address.c b/src/platform/tests/test-address.c index 20f1bd9ed6..a81b108171 100644 --- a/src/platform/tests/test-address.c +++ b/src/platform/tests/test-address.c @@ -60,14 +60,14 @@ test_ip4_address (void) /* Add address */ g_assert (!nm_platform_ip4_address_exists (ifindex, addr, IP4_PLEN)); no_error (); - g_assert (nm_platform_ip4_address_add (ifindex, addr, 0, IP4_PLEN, lifetime, preferred)); + g_assert (nm_platform_ip4_address_add (ifindex, addr, 0, IP4_PLEN, lifetime, preferred, NULL)); no_error (); g_assert (nm_platform_ip4_address_exists (ifindex, addr, IP4_PLEN)); no_error (); accept_signal (address_added); /* Add address again (aka update) */ - g_assert (nm_platform_ip4_address_add (ifindex, addr, 0, IP4_PLEN, lifetime, preferred)); + g_assert (nm_platform_ip4_address_add (ifindex, addr, 0, IP4_PLEN, lifetime, preferred, NULL)); no_error (); accept_signal (address_changed); @@ -183,7 +183,7 @@ test_ip4_address_external (void) /* Add/delete conflict */ run_command ("ip address add %s/%d dev %s valid_lft %d preferred_lft %d", IP4_ADDRESS, IP4_PLEN, DEVICE_NAME, lifetime, preferred); - g_assert (nm_platform_ip4_address_add (ifindex, addr, 0, IP4_PLEN, lifetime, preferred)); + g_assert (nm_platform_ip4_address_add (ifindex, addr, 0, IP4_PLEN, lifetime, preferred, NULL)); no_error (); g_assert (nm_platform_ip4_address_exists (ifindex, addr, IP4_PLEN)); accept_signal (address_added); diff --git a/src/platform/tests/test-cleanup.c b/src/platform/tests/test-cleanup.c index d94b359559..d8e8a7d513 100644 --- a/src/platform/tests/test-cleanup.c +++ b/src/platform/tests/test-cleanup.c @@ -41,7 +41,7 @@ test_cleanup_internal () g_assert (ifindex > 0); /* Add routes and addresses */ - g_assert (nm_platform_ip4_address_add (ifindex, addr4, 0, plen4, lifetime, preferred)); + g_assert (nm_platform_ip4_address_add (ifindex, addr4, 0, plen4, lifetime, preferred, NULL)); g_assert (nm_platform_ip6_address_add (ifindex, addr6, in6addr_any, plen6, lifetime, preferred, flags)); g_assert (nm_platform_ip4_route_add (ifindex, gateway4, 32, INADDR_ANY, metric, mss)); g_assert (nm_platform_ip4_route_add (ifindex, network4, plen4, gateway4, metric, mss)); diff --git a/src/settings/plugins/ifcfg-rh/plugin.c b/src/settings/plugins/ifcfg-rh/plugin.c index 0f8fcb8a00..4b70813221 100644 --- a/src/settings/plugins/ifcfg-rh/plugin.c +++ b/src/settings/plugins/ifcfg-rh/plugin.c @@ -377,7 +377,7 @@ ifcfg_dir_changed (GFileMonitor *monitor, gpointer user_data) { SCPluginIfcfg *plugin = SC_PLUGIN_IFCFG (user_data); - char *path, *ifcfg_path; + char *path, *base, *ifcfg_path; NMIfcfgConnection *connection; path = g_file_get_path (file); @@ -386,8 +386,14 @@ ifcfg_dir_changed (GFileMonitor *monitor, return; } - /* Given any ifcfg, keys, or routes file, get the ifcfg file path */ - ifcfg_path = utils_get_ifcfg_path (path); + base = g_file_get_basename (file); + if (utils_is_ifcfg_alias_file (base, NULL)) { + /* Alias file changed. Get the base ifcfg file from it */ + ifcfg_path = utils_get_ifcfg_from_alias (path); + } else { + /* Given any ifcfg, keys, or routes file, get the ifcfg file path */ + ifcfg_path = utils_get_ifcfg_path (path); + } if (ifcfg_path) { connection = find_by_path (plugin, ifcfg_path); switch (event_type) { @@ -407,6 +413,7 @@ ifcfg_dir_changed (GFileMonitor *monitor, g_free (ifcfg_path); } g_free (path); + g_free (base); } static void @@ -459,6 +466,8 @@ read_connections (SCPluginIfcfg *plugin) if (utils_should_ignore_file (item, TRUE)) continue; + if (utils_is_ifcfg_alias_file (item, NULL)) + continue; full_path = g_build_filename (IFCFG_DIR, item, NULL); if (!utils_get_ifcfg_name (full_path, TRUE)) diff --git a/src/settings/plugins/ifcfg-rh/reader.c b/src/settings/plugins/ifcfg-rh/reader.c index c1fd181463..65142b92f5 100644 --- a/src/settings/plugins/ifcfg-rh/reader.c +++ b/src/settings/plugins/ifcfg-rh/reader.c @@ -50,6 +50,7 @@ #include <nm-setting-bridge-port.h> #include <nm-setting-dcb.h> #include <nm-setting-generic.h> +#include <nm-util-private.h> #include <nm-utils.h> #include "wifi-utils.h" @@ -641,10 +642,9 @@ static gboolean read_full_ip4_address (shvarFile *ifcfg, const char *network_file, gint32 which, - NMIP4Address **out_address, + NMIP4Address *addr, GError **error) { - NMIP4Address *addr; char *ip_tag, *prefix_tag, *netmask_tag, *gw_tag; guint32 tmp; gboolean success = FALSE; @@ -654,12 +654,10 @@ read_full_ip4_address (shvarFile *ifcfg, g_return_val_if_fail (which >= -1, FALSE); g_return_val_if_fail (ifcfg != NULL, FALSE); g_return_val_if_fail (network_file != NULL, FALSE); - g_return_val_if_fail (out_address != NULL, FALSE); - g_return_val_if_fail (*out_address == NULL, FALSE); + g_return_val_if_fail (addr != NULL, FALSE); if (error) g_return_val_if_fail (*error == NULL, FALSE); - addr = nm_ip4_address_new (); ip_tag = get_numbered_tag ("IPADDR", which); prefix_tag = get_numbered_tag ("PREFIX", which); netmask_tag = get_numbered_tag ("NETMASK", which); @@ -668,13 +666,12 @@ read_full_ip4_address (shvarFile *ifcfg, /* IP address */ if (!read_ip4_address (ifcfg, ip_tag, &tmp, error)) goto done; - if (!tmp) { - nm_ip4_address_unref (addr); - addr = NULL; - success = TRUE; /* done */ + if (tmp) + nm_ip4_address_set_address (addr, tmp); + else if (!nm_ip4_address_get_address (addr)) { + success = TRUE; goto done; } - nm_ip4_address_set_address (addr, tmp); /* Gateway */ if (!read_ip4_address (ifcfg, gw_tag, &tmp, error)) @@ -741,13 +738,9 @@ read_full_ip4_address (shvarFile *ifcfg, goto done; } - *out_address = addr; success = TRUE; done: - if (!success && addr) - nm_ip4_address_unref (addr); - g_free (ip_tag); g_free (prefix_tag); g_free (netmask_tag); @@ -1387,9 +1380,12 @@ make_ip4_setting (shvarFile *ifcfg, for (i = -1; i < 256; i++) { NMIP4Address *addr = NULL; - if (!read_full_ip4_address (ifcfg, network_file, i, &addr, error)) + addr = nm_ip4_address_new (); + if (!read_full_ip4_address (ifcfg, network_file, i, addr, error)) goto done; - if (!addr) { + if (!nm_ip4_address_get_address (addr)) { + nm_ip4_address_unref (addr); + /* The first mandatory variable is 2-indexed (IPADDR2) * Variables IPADDR, IPADDR0 and IPADDR1 are optional */ if (i > 1) @@ -1516,6 +1512,101 @@ done: return NULL; } +static void +read_aliases (NMSettingIP4Config *s_ip4, const char *filename, const char *network_file) +{ + GDir *dir; + char *dirname, *base; + shvarFile *parsed; + NMIP4Address *base_addr; + GError *err = NULL; + + g_return_if_fail (s_ip4 != NULL); + g_return_if_fail (filename != NULL); + + base_addr = nm_setting_ip4_config_get_address (s_ip4, 0); + if (!base_addr) + return; + + dirname = g_path_get_dirname (filename); + g_return_if_fail (dirname != NULL); + base = g_path_get_basename (filename); + g_return_if_fail (base != NULL); + + dir = g_dir_open (dirname, 0, &err); + if (dir) { + const char *item; + NMIP4Address *addr; + gboolean ok; + + while ((item = g_dir_read_name (dir))) { + char *full_path, *device; + const char *p; + + if (!utils_is_ifcfg_alias_file (item, base)) + continue; + + full_path = g_build_filename (dirname, item, NULL); + + p = strchr (item, ':'); + g_assert (p != NULL); /* we know this is true from utils_is_ifcfg_alias_file() */ + for (p++; *p; p++) { + if (!g_ascii_isalnum (*p) && *p != '_') { + PLUGIN_WARN (IFCFG_PLUGIN_NAME, " alias: ignoring alias file '%s' with invalid name", full_path); + g_free (full_path); + continue; + } + } + + parsed = svNewFile (full_path); + if (!parsed) { + PLUGIN_WARN (IFCFG_PLUGIN_NAME, " alias: couldn't parse file '%s'", full_path); + g_free (full_path); + continue; + } + + device = svGetValue (parsed, "DEVICE", FALSE); + if (!device) { + PLUGIN_WARN (IFCFG_PLUGIN_NAME, " alias: file '%s' has no DEVICE", full_path); + svCloseFile (parsed); + g_free (full_path); + continue; + } + /* We know that item starts with IFCFG_TAG from utils_is_ifcfg_alias_file() */ + if (strcmp (device, item + strlen (IFCFG_TAG)) != 0) { + PLUGIN_WARN (IFCFG_PLUGIN_NAME, " alias: file '%s' has invalid DEVICE (%s) for filename", full_path, device); + g_free (device); + svCloseFile (parsed); + g_free (full_path); + continue; + } + + addr = nm_ip4_address_dup (base_addr); + ok = read_full_ip4_address (parsed, network_file, -1, addr, &err); + svCloseFile (parsed); + if (ok) { + if (!NM_UTIL_PRIVATE_CALL (nm_setting_ip4_config_add_address_with_label (s_ip4, addr, device))) + PLUGIN_WARN (IFCFG_PLUGIN_NAME, " alias: duplicate IP4 address in alias file %s", item); + } else { + PLUGIN_WARN (IFCFG_PLUGIN_NAME, " alias: error reading IP4 address from '%s': %s", full_path, err ? err->message : "no address") + g_clear_error (&err); + } + nm_ip4_address_unref (addr); + + g_free (device); + g_free (full_path); + } + + g_dir_close (dir); + } else { + PLUGIN_WARN (IFCFG_PLUGIN_NAME, " alias: can not read directory '%s': %s", dirname, err->message); + g_error_free (err); + } + + g_free (base); + g_free (dirname); +} + static NMSetting * make_ip6_setting (shvarFile *ifcfg, const char *network_file, @@ -5106,8 +5197,10 @@ connection_from_file (const char *filename, g_object_unref (connection); connection = NULL; goto done; - } else + } else { + read_aliases (NM_SETTING_IP4_CONFIG (s_ip4), filename, network_file); nm_connection_add_setting (connection, s_ip4); + } /* There is only one DOMAIN variable and it is read and put to IPv4 config * But if IPv4 is disabled or the config fails for some reason, we read diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/Makefile.am b/src/settings/plugins/ifcfg-rh/tests/network-scripts/Makefile.am index db9cba22b7..ef22288588 100644 --- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/Makefile.am +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/Makefile.am @@ -117,8 +117,24 @@ EXTRA_DIST = \ ifcfg-test-team-port \ ifcfg-test-team-port-empty-config +# make target dependencies can't have colons in their names, which ends up +# meaning that we can't add the alias files to EXTRA_DIST +ALIAS_FILES = \ + ifcfg-aliasem0 \ + ifcfg-aliasem0:1 \ + ifcfg-aliasem0:2 \ + ifcfg-aliasem0:99 \ + ifcfg-aliasem1 \ + ifcfg-aliasem1:1 \ + ifcfg-aliasem1:2 + +dist-hook: + @for f in $(ALIAS_FILES); do \ + cp $(abs_srcdir)/$$f $(distdir)/; \ + done + check-local: - @for f in $(EXTRA_DIST); do \ + @for f in $(EXTRA_DIST) $(ALIAS_FILES); do \ chmod 0600 $(abs_srcdir)/$$f; \ done diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-aliasem0 b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-aliasem0 new file mode 100644 index 0000000000..4f9d645c8a --- /dev/null +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-aliasem0 @@ -0,0 +1,12 @@ +TYPE=Ethernet +DEVICE=aliasem0 +HWADDR=00:11:22:33:44:55 +BOOTPROTO=none +ONBOOT=yes +DNS1=4.2.2.1 +DNS2=4.2.2.2 +IPADDR=192.168.1.5 +PREFIX=24 +NETMASK=255.255.255.0 +GATEWAY=192.168.1.1 +IPV6INIT=no diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-aliasem0:1 b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-aliasem0:1 new file mode 100644 index 0000000000..37c0df1185 --- /dev/null +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-aliasem0:1 @@ -0,0 +1,2 @@ +DEVICE=aliasem0:1 +IPADDR=192.168.1.6 diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-aliasem0:2 b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-aliasem0:2 new file mode 100644 index 0000000000..0c3b6a7e91 --- /dev/null +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-aliasem0:2 @@ -0,0 +1,2 @@ +DEVICE=aliasem0:2 +IPADDR=192.168.1.9 diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-aliasem0:99 b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-aliasem0:99 new file mode 100644 index 0000000000..5df3f154b1 --- /dev/null +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-aliasem0:99 @@ -0,0 +1,2 @@ +DEVICE=aliasem0:99 +IPADDR=192.168.1.99 diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-aliasem1 b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-aliasem1 new file mode 100644 index 0000000000..f6fefc0d0c --- /dev/null +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-aliasem1 @@ -0,0 +1,12 @@ +TYPE=Ethernet +DEVICE=aliasem1 +HWADDR=00:11:22:33:44:55 +BOOTPROTO=none +ONBOOT=yes +DNS1=4.2.2.1 +DNS2=4.2.2.2 +IPADDR=192.168.1.5 +PREFIX=24 +NETMASK=255.255.255.0 +GATEWAY=192.168.1.1 +IPV6INIT=no diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-aliasem1:1 b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-aliasem1:1 new file mode 100644 index 0000000000..21971611ef --- /dev/null +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-aliasem1:1 @@ -0,0 +1,2 @@ +# bad, no DEVICE +IPADDR=192.168.1.12 diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-aliasem1:2 b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-aliasem1:2 new file mode 100644 index 0000000000..b2ea218024 --- /dev/null +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-aliasem1:2 @@ -0,0 +1,3 @@ +# bad: wrong DEVICE +DEVICE=aliasem0:2 +IPADDR=192.168.1.20 diff --git a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c index e4346c6bc3..f45d788ece 100644 --- a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c +++ b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c @@ -46,6 +46,7 @@ #include <nm-setting-serial.h> #include <nm-setting-vlan.h> #include <nm-setting-dcb.h> +#include <nm-util-private.h> #include "nm-test-helpers.h" #include "NetworkManagerUtils.h" @@ -2829,6 +2830,271 @@ test_read_write_802_1X_subj_matches (void) g_object_unref (reread); } +#define TEST_IFCFG_ALIASES_GOOD TEST_IFCFG_DIR"/network-scripts/ifcfg-aliasem0" + +static void +test_read_wired_aliases_good (void) +{ + NMConnection *connection; + NMSettingConnection *s_con; + NMSettingIP4Config *s_ip4; + char *unmanaged = NULL; + char *keyfile = NULL; + char *routefile = NULL; + char *route6file = NULL; + gboolean ignore_error = FALSE; + GError *error = NULL; + const char *tmp; + const char *expected_id = "System aliasem0"; + int expected_num_addresses = 4, expected_prefix = 24; + const char *expected_address[4] = { "192.168.1.5", "192.168.1.6", "192.168.1.9", "192.168.1.99" }; + const char *expected_label[4] = { NULL, "aliasem0:1", "aliasem0:2", "aliasem0:99" }; + const char *expected_gateway[4] = { "192.168.1.1", "192.168.1.1", "192.168.1.1", "192.168.1.1" }; + int i, j; + + connection = connection_from_file (TEST_IFCFG_ALIASES_GOOD, + NULL, + TYPE_ETHERNET, + NULL, + &unmanaged, + &keyfile, + &routefile, + &route6file, + &error, + &ignore_error); + ASSERT (connection != NULL, + "aliases-good-read", "failed to read %s: %s", TEST_IFCFG_ALIASES_GOOD, error->message); + + ASSERT (nm_connection_verify (connection, &error), + "aliases-good-verify", "failed to verify %s: %s", TEST_IFCFG_ALIASES_GOOD, error->message); + + /* ===== CONNECTION SETTING ===== */ + + s_con = nm_connection_get_setting_connection (connection); + ASSERT (s_con != NULL, + "aliases-good-verify-connection", "failed to verify %s: missing %s setting", + TEST_IFCFG_ALIASES_GOOD, + NM_SETTING_CONNECTION_SETTING_NAME); + + /* ID */ + tmp = nm_setting_connection_get_id (s_con); + ASSERT (tmp != NULL, + "aliases-good-verify-connection", "failed to verify %s: missing %s / %s key", + TEST_IFCFG_ALIASES_GOOD, + NM_SETTING_CONNECTION_SETTING_NAME, + NM_SETTING_CONNECTION_ID); + ASSERT (strcmp (tmp, expected_id) == 0, + "aliases-good-verify-connection", "failed to verify %s: unexpected %s / %s key value", + TEST_IFCFG_ALIASES_GOOD, + NM_SETTING_CONNECTION_SETTING_NAME, + NM_SETTING_CONNECTION_ID); + + /* ===== IPv4 SETTING ===== */ + + s_ip4 = nm_connection_get_setting_ip4_config (connection); + ASSERT (s_ip4 != NULL, + "aliases-good-verify-ip4", "failed to verify %s: missing %s setting", + TEST_IFCFG_ALIASES_GOOD, + NM_SETTING_IP4_CONFIG_SETTING_NAME); + + /* Method */ + tmp = nm_setting_ip4_config_get_method (s_ip4); + ASSERT (strcmp (tmp, NM_SETTING_IP4_CONFIG_METHOD_MANUAL) == 0, + "aliases-good-verify-ip4", "failed to verify %s: unexpected %s / %s key value", + TEST_IFCFG_ALIASES_GOOD, + NM_SETTING_IP4_CONFIG_SETTING_NAME, + NM_SETTING_IP4_CONFIG_METHOD); + + ASSERT (nm_setting_ip4_config_get_num_addresses (s_ip4) == expected_num_addresses, + "aliases-good-verify-ip4", "failed to verify %s: unexpected %s / %s key value", + TEST_IFCFG_ALIASES_GOOD, + NM_SETTING_IP4_CONFIG_SETTING_NAME, + NM_SETTING_IP4_CONFIG_ADDRESSES); + + /* Addresses */ + for (i = 0; i < expected_num_addresses; i++) { + NMIP4Address *ip4_addr; + char buf[INET_ADDRSTRLEN]; + struct in_addr addr; + + ip4_addr = nm_setting_ip4_config_get_address (s_ip4, i); + ASSERT (ip4_addr, + "aliases-good-verify-ip4", "failed to verify %s: missing IP4 address #%d", + TEST_IFCFG_ALIASES_GOOD, + i); + + addr.s_addr = nm_ip4_address_get_address (ip4_addr); + ASSERT (inet_ntop (AF_INET, &addr, buf, sizeof (buf)) > 0, + "aliases-good-verify-ip4", "failed to verify %s: couldn't convert IP address #%d", + TEST_IFCFG_ALIASES_GOOD, + i); + + for (j = 0; j < expected_num_addresses; j++) { + if (!g_strcmp0 (buf, expected_address[j])) + break; + } + + ASSERT (j < expected_num_addresses, + "aliases-good-verify-ip4", "failed to verify %s: unexpected IP4 address #%d", + TEST_IFCFG_ALIASES_GOOD, + i); + + ASSERT (nm_ip4_address_get_prefix (ip4_addr) == expected_prefix, + "aliases-good-verify-ip4", "failed to verify %s: unexpected IP4 address prefix #%d", + TEST_IFCFG_ALIASES_GOOD, + i); + + if (expected_gateway[j]) { + ASSERT (inet_pton (AF_INET, expected_gateway[j], &addr) > 0, + "aliases-good-verify-ip4", "failed to verify %s: couldn't convert IP address gateway #%d", + TEST_IFCFG_ALIASES_GOOD, + i); + } else + addr.s_addr = 0; + ASSERT (nm_ip4_address_get_gateway (ip4_addr) == addr.s_addr, + "aliases-good-verify-ip4", "failed to verify %s: unexpected IP4 address gateway #%d", + TEST_IFCFG_ALIASES_GOOD, + i); + + ASSERT (g_strcmp0 (NM_UTIL_PRIVATE_CALL (nm_setting_ip4_config_get_address_label (s_ip4, i)), expected_label[j]) == 0, + "aliases-good-verify-ip4", "failed to verify %s: unexpected IP4 address label #%d", + TEST_IFCFG_ALIASES_GOOD, + i); + + expected_address[j] = NULL; + expected_gateway[j] = NULL; + expected_label[j] = NULL; + } + + for (i = 0; i < expected_num_addresses; i++) { + ASSERT (expected_address[i] == NULL, + "aliases-good-verify-ip4", "failed to verify %s: did not find IP4 address %s", + TEST_IFCFG_ALIASES_GOOD, + expected_address[i]); + } + + g_free (keyfile); + g_free (routefile); + g_free (route6file); + g_object_unref (connection); +} + +#define TEST_IFCFG_ALIASES_BAD TEST_IFCFG_DIR"/network-scripts/ifcfg-aliasem1" + +static void +test_read_wired_aliases_bad (void) +{ + NMConnection *connection; + NMSettingConnection *s_con; + NMSettingIP4Config *s_ip4; + char *unmanaged = NULL; + char *keyfile = NULL; + char *routefile = NULL; + char *route6file = NULL; + gboolean ignore_error = FALSE; + GError *error = NULL; + const char *tmp; + const char *expected_id = "System aliasem1"; + int expected_num_addresses = 1, expected_prefix = 24; + const char *expected_address = "192.168.1.5"; + const char *expected_label = NULL; + const char *expected_gateway = "192.168.1.1"; + NMIP4Address *ip4_addr; + struct in_addr addr; + + connection = connection_from_file (TEST_IFCFG_ALIASES_BAD, + NULL, + TYPE_ETHERNET, + NULL, + &unmanaged, + &keyfile, + &routefile, + &route6file, + &error, + &ignore_error); + ASSERT (connection != NULL, + "aliases-bad-read", "failed to read %s: %s", TEST_IFCFG_ALIASES_BAD, error->message); + + ASSERT (nm_connection_verify (connection, &error), + "aliases-bad-verify", "failed to verify %s: %s", TEST_IFCFG_ALIASES_BAD, error->message); + + /* ===== CONNECTION SETTING ===== */ + + s_con = nm_connection_get_setting_connection (connection); + ASSERT (s_con != NULL, + "aliases-bad-verify-connection", "failed to verify %s: missing %s setting", + TEST_IFCFG_ALIASES_BAD, + NM_SETTING_CONNECTION_SETTING_NAME); + + /* ID */ + tmp = nm_setting_connection_get_id (s_con); + ASSERT (tmp != NULL, + "aliases-bad-verify-connection", "failed to verify %s: missing %s / %s key", + TEST_IFCFG_ALIASES_BAD, + NM_SETTING_CONNECTION_SETTING_NAME, + NM_SETTING_CONNECTION_ID); + ASSERT (strcmp (tmp, expected_id) == 0, + "aliases-bad-verify-connection", "failed to verify %s: unexpected %s / %s key value", + TEST_IFCFG_ALIASES_BAD, + NM_SETTING_CONNECTION_SETTING_NAME, + NM_SETTING_CONNECTION_ID); + + /* ===== IPv4 SETTING ===== */ + + s_ip4 = nm_connection_get_setting_ip4_config (connection); + ASSERT (s_ip4 != NULL, + "aliases-bad-verify-ip4", "failed to verify %s: missing %s setting", + TEST_IFCFG_ALIASES_BAD, + NM_SETTING_IP4_CONFIG_SETTING_NAME); + + /* Method */ + tmp = nm_setting_ip4_config_get_method (s_ip4); + ASSERT (strcmp (tmp, NM_SETTING_IP4_CONFIG_METHOD_MANUAL) == 0, + "aliases-bad-verify-ip4", "failed to verify %s: unexpected %s / %s key value", + TEST_IFCFG_ALIASES_BAD, + NM_SETTING_IP4_CONFIG_SETTING_NAME, + NM_SETTING_IP4_CONFIG_METHOD); + + ASSERT (nm_setting_ip4_config_get_num_addresses (s_ip4) == expected_num_addresses, + "aliases-bad-verify-ip4", "failed to verify %s: unexpected %s / %s key value", + TEST_IFCFG_ALIASES_BAD, + NM_SETTING_IP4_CONFIG_SETTING_NAME, + NM_SETTING_IP4_CONFIG_ADDRESSES); + + /* Addresses */ + ip4_addr = nm_setting_ip4_config_get_address (s_ip4, 0); + ASSERT (ip4_addr, + "aliases-bad-verify-ip4", "failed to verify %s: missing IP4 address", + TEST_IFCFG_ALIASES_BAD); + + ASSERT (inet_pton (AF_INET, expected_address, &addr) > 0, + "aliases-bad-verify-ip4", "failed to verify %s: couldn't convert IP address", + TEST_IFCFG_ALIASES_BAD); + ASSERT (nm_ip4_address_get_address (ip4_addr) == addr.s_addr, + "aliases-bad-verify-ip4", "failed to verify %s: unexpected IP4 address", + TEST_IFCFG_ALIASES_BAD); + + ASSERT (nm_ip4_address_get_prefix (ip4_addr) == expected_prefix, + "aliases-bad-verify-ip4", "failed to verify %s: unexpected IP4 address prefix", + TEST_IFCFG_ALIASES_BAD); + + ASSERT (inet_pton (AF_INET, expected_gateway, &addr) > 0, + "aliases-bad-verify-ip4", "failed to verify %s: couldn't convert IP address gateway", + TEST_IFCFG_ALIASES_BAD); + ASSERT (nm_ip4_address_get_gateway (ip4_addr) == addr.s_addr, + "aliases-bad-verify-ip4", "failed to verify %s: unexpected IP4 address gateway", + TEST_IFCFG_ALIASES_BAD); + + ASSERT (g_strcmp0 (NM_UTIL_PRIVATE_CALL (nm_setting_ip4_config_get_address_label (s_ip4, 0)), expected_label) == 0, + "aliases-bad-verify-ip4", "failed to verify %s: unexpected IP4 address label", + TEST_IFCFG_ALIASES_BAD); + + g_free (keyfile); + g_free (routefile); + g_free (route6file); + g_object_unref (connection); +} + #define TEST_IFCFG_WIFI_OPEN TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wifi-open" static void @@ -7799,6 +8065,208 @@ test_write_wired_8021x_tls (NMSetting8021xCKScheme scheme, g_object_unref (reread); } +#define TEST_SCRATCH_ALIAS_BASE TEST_SCRATCH_DIR "/network-scripts/ifcfg-alias0" + +static void +test_write_wired_aliases (void) +{ + NMConnection *connection; + NMConnection *reread; + NMSettingConnection *s_con; + NMSettingWired *s_wired; + NMSettingIP4Config *s_ip4; + char *uuid; + int num_addresses = 4; + guint32 ip[] = { 0x01010101, 0x01010102, 0x01010103, 0x01010104 }; + const char *label[] = { NULL, "alias0:2", NULL, "alias0:3" }; + const guint32 gw = htonl (0x01010101); + const guint32 prefix = 24; + NMIP4Address *addr; + gboolean success; + GError *error = NULL; + char *testfile = NULL; + char *unmanaged = NULL; + char *keyfile = NULL; + char *routefile = NULL; + char *route6file = NULL; + gboolean ignore_error = FALSE; + shvarFile *ifcfg; + int i, j; + + connection = nm_connection_new (); + ASSERT (connection != NULL, + "wired-aliases-write", "failed to allocate new connection"); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new (); + ASSERT (s_con != NULL, + "wired-aliases-write", "failed to allocate new %s setting", + NM_SETTING_CONNECTION_SETTING_NAME); + nm_connection_add_setting (connection, NM_SETTING (s_con)); + + uuid = nm_utils_uuid_generate (); + g_object_set (s_con, + NM_SETTING_CONNECTION_ID, "alias0", + NM_SETTING_CONNECTION_UUID, uuid, + NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME, + NULL); + g_free (uuid); + + /* Wired setting */ + s_wired = (NMSettingWired *) nm_setting_wired_new (); + ASSERT (s_wired != NULL, + "wired-aliases-write", "failed to allocate new %s setting", + NM_SETTING_WIRED_SETTING_NAME); + nm_connection_add_setting (connection, NM_SETTING (s_wired)); + + /* IP4 setting */ + s_ip4 = (NMSettingIP4Config *) nm_setting_ip4_config_new (); + ASSERT (s_ip4 != NULL, + "wired-aliases-write", "failed to allocate new %s setting", + NM_SETTING_IP4_CONFIG_SETTING_NAME); + nm_connection_add_setting (connection, NM_SETTING (s_ip4)); + + g_object_set (s_ip4, + NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_MANUAL, + NM_SETTING_IP4_CONFIG_MAY_FAIL, TRUE, + NULL); + + for (i = 0; i < num_addresses; i++) { + addr = nm_ip4_address_new (); + nm_ip4_address_set_address (addr, ip[i]); + nm_ip4_address_set_prefix (addr, prefix); + nm_ip4_address_set_gateway (addr, gw); + NM_UTIL_PRIVATE_CALL (nm_setting_ip4_config_add_address_with_label (s_ip4, addr, label[i])); + nm_ip4_address_unref (addr); + } + + ASSERT (nm_connection_verify (connection, &error) == TRUE, + "wired-aliases-write", "failed to verify connection: %s", + (error && error->message) ? error->message : "(unknown)"); + + /* Create some pre-existing alias files, to make sure they get overwritten / deleted. */ + ifcfg = svCreateFile (TEST_SCRATCH_ALIAS_BASE ":2"); + svSetValue (ifcfg, "DEVICE", "alias0:2", FALSE); + svSetValue (ifcfg, "IPADDR", "192.168.1.2", FALSE); + svWriteFile (ifcfg, 0644); + svCloseFile (ifcfg); + ASSERT (g_file_test (TEST_SCRATCH_ALIAS_BASE ":2", G_FILE_TEST_EXISTS), + "wired-aliases-write", "failed to write extra alias file"); + + ifcfg = svCreateFile (TEST_SCRATCH_ALIAS_BASE ":5"); + svSetValue (ifcfg, "DEVICE", "alias0:5", FALSE); + svSetValue (ifcfg, "IPADDR", "192.168.1.5", FALSE); + svWriteFile (ifcfg, 0644); + svCloseFile (ifcfg); + ASSERT (g_file_test (TEST_SCRATCH_ALIAS_BASE ":5", G_FILE_TEST_EXISTS), + "wired-aliases-write", "failed to write extra alias file"); + + /* Save the ifcfg */ + success = writer_new_connection (connection, + TEST_SCRATCH_DIR "/network-scripts/", + &testfile, + &error); + ASSERT (success == TRUE, + "wired-aliases-write", "failed to write connection to disk: %s", + (error && error->message) ? error->message : "(unknown)"); + + ASSERT (testfile != NULL, + "wired-aliases-write", "didn't get ifcfg file path back after writing connection"); + + /* Re-check the alias files */ + ASSERT (g_file_test (TEST_SCRATCH_ALIAS_BASE ":2", G_FILE_TEST_EXISTS), + "wired-aliases-write", "saving failed to write ifcfg-alias0:2"); + ASSERT (g_file_test (TEST_SCRATCH_ALIAS_BASE ":3", G_FILE_TEST_EXISTS), + "wired-aliases-write", "saving failed to write ifcfg-alias0:3"); + ASSERT (!g_file_test (TEST_SCRATCH_ALIAS_BASE ":5", G_FILE_TEST_EXISTS), + "wired-aliases-write", "saving failed to delete unused ifcfg-alias0:5"); + + /* re-read the connection for comparison */ + reread = connection_from_file (testfile, + NULL, + TYPE_ETHERNET, + NULL, + &unmanaged, + &keyfile, + &routefile, + &route6file, + &error, + &ignore_error); + unlink (testfile); + unlink (TEST_SCRATCH_ALIAS_BASE ":2"); + unlink (TEST_SCRATCH_ALIAS_BASE ":3"); + + ASSERT (reread != NULL, + "wired-aliases-write-reread", "failed to read %s: %s", testfile, error->message); + + ASSERT (nm_connection_verify (reread, &error), + "wired-aliases-write-reread-verify", "failed to verify %s: %s", testfile, error->message); + + /* nm_connection_compare() is not guaranteed to succeed, because the + * aliases get read back in essentially random order. So just + * verify the aliases manually. + */ + s_ip4 = nm_connection_get_setting_ip4_config (connection); + ASSERT (nm_setting_ip4_config_get_num_addresses (s_ip4) == num_addresses, + "wired-aliases-write-verify-ip4", "failed to verify %s: unexpected %s / %s key value", + testfile, + NM_SETTING_IP4_CONFIG_SETTING_NAME, + NM_SETTING_IP4_CONFIG_ADDRESSES); + + /* Addresses */ + for (i = 0; i < num_addresses; i++) { + guint32 addrbytes; + + addr = nm_setting_ip4_config_get_address (s_ip4, i); + ASSERT (addr, + "wired-aliases-write-verify-ip4", "failed to verify %s: missing IP4 address #%d", + testfile, + i); + + addrbytes = nm_ip4_address_get_address (addr); + for (j = 0; j < num_addresses; j++) { + if (addrbytes == ip[j]) + break; + } + + ASSERT (j < num_addresses, + "wired-aliases-write-verify-ip4", "failed to verify %s: unexpected IP4 address #%d", + testfile, + i); + + ASSERT (nm_ip4_address_get_prefix (addr) == prefix, + "wired-aliases-write-verify-ip4", "failed to verify %s: unexpected IP4 address prefix #%d", + testfile, + i); + + ASSERT (nm_ip4_address_get_gateway (addr) == gw, + "wired-aliases-write-verify-ip4", "failed to verify %s: unexpected IP4 address gateway #%d", + testfile, + i); + + ASSERT (g_strcmp0 (NM_UTIL_PRIVATE_CALL (nm_setting_ip4_config_get_address_label (s_ip4, i)), label[j]) == 0, + "wired-aliases-write-verify-ip4", "failed to verify %s: unexpected IP4 address label #%d", + testfile, + i); + + ip[j] = 0; + } + + for (i = 0; i < num_addresses; i++) { + ASSERT (ip[i] == 0, + "wired-aliases-write-verify-ip4", "failed to verify %s: did not find IP4 address 0x%08x", + testfile, + ip[i]); + } + + g_free (testfile); + g_free (keyfile); + g_free (routefile); + g_free (route6file); + g_object_unref (connection); + g_object_unref (reread); +} + static void test_write_wifi_open (void) { @@ -13869,6 +14337,8 @@ int main (int argc, char **argv) test_read_wired_8021x_tls_secret_flags (TEST_IFCFG_WIRED_8021X_TLS_ALWAYS, NM_SETTING_SECRET_FLAG_AGENT_OWNED | NM_SETTING_SECRET_FLAG_NOT_SAVED); g_test_add_func (TPATH "802-1x/subj-mathes", test_read_write_802_1X_subj_matches); + test_read_wired_aliases_good (); + test_read_wired_aliases_bad (); test_read_wifi_open (); test_read_wifi_open_auto (); test_read_wifi_open_ssid_hex (); @@ -13918,6 +14388,7 @@ int main (int argc, char **argv) test_write_wired_8021x_tls (NM_SETTING_802_1X_CK_SCHEME_PATH, NM_SETTING_SECRET_FLAG_NOT_SAVED); test_write_wired_8021x_tls (NM_SETTING_802_1X_CK_SCHEME_PATH, NM_SETTING_SECRET_FLAG_AGENT_OWNED | NM_SETTING_SECRET_FLAG_NOT_SAVED); test_write_wired_8021x_tls (NM_SETTING_802_1X_CK_SCHEME_BLOB, NM_SETTING_SECRET_FLAG_NONE); + test_write_wired_aliases (); test_write_wifi_open (); test_write_wifi_open_hex_ssid (); test_write_wifi_wep (); diff --git a/src/settings/plugins/ifcfg-rh/utils.c b/src/settings/plugins/ifcfg-rh/utils.c index 0c87a7f973..5abb4282e5 100644 --- a/src/settings/plugins/ifcfg-rh/utils.c +++ b/src/settings/plugins/ifcfg-rh/utils.c @@ -367,3 +367,49 @@ utils_ignore_ip_config (NMConnection *connection) return FALSE; } + +/* Find out if the 'alias' file name might be an alias file for 'ifcfg' file name, + * or any alias when 'ifcfg' is NULL. Does not check that it's actually a valid + * alias name; that happens in reader.c + */ +gboolean +utils_is_ifcfg_alias_file (const char *alias, const char *ifcfg) +{ + g_return_val_if_fail (alias != NULL, FALSE); + + if (strncmp (alias, IFCFG_TAG, strlen (IFCFG_TAG))) + return FALSE; + + if (ifcfg) { + size_t len = strlen (ifcfg); + + return (strncmp (alias, ifcfg, len) == 0 && alias[len] == ':'); + } else { + return (strchr (alias, ':') != NULL); + } +} + +char * +utils_get_ifcfg_from_alias (const char *alias) +{ + char *base, *ptr, *ifcfg = NULL; + + g_return_val_if_fail (alias != NULL, NULL); + + base = g_path_get_basename (alias); + g_return_val_if_fail (base != NULL, NULL); + + if (utils_is_ifcfg_alias_file (base, NULL)) { + ifcfg = g_strdup (alias); + ptr = strrchr (ifcfg, ':'); + if (ptr) + *ptr = '\0'; + else { + g_free (ifcfg); + ifcfg = NULL; + } + } + + g_free (base); + return ifcfg; +} diff --git a/src/settings/plugins/ifcfg-rh/utils.h b/src/settings/plugins/ifcfg-rh/utils.h index 2c94aacaa0..95af828bb1 100644 --- a/src/settings/plugins/ifcfg-rh/utils.h +++ b/src/settings/plugins/ifcfg-rh/utils.h @@ -50,5 +50,8 @@ gboolean utils_has_route_file_new_syntax (const char *filename); gboolean utils_ignore_ip_config (NMConnection *connection); +gboolean utils_is_ifcfg_alias_file (const char *alias, const char *ifcfg); +char *utils_get_ifcfg_from_alias (const char *alias); + #endif /* _UTILS_H_ */ diff --git a/src/settings/plugins/ifcfg-rh/writer.c b/src/settings/plugins/ifcfg-rh/writer.c index 5584f6ed3e..720616d1d5 100644 --- a/src/settings/plugins/ifcfg-rh/writer.c +++ b/src/settings/plugins/ifcfg-rh/writer.c @@ -39,6 +39,7 @@ #include <nm-setting-vlan.h> #include <nm-setting-team.h> #include <nm-setting-team-port.h> +#include <nm-util-private.h> #include <nm-utils.h> #include "common.h" @@ -1845,7 +1846,7 @@ write_ip4_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) char *addr_key, *prefix_key, *netmask_key, *gw_key, *metric_key, *tmp; char *route_path = NULL; gint32 j; - guint32 i, num; + guint32 i, n, num; GString *searches; gboolean success = FALSE; gboolean fake_ip4 = FALSE; @@ -1909,48 +1910,68 @@ write_ip4_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) else if (!strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_SHARED)) svSetValue (ifcfg, "BOOTPROTO", "shared", FALSE); - /* Write out IPADDR0 .. IPADDR255, PREFIX0 .. PREFIX255, GATEWAY0 .. GATEWAY255 - * Possible NETMASK<n> is removed only (it's obsolete) */ - num = nm_setting_ip4_config_get_num_addresses (s_ip4); + /* Clear out un-numbered IP address fields */ svSetValue (ifcfg, "IPADDR", NULL, FALSE); svSetValue (ifcfg, "PREFIX", NULL, FALSE); svSetValue (ifcfg, "NETMASK", NULL, FALSE); svSetValue (ifcfg, "GATEWAY", NULL, FALSE); - for (i = 0; i < 256; i++) { + + /* Write out IPADDR<n>, PREFIX<n>, GATEWAY<n> for current IP addresses + * without labels. Unset obsolete NETMASK<n>. + */ + num = nm_setting_ip4_config_get_num_addresses (s_ip4); + for (i = n = 0; i < num; i++) { char buf[INET_ADDRSTRLEN]; NMIP4Address *addr; guint32 ip; - addr_key = g_strdup_printf ("IPADDR%d", i); - prefix_key = g_strdup_printf ("PREFIX%d", i); - netmask_key = g_strdup_printf ("NETMASK%d", i); - gw_key = g_strdup_printf ("GATEWAY%d", i); + if (i > 0 && NM_UTIL_PRIVATE_CALL (nm_setting_ip4_config_get_address_label (s_ip4, i))) + continue; - if (i >= num) { - svSetValue (ifcfg, addr_key, NULL, FALSE); - svSetValue (ifcfg, prefix_key, NULL, FALSE); - svSetValue (ifcfg, netmask_key, NULL, FALSE); - svSetValue (ifcfg, gw_key, NULL, FALSE); - } else { - addr = nm_setting_ip4_config_get_address (s_ip4, i); + addr_key = g_strdup_printf ("IPADDR%d", n); + prefix_key = g_strdup_printf ("PREFIX%d", n); + netmask_key = g_strdup_printf ("NETMASK%d", n); + gw_key = g_strdup_printf ("GATEWAY%d", n); + + addr = nm_setting_ip4_config_get_address (s_ip4, i); + + memset (buf, 0, sizeof (buf)); + ip = nm_ip4_address_get_address (addr); + inet_ntop (AF_INET, (const void *) &ip, &buf[0], sizeof (buf)); + svSetValue (ifcfg, addr_key, &buf[0], FALSE); + tmp = g_strdup_printf ("%u", nm_ip4_address_get_prefix (addr)); + svSetValue (ifcfg, prefix_key, tmp, FALSE); + g_free (tmp); + + svSetValue (ifcfg, netmask_key, NULL, FALSE); + + if (nm_ip4_address_get_gateway (addr)) { memset (buf, 0, sizeof (buf)); - ip = nm_ip4_address_get_address (addr); + ip = nm_ip4_address_get_gateway (addr); inet_ntop (AF_INET, (const void *) &ip, &buf[0], sizeof (buf)); - svSetValue (ifcfg, addr_key, &buf[0], FALSE); + svSetValue (ifcfg, gw_key, &buf[0], FALSE); + } else + svSetValue (ifcfg, gw_key, NULL, FALSE); - tmp = g_strdup_printf ("%u", nm_ip4_address_get_prefix (addr)); - svSetValue (ifcfg, prefix_key, tmp, FALSE); - g_free (tmp); + g_free (addr_key); + g_free (prefix_key); + g_free (netmask_key); + g_free (gw_key); + n++; + } - if (nm_ip4_address_get_gateway (addr)) { - memset (buf, 0, sizeof (buf)); - ip = nm_ip4_address_get_gateway (addr); - inet_ntop (AF_INET, (const void *) &ip, &buf[0], sizeof (buf)); - svSetValue (ifcfg, gw_key, &buf[0], FALSE); - } else - svSetValue (ifcfg, gw_key, NULL, FALSE); - } + /* Clear remaining IPADDR<n..255>, etc */ + for (; n < 256; n++) { + addr_key = g_strdup_printf ("IPADDR%d", n); + prefix_key = g_strdup_printf ("PREFIX%d", n); + netmask_key = g_strdup_printf ("NETMASK%d", n); + gw_key = g_strdup_printf ("GATEWAY%d", n); + + svSetValue (ifcfg, addr_key, NULL, FALSE); + svSetValue (ifcfg, prefix_key, NULL, FALSE); + svSetValue (ifcfg, netmask_key, NULL, FALSE); + svSetValue (ifcfg, gw_key, NULL, FALSE); g_free (addr_key); g_free (prefix_key); @@ -2120,6 +2141,102 @@ out: return success; } +static void +write_ip4_aliases (NMConnection *connection, char *base_ifcfg_path) +{ + NMSettingIP4Config *s_ip4; + char *base_ifcfg_dir, *base_ifcfg_name, *base_name; + int i, num, base_ifcfg_path_len, base_ifcfg_name_len, base_name_len; + GDir *dir; + + base_ifcfg_path_len = strlen (base_ifcfg_path); + base_ifcfg_dir = g_path_get_dirname (base_ifcfg_path); + base_ifcfg_name = g_path_get_basename (base_ifcfg_path); + base_ifcfg_name_len = strlen (base_ifcfg_name); + base_name = base_ifcfg_name + strlen (IFCFG_TAG); + base_name_len = strlen (base_name); + + /* Remove all existing aliases for this file first */ + dir = g_dir_open (base_ifcfg_dir, 0, NULL); + if (dir) { + const char *item; + + while ((item = g_dir_read_name (dir))) { + char *full_path; + + if ( strncmp (item, base_ifcfg_name, base_ifcfg_name_len) != 0 + || item[base_ifcfg_name_len] != ':') + continue; + + full_path = g_build_filename (base_ifcfg_dir, item, NULL); + unlink (full_path); + g_free (full_path); + } + + g_dir_close (dir); + } + + if (utils_ignore_ip_config (connection)) + return; + + s_ip4 = nm_connection_get_setting_ip4_config (connection); + if (!s_ip4) + return; + + num = nm_setting_ip4_config_get_num_addresses (s_ip4); + for (i = 0; i < num; i++) { + const char *label, *p; + char buf[INET_ADDRSTRLEN], *path, *tmp; + NMIP4Address *addr; + guint32 ip; + shvarFile *ifcfg; + + label = NM_UTIL_PRIVATE_CALL (nm_setting_ip4_config_get_address_label (s_ip4, i)); + if (!label) + continue; + if ( strncmp (label, base_name, base_name_len) != 0 + || label[base_name_len] != ':') + continue; + + for (p = label; *p; p++) { + if (!g_ascii_isalnum (*p) && *p != '_' && *p != ':') + break; + } + if (*p) + continue; + + path = g_strdup_printf ("%s%s", base_ifcfg_path, label + base_name_len); + ifcfg = svCreateFile (path); + g_free (path); + + svSetValue (ifcfg, "DEVICE", label, FALSE); + + addr = nm_setting_ip4_config_get_address (s_ip4, i); + + memset (buf, 0, sizeof (buf)); + ip = nm_ip4_address_get_address (addr); + inet_ntop (AF_INET, (const void *) &ip, &buf[0], sizeof (buf)); + svSetValue (ifcfg, "IPADDR", &buf[0], FALSE); + + tmp = g_strdup_printf ("%u", nm_ip4_address_get_prefix (addr)); + svSetValue (ifcfg, "PREFIX", tmp, FALSE); + g_free (tmp); + + if (nm_ip4_address_get_gateway (addr)) { + memset (buf, 0, sizeof (buf)); + ip = nm_ip4_address_get_gateway (addr); + inet_ntop (AF_INET, (const void *) &ip, &buf[0], sizeof (buf)); + svSetValue (ifcfg, "GATEWAY", &buf[0], FALSE); + } + + svWriteFile (ifcfg, 0644); + svCloseFile (ifcfg); + } + + g_free (base_ifcfg_name); + g_free (base_ifcfg_dir); +} + static gboolean write_route6_file (const char *filename, NMSettingIP6Config *s_ip6, GError **error) { @@ -2517,6 +2634,7 @@ write_connection (NMConnection *connection, if (!write_ip4_setting (connection, ifcfg, error)) goto out; + write_ip4_aliases (connection, ifcfg_name); if (!write_ip6_setting (connection, ifcfg, error)) goto out; diff --git a/src/settings/plugins/keyfile/writer.c b/src/settings/plugins/keyfile/writer.c index 2f8c7cc125..e03ad3d8e7 100644 --- a/src/settings/plugins/keyfile/writer.c +++ b/src/settings/plugins/keyfile/writer.c @@ -195,6 +195,17 @@ ip4_addr_writer (GKeyFile *file, } static void +ip4_addr_label_writer (GKeyFile *file, + const char *keyfile_dir, + const char *uuid, + NMSetting *setting, + const char *key, + const GValue *value) +{ + /* skip */ +} + +static void ip4_route_writer (GKeyFile *file, const char *keyfile_dir, const char *uuid, @@ -773,6 +784,9 @@ static KeyWriter key_writers[] = { { NM_SETTING_IP4_CONFIG_SETTING_NAME, NM_SETTING_IP4_CONFIG_ADDRESSES, ip4_addr_writer }, + { NM_SETTING_IP4_CONFIG_SETTING_NAME, + "address-labels", + ip4_addr_label_writer }, { NM_SETTING_IP6_CONFIG_SETTING_NAME, NM_SETTING_IP6_CONFIG_ADDRESSES, ip6_addr_writer }, |